Digital Risk Protection

Complete

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

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:

SchemaUsed ByFields
UpdateBrandAlertRequestPATCH /brand-alerts/{id}status (optional), takedown_status (optional)
UpdateMentionStatusRequestPATCH /dark-web/{id}status (required)
UpdateExecutiveThreatRequestPATCH /executive-threats/{id}status (required)
SearchRequestPOST /searchquery (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 & PathDescription
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 & PathDescription
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 & PathDescription
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 & PathDescription
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 & PathDescription
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

RouteFileDescription
/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

FieldTypeDescription
idstrStable ID (e.g. "ba-001" in seed, UUID4 in production)
alert_typestrdomain_impersonation | typosquat | phishing_kit | logo_abuse | social_impersonation
severitystrcritical | high | medium | low
titlestrShort description of the threat (e.g. "Lookalike domain: threatops-secure.com")
descriptionstrFull narrative description including observed behavior
sourcestrDetection source: urlscan | phishtank | certstream | social_monitor
detected_urlstrFull URL of the detected threat (empty string if domain-only detection)
detected_domainstrRegistered domain causing the alert
similarity_scorefloatString similarity to brand (0.0 to 1.0; seed data ranges 0.78-0.94)
statusstrnew | investigating | takedown_requested | resolved | false_positive
takedown_statusstrnone | requested | in_progress | completed | failed
evidencelist[str]Evidence artifact filenames (screenshots, WHOIS records, SSL cert details)
created_atstr (ISO 8601)Detection timestamp (defaults to datetime.utcnow().isoformat())

DarkWebMention

FieldTypeDescription
idstrStable ID (e.g. "dw-001")
source_typestrpaste_site | forum | marketplace | telegram | onion_site
source_namestrHuman-readable source name (e.g. "BreachForums", "Russian Market")
titlestrSummary title of the detected mention
content_previewstrRedacted content excerpt (sensitive details masked with [REDACTED] and block chars)
matched_keywordslist[str]Brand keywords that triggered the detection
threat_levelstrcritical | high | medium | low
categorystrcredentials | data_leak | exploit | access_sale | pii
statusstrnew | reviewed | actionable | false_positive | archived
detected_atstr (ISO 8601)Detection timestamp
url_hashstrMD5 hash of the original URL (used for deduplication without storing the live onion/paste URL)

CredentialLeak

FieldTypeDescription
idstrStable ID (e.g. "cl-001")
sourcestrbreach_db | paste_site | dark_web | combo_list
breach_namestrName of the originating breach or dataset (e.g. "MegaCorp Data Breach 2026")
email_domainstrAffected email domain (e.g. "threatops.com")
exposed_countintTotal number of accounts exposed in this leak
credential_typeslist[str]Types of data exposed: email | password_hash | plaintext | personal_info
breach_datestrDate the original breach occurred (YYYY-MM-DD)
detected_atstr (ISO 8601)Timestamp when ThreatOps DRP detected the leak
severitystrcritical | high | medium | low (plaintext credentials = critical)
statusstrnew | notified | password_reset | resolved
affected_accountslist[str]List of affected email addresses (returned on detail endpoint, not in list)

ExecutiveThreat

FieldTypeDescription
idstrStable ID (e.g. "et-001")
executive_namestrFull name of the targeted executive
executive_titlestrTitle (CEO, CTO, CISO, VP Sales, etc.)
threat_typestrimpersonation | doxxing | targeted_phishing | social_engineering
sourcestrWhere the threat was detected (e.g. "LinkedIn / Twitter", "Email gateway logs")
descriptionstrDetailed narrative of the threat including observed TTPs
severitystrcritical | high | medium | low
statusstrnew | investigating | mitigated | resolved
detected_atstr (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:

ComponentMax ContributionWeight Basis
Brand Risk30 pointsSum of severity weights for all non-resolved/non-FP brand alerts
Dark Web Risk30 pointsSum of severity weights for all non-archived/non-FP dark web mentions
Credential Risk25 pointsSum of severity weights plus exposed_count contribution (capped at 10 per leak) for unresolved leaks
Executive Risk15 pointsSum 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:

Brand Risk (max 30)
Dark Web Risk (max 30)
Credential Risk (max 25)
Executive Risk (max 15)

Prerequisites

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

Dark Web Tab

Credential Leaks Tab

Executive Threats Tab

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.