Digital Risk Protection
Overview
Digital Risk Protection (DRP) extends ThreatOps beyond the perimeter. While the core SOC platform monitors internal telemetry from SIEMs, DRP continuously scans the external threat surface: lookalike domains registering against brand names, dark web forums selling customer data, credential combo lists containing employee emails, and social media accounts impersonating executives.
For SOCaaS customers, especially those in regulated industries, external risk is just as consequential as internal. A phishing kit mimicking your customer portal can exfiltrate credentials before a single SIEM alert fires. Credential leaks in third-party breaches expose employee accounts that are later used in targeted intrusions. This module provides a unified dashboard covering all four external risk categories, a composite risk score, and workflow actions (status updates, takedown requests, password resets) that analysts can execute directly from the UI.
Architecture note: Like the Blog module, the DRP service uses in-memory dataclass collections seeded with realistic mock data rather than database-backed ORM models. The service is a singleton (digital_risk_service = DigitalRiskService()) instantiated once at module load. A production iteration would replace the in-memory dicts with async database queries against a dedicated DRP schema.
What Was Proposed
- Brand monitoring covering domain impersonation (lookalike domains, typosquats), phishing kit detection, logo abuse, and social media impersonation
- Dark web intelligence monitoring across paste sites, forums, dark web marketplaces, Telegram channels, and onion sites for mentions of brand keywords
- Credential leak detection identifying company email domains appearing in breach databases, combo lists, stealer log exports, and paste sites
- Executive threat monitoring for impersonation, doxxing, spear-phishing, and voice/deepfake social engineering campaigns targeting named executives
- A composite digital risk score (0-100) with component breakdown across all four categories
- Analyst workflow actions: status transitions, takedown request initiation, password reset triggering for affected accounts
- Cross-category search to quickly find all mentions of a keyword across brand alerts, dark web, credentials, and executive threats
- Dashboard statistics surfacing counts, critical-severity items, and active remediations at a glance
What's Built
| Brand alerts: list with severity/status/type filters, get by ID, update status/takedown, initiate takedown | ✓ Complete |
| Dark web mentions: list with source_type/category/status/threat_level filters, update status | ✓ Complete |
| Credential leaks: list with status/severity filters, get details, trigger password reset action | ✓ Complete |
| Executive threats: list with status filter, update status | ✓ Complete |
| Composite risk score engine with 4-component weighted calculation (0-100) | ✓ Complete |
| Aggregated stats endpoint covering all four categories plus risk score | ✓ Complete |
| Cross-category keyword search endpoint | ✓ Complete |
| Seed data: 7 brand alerts, 8 dark web mentions, 5 credential leaks, 4 executive threats | ✓ Complete |
| Full frontend dashboard with 4-tab navigation, stats grid, risk score meter, per-category tables with actions | ✓ Complete |
| Frontend takedown request button, password reset trigger, status update dropdowns | ✓ Complete |
| Frontend mock data fallback when API is unavailable | ✓ Complete |
| Live OSINT integrations (CertStream, PhishTank, URLScan, BreachDirectory) | ○ Future |
| Automated takedown workflow via registrar and hosting provider APIs | ○ Future |
| Postgres-backed persistence and historical trend tracking | ○ Future |
| Email/Slack notifications for new critical DRP alerts | ○ Future |
Architecture
Service Layer
File: platform/api/app/services/digital_risk.py
The DigitalRiskService class owns all business logic. It is instantiated as a module-level singleton and injected into the router via direct import. Four in-memory dictionaries keyed by entity ID back each protection category:
_brand_alerts: Dict[str, BrandAlert] _dark_web_mentions: Dict[str, DarkWebMention] _credential_leaks: Dict[str, CredentialLeak] _executive_threats: Dict[str, ExecutiveThreat]
All four are populated by _init_mock_data() called from __init__. The service logs initialization counts at INFO level. Each public method on the service is a synchronous Python function (no async needed since there are no I/O operations against external systems in the current build).
API Router
File: platform/api/app/routers/digital_risk.py
Router prefix: /api/v1/digital-risk — FastAPI tag: digital-risk-protection
The router imports digital_risk_service from the service module and delegates all logic to it. Route handlers are thin: they call the service, check for None returns (indicating not-found), and wrap responses in dicts with a top-level key naming the collection (e.g. {"brand_alerts": [...], "total": N}).
Pydantic request models defined in the router file handle mutation payloads:
| Schema | Used By | Fields |
|---|---|---|
UpdateBrandAlertRequest | PATCH /brand-alerts/{id} | status (optional), takedown_status (optional) |
UpdateMentionStatusRequest | PATCH /dark-web/{id} | status (required) |
UpdateExecutiveThreatRequest | PATCH /executive-threats/{id} | status (required) |
SearchRequest | POST /search | query (str, 1-500 chars) |
Frontend
File: platform/frontend/src/app/digital-risk/page.tsx
A single large "use client" page component using useState and useEffect. On mount it fires four parallel API calls (stats, brand alerts, dark web, credentials, executive threats) and falls back to inline mock data if any call fails. The page renders a tabbed navigation across the four protection categories plus an overview tab with the risk score meter and stats grid.
Four Protection Pillars
Brand Alerts
Monitors for domain impersonation, typosquats, phishing kits, logo abuse, and social impersonation. Sources: CertStream (new cert issuances), PhishTank, URLScan, and social monitor. Supports takedown workflow with status tracking.
Dark Web Mentions
Tracks keyword matches across paste sites (Pastebin, PrivateBin), forums (BreachForums, XSS.is), dark web marketplaces (Russian Market, Genesis Market), Telegram channels, and onion sites. Content is redacted in previews.
Credential Leaks
Identifies company email domains in breach databases, paste sites, dark web data, and stealer/combo lists. Tracks exposed account counts, credential types (plaintext, hash, PII), and provides password reset trigger action.
Executive Threats
Monitors named executives for impersonation (fake social profiles), doxxing (personal info leaks), targeted spear-phishing, and voice/deepfake social engineering. Tracks by executive name and title.
API Endpoints
Brand Alerts
| Method & Path | Description |
|---|---|
| GET /api/v1/digital-risk/brand-alerts | List brand alerts. Filters: severity (critical/high/medium/low), status (new/investigating/takedown_requested/resolved/false_positive), alert_type (domain_impersonation/typosquat/phishing_kit/logo_abuse/social_impersonation). Sorted by created_at desc. |
| GET /api/v1/digital-risk/brand-alerts/{alert_id} | Get single brand alert by ID. Returns 404 if not found. |
| PATCH /api/v1/digital-risk/brand-alerts/{alert_id} | Update brand alert status and/or takedown_status. Both fields are optional; supply only what needs changing. |
| POST /api/v1/digital-risk/brand-alerts/{alert_id}/takedown | Initiate takedown process. Sets status=takedown_requested, takedown_status=requested. Returns confirmation with estimated completion window (24-72 hours). |
Dark Web Mentions
| Method & Path | Description |
|---|---|
| GET /api/v1/digital-risk/dark-web | List dark web mentions. Filters: source_type (paste_site/forum/marketplace/telegram/onion_site), category (credentials/data_leak/exploit/access_sale/pii), status (new/reviewed/actionable/false_positive/archived), threat_level (critical/high/medium/low). |
| PATCH /api/v1/digital-risk/dark-web/{mention_id} | Update dark web mention status (new/reviewed/actionable/false_positive/archived). Returns 404 if mention_id unknown. |
Credential Leaks
| Method & Path | Description |
|---|---|
| GET /api/v1/digital-risk/credential-leaks | List credential leaks. Filters: status (new/notified/password_reset/resolved), severity (critical/high/medium/low). Sorted by detected_at desc. |
| GET /api/v1/digital-risk/credential-leaks/{leak_id} | Get full details of a credential leak including affected_accounts list. Returns 404 if not found. |
| POST /api/v1/digital-risk/credential-leaks/{leak_id}/reset | Trigger password reset for all affected accounts in the leak. Sets status=password_reset. Returns count of affected accounts and confirmation message. |
Executive Threats
| Method & Path | Description |
|---|---|
| GET /api/v1/digital-risk/executive-threats | List executive threats. Filter: status (new/investigating/mitigated/resolved). Sorted by detected_at desc. |
| PATCH /api/v1/digital-risk/executive-threats/{threat_id} | Update executive threat status. Returns 404 if threat_id unknown. |
Statistics, Risk Score, and Search
| Method & Path | Description |
|---|---|
| GET /api/v1/digital-risk/stats | Aggregate statistics for all four categories plus embedded risk_score object. Returns counts for total, critical, active/actionable/unresolved items, and takedowns in progress. |
| GET /api/v1/digital-risk/risk-score | Composite risk score (0-100) with level (critical/high/medium/low) and component breakdown: brand_risk (0-30), dark_web_risk (0-30), credential_risk (0-25), executive_risk (0-15). |
| POST /api/v1/digital-risk/search | Cross-category keyword search. Body: {"query": "string"}. Searches title, description/content, domain, keyword lists, and executive names. Returns results grouped by category with a total count. |
Example: Get Risk Score
GET /api/v1/digital-risk/risk-score
# Response (200):
{
"score": 78,
"level": "critical",
"components": {
"brand_risk": 25,
"dark_web_risk": 28,
"credential_risk": 15,
"executive_risk": 10
}
}
Example: Initiate Takedown
POST /api/v1/digital-risk/brand-alerts/ba-001/takedown
# Response (200):
{
"alert_id": "ba-001",
"takedown_status": "requested",
"message": "Takedown request submitted for threatops-secure.com",
"estimated_completion": "24-72 hours"
}
Example: Cross-Category Search
POST /api/v1/digital-risk/search
Content-Type: application/json
{"query": "threatops"}
# Response (200):
{
"query": "threatops",
"total_results": 12,
"results": {
"brand_alerts": [ {...}, {...} ],
"dark_web_mentions": [ {...}, {...} ],
"credential_leaks": [ {...} ],
"executive_threats": [ {...} ]
}
}
Frontend Routes
| Route | File | Description |
|---|---|---|
| /digital-risk | src/app/digital-risk/page.tsx |
Full DRP dashboard. Tabbed navigation: Overview (risk score + stats), Brand Alerts, Dark Web, Credential Leaks, Executive Threats. Fetches from all DRP API endpoints on mount. |
Data Models
All four entity types are Python dataclasses defined in platform/api/app/services/digital_risk.py. Each implements a to_dict() method using dataclasses.asdict() for JSON serialization.
BrandAlert
| Field | Type | Description |
|---|---|---|
id | str | Stable ID (e.g. "ba-001" in seed, UUID4 in production) |
alert_type | str | domain_impersonation | typosquat | phishing_kit | logo_abuse | social_impersonation |
severity | str | critical | high | medium | low |
title | str | Short description of the threat (e.g. "Lookalike domain: threatops-secure.com") |
description | str | Full narrative description including observed behavior |
source | str | Detection source: urlscan | phishtank | certstream | social_monitor |
detected_url | str | Full URL of the detected threat (empty string if domain-only detection) |
detected_domain | str | Registered domain causing the alert |
similarity_score | float | String similarity to brand (0.0 to 1.0; seed data ranges 0.78-0.94) |
status | str | new | investigating | takedown_requested | resolved | false_positive |
takedown_status | str | none | requested | in_progress | completed | failed |
evidence | list[str] | Evidence artifact filenames (screenshots, WHOIS records, SSL cert details) |
created_at | str (ISO 8601) | Detection timestamp (defaults to datetime.utcnow().isoformat()) |
DarkWebMention
| Field | Type | Description |
|---|---|---|
id | str | Stable ID (e.g. "dw-001") |
source_type | str | paste_site | forum | marketplace | telegram | onion_site |
source_name | str | Human-readable source name (e.g. "BreachForums", "Russian Market") |
title | str | Summary title of the detected mention |
content_preview | str | Redacted content excerpt (sensitive details masked with [REDACTED] and block chars) |
matched_keywords | list[str] | Brand keywords that triggered the detection |
threat_level | str | critical | high | medium | low |
category | str | credentials | data_leak | exploit | access_sale | pii |
status | str | new | reviewed | actionable | false_positive | archived |
detected_at | str (ISO 8601) | Detection timestamp |
url_hash | str | MD5 hash of the original URL (used for deduplication without storing the live onion/paste URL) |
CredentialLeak
| Field | Type | Description |
|---|---|---|
id | str | Stable ID (e.g. "cl-001") |
source | str | breach_db | paste_site | dark_web | combo_list |
breach_name | str | Name of the originating breach or dataset (e.g. "MegaCorp Data Breach 2026") |
email_domain | str | Affected email domain (e.g. "threatops.com") |
exposed_count | int | Total number of accounts exposed in this leak |
credential_types | list[str] | Types of data exposed: email | password_hash | plaintext | personal_info |
breach_date | str | Date the original breach occurred (YYYY-MM-DD) |
detected_at | str (ISO 8601) | Timestamp when ThreatOps DRP detected the leak |
severity | str | critical | high | medium | low (plaintext credentials = critical) |
status | str | new | notified | password_reset | resolved |
affected_accounts | list[str] | List of affected email addresses (returned on detail endpoint, not in list) |
ExecutiveThreat
| Field | Type | Description |
|---|---|---|
id | str | Stable ID (e.g. "et-001") |
executive_name | str | Full name of the targeted executive |
executive_title | str | Title (CEO, CTO, CISO, VP Sales, etc.) |
threat_type | str | impersonation | doxxing | targeted_phishing | social_engineering |
source | str | Where the threat was detected (e.g. "LinkedIn / Twitter", "Email gateway logs") |
description | str | Detailed narrative of the threat including observed TTPs |
severity | str | critical | high | medium | low |
status | str | new | investigating | mitigated | resolved |
detected_at | str (ISO 8601) | Detection timestamp |
Risk Score Engine
The get_risk_score() method in DigitalRiskService computes a composite digital risk score from 0 to 100 using a weighted multi-component model:
| Component | Max Contribution | Weight Basis |
|---|---|---|
| Brand Risk | 30 points | Sum of severity weights for all non-resolved/non-FP brand alerts |
| Dark Web Risk | 30 points | Sum of severity weights for all non-archived/non-FP dark web mentions |
| Credential Risk | 25 points | Sum of severity weights plus exposed_count contribution (capped at 10 per leak) for unresolved leaks |
| Executive Risk | 15 points | Sum of severity weights for active (non-resolved/non-mitigated) executive threats |
Severity weights applied: critical = 25 pts, high = 15 pts, medium = 8 pts, low = 3 pts. Each component is individually capped at its maximum before summing. Final score is capped at 100.
Score-to-level mapping: ≥75 = critical, ≥50 = high, ≥25 = medium, <25 = low.
Component Weight Visualization
Max contribution of each component to the 100-point total:
Prerequisites
- FastAPI —
APIRouter,HTTPException,Query. Already present in platform requirements. - Pydantic v2 —
BaseModel,Fieldfor request schema validation in the router. - Python stdlib —
dataclasses(dataclass, field, asdict),datetime,logging,typing,uuid. No third-party packages beyond FastAPI/Pydantic. - No database — In-memory dataclass collections. No ORM models, no migrations, no database connection required.
- No external OSINT APIs — Current build uses seeded mock data. Future live integrations would add CertStream (WebSocket), PhishTank (API key), URLScan (API key), and BreachDirectory as optional dependencies.
- Frontend — Requires
@/lib/api-client, Next.js App Router, Lucide React icons (Shield, Globe, Eye, KeyRound, UserX, AlertTriangle, Search, ExternalLink, ArrowDownCircle, RefreshCw, Filter, TrendingUp, Loader2, CheckCircle2, XCircle, Clock, MessageSquare, ShoppingCart, FileText, Send, Ban, Fingerprint, Link2, Users).
To register the router with the FastAPI application, ensure the following is present in platform/api/app/main.py:
from app.routers import digital_risk app.include_router(digital_risk.router)
UI Layout
Page Header
Shield icon + "Digital Risk Protection" H1 title, subtitle describing the four monitoring areas. Stats grid: 4 stat cards displaying Brand Alerts active count, Dark Web Mentions actionable count, Credential Leaks (total exposed accounts), and Executive Threats active count. Risk Score meter displays the composite 0-100 score with color-coded level badge and component breakdown bars.
Tab Navigation
Five tabs across the top of the content area: Overview, Brand Alerts, Dark Web, Credential Leaks, Executive Threats. Each tab shows a count badge. Clicking a tab switches the main content panel without a page reload.
Brand Alerts Tab
- Filter bar with severity and status dropdowns
- Table columns: Alert Type (icon + label), Domain / URL, Similarity score (percentage), Status badge, Takedown Status badge, Source, Age
- Action buttons per row: "Request Takedown" (calls POST /brand-alerts/{id}/takedown), status update dropdown (PATCH)
- Color-coded severity badges: red=critical, orange=high, yellow=medium, slate=low
Dark Web Tab
- Filter bar with source type and category dropdowns
- Table columns: Source type icon, Source name, Title, Content preview (truncated), Matched keywords chips, Threat level badge, Category badge, Status, Detected time
- Status update action per row (PATCH /dark-web/{id})
- Content preview text shows "[REDACTED]" and block character masking for sensitive content
Credential Leaks Tab
- Table columns: Breach name, Email domain, Exposed count (bold), Credential types chips, Source, Breach date, Status badge, Severity badge
- Per-row action: "Trigger Password Reset" button (POST /credential-leaks/{id}/reset) disabled once status = password_reset or resolved
- Expandable row or detail view shows
affected_accountslist (fetched from GET /credential-leaks/{id})
Executive Threats Tab
- Card-style layout (not table) for executive threats, showing executive name, title, threat type icon, severity badge, source, detected date, and full description
- Status update dropdown per card (new / investigating / mitigated / resolved)
- Critical threats highlighted with red border accent on card
Mock data fallback: The frontend defines complete mockStats, mockBrandAlerts, mockDarkWebMentions, mockCredentialLeaks, and mockExecutiveThreats constants. If any API call fails (e.g. API is offline), the frontend falls back to these values and shows a subtle warning. This ensures the UI is always demonstrable regardless of backend availability.