Skip to content

rksharma-owg/Exposure_Sentinel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🛡️ Exposure Sentinel

Authorized organizational exposure-monitoring agent for the DeHashed Search API

CI Python 3.12 FastAPI License: Apache 2.0 Tests Code Style: Ruff

A production-ready, CLI-first Python security tool that monitors credential-exposure records for assets your organization has explicitly verified. Integrates with the official DeHashed Search API and Data Wells API.

Features · Quick Start · CLI Reference · Exports · Security · Architecture · Deploy


📋 Table of Contents


🎯 Overview

Exposure Sentinel is an authorized exposure-monitoring agent. It is not a public person-search tool. Every query is restricted to:

  1. Domains your organization has verified via DNS TXT challenge
  2. Emails explicitly added to your authorized investigation list (with a mandatory ticket #)
  3. An investigation reason required for every single lookup

The tool sits between your security team and the DeHashed API, enforcing scope, redacting sensitive fields, recording an immutable audit trail, and producing professional reports in 7 formats.

Why Exposure Sentinel?

Without Exposure Sentinel With Exposure Sentinel
Analysts paste API keys into chat scripts Keys live in a secrets manager, never exposed
No audit trail of who searched what Every action recorded immutably
Results often leak plaintext passwords Redaction layer masks all sensitive fields
No scope enforcement — any email searchable Hard scope filter on verified domains + authorized emails
Reports ad-hoc, copy-paste, error-prone 7 export formats: JSON, CSV, HTML, PDF, XLSX, MD, SARIF
No severity prioritization Multi-factor severity scoring (credentials, MFA, recency, …)

✨ Features

🔐 Secure API Key Lifecycle

  • Masked input via getpass in CLI, password field in web UI
  • Real authenticated validation against api.dehashed.com — never simulated
  • 8 pluggable backends: local encrypted, env, Docker secret, K8s secret, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault
  • Masked display onlydh_****8F2A
  • Never logged, returned by API, persisted to PostgreSQL, stored in browser, committed to source, or sent to the LLM agent
  • Sanitized error categories: invalid_api_key, unauthorized, subscription_or_quota, rate_limit_exceeded, network_failure, dehashed_service_unavailable, bad_query, unknown
  • Fail-closed production mode — 503 if key/DB/secrets unavailable

🌐 Domain Verification & Scope Enforcement

  • DNS TXT challenge ownership proof (32-byte random token)
  • Verified-domain allowlist — DeHashed results are filtered against it
  • Authorized investigation email list — each entry requires a ticket # / reason
  • Query injection prevention — DeHashed DSL validated against field allow-list

📊 Exposure Scanning & Severity Scoring

  • One-command scans against your verified scope
  • Diff vs. previous scanis_new flag on every record
  • Severity score (0-100) combining:
    • Plaintext password exposure (+40)
    • Password hash exposure (+25)
    • Privileged account status (+15)
    • MFA disabled (+10)
    • Breach recency (+3 to +15)
    • Distinct data wells (+2 each, max 5)
    • Active account status (+10 / -5)
  • Severity buckets: CRITICAL ≥70, HIGH ≥45, MEDIUM ≥25, LOW ≥10, INFO <10
  • Auto-incident creation for CRITICAL/HIGH findings with remediation recommendations

📤 Multi-Format Export

  • 7 formats: JSON, CSV, HTML (styled), PDF (printable), XLSX (multi-sheet), Markdown, SARIF 2.1.0
  • All exports redacted — no plaintext passwords or hashes ever reach a file
  • Use cases: programmatic consumption, Excel/Sheets import, browser view, printable report, SIEM ingestion, Jira/Confluence tickets

🛡️ Defense-in-Depth Security

  • Envelope encryption at rest (KEK + DEK via Fernet / AES-128-CBC + HMAC-SHA256)
  • RBACadmin, analyst, viewer roles
  • Immutable audit log — every key lifecycle event recorded with masked + fingerprinted representation
  • Redaction layer — passwords, hashes, SSNs, credit cards, phone numbers masked before any output
  • Rate limiting — per-user and per-org (Redis-backed in server mode)
  • Secret-scrubbing log filter — defense-in-depth against accidental key leakage
  • Query allow-list — blocks SQL-injection-style payloads against DeHashed DSL

🔌 Integrations

  • Slack — webhook alerts with color-coded severity
  • Microsoft Teams — MessageCard alerts
  • Jira — auto-create issues
  • ServiceNow — auto-create incidents
  • Splunk — HEC ingestion
  • Microsoft Sentinel — Log Analytics ingestion

🤖 MCP / LLM Agent (Optional)

  • 15 MCP tools for LLM agents
  • LLM never receives: API key, plaintext passwords, raw DeHashed responses, full PII
  • Deterministic auth/policy/redaction layer between DeHashed and the agent

🚀 Quick Start

Prerequisites

  • Python 3.12+
  • A DeHashed subscription and API key
  • (Optional) PostgreSQL + Redis for server mode; SQLite works for standalone CLI

Install

git clone https://github.com/rksharma-owg/Exposure_Sentinel.git
cd Exposure_Sentinel/backend
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

First-Run Secure Setup

$ exposure-sentinel setup

╭────────────────────────────────────────────────────────╮
│ Exposure Sentinel                                      │
│ Authorized organizational exposure monitoring · v1.0.0 │
╰────────────────────────────────────────────────────────╯

ℹ Checking for existing DeHashed API key…
ℹ No DeHashed API key is configured.
ℹ The key will be validated with a real authenticated request to DeHashed.
ℹ It will never be printed back, logged, or shared with the LLM agent.

Enter your DeHashed API key: ************
ℹ Validating DeHashed API credentials…
✓ Connection successful.
✓ API key stored securely.
✓ DeHashed monitoring is now enabled.
ℹ Configured key: dh_****cdef

Register & Verify a Domain

$ exposure-sentinel domains add example.com

✓ Domain example.com registered (pending verification).
ℹ Publish this DNS TXT record:
ℹ   Name:  exposure-sentinel-verify.example.com
ℹ   Value: exposure-sentinel-verify=AbC123...XYZ789
ℹ Then run: exposure-sentinel domains verify 1

# After DNS propagates:
$ exposure-sentinel domains verify 1
✓ Domain example.com verified!

Run Your First Scan

$ exposure-sentinel scan --reason "Quarterly audit TICKET-42"

✓ Scan c547e008-6106-4b3e-9de4-7aceb5ec3294 completed.
ℹ   Records found: 1
ℹ   New records:   1
ℹ   Status:        succeeded
ℹ   Run exposure-sentinel export c547e008-6106-4b3e-9de4-7aceb5ec3294 --format pdf --out report.pdf to export.

View & Export Results

# View in terminal (rich tables, color-coded severity)
exposure-sentinel history
exposure-sentinel records c547e008

# Export to any of 7 formats
exposure-sentinel export c547e008 --format pdf --out report.pdf
exposure-sentinel export c547e008 --format xlsx --out report.xlsx
exposure-sentinel export c547e008 --format sarif --out scan.sarif
exposure-sentinel export c547e008 --format markdown --out scan.md

📚 CLI Reference

Top-level commands

Command Purpose
setup First-run secure API-key onboarding wizard (masked input via getpass)
status Show masked configuration status
test Run a real authenticated request to DeHashed
search Search exposure records for a verified domain or authorized email
scan Run an exposure scan against the verified scope
history List recent scans
records <scan-id> List exposure records from a scan
incidents [scan-id] List incidents and remediation recommendations
audit View immutable audit log
export <scan-id> Export scan results to a file
server (Optional) Run the FastAPI server + web UI
version Show version
keys Sub-app: API-key lifecycle management
domains Sub-app: domain registration & verification
emails Sub-app: authorized investigation emails
config Sub-app: view / edit CLI configuration
integrations Sub-app: integration adapters

keys sub-app

exposure-sentinel keys list       # Show masked API key metadata
exposure-sentinel keys add        # Add a new key (alias for setup)
exposure-sentinel keys replace    # Replace the current key
exposure-sentinel keys remove     # Remove key — disables all scans immediately
exposure-sentinel keys test       # Alias for `test`

domains sub-app

exposure-sentinel domains list
exposure-sentinel domains add example.com
exposure-sentinel domains verify 1
exposure-sentinel domains remove 1

emails sub-app

exposure-sentinel emails list
exposure-sentinel emails add alice@example.com --reason "TICKET-42 investigation"
exposure-sentinel emails remove alice@example.com

search command

# Search by verified domain
exposure-sentinel search --domain example.com --reason "TICKET-42"

# Search by authorized email
exposure-sentinel search --email alice@example.com --reason "TICKET-42"

# Limit results and export immediately
exposure-sentinel search --domain example.com --reason "TICKET-42" \
  --size 50 --export pdf --out search.pdf

scan command

# Full scan of verified scope
exposure-sentinel scan --reason "Quarterly audit"

# Restrict to a single domain
exposure-sentinel scan --reason "TICKET-99" --domain example.com

export command

exposure-sentinel export <scan-id> --format <fmt> --out <path>

config sub-app

exposure-sentinel config show
exposure-sentinel config set mode standalone       # standalone | server | embedded
exposure-sentinel config set server_url https://es.internal.example.com
exposure-sentinel config set org_id my-org

integrations sub-app

exposure-sentinel integrations list   # Show configured adapters
exposure-sentinel integrations test   # Send a test alert

📤 Export Formats

Format Flag Use Case Sample Output
JSON --format json Programmatic consumption, API integration Structured JSON with scan, records, incidents
CSV --format csv Excel/Sheets import, data analysis Flat one-row-per-record CSV
HTML --format html Browser-openable styled report Self-contained HTML with severity color-coding
PDF --format pdf Printable report for sharing Landscape PDF with tables and severity badges
XLSX --format xlsx Multi-sheet Excel workbook Summary + Records + Incidents sheets
Markdown --format markdown Jira / Confluence / GitHub tickets Markdown with tables and badges
SARIF --format sarif SIEM / code-scanning consumers SARIF 2.1.0 compliant JSON

Example: Generate all formats for a scan

SCAN_ID=c547e008-6106-4b3e-9de4-7aceb5ec3294
mkdir -p reports
for fmt in json csv html pdf xlsx markdown sarif; do
  exposure-sentinel export $SCAN_ID --format $fmt --out reports/scan.$fmt
done

All exports are redacted — passwords become [REDACTED:password], hashes become hash:abc123…, SSNs / credit cards / phone numbers are scrubbed. This is verified by automated tests.


🔒 Security Guarantees

The DeHashed API key is NEVER:

❌ Never ✓ Instead
Printed to terminal Only dh_****8F2A masked form shown
Logged (any level) Secret-scrubbing log filter as defense-in-depth
Returned by any API endpoint Status endpoints return only masked metadata
Persisted to PostgreSQL Only masked + fingerprinted reference row
Stored in browser local storage Web UI receives only masked form
Committed to source code .gitignore excludes .env, *.key, secrets.enc
Sent to the LLM agent MCP tools return only masked metadata
Stored in plain text on disk Envelope-encrypted (KEK + DEK via Fernet)

Stored only in approved secrets managers:

  • Local encrypted (default for CLI): Fernet envelope-encrypted file at ~/.exposure-sentinel/secrets.enc
  • Docker secret: /run/secrets/dehashed_api_key
  • Kubernetes secret: mounted file at /etc/secrets/dehashed_api_key
  • AWS Secrets Manager: /exposure-sentinel/dehashed_api_key
  • Azure Key Vault: stored as a Key Vault secret
  • GCP Secret Manager: projects/$PROJECT/secrets/dehashed_api_key
  • HashiCorp Vault: KV v2 at secret/exposure-sentinel/dehashed_api_key

RBAC roles

Role Can do
admin Configure / replace / remove API keys, register domains, run scans, view everything
analyst Run scans, view results, create incidents, manage authorized emails
viewer Read-only access to dashboards and historical scan results

Audit log

Every sensitive operation records an immutable audit event with:

  • Timestamp, actor (user ID + email + roles)
  • Event type (api_key.created, api_key.removed, scan.started, rbac.denied, etc.)
  • Outcome (success / failure / denied)
  • For key events: masked key + truncated SHA-256 fingerprint (never the key value)
  • Request ID, IP address, user agent (when available)

🏗 Architecture

                   ┌─────────────────────────────────────────┐
                   │             Analyst / Admin             │
                   └────────┬────────────────────────┬───────┘
                            │ CLI (Typer + Rich)     │ HTTPS (optional)
                            │ PRIMARY INTERFACE      │
                            ▼                        ▼
                ┌──────────────────────┐   ┌──────────────────────┐
                │ exposure-sentinel    │   │  React + TS frontend │
                │ (Typer + Rich CLI)   │   │  (optional server)   │
                └──────────┬───────────┘   └──────────┬───────────┘
                           │                          │
                           ▼                          ▼
                ┌────────────────────────────────────────────┐
                │              FastAPI backend                │
                │  ┌─────────┐  ┌──────────┐  ┌───────────┐  │
                │  │  API    │  │  Agent   │  │   MCP     │  │
                │  │ routes  │  │  route   │  │  server   │  │
                │  └────┬────┘  └────┬─────┘  └─────┬─────┘  │
                │       └────────────┴──────────────┘        │
                │                Service layer               │
                │  ApiKeyService · DomainVerification        │
                │  ExposureScanner · SeverityEngine          │
                │  DehashedClient (ONLY caller of DeHashed)  │
                │  Redaction + RBAC + rate-limit + audit     │
                └───────────┬─────────────────────────────────┘
                            │
              ┌─────────────┼─────────────────────┐
              ▼             ▼                     ▼
        ┌──────────┐  ┌──────────┐         ┌──────────────┐
        │ Secrets  │  │ Postgres │         │    Redis     │
        │ store    │  │ / SQLite │         │ (rate-limit, │
        │ (KEK-    │  │ (audit,  │         │  Celery)     │
        │ wrapped) │  │ scans)   │         │              │
        └────┬─────┘  └──────────┘         └──────────────┘
             │
             │ plaintext key lives in memory only during
             │ a single outbound request to DeHashed
             │
        ┌────┴──────┐  pluggable: local_encrypted | env | docker | k8s
        │ KMS/Vault │                | aws_sm | azure_kv | gcp_sm | vault
        └───────────┘

                ┌─────────────────────────────────────────┐
                │           Integrations (outbound)       │
                │ Slack · Teams · Jira · ServiceNow       │
                │ Splunk · Microsoft Sentinel             │
                └─────────────────────────────────────────┘

Trust boundaries

  1. User → Frontend: HTTPS. Password field for API key. Key held in browser memory only until submitted, then dropped.
  2. Frontend → Backend: HTTPS. JWT bearer auth. RBAC enforced on every write endpoint.
  3. Backend → DeHashed: HTTPS. Bearer key fetched per-request from secrets store, never cached in module-level state.
  4. Backend → Secrets store: in-process. Plaintext key returned only to the DehashedClient instance for one request.
  5. Backend → LLM agent (MCP): MCP handlers NEVER receive the key. Tool inputs are non-sensitive identifiers only. Outputs redacted + scope-filtered.

See docs/ARCHITECTURE.md for full details.


🔑 Secrets Backends

Select via EXPOSURE_SENTINEL_SECRETS_BACKEND env var:

Backend Use Case Example Config
local_encrypted Single-host / CLI (encrypted file at rest) deploy/secrets/.env.local_encrypted.example
env Dev only — never use in production deploy/secrets/.env.env.example
docker_secret Docker Swarm / Compose secrets deploy/secrets/.env.docker_secret.example
k8s_secret Kubernetes mounted secret deploy/secrets/.env.k8s_secret.example
aws_sm AWS Secrets Manager deploy/secrets/.env.aws_sm.example
azure_kv Azure Key Vault deploy/secrets/.env.azure_kv.example
gcp_sm Google Secret Manager deploy/secrets/.env.gcp_sm.example
vault HashiCorp Vault KV v2 deploy/secrets/.env.vault.example

🔌 Integrations

Each integration is independently toggleable via env vars / secrets manager references.

Integration Env Vars Auth Method
Slack EXPOSURE_SENTINEL_SLACK_WEBHOOK_URL Incoming Webhook
Microsoft Teams EXPOSURE_SENTINEL_TEAMS_WEBHOOK_URL Incoming Webhook
Jira EXPOSURE_SENTINEL_JIRA_BASE_URL + token ref Bearer token
ServiceNow EXPOSURE_SENTINEL_SERVICENOW_BASE_URL + token ref Bearer token
Splunk EXPOSURE_SENTINEL_SPLUNK_HEC_URL + token ref HEC token
Microsoft Sentinel EXPOSURE_SENTINEL_SENTINEL_WORKSPACE_ID + key ref HMAC-SHA256 shared key

Test your integrations:

exposure-sentinel integrations list   # Show which are configured
exposure-sentinel integrations test   # Send a test alert

🚢 Production Deployment

Docker Compose (reference deployment)

# Generate a strong KEK and JWT secret
export EXPOSURE_SENTINEL_MASTER_KEK=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
export EXPOSURE_SENTINEL_JWT_SECRET=$(openssl rand -hex 32)

# Start everything
docker compose up -d

The compose file starts: PostgreSQL, Redis, backend, Celery worker, Celery beat, and the frontend. See docker-compose.yml.

Kubernetes

See deploy/k8s/deployment.yaml for a Deployment that mounts the DeHashed key as a Kubernetes secret and includes readiness/liveness probes that fail closed.

Required environment variables for production

Variable Purpose
EXPOSURE_SENTINEL_MASTER_KEK 32-byte url-safe base64 Fernet key (KEK)
EXPOSURE_SENTINEL_JWT_SECRET Long random secret for JWT signing
EXPOSURE_SENTINEL_SECRETS_BACKEND One of the backends above
EXPOSURE_SENTINEL_DATABASE_URL PostgreSQL async URL
EXPOSURE_SENTINEL_REDIS_URL Redis URL (for rate limiting + Celery)
EXPOSURE_SENTINEL_FAIL_CLOSED true (enforced in production anyway)

In production, the readiness probe (/readyz) returns 503 if the database, secrets backend, or API key is unavailable. The app fails closed rather than running unprotected.


🧪 Testing

cd backend
pytest -q

33/33 tests passing — unit + integration tests covering:

  • Secrets store & envelope encryption (5 tests)
  • Redaction layer (3 tests)
  • Severity scoring engine (3 tests)
  • DeHashed client query validation & error mapping (8 tests, mocked via respx)
  • Audit log integrity (2 tests)
  • Full onboarding flow integration (3 tests)
  • CLI end-to-end smoke test (9 tests)

All DeHashed API calls in the test suite are mocked via respx. No real network calls are made. Production mode never simulates a successful API connection.


📖 Documentation

  • 📐 Architecture — component diagram, trust boundaries, data model, lifecycle flows
  • 🛡️ Threat Model — STRIDE-style analysis with mitigations
  • 📚 OpenAPI spec — served at /api/openapi.json when running in server mode
  • 🔧 Secure config examples — in deploy/secrets/

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Run tests: cd backend && pytest -q
  4. Run linters: ruff check app && mypy app
  5. Commit with conventional commits: feat: ..., fix: ..., docs: ...
  6. Open a pull request

Security disclosures

Do NOT open a public GitHub issue for security vulnerabilities. Instead, email the maintainers privately. Include:

  • Description of the issue
  • Steps to reproduce
  • Affected versions
  • Suggested fix (if any)

📄 License

Apache License 2.0 — see LICENSE.


⚠️ Responsible Use

Exposure Sentinel is designed for authorized organizational monitoring only. You must:

  1. Only search domains you own or have written authorization to monitor
  2. Only search emails of employees / contractors who have consented to monitoring per your organization's policies and applicable law (e.g., GDPR, CCPA, employment contracts)
  3. Comply with DeHashed's Terms of Service
  4. Comply with all applicable laws in your jurisdiction

The maintainers are not responsible for misuse of this tool.


Built with security as the first priority.

Report a vulnerability · Open an issue · View the docs

About

Security-first credential exposure monitoring for verified organizational assets using the DeHashed API, with scope enforcement, redaction, audit logs, and reporting.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors