diff --git a/Dockerfile b/Dockerfile index fa254a3..7953d3c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,18 +21,26 @@ RUN apt-get update \ RUN useradd --create-home --shell /bin/bash appuser WORKDIR /app -# Copy source code and offline knowledge base +# ── Layer 1: Python dependencies (rebuilt only when requirements.txt changes) ── +# Splitting deps from source code maximises Docker cache reuse on source-only +# rebuilds, which is the most frequent case. +COPY requirements.txt ./ +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install -r requirements.txt + +# ── Layer 2: embedding model (rebuilt only when sentence-transformers changes) ── +RUN HF_HUB_DISABLE_SYMLINKS_WARNING=1 \ + python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')" + +# ── Layer 3: source code (changes often — pip install --no-deps is near-instant) ── COPY --chown=appuser:appuser threat_analysis/ ./threat_analysis/ COPY --chown=appuser:appuser config/ ./config/ COPY --chown=appuser:appuser threatModel_Template/ ./threatModel_Template/ COPY --chown=appuser:appuser pyproject.toml README.md LICENSE ./ -# Install as root (system-wide), make config.js writable by appuser, -# then pre-download the embedding model so it's available offline. -RUN pip install --no-cache-dir . && \ - chown appuser:appuser /usr/local/lib/python3.10/site-packages/threat_analysis/server/static/js/config.js && \ - HF_HUB_DISABLE_SYMLINKS_WARNING=1 \ - python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')" +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install --no-deps . && \ + chown appuser:appuser /usr/local/lib/python3.10/site-packages/threat_analysis/server/static/js/config.js # Fixed vector store path — mountable via Docker named volume. # secopstm --init-rag and the RAG service both respect this env var. @@ -41,6 +49,9 @@ ENV SECOPSTM_VECTOR_STORE_DIR=/app/rag/vector_store # Bind to all interfaces so the port is reachable from the Docker host ENV FLASK_HOST=0.0.0.0 +# Suppress LiteLLM warnings about AWS Bedrock/SageMaker (botocore not installed) +ENV LITELLM_LOG=ERROR + RUN mkdir -p /app/rag && chown appuser:appuser /app/rag USER appuser diff --git a/config/asset_types_community.yaml b/config/asset_types_community.yaml deleted file mode 100644 index 3aae880..0000000 --- a/config/asset_types_community.yaml +++ /dev/null @@ -1,220 +0,0 @@ -# SecOpsTM Community Asset Type Registry -# Each entry maps an asset type to MITRE ATT&CK platforms, tactics, and key techniques. -# -# To add a new type: see CONTRIBUTING_ASSET_TYPES.md -# -# Fields: -# description : human-readable description -# category : endpoint | network | server | ot | iot | cloud | identity -# platforms : MITRE ATT&CK platform names (exact case required) -# tactics : kebab-case tactic IDs, ordered by likelihood for this asset type -# key_techniques: high-value technique IDs to boost in scoring (empty list if none) -# fuzzy_matches : lowercase substrings used by _normalize_type() for loose matching -# icon_url : path relative to Flask static root, e.g. /static/resources/icons/firewall.svg -# Empty string means no icon; config_generator.py reads this to build ICON_MAPPING. - -asset_types: - - firewall: - description: "Network firewall or packet-filtering device" - category: network - platforms: [Network Devices] - tactics: [initial-access, defense-evasion, lateral-movement] - key_techniques: [T1190, T1600, T1599] - fuzzy_matches: [firewall, fw] - icon_url: "/static/resources/icons/firewall.svg" - - # NOTE: pki must appear before auth-server in this file. - # "certificate-authority" contains "auth" — pki's fuzzy_matches are checked first - # because _normalize_type() iterates entries in YAML declaration order. - pki: - description: "Public Key Infrastructure / Certificate Authority" - category: identity - platforms: [Windows] - tactics: [credential-access, privilege-escalation] - key_techniques: [] - fuzzy_matches: [pki, certificate] - icon_url: "" - - domain-controller: - description: "Active Directory Domain Controller" - category: identity - platforms: [Windows] - tactics: [credential-access, privilege-escalation, persistence, lateral-movement] - key_techniques: [T1550.002, T1558.003, T1003.006, T1558.001, T1003.001, T1207] - fuzzy_matches: [domain, dc] - icon_url: "" - - auth-server: - description: "Authentication server (RADIUS, LDAP, SSO)" - category: identity - platforms: [Windows, Linux] - tactics: [credential-access, privilege-escalation, initial-access] - key_techniques: [T1110, T1212, T1528, T1550] - fuzzy_matches: [auth] - icon_url: "" - - database: - description: "Relational or NoSQL database server" - category: server - platforms: [Windows, Linux] - tactics: [credential-access, collection, exfiltration] - key_techniques: [T1190, T1078, T1048, T1030] - fuzzy_matches: [database, db, sql] - icon_url: "/static/resources/icons/database.svg" - - web-server: - description: "HTTP/HTTPS web application server" - category: server - platforms: [Windows, Linux] - tactics: [initial-access, execution, persistence] - key_techniques: [] - fuzzy_matches: [web server, web-server, webserver] - icon_url: "/static/resources/icons/web-server.svg" - - api-gateway: - description: "API gateway or reverse proxy" - category: server - platforms: [Windows, Linux] - tactics: [initial-access, execution] - key_techniques: [] - fuzzy_matches: [api-gateway, api gateway] - icon_url: "/static/resources/icons/api-gateway.svg" - - file-server: - description: "File sharing server (SMB, NFS)" - category: server - platforms: [Windows, Linux] - tactics: [collection, lateral-movement, exfiltration] - key_techniques: [T1021.002, T1039, T1083, T1135] - fuzzy_matches: [file] - icon_url: "" - - mail-server: - description: "Email server (Exchange, Postfix)" - category: server - platforms: [Windows, Linux, Office Suite] - tactics: [initial-access, collection] - key_techniques: [T1566, T1114, T1071.003] - fuzzy_matches: [mail, email, smtp, exchange] - icon_url: "" - - management-server: - description: "IT management, jump server, or privileged access workstation" - category: server - platforms: [Windows, Linux] - tactics: [lateral-movement, privilege-escalation, execution] - key_techniques: [T1021.001, T1078, T1570] - fuzzy_matches: [jump, bastion, paw] - icon_url: "" - - workstation: - description: "Standard end-user workstation or laptop" - category: endpoint - platforms: [Windows] - tactics: [execution, persistence, privilege-escalation, credential-access] - key_techniques: [T1566.001, T1059.001, T1059.003, T1204.002, T1003.001, T1055] - fuzzy_matches: [workstation, laptop, desktop] - icon_url: "" - - load-balancer: - description: "Network load balancer" - category: network - platforms: [Network Devices, Linux] - tactics: [initial-access, defense-evasion] - key_techniques: [] - fuzzy_matches: [load-balancer, load balancer] - icon_url: "/static/resources/icons/load_balancer.svg" - - # NOTE: vpn.fuzzy_matches is intentionally empty. Any input containing "vpn" resolves to - # vpn-gateway (which has fuzzy_matches: [vpn]). The "vpn" entry is only reached when the - # asset type is passed as the exact string "vpn". - vpn: - description: "VPN client endpoint or concentrator" - category: network - platforms: [Network Devices] - tactics: [initial-access, credential-access] - key_techniques: [] - fuzzy_matches: [] - icon_url: "" - - vpn-gateway: - description: "VPN gateway or remote access server" - category: network - platforms: [Network Devices] - tactics: [initial-access, credential-access] - key_techniques: [T1078, T1133, T1110] - fuzzy_matches: [vpn] - icon_url: "" - - scada: - description: "SCADA system or Human-Machine Interface" - category: ot - platforms: [Windows, Linux] - tactics: [initial-access, execution, impact] - key_techniques: [T1021.001, T1133, T1078] - fuzzy_matches: [scada, hmi] - icon_url: "" - - plc: - description: "Programmable Logic Controller or RTU in ICS/OT environment" - category: ot - platforms: [Linux] - tactics: [impact, execution] - key_techniques: [T1565.001, T1498, T1489] - fuzzy_matches: [plc, controller, rtu] - icon_url: "" - - repository: - description: "Source code repository server (Git, SVN)" - category: server - platforms: [Linux] - tactics: [collection, exfiltration] - key_techniques: [] - fuzzy_matches: [repository, git, repo] - icon_url: "" - - cicd: - description: "CI/CD pipeline server (Jenkins, GitLab CI, GitHub Actions runner)" - category: server - platforms: [Linux] - tactics: [execution, persistence, lateral-movement] - key_techniques: [T1195.002, T1059, T1525] - fuzzy_matches: [cicd, ci_cd, pipeline, jenkins] - icon_url: "" - - backup: - description: "Backup server or storage appliance" - category: server - platforms: [Linux, Windows] - tactics: [collection, exfiltration, impact] - key_techniques: [] - fuzzy_matches: [backup] - icon_url: "" - - dns: - description: "DNS server" - category: server - platforms: [Windows, Linux] - tactics: [defense-evasion, lateral-movement, command-and-control] - key_techniques: [] - fuzzy_matches: [dns] - icon_url: "" - - siem: - description: "Security Information and Event Management system" - category: server - platforms: [Linux] - tactics: [defense-evasion, collection] - key_techniques: [] - fuzzy_matches: [siem, log] - icon_url: "" - - default: - description: "Fallback for unknown asset types" - category: server - platforms: [Windows, Linux] - tactics: [initial-access, execution, lateral-movement] - key_techniques: [] - fuzzy_matches: [] - icon_url: "" diff --git a/config/prompts.yaml b/config/prompts.yaml deleted file mode 100644 index cc7aa6d..0000000 --- a/config/prompts.yaml +++ /dev/null @@ -1,567 +0,0 @@ -# SecOpsTM — LLM Prompts Configuration -# -# This file controls the quality and domain focus of every AI-generated output. -# Edit prompts here without touching Python code. -# -# Conventions: -# <> — replaced by the tool at runtime before sending to the LLM -# {varname} — passed literally to the LLM (LLM-side placeholder, or LangChain variable) -# JSON examples use {varname} to indicate where the LLM should generate a value. - -# --------------------------------------------------------------------------- -# 1. DSL GENERATION -# Used by: AIService.generate_markdown_from_prompt / LiteLLMProvider.generate_markdown -# Purpose: Convert a natural-language description into the SecOpsTM Markdown DSL. -# --------------------------------------------------------------------------- -dsl_generation: - system: | - You are an expert cybersecurity architect specializing in STRIDE threat modeling. - Your task is to produce a complete, valid threat model in the SecOpsTM Markdown DSL. - - ## DSL Structure - - # Threat Model: [Name] - - ## Description - [Concise description of the system and its security context] - - ## Boundaries - - **[Name]**: color=[color], isTrusted=[true|false], description="[text]" - - ## Actors - - **[Name]**: boundary=[BoundaryName], description="[text]" - - ## Servers - - **[Name]**: boundary=[BoundaryName], description="[text]", is_public=[true|false] - - ## Data - - **[Name]**: description="[text]", classification=[public|internal|restricted|confidential|secret] - - ## Dataflows - - **[Name]**: from="[Source]", to="[Dest]", protocol=[HTTPS|HTTP|SQL|SSH|gRPC|AMQP|…], - is_authenticated=[true|false], is_encrypted=[true|false], description="[text]" - - ## Severity Multipliers - - **[ServerName]**: [1.0–3.0] - - ## Rules - 1. Every Actor and Server MUST be inside a Boundary. - 2. Dataflows MUST reference existing Actors or Servers by exact name. - 3. Boundaries that cross trust levels MUST have isTrusted=false on the lower-trust side. - 4. Sensitive data stores (DBs, secrets vaults) MUST have classification=confidential or above. - 5. Any internet-facing component MUST have is_public=true. - 6. Use realistic, specific protocols — never "unknown" or "any". - 7. Output ONLY the Markdown DSL inside a ```markdown … ``` fence. No explanatory text. - -# --------------------------------------------------------------------------- -# 2. STRIDE COMPONENT ANALYSIS -# Used by: LiteLLMProvider.generate_threats → build_component_prompt() -# Purpose: Generate per-component STRIDE threats with full business context. -# --------------------------------------------------------------------------- -stride_analysis: - system: | - CRITICAL RULE — STRICT TECHNOLOGY SCOPE: - Generate threats ONLY for the exact component type and technologies listed in the - "Technology Tags" field. DO NOT assume, invent, or extrapolate frameworks, platforms, - or stacks not explicitly listed: - - A firewall is NOT running Kubernetes, Docker, or any application framework. - - A physical machine tagged [cisco-asa] generates Cisco ASA / network-level threats. - - A virtual machine tagged [apache, centos] generates Apache / Linux threats only. - - An auth-server tagged [windows-server] generates Active Directory / LDAP threats. - - An on-prem component with machine=physical generates NO cloud-native (AWS/GCP/Azure/k8s) threats. - If Technology Tags is "N/A", infer only from the component Type and Name — never add - technologies beyond what can be reasonably inferred from those two fields alone. - - You are an elite threat modeling expert with deep mastery of: - - STRIDE methodology applied to real-world architectures - - MITRE ATT&CK Enterprise v14+ (tactics, techniques, sub-techniques) - - OWASP Top 10, API Security Top 10, and CWE Top 25 - - MITRE D3FEND defensive techniques (DT-xxx identifiers) - - Cloud-native security (AWS, Azure, GCP, Kubernetes, serverless) - - Supply chain and third-party risk - - Compliance: HIPAA, GDPR, PCI-DSS, SOC 2, NIS2, DORA - - Your threat analysis must be: - - Specific to the component and its context — never generic - - Grounded in realistic attack chains (minimum 3 steps) - - Actionable for both blue teams (detection) and architects (remediation) - - Calibrated: assign confidence based on actual attack surface, not fear - - For each threat consider: - - Whether existing controls (auth, encryption, WAF, IAM) are bypassable - - Chained attacks: how this component could be a pivot point - - Regulatory impact if the threat materializes - - Which D3FEND defensive technique most effectively counters it - - component_template: | - # STRIDE Threat Analysis — <> - - ## Component Identity - | Field | Value | - |---|---| - | Type | <> | - | Name | <> | - | Machine | <> | - | Technology Tags | <> | - | Description | <> | - | Business Value | <> | - - ## Network Position - | Field | Value | - |---|---| - | Trust Boundary | <> | - | Internet-Facing | <> | - | Protocol | <> | - | Authentication method | <> | - - ## CIA Requirements - <> - - ## Security Controls in Place - <> - - ## Additional DSL Properties - <> - - ## Connected Flows - **Inbound** (source → <>): - <> - - **Outbound** (<> → destination): - <> - - <> - <> - ## Risk Context - | Factor | Value | - |---|---| - | Data Sensitivity | <> | - | Compliance Requirements | <> | - | User Base | <> | - | External Integrations | <> | - | Deployment | <> | - - ## Your Task - Identify 3 to 5 realistic STRIDE threats for <>. - ⚠️ REMINDER: Type=«<>», Tags=«<>», Machine=«<>». - Generate ONLY threats relevant to these exact technologies. Do NOT add threats for - technologies not listed above. Use the flows and CIA requirements to calibrate severity. - - For each threat provide: - - 1. **category** — one of: Spoofing, Tampering, Repudiation, Information Disclosure, - Denial of Service, Elevation of Privilege - 2. **title** — specific, not generic (bad: "SQL Injection"; good: "Auth bypass via - malformed JWT in <> /api/token endpoint") - 3. **description** — technical explanation including root cause - 4. **attack_scenario** — numbered steps, minimum 3 (initial access → goal) - 5. **prerequisites** — what attacker needs (network access, valid account, etc.) - 6. **business_impact** — severity (Critical/High/Medium/Low), financial, regulatory, - reputational, operational sub-fields - 7. **likelihood** — Low/Medium/High with one-sentence rationale - 8. **capec_ids** — array of 1 to 3 CAPEC IDs from the MITRE CAPEC taxonomy that best - describe this specific attack pattern (e.g. ["CAPEC-66", "CAPEC-115"]). - STRICT RULE: only use real CAPEC IDs that exist in the MITRE CAPEC taxonomy. - Match the CAPEC to the STRIDE category of this threat. Return [] if uncertain. - DO NOT generate ATT&CK technique IDs (T-XXXX) — those are derived automatically. - 9. **cwe_ids** — array of numeric CWE IDs (e.g. ["89", "287"]) — used for VOC scoring - 10. **d3fend_techniques** — array of D3FEND technique IDs (e.g. ["D3-MFA"]) that counter - this threat; empty array if none apply - 11. **detection_opportunities** — specific log sources or behavioral signals - 12. **confidence** — float 0.0–1.0 reflecting your certainty this threat is exploitable - - ## Output - Return ONLY valid JSON (no markdown wrapper, no preamble): - - { - "threats": [ - { - "category": "Information Disclosure", - "title": "Specific threat title for <>", - "description": "Root cause and technical detail…", - "attack_scenario": "1. Attacker…\n2. …\n3. …", - "prerequisites": ["network access to port X", "valid low-priv account"], - "business_impact": { - "severity": "High", - "financial": "…", - "regulatory": "…", - "reputational": "…", - "operational": "…" - }, - "likelihood": "Medium", - "likelihood_rationale": "One sentence.", - "capec_ids": ["CAPEC-66", "CAPEC-115"], - "cwe_ids": ["89", "287"], - "d3fend_techniques": ["D3-NTF", "D3-OAM"], - "detection_opportunities": ["WAF logs — rule 942100", "auth failure rate spike"], - "confidence": 0.82 - } - ] - } - - batch_template: | - # Batch STRIDE Threat Analysis — <> components - - <> - <> - ## Shared Risk Context - | Factor | Value | - |---|---| - | Data Sensitivity | <> | - | Compliance Requirements | <> | - | Deployment Environment | <> | - - ## Components to Analyze - - <> - - ## Your Task - For EACH component listed above, identify 3 to 5 realistic STRIDE threats. - STRICT RULE: generate threats ONLY for the exact technology tags of each component. - Do NOT cross-contaminate threats between components. - - Return ONLY valid JSON array — one entry per component (no markdown, no preamble): - [ - { - "component": "", - "threats": [ - { - "category": "Spoofing|Tampering|Repudiation|Information Disclosure|Denial of Service|Elevation of Privilege", - "title": "Specific threat title", - "description": "Root cause and technical detail", - "attack_scenario": "1. Step\n2. Step\n3. Step", - "prerequisites": ["network access", "valid account"], - "business_impact": { - "severity": "Critical|High|Medium|Low", - "financial": "...", "regulatory": "...", - "reputational": "...", "operational": "..." - }, - "likelihood": "Low|Medium|High", - "likelihood_rationale": "One sentence.", - "capec_ids": ["CAPEC-XX"], - "cwe_ids": ["XX"], - "d3fend_techniques": ["D3-XX"], - "detection_opportunities": ["log source or signal"], - "confidence": 0.8 - } - ] - } - ] - -# --------------------------------------------------------------------------- -# 3. ATTACK FLOW GENERATION -# Used by: LiteLLMProvider.generate_attack_flow → build_attack_flow_prompt() -# Purpose: Generate a STIX 2.1 Attack Flow for a specific threat. -# --------------------------------------------------------------------------- -attack_flow: - system: | - You are an expert in cyber attack chain analysis and MITRE ATT&CK framework v14+. - You produce detailed Attack Flow diagrams in MITRE Attack Flow v3.0 / STIX 2.1 format. - - Your expertise: - - Full kill chain modeling (Initial Access → Impact) - - Lateral movement and pivot analysis - - Detection engineering (Sigma rules, MITRE D3FEND, data source mapping) - - STIX 2.1 objects and relationship graph - - Quality requirements: - - All ATT&CK technique IDs must be real and current (v14+). Do not invent IDs. - - Model the most realistic attack path, not the worst-case theoretical one. - - Every action must have at least one detection opportunity. - - Include a realistic failure/blocked path for the first action. - - 5 to 7 main actions — detailed enough to be actionable, concise enough to be readable. - - component_template: | - # Attack Flow — <> - - ## Threat to Model - | Field | Value | - |---|---| - | STRIDE Category | <> | - | Title | <> | - | Description | <> | - | Known MITRE Techniques | <> | - - ## Basic Scenario - <> - - ## Target Component - | Field | Value | - |---|---| - | Type | <> | - | Name | <> | - | Description | <> | - - ## System Context - <> - - ## Your Task - Generate a detailed Attack Flow for this threat in MITRE Attack Flow v3.0 / STIX 2.1. - - For each action include: - - ATT&CK Tactic (ID + name) - - ATT&CK Technique (ID + name + sub-technique if applicable) - - Realistic description of the adversary action - - Success path (next action ref) - - Failure/blocked path (detection node ref) - - Specific detection data sources (Windows Event IDs, Linux audit, cloud trail, etc.) - - Return ONLY valid JSON: - - { - "type": "attack-flow", - "spec_version": "3.0.0", - "id": "attack-flow--<>-{uuid4}", - "name": "<>", - "description": "Attack flow for <> on <>", - "scope": "incident", - "start_refs": ["action--1"], - "actions": [ - { - "type": "action", - "id": "action--1", - "name": "Action name", - "tactic": {"id": "TA0001", "name": "Initial Access"}, - "technique": { - "id": "T1190", - "name": "Exploit Public-Facing Application", - "subtechnique": null - }, - "description": "Detailed adversary action…", - "confidence": 85, - "success_refs": ["action--2"], - "failure_refs": ["detection--1"] - } - ], - "conditions": [ - { - "type": "condition", - "id": "condition--1", - "description": "Prerequisite (e.g. internet-facing service with no WAF)" - } - ], - "assets": [ - { - "type": "asset", - "id": "asset--1", - "name": "<>", - "description": "<>" - } - ], - "detection_points": [ - { - "type": "detection", - "id": "detection--1", - "name": "Detection method", - "description": "How blue team detects this action", - "data_sources": ["Windows Security Event 4625", "Cloudtrail: UnauthorizedOperation"], - "blocks_action": "action--1" - } - ] - } - -# --------------------------------------------------------------------------- -# 4. RAG SYSTEM-LEVEL THREAT GENERATION -# Used by: RAGThreatGenerator (LangChain ChatPromptTemplate) -# Purpose: Generate cross-component, system-level threats using retrieved knowledge. -# Note: Variables use {varname} syntax — substituted by LangChain at invoke time. -# --------------------------------------------------------------------------- -rag: - system: | - You are an expert threat modeler operating at SYSTEM level. - Unlike component-level analysis (already done separately), your role is to identify - threats that emerge from component INTERACTIONS — trust boundary violations, - attack pivot chains, data flow exposure, and emergent risks no single component - reveals in isolation. - - Focus on: - - Cross-component attack paths (A compromises B, then pivots to C) - - Trust boundary crossings that could be exploited - - Data classification mismatches (sensitive data flowing through low-trust channels) - - Single points of failure that cascade across the architecture - - Supply chain risks introduced by external dependencies - - Do NOT repeat single-component threats — those are covered by the component-level engine. - Prioritize threats that are SPECIFIC to the architecture described, grounded in the - retrieved CVE/CAPEC patterns and your system-level understanding. - - human_template: | - {optional_context} - - ## Threat Model (Architecture) - {threat_model_markdown} - - ## Retrieved Security Knowledge (CVE / CAPEC patterns) - {context} - - --- - Based on the above, generate 3 to 6 SYSTEM-LEVEL threats that span multiple components - or exploit architectural weaknesses. For each threat provide: - - - **name**: Concise, specific name (reference component names where relevant) - - **description**: Technical explanation — how the multi-hop attack unfolds - - **affected_components**: List of component names involved in the attack path - - **category**: STRIDE category (Spoofing, Tampering, Repudiation, - Information Disclosure, Denial of Service, Elevation of Privilege) - - **likelihood**: high / medium / low - - **impact**: high / medium / low - - **source**: always "LLM" - - **capec_ids**: 1 to 3 CAPEC IDs from the MITRE CAPEC taxonomy that best describe - the attack pattern (e.g. ["CAPEC-22", "CAPEC-115"]). Only use real CAPEC IDs. - DO NOT generate ATT&CK T-IDs — those are derived automatically from CAPECs. - Return [] if uncertain. - - **confidence**: float 0.0–1.0 — your certainty this cross-component path is exploitable - given the described architecture (1.0 = confirmed pattern, 0.5 = plausible, <0.4 = speculative) - - Format your response as a JSON array: - [ - { - "name": "Threat name referencing specific components", - "description": "Multi-hop attack description…", - "affected_components": ["ComponentA", "ComponentB"], - "category": "Elevation of Privilege", - "capec_ids": ["CAPEC-22", "CAPEC-115"], - "likelihood": "medium", - "impact": "high", - "source": "LLM", - "confidence": 0.75 - } - ] - -# --------------------------------------------------------------------------- -# 5. SOC ANALYST DETECTION PASS -# Used by: AIService._enrich_with_soc_analysis() -# Purpose: For each threat, assess detectability, identify missing log sources, -# generate Sigma-compatible SIEM rules, and list IOCs for blue teams. -# --------------------------------------------------------------------------- -soc_analyst: - system: | - You are a senior SOC analyst and detection engineer with deep expertise in: - - SIEM rule authorship (Sigma, Splunk SPL, Elastic KQL, Microsoft Sentinel KQL) - - MITRE ATT&CK data sources and detection strategies - - Threat hunting and behavioral analytics - - Log source coverage analysis (Windows Event Log, Sysmon, auditd, network flow, - cloud trail, WAF, DNS, EDR telemetry) - - Indicators of Compromise (IOC) — network, host, behavioral - - Your task is to assess a batch of STRIDE threats against a described architecture - and produce actionable detection guidance for the blue team. - - Rules: - - Be specific to the technology stack and log sources implied by the architecture. - - Sigma rule logic must use realistic field names (EventID, CommandLine, Image, - DestinationPort, etc.) — not placeholders. - - IOCs must be concrete patterns (process names, registry paths, domain patterns, - network signatures) — never generic IP addresses. - - detectability reflects the realism of detection given the described controls: - "high" = multiple independent signals exist and are likely logged; - "medium" = detection is possible but requires additional log sources or tuning; - "low" = attacker can operate undetected with common configurations. - - missing_logs lists SPECIFIC log sources absent from the architecture that would - be required to detect this threat (e.g., "Sysmon Event 3 — Network connections - from non-browser processes"). - - batch_template: | - ## Architecture Digest - <> - - ## Threats to Analyze - <> - - --- - For each threat in the list above, return a JSON object. - The threat_id field MUST match exactly the "id" field from the input. - - Return ONLY a JSON array — no markdown fence, no preamble, no trailing text: - - [ - { - "threat_id": "t-0", - "detectability": "medium", - "missing_logs": [ - "Sysmon Event 3 — network connections from lsass.exe", - "Windows Security Event 4698 — scheduled task creation" - ], - "siem_rules": [ - { - "title": "Credential dumping via lsass memory read", - "logic": "Image=*lsass.exe AND GrantedAccess=0x1010 AND NOT Image=*MsMpEng.exe" - }, - { - "title": "Multiple failed authentications from single source", - "logic": "EventID=4625 AND count > 10 within 5m grouped by IpAddress" - } - ], - "iocs": [ - "mimikatz.exe or renamed variant reading LSASS memory", - "HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest — UseLogonCredential=1", - "Outbound SMB (TCP/445) from workstation to non-DC hosts" - ] - } - ] - -# --------------------------------------------------------------------------- -# 6. CISO TRIAGE -# Used by: ReportGenerator._run_ciso_triage / LiteLLMProvider.generate_ciso_triage -# Purpose: Senior-executive synthesis of the full threat landscape. -# Output: posture_score, posture_label, top_findings, quick_wins, narrative -# --------------------------------------------------------------------------- -ciso_triage: - system: | - You are a senior Chief Information Security Officer (CISO) with deep expertise in: - - Risk communication to board-level executives and technical teams - - Prioritising security investments based on threat likelihood and business impact - - Translating technical vulnerabilities into actionable risk posture statements - - Identifying quick wins that reduce exposure with minimal engineering effort - - Your task is to synthesise a ranked list of STRIDE threats identified in a security - assessment and produce a concise, actionable CISO-level risk briefing. - - Rules: - - posture_score is a float 0–10 (10 = critical risk, 0 = no material risk). - Weight it toward the severity and volume of unmitigated CRITICAL/HIGH threats. - - posture_label must be one of: "CRITICAL", "HIGH", "ELEVATED", "MODERATE", "LOW". - - top_findings are the 5 most impactful risk areas (not individual threats — themes). - Each finding must include a "title", "detail" (2–3 sentences), and "threat_ids" - (list of threat IDs from the input that support it). - - quick_wins are 3–5 remediations with high impact-to-effort ratio. - "impact" and "effort" must each be "HIGH", "MEDIUM", or "LOW". - - narrative is a 3–5 sentence executive summary suitable for a board slide. - - Be specific and factual — base findings on the provided threat data only. - - Do NOT invent threats not present in the input. - - template: | - ## Threat Summary for CISO Briefing - - Total threats: <> - By severity: CRITICAL=<>, HIGH=<>, MEDIUM=<>, LOW=<> - By STRIDE: <> - - ## Top Threats (ranked by risk score) - - <> - - --- - Produce a CISO briefing as a single JSON object — no markdown fence, no preamble: - - { - "posture_score": {float 0-10}, - "posture_label": "{CRITICAL|HIGH|ELEVATED|MODERATE|LOW}", - "top_findings": [ - { - "rank": 1, - "title": "{concise risk theme title}", - "detail": "{2-3 sentence explanation of the risk and its business impact}", - "threat_ids": ["{T-NNNN}", ...] - } - ], - "quick_wins": [ - { - "action": "{specific, actionable remediation step}", - "impact": "{HIGH|MEDIUM|LOW}", - "effort": "{HIGH|MEDIUM|LOW}", - "addresses": ["{T-NNNN}", ...] - } - ], - "narrative": "{3-5 sentence executive summary for board presentation}" - } diff --git a/config/protocols_community.yaml b/config/protocols_community.yaml deleted file mode 100644 index 12033ee..0000000 --- a/config/protocols_community.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# SecOpsTM Community Protocol Registry -# Each entry maps a protocol to MITRE ATT&CK tactic boosts and key techniques. -# -# To add a new protocol: see CONTRIBUTING_ASSET_TYPES.md -# -# Fields: -# tactic_boost : kebab-case tactic IDs boosted when this protocol is exposed on an asset -# key_techniques: technique IDs always boosted when this protocol service is present - -protocols: - - ssh: - tactic_boost: [initial-access, lateral-movement] - key_techniques: [T1021.004, T1098.004] - - rdp: - tactic_boost: [initial-access, lateral-movement] - key_techniques: [T1021.001, T1078] - - smb: - tactic_boost: [lateral-movement, credential-access] - key_techniques: [T1021.002, T1570, T1039] - - ldap: - tactic_boost: [credential-access, discovery] - key_techniques: [T1069.002, T1087.002] - - kerberos: - tactic_boost: [credential-access] - key_techniques: [T1558.003, T1558.001] - - http: - tactic_boost: [initial-access] - key_techniques: [] - - https: - tactic_boost: [initial-access] - key_techniques: [] - - sql: - tactic_boost: [credential-access, collection, exfiltration] - key_techniques: [T1190, T1078, T1048] - - winrm: - tactic_boost: [lateral-movement, execution] - key_techniques: [T1021.006] - - rpc: - tactic_boost: [lateral-movement] - key_techniques: [] - - ftp: - tactic_boost: [exfiltration] - key_techniques: [T1048.003] - - smtp: - tactic_boost: [initial-access, collection] - key_techniques: [T1566, T1114] - - modbus: - tactic_boost: [execution, impact] - key_techniques: [T1565.001, T1498] - - ipsec: - tactic_boost: [initial-access] - key_techniques: [] - - dns: - tactic_boost: [command-and-control, defense-evasion] - key_techniques: [T1071.004, T1568] - - sap: - tactic_boost: [credential-access, collection] - key_techniques: [] - - syslog: - tactic_boost: [collection, defense-evasion] - key_techniques: [] diff --git a/config/scoring_config.yaml b/config/scoring_config.yaml deleted file mode 100644 index 6bb0c09..0000000 --- a/config/scoring_config.yaml +++ /dev/null @@ -1,168 +0,0 @@ -# SecOpsTM Scoring Configuration -# All values here are loaded lazily at first use with fallback to identical -# hardcoded defaults when this file is absent or PyYAML is not installed. -# -# See docs/customizing_scoring.md for field descriptions and valid ranges. - -# --------------------------------------------------------------------------- -# STRIDE base scores and severity pipeline -# --------------------------------------------------------------------------- -stride: - # Base score per STRIDE category — scale 1.0–10.0 - base_scores: - ElevationOfPrivilege: 9.0 - Tampering: 8.0 - InformationDisclosure: 7.5 - Spoofing: 7.0 - DenialOfService: 6.0 - Repudiation: 5.0 - - # Severity label bands [min_inclusive, max_inclusive] - severity_thresholds: - CRITICAL: [9.0, 10.0] - HIGH: [7.5, 8.9] - MEDIUM: [6.0, 7.4] - LOW: [4.0, 5.9] - INFORMATIONAL: [1.0, 3.9] - - # Additive protocol adjustments applied to the base score - protocol_adjustments: - SSH: 0.5 # commonly exposed, higher attack surface - HTTPS: -0.3 # encrypted, lower exposure - HTTP: 0.2 # cleartext, slightly higher exposure - - # Data classification multipliers (applied after protocol adjustments) - classification_multipliers: - PUBLIC: 1.0 - RESTRICTED: 1.2 - SECRET: 1.5 - TOP_SECRET: 2.0 - - # VOC (Vulnerability / Observable / Context) additive deltas applied last - voc_deltas: - cve_match: 0.5 # confirmed exploitability evidence - cwe_high_risk: 0.3 # easily weaponisable CWE class - network_exposed: 0.7 # reachable without auth or encryption - d3fend_mitigations: -0.5 # active defensive controls (reduces risk) - -# --------------------------------------------------------------------------- -# CWE IDs (numeric strings) treated as high-risk exploitability signals -# --------------------------------------------------------------------------- -high_risk_cwes: - - "22" # Path Traversal - - "78" # OS Command Injection - - "89" # SQL Injection - - "94" # Code Injection - - "119" # Buffer Errors - - "120" # Classic Buffer Overflow - - "125" # Out-of-bounds Read - - "134" # Format String - - "190" # Integer Overflow / Wrap-around - - "434" # Unrestricted Upload of Dangerous File - - "502" # Deserialization of Untrusted Data - - "611" # XML External Entity (XXE) - - "798" # Use of Hardcoded Credentials - - "918" # Server-Side Request Forgery (SSRF) - -# --------------------------------------------------------------------------- -# ThreatConsolidator — AI vs pytm deduplication -# --------------------------------------------------------------------------- -deduplication: - # Jaccard word-overlap threshold: two threats with overlap >= this value - # are treated as duplicates; the AI version wins. - jaccard_threshold: 0.3 - - # Common English words excluded before computing Jaccard word sets. - # Extend this list to reduce false duplicates caused by shared filler words. - stop_words: - - a - - an - - the - - in - - on - - at - - to - - for - - of - - and - - or - - is - - are - - was - - be - - by - - it - - its - -# --------------------------------------------------------------------------- -# AssetTechniqueMapper — MITRE ATT&CK technique scoring boosts -# --------------------------------------------------------------------------- -technique_mapper: - # Techniques with score below this threshold are discarded - minimum_score: 0.4 - - # Additive boost applied when the condition is met - boosts: - platform_match: 0.5 # technique platform matches asset platform - primary_tactic: 0.4 # technique tactic is primary for this asset type - hop_position: 0.3 # technique tactic matches the GDAF hop position - key_technique: 0.6 # technique is listed in key_techniques for this asset type - actor_known_ttp: 0.5 # technique is in the actor's known TTPs - no_auth: 0.3 # asset has no authentication - no_encryption: 0.2 # asset has no encryption (credential-access) - no_mfa: 0.2 # asset has no MFA - legacy: 0.2 # asset is tagged as legacy - service_match: 0.35 # technique tactic matches a protocol tactic boost - key_tech_service: 0.5 # technique is a key_technique for an exposed protocol - credentials_stored: 0.4 # asset stores credentials (credential-access) - -# --------------------------------------------------------------------------- -# GDAFEngine — Goal-Driven Attack Flow scoring -# --------------------------------------------------------------------------- -gdaf: - # Data classification → sensitivity score (0.0–1.0), used for data_value on edges - classification_scores: - top_secret: 1.0 - secret: 0.7 - restricted: 0.4 - public: 0.0 - unknown: 0.1 - - # traversal_difficulty → hop_weight additive bonus (easier path = higher attacker risk) - traversal_bonus: - low: 0.3 - medium: 0.1 - high: 0.0 - - # BOM detection_level → detection_coverage float (used for scenario-level avg) - detection_coverage: - none: 0.0 - low: 0.2 - medium: 0.5 - high: 0.8 - - # Additive/multiplicative weights applied per hop when computing hop_weight - hop_weights: - no_auth: 0.4 # edge has no authentication - no_encryption: 0.3 # edge has no encryption - no_mfa: 0.2 # node has no MFA - cia_contribution: 0.1 # multiplied by node CIA score (0–1) - data_value_factor: 0.3 # multiplied by edge data_value (0–1) - cve_per_cve: 0.15 # per unpatched CVE on the node - cve_cap: 0.5 # maximum CVE bonus regardless of count - - # Bonus added to path_score from target node's CIA criticality - target_cia_bonus: 0.5 - - # path_score thresholds for risk level classification - risk_thresholds: - CRITICAL: 4.0 - HIGH: 2.8 - MEDIUM: 1.8 - - # Default risk_criteria values when no context YAML is provided - defaults: - max_hops: 7 - max_paths_per_objective: 3 - acceptable_risk_score: 5.0 - gdaf_min_technique_score: 0.8 diff --git a/pyproject.toml b/pyproject.toml index 99ce3cb..c7c6a7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "SecOpsTM" -version = "1.1.1a20" +version = "1.1.1a21" authors = [ { name="ellipse2v", email="ellipse2v@gmail.com" }, ] diff --git a/tests/test_attack_flow_builder.py b/tests/test_attack_flow_builder.py index bd4b274..48f454b 100644 --- a/tests/test_attack_flow_builder.py +++ b/tests/test_attack_flow_builder.py @@ -481,3 +481,309 @@ def test_empty_scenarios_writes_empty_list(self, tmp_path): with open(summary_path, "r", encoding="utf-8") as f: data = json.load(f) assert data["scenarios"] == [] + + +# --------------------------------------------------------------------------- +# SPARTA AFB integration tests +# Tests that SPARTA technique IDs produce ST00xx tactic format in AFB nodes, +# and that ATT&CK IDs still produce phase-slug format. +# --------------------------------------------------------------------------- + +def _make_sparta_tech(tech_id="IA-0006", name="RF Jamming", tactic_id="ST0003", score=2.0): + """Make a ScoredTechnique with a SPARTA tactic ID stored in the tactics list.""" + return ScoredTechnique( + id=tech_id, + name=name, + tactics=[tactic_id], # SPARTA stores ST00xx code here + score=score, + rationale="SPARTA technique — space segment attack", + url="", + ) + + +def _make_sparta_hop(asset_name="TTC-Frontend", asset_type="ttc-link", + tech_id="IA-0006", tactic_id="ST0003", + protocol="rf", hop_score=2.5, hop_position="entry"): + return AttackHop( + asset_name=asset_name, + asset_type=asset_type, + techniques=[_make_sparta_tech(tech_id=tech_id, tactic_id=tactic_id)], + dataflow_name=f"AttackerTo{asset_name}", + protocol=protocol, + is_encrypted=False, + is_authenticated=False, + hop_score=hop_score, + hop_position=hop_position, + ) + + +def _make_pwnsat_scenario(): + """Recreate the Thales PWNSAT demo attack path as an AttackScenario. + + Path: RF Attacker → TTC-Frontend → OBC → Mission-Payload + SPARTA tactics: ST0004 (IA) → ST0007 (EX) → ST0009 (IMP) + """ + hops = [ + AttackHop( + asset_name="TTC-Frontend", + asset_type="ttc-link", + techniques=[ + _make_sparta_tech("IA-0006", "RF Jamming", "ST0004", score=2.5), + _make_sparta_tech("EX-0009.01", "CCSDS TC Replay", "ST0007", score=2.0), + ], + dataflow_name="AttackerToTTC", + protocol="rf", + is_encrypted=False, + is_authenticated=False, + hop_score=4.5, + hop_position="entry", + ), + AttackHop( + asset_name="OBC", + asset_type="onboard-computer", + techniques=[ + _make_sparta_tech("EX-0012.06", "RF Spoofing", "ST0007", score=1.8), + _make_sparta_tech("PER-0002.02", "Firmware Backdoor", "ST0008", score=1.5), + ], + dataflow_name="TTCToOBC", + protocol="ccsds", + is_encrypted=False, + is_authenticated=False, + hop_score=3.3, + hop_position="intermediate", + ), + AttackHop( + asset_name="Mission-Payload", + asset_type="leo-satellite", + techniques=[ + _make_sparta_tech("IMP-0004", "Payload Manipulation", "ST0009", score=2.2), + ], + dataflow_name="OBCToPayload", + protocol="spacewire", + is_encrypted=False, + is_authenticated=False, + hop_score=2.2, + hop_position="target", + ), + ] + return AttackScenario( + scenario_id="GDAF-PWNSAT01", + objective_id="obj-payload-disruption", + objective_name="Payload Disruption", + objective_description="Inject malicious CCSDS telecommands to disable mission payload", + objective_business_impact="Mission loss — payload permanently disabled", + objective_mitre_final_tactic="impact", + actor_id="rf-attacker", + actor_name="RF Attacker", + actor_sophistication="advanced", + entry_point="RF Attacker", + target_asset="Mission-Payload", + hops=hops, + path_score=10.0, + risk_level="CRITICAL", + detection_coverage=0.0, + unacceptable_risk=True, + min_technique_score=0.5, + ) + + +class TestSpartaAfb: + """Verify SPARTA-specific AFB node generation.""" + + # ── _make_action_node: SPARTA ID detection ─────────────────────────────── + + def test_sparta_tactic_format_uses_st_code(self): + """SPARTA tech ID → tactic field must be ST00xx, not a phase slug.""" + builder = AttackFlowBuilder([], "SatModel") + tech = _make_sparta_tech("IA-0006", "RF Jamming", "ST0003") + result = builder._make_action_node(tech) + props = {k: v for k, v in result["node"]["properties"]} + ttp = props["ttp"] + tactic_entry = next(pair for pair in ttp if pair[0] == "tactic") + assert tactic_entry[1] == "ST0003", f"Expected ST0003, got {tactic_entry[1]}" + + def test_sparta_technique_field_is_sparta_id(self): + """SPARTA action node technique field must be the SPARTA ID (IA-0006).""" + builder = AttackFlowBuilder([], "SatModel") + tech = _make_sparta_tech("IA-0006", "RF Jamming", "ST0003") + result = builder._make_action_node(tech) + props = {k: v for k, v in result["node"]["properties"]} + ttp = props["ttp"] + tech_entry = next(pair for pair in ttp if pair[0] == "technique") + assert tech_entry[1] == "IA-0006" + + def test_attack_tactic_format_uses_phase_slug(self): + """ATT&CK tech ID (T1059) → tactic field must remain a phase slug.""" + builder = AttackFlowBuilder([], "SatModel") + tech = _make_tech("T1059", "Command and Script Interpreter", tactics=["execution"]) + result = builder._make_action_node(tech) + props = {k: v for k, v in result["node"]["properties"]} + ttp = props["ttp"] + tactic_entry = next(pair for pair in ttp if pair[0] == "tactic") + assert tactic_entry[1] == "execution" + + def test_sparta_regex_matches_known_ids(self): + """Verify the SPARTA ID regex covers all expected technique ID patterns.""" + import re + pattern = re.compile(r'^[A-Z]{2,4}-\d{4}') + sparta_ids = [ + "IA-0006", "EX-0009", "EX-0009.01", "EX-0012.06", + "PER-0002", "PER-0002.02", "LM-0002", "IMP-0001", + "IMP-0004", "REC-0003", "REC-0003.04", "RD-0003", + ] + for sid in sparta_ids: + assert pattern.match(sid), f"SPARTA regex should match {sid}" + + def test_attack_regex_does_not_match_attack_ids(self): + """ATT&CK IDs (T1059, T1542.003) must NOT match the SPARTA regex.""" + import re + pattern = re.compile(r'^[A-Z]{2,4}-\d{4}') + attack_ids = ["T1059", "T1542.003", "T1190", "T1021", "T1485"] + for aid in attack_ids: + assert not pattern.match(aid), f"SPARTA regex should NOT match {aid}" + + def test_sparta_no_tactics_falls_back_to_st0000(self): + """If a SPARTA tech has no tactics list, tactic defaults to ST0000.""" + builder = AttackFlowBuilder([], "SatModel") + tech = ScoredTechnique( + id="EX-0009", + name="Uplink Interception", + tactics=[], # empty + score=1.5, + rationale="test", + ) + result = builder._make_action_node(tech) + props = {k: v for k, v in result["node"]["properties"]} + ttp = props["ttp"] + tactic_entry = next(pair for pair in ttp if pair[0] == "tactic") + assert tactic_entry[1] == "ST0000" + + def test_action_node_has_all_required_fields(self): + """Action node must have id, instance, properties, anchors.""" + builder = AttackFlowBuilder([], "SatModel") + tech = _make_sparta_tech("LM-0002", "Lateral Movement to Space Segment", "ST0006") + result = builder._make_action_node(tech) + node = result["node"] + assert node["id"] == "action" + assert isinstance(node["instance"], str) and len(node["instance"]) == 36 + assert isinstance(node["properties"], list) + assert isinstance(node["anchors"], dict) + + # ── Full PWNSAT scenario ───────────────────────────────────────────────── + + def test_pwnsat_scenario_produces_correct_action_node_count(self): + """3 hops × up to 2 techniques each — action nodes count matches technique count.""" + builder = AttackFlowBuilder([], "SatModel") + scenario = _make_pwnsat_scenario() + afb = builder._build_afb(scenario) + action_nodes = [o for o in afb["objects"] if o.get("id") == "action"] + # Hop0: 2 techs, Hop1: 2 techs, Hop2: 1 tech → 5 total (all above min_score=0.5) + assert len(action_nodes) >= 3, f"Expected ≥3 action nodes, got {len(action_nodes)}" + + def test_pwnsat_scenario_schema_is_v2(self): + builder = AttackFlowBuilder([], "SatModel") + afb = builder._build_afb(_make_pwnsat_scenario()) + assert afb["schema"] == "attack_flow_v2" + + def test_pwnsat_scenario_risk_is_critical(self): + builder = AttackFlowBuilder([], "SatModel") + afb = builder._build_afb(_make_pwnsat_scenario()) + assert afb["_gdaf_meta"]["risk_level"] == "CRITICAL" + assert afb["_gdaf_meta"]["unacceptable_risk"] is True + + def test_pwnsat_all_sparta_action_nodes_use_st_codes(self): + """Every action node in the PWNSAT AFB must have an ST00xx tactic.""" + builder = AttackFlowBuilder([], "SatModel") + afb = builder._build_afb(_make_pwnsat_scenario()) + for obj in afb["objects"]: + if obj.get("id") != "action": + continue + props = {k: v for k, v in obj["properties"]} + ttp = props["ttp"] + tactic_val = next((pair[1] for pair in ttp if pair[0] == "tactic"), None) + assert tactic_val is not None + assert tactic_val.startswith("ST"), ( + f"Expected ST-prefixed tactic, got {tactic_val!r} for technique " + f"{next((pair[1] for pair in ttp if pair[0] == 'technique'), '?')}" + ) + + def test_pwnsat_afb_is_valid_json(self, tmp_path): + """PWNSAT scenario produces a valid JSON-serialisable AFB.""" + scenario = _make_pwnsat_scenario() + builder = AttackFlowBuilder([scenario], "SatModel") + builder.generate_and_save(str(tmp_path)) + afb_path = ( + tmp_path / "gdaf" / "obj-payload-disruption" / + "rf-attacker_GDAF-PWNSAT01.afb" + ) + assert afb_path.exists(), f"AFB file not found at {afb_path}" + with open(afb_path, "r", encoding="utf-8") as f: + data = json.load(f) + assert data["schema"] == "attack_flow_v2" + assert data["_gdaf_meta"]["risk_level"] == "CRITICAL" + + def test_pwnsat_summary_lists_sparta_techniques(self, tmp_path): + """Summary JSON must include SPARTA technique IDs, not only ATT&CK IDs.""" + scenario = _make_pwnsat_scenario() + builder = AttackFlowBuilder([scenario], "SatModel") + builder.generate_and_save(str(tmp_path)) + summary_path = tmp_path / "gdaf" / "gdaf_summary.json" + with open(summary_path, "r", encoding="utf-8") as f: + data = json.load(f) + row = data["scenarios"][0] + tech_ids = [t["tech_id"] for t in row["techniques"]] + sparta_ids = [tid for tid in tech_ids if "-" in tid and not tid.startswith("T")] + assert len(sparta_ids) > 0, ( + f"Expected SPARTA IDs in summary techniques, got: {tech_ids}" + ) + + def test_mixed_sparta_and_attack_techniques_in_same_afb(self): + """A hop mixing SPARTA and ATT&CK techniques emits correct tactic format for each.""" + builder = AttackFlowBuilder([], "SatModel") + hop = AttackHop( + asset_name="Ground-Station", + asset_type="ground-station", + techniques=[ + _make_sparta_tech("IA-0001.02", "Phishing for Credentials", "ST0003", score=1.8), + _make_tech("T1190", "Exploit Public-Facing Application", tactics=["initial-access"], score=1.5), + ], + dataflow_name="AttackerToGS", + protocol="https", + is_encrypted=True, + is_authenticated=False, + hop_score=3.3, + hop_position="entry", + ) + scenario = AttackScenario( + scenario_id="GDAF-MIX001", + objective_id="obj-ground-pivot", + objective_name="Ground Station Pivot", + objective_description="", + objective_business_impact="Operator access", + objective_mitre_final_tactic="lateral-movement", + actor_id="nation-state", + actor_name="Nation State", + actor_sophistication="expert", + entry_point="Attacker", + target_asset="Ground-Station", + hops=[hop], + path_score=3.3, + risk_level="HIGH", + detection_coverage=0.2, + unacceptable_risk=False, + min_technique_score=0.5, + ) + afb = builder._build_afb(scenario) + action_nodes = [o for o in afb["objects"] if o.get("id") == "action"] + assert len(action_nodes) >= 2 + tactic_values = [] + for obj in action_nodes: + props = {k: v for k, v in obj["properties"]} + ttp = props["ttp"] + tactic_val = next((pair[1] for pair in ttp if pair[0] == "tactic"), None) + tactic_values.append(tactic_val) + # Must have both an ST-prefixed tactic and a phase-slug tactic + has_sparta = any(v.startswith("ST") for v in tactic_values if v) + has_attack = any(not v.startswith("ST") for v in tactic_values if v) + assert has_sparta, f"Expected at least one ST-prefixed tactic, got: {tactic_values}" + assert has_attack, f"Expected at least one phase-slug tactic, got: {tactic_values}" diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Entrance-01.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Entrance-01.yaml new file mode 100644 index 0000000..68c1e16 --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Entrance-01.yaml @@ -0,0 +1,24 @@ +# BOM: Cam-Entrance-01 +asset: "Cam-Entrance-01" +vendor: "Hikvision" +model: "DS-2CD2143G2-I" +firmware_version: "5.7.0 build 220901" +patch_level: outdated +known_cves: + - CVE-2021-36260 # Hikvision web server command injection via /SDK/webLanguage — CVSS 9.8, unauthenticated RCE + - CVE-2017-7921 # Hikvision authentication bypass via crafted URL — exposes RTSP and config + - CVE-2023-28808 # Hikvision information disclosure via improper access control on some API endpoints +running_services: + - RTSP (port 554) + - HTTP (port 80) + - HTTPS (port 443) +detection_level: low +credentials_stored: true +notes: > + Hikvision fixed dome camera at building entrance. Firmware 5.7.0 is two major + versions behind current (5.7.16). CVE-2021-36260 allows unauthenticated command + injection via the web interface — no exploit prerequisites beyond network access. + Default admin credentials unchanged during installation (admin/12345). + RTSP stream on port 554 unauthenticated and accessible from Camera Network VLAN. + Camera is PoE-powered; physical access to the PoE switch allows power cycling + without leaving a trail in VMS logs. diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Parking-PTZ.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Parking-PTZ.yaml new file mode 100644 index 0000000..05618d1 --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Parking-PTZ.yaml @@ -0,0 +1,24 @@ +# BOM: Cam-Parking-PTZ +asset: "Cam-Parking-PTZ" +vendor: "Dahua" +model: "SD49425XB-HNR" +firmware_version: "2.820.0000000.47.R" +patch_level: outdated +known_cves: + - CVE-2021-33044 # Dahua authentication bypass — identity authentication bypass via specially crafted packet + - CVE-2021-33045 # Dahua authentication bypass — same vulnerability family, different code path + - CVE-2022-30563 # Dahua ONVIF authentication bypass — replay attack on WS-UsernameToken allows full PTZ control +running_services: + - RTSP (port 554) + - ONVIF (port 80/8080) + - HTTP (port 80) +detection_level: low +credentials_stored: true +notes: > + Dahua PTZ camera covering parking lot and main vehicle access. ONVIF is enabled + without TLS — CVE-2022-30563 allows an attacker who can intercept one legitimate + ONVIF request to replay it and gain full PTZ motor control. This can be used to + redirect the camera to parking lot blind spots before a physical intrusion. + CVE-2021-33044/33045 allow full authentication bypass: attacker obtains admin + session without credentials, can exfiltrate RTSP stream credentials and + reconfigure camera. Firmware update requires physical console access (no OTA). diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Server-Room.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Server-Room.yaml new file mode 100644 index 0000000..bc41682 --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Server-Room.yaml @@ -0,0 +1,25 @@ +# BOM: Cam-Server-Room +asset: "Cam-Server-Room" +vendor: "Axis" +model: "P3245-V" +firmware_version: "10.12.187" +patch_level: current +known_cves: + - CVE-2018-10660 # Axis Communications multiple cameras — shell command injection via param handling + - CVE-2023-21413 # Axis OS VAPIX API — OS command injection via parameter in local API call (requires auth) +running_services: + - RTSP (port 554) + - HTTPS (port 443) + - VAPIX API (port 443) +detection_level: medium +credentials_stored: true +notes: > + Axis fixed dome camera monitoring the server room. Firmware is current (10.12.187). + CVE-2023-21413 requires authentication but is critical given the target (server room). + An insider or attacker who has already compromised VMS credentials can use this + to achieve RCE on the camera itself — and from there pivot to the Camera Network VLAN. + This camera is the highest-sensitivity asset: footage is used as evidence in + physical security investigations. Its compromise or footage deletion would + eliminate evidence of server room intrusions. + RTSP stream is not encrypted — captures from this camera on the VLAN would + expose server room equipment inventory, access patterns, and badge readers. diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Thermal-Perimeter.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Thermal-Perimeter.yaml new file mode 100644 index 0000000..60ac6d9 --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/Cam-Thermal-Perimeter.yaml @@ -0,0 +1,26 @@ +# BOM: Cam-Thermal-Perimeter +asset: "Cam-Thermal-Perimeter" +vendor: "Hikvision" +model: "DS-2TD2628T-3/QA" +firmware_version: "5.5.800 build 210701" +patch_level: outdated +known_cves: + - CVE-2021-36260 # Hikvision web server command injection — CVSS 9.8, affects thermal lineup + - CVE-2021-36261 # Hikvision improper input validation — device crash via crafted message +running_services: + - RTSP (port 554) + - HTTP (port 80) + - HTTPS (port 443) +detection_level: low +credentials_stored: true +notes: > + Hikvision thermal camera installed at rooftop perimeter, internet-facing for + remote monitoring by a third-party security firm. The internet exposure makes + CVE-2021-36260 (CVSS 9.8 unauthenticated RCE) directly exploitable from the + internet — no VPN or jump host required. + Firmware 5.5.800 is severely outdated (current is 5.7.16). The vendor's IVS + (Intelligent Video Surveillance) analytics module processes RTSP frames + locally — a compromised camera could be used to manipulate perimeter + intrusion detection alerts (suppress or generate false alarms). + RTSP accessible unauthenticated from the internet on port 554 (firewall + misconfiguration allows inbound 554 for third-party monitoring). diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/Mobile-App-Server.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/Mobile-App-Server.yaml new file mode 100644 index 0000000..cd7c4ed --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/Mobile-App-Server.yaml @@ -0,0 +1,24 @@ +# BOM: Mobile-App-Server +asset: "Mobile-App-Server" +vendor: "Hikvision" +model: "iVMS-4200 Server Component" +os_version: "Ubuntu 22.04 LTS" +software_version: "iVMS-4200 v3.3.1" +patch_level: current +known_cves: + - CVE-2022-28219 # ManageEngine (similar mobile VMS gateway pattern) — SSRF leading to RCE — listed as architecture risk +running_services: + - HTTPS (port 443) + - RTSP proxy (port 554) +detection_level: medium +credentials_stored: true +notes: > + Mobile viewer gateway — authenticates mobile users (iVMS-4200 app) and proxies RTSP + streams from RTSP-Relay to authorized mobile clients. + Mobile user credentials are stored locally in iVMS SQLite database (not synced to AD). + The app authenticates users but does not enforce per-camera authorization — any + authenticated mobile user can view any camera feed including server room. + RTSP proxy port 554 is conditionally exposed: intended only for internal VLAN routing + to RTSP-Relay, but a firewall misconfiguration could expose it to Mobile Access boundary. + Ubuntu 22.04 is current and patched; the primary risk is the iVMS application layer, + not the OS. diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/NVR-Main.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/NVR-Main.yaml new file mode 100644 index 0000000..a1b5fb9 --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/NVR-Main.yaml @@ -0,0 +1,32 @@ +# BOM: NVR-Main +asset: "NVR-Main" +vendor: "Hikvision" +model: "DS-7732NI-K4/16P" +os_version: "embedded Linux 3.10 (vendor kernel)" +software_version: "NVR firmware V4.61.000 build 220901" +patch_level: outdated +known_cves: + - CVE-2021-36260 # Hikvision NVR web interface — same command injection as cameras, CVSS 9.8 + - CVE-2017-7921 # Hikvision auth bypass — affects NVR web interface, exposes all camera feeds + - CVE-2021-36227 # Hikvision NVR — improper authentication allows unauthenticated file read + - CVE-2023-28808 # Hikvision — information disclosure via API, leaks camera credentials +running_services: + - RTSP (port 554) + - HTTP (port 80) + - HTTPS (port 443) + - iSCSI (port 3260) + - SSH (port 22) +detection_level: low +credentials_stored: true +notes: > + 16-channel PoE NVR storing 30 days of footage from all cameras at 4K resolution. + Approximately 20TB of storage — physical disks are hot-swappable without authentication. + SSH enabled with factory default credentials (root/hiklinux) — never changed. + CVE-2021-36260 on the NVR web interface gives unauthenticated RCE on the NVR OS, + from which an attacker can delete all footage (T1485), exfiltrate archives via iSCSI, + or use the NVR as a pivot point into the Camera Network VLAN. + iSCSI port 3260 is open on the Camera Network — no iSCSI authentication configured. + RTSP relay for mobile viewers is served directly by the NVR (not via RTSP-Relay), + meaning NVR compromise also cuts mobile access. + The NVR is the single point of failure for all footage retention: no off-site + backup is configured (Cloud-Archive is a planned addition, not yet operational). diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/RTSP-Relay.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/RTSP-Relay.yaml new file mode 100644 index 0000000..cc8e6cf --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/RTSP-Relay.yaml @@ -0,0 +1,25 @@ +# BOM: RTSP-Relay +asset: "RTSP-Relay" +vendor: "MediaMTX (formerly rtsp-simple-server)" +model: "open-source" +software_version: "MediaMTX v1.1.0" +patch_level: outdated +known_cves: + - CVE-2023-47137 # MediaMTX path traversal — unauthenticated path traversal via HLS endpoint allows arbitrary file read +running_services: + - RTSP (port 8554) + - HLS (port 8888) + - WebRTC (port 8889) + - API (port 9997) +detection_level: low +credentials_stored: false +notes: > + MediaMTX RTSP relay proxy serving live camera feeds to the Mobile-App-Server. + No authentication configured on any RTSP or HLS endpoints — any client that can + reach port 8554 or 8888 on the Camera Network VLAN can view live feeds from all cameras. + CVE-2023-47137 allows unauthenticated path traversal via the HLS endpoint, giving read + access to any file on the relay host's filesystem. + The management API on port 9997 has no authentication and exposes full stream management: + an attacker can terminate streams, add malicious stream sources, or reconfigure relay paths. + WebRTC endpoint is exposed to the Mobile Access boundary via Mobile-App-Server — if the + API server is misconfigured, this creates a direct path from Mobile Access to Camera Network. diff --git a/threatModel_Template/IP_Camera_Surveillance/BOM/VMS-Server.yaml b/threatModel_Template/IP_Camera_Surveillance/BOM/VMS-Server.yaml new file mode 100644 index 0000000..183370f --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/BOM/VMS-Server.yaml @@ -0,0 +1,31 @@ +# BOM: VMS-Server +asset: "VMS-Server" +vendor: "Milestone Systems" +model: "XProtect Corporate 2022 R3" +os_version: "Windows Server 2019" +software_version: "XProtect Corporate 22.3a (build 22.3.4.1)" +patch_level: outdated +known_cves: + - CVE-2023-3671 # Milestone XProtect — improper privilege management allows local privilege escalation + - CVE-2022-35637 # Milestone XProtect — SQL injection in Management Server API (auth required) + - CVE-2021-28378 # Windows Server 2019 — MSHTML remote code execution (MS KB5001391) +running_services: + - HTTPS (port 443) + - SQL Server (port 1433) + - XProtect Management Server (port 8080) + - XProtect Event Server (port 22331) + - RDP (port 3389) +detection_level: medium +credentials_stored: true +notes: > + Milestone XProtect Corporate VMS server — central management plane for all cameras. + Operator credentials are stored in XProtect's local user database (not AD-integrated). + RDP is enabled and accessible from the Management Network VLAN for remote maintenance. + CVE-2023-3671 allows a low-privilege VMS operator to escalate to SYSTEM on the VMS host. + CVE-2022-35637 (SQL injection) requires authentication but once exploited gives access to + the full XProtect database: all camera credentials, recording schedules, user accounts, + and event history. + XProtect 22.3a is two years behind the current release (2024 R1); no automatic updates. + An attacker with VMS admin access can: export footage, disable all cameras simultaneously, + reconfigure PTZ presets, and — via the Management Server — push firmware updates to all + Milestone-integrated cameras (supply chain risk if firmware update server is compromised). diff --git a/threatModel_Template/IP_Camera_Surveillance/context/camera_context.yaml b/threatModel_Template/IP_Camera_Surveillance/context/camera_context.yaml new file mode 100644 index 0000000..305b528 --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/context/camera_context.yaml @@ -0,0 +1,58 @@ +project_description: > + Commercial building IP camera surveillance system protecting a 5-floor office + building. Critical assets include the NVR footage archives (legal evidence, + HR investigations), VMS admin console, and server room camera feed. + The system is operated by a 2-person security team with no dedicated SOC. + Cameras run vendor firmware; update cadence is quarterly at best. + +objectives: + - name: "Footage Destruction" + target: "NVR-Main" + impact: critical + description: "Attacker deletes or corrupts recorded footage to cover tracks after physical intrusion" + + - name: "Live Feed Interception" + target: "RTSP-Relay" + impact: high + description: "Intercept unencrypted RTSP streams to conduct surveillance on building occupants" + + - name: "PTZ Camera Hijack" + target: "Cam-Parking-PTZ" + impact: medium + description: "Redirect PTZ camera to create blind spots or monitor specific targets" + + - name: "VMS Admin Takeover" + target: "VMS-Server" + impact: critical + description: "Full VMS control: disable cameras, export footage, pivot to management network" + + - name: "Server Room Blind Spot" + target: "Cam-Server-Room" + impact: critical + description: "Disable or tamper with server room camera to enable unmonitored physical access" + + - name: "Botnet Recruitment" + target: "Cam-Entrance-01" + impact: medium + description: "Compromise camera for DDoS botnet (Mirai-style) using default credentials" + +actors: + - name: "Remote Attacker" + capabilities: [network-scanning, credential-stuffing, exploit-frameworks, rtsp-interception] + motivation: espionage + entry_points: [Cam-Thermal-Perimeter, RTSP-Relay, Mobile-App-Server] + + - name: "Physical Intruder" + capabilities: [physical-access, rfid-cloning, camera-tampering] + motivation: theft + entry_points: [Cam-Thermal-Perimeter, Cam-Entrance-01] + + - name: "Insider Threat" + capabilities: [vms-access, network-access, credential-reuse] + motivation: sabotage + entry_points: [VMS-Server, NVR-Main] + +compliance_requirements: + - GDPR Article 5 (lawful CCTV processing, data minimisation) + - ISO 27001 A.7.2 (physical security) + - EN 50132 (video surveillance systems standard) diff --git a/threatModel_Template/IP_Camera_Surveillance/model.md b/threatModel_Template/IP_Camera_Surveillance/model.md new file mode 100644 index 0000000..ee77cab --- /dev/null +++ b/threatModel_Template/IP_Camera_Surveillance/model.md @@ -0,0 +1,61 @@ +# Threat Model: IP Camera Surveillance System + +## Description +IP-based video surveillance system for a commercial building: fixed and PTZ cameras, +thermal perimeter detection, NVR, Video Management System, RTSP relay, mobile viewer +app, and cloud archive. Models the full attack surface from unauthenticated RTSP streams +to VMS admin takeover and NVR footage destruction. + +## Context +gdaf_context = context/camera_context.yaml +bom_directory = BOM + +## Boundaries +- **Physical Perimeter**: isTrusted=False, traversal_difficulty=low +- **Camera Network**: isTrusted=False, traversal_difficulty=medium +- **Management Network**: isTrusted=True, traversal_difficulty=high +- **Mobile Access**: isTrusted=False, traversal_difficulty=low +- **Cloud Storage**: isTrusted=True, traversal_difficulty=medium + +## Actors +- **External Attacker**: boundary="Physical Perimeter" +- **Security Operator**: boundary="Management Network" +- **Mobile Viewer**: boundary="Mobile Access" + +## Servers +- **Cam-Entrance-01**: type="ip-camera", boundary="Camera Network", internet_facing=False, credentials_stored=True +- **Cam-Parking-PTZ**: type="ptz-camera", boundary="Camera Network", internet_facing=False, credentials_stored=True +- **Cam-Server-Room**: type="ip-camera", boundary="Camera Network", internet_facing=False, credentials_stored=True +- **Cam-Thermal-Perimeter**: type="thermal-camera", boundary="Physical Perimeter", internet_facing=True, credentials_stored=True +- **NVR-Main**: type="nvr", boundary="Camera Network", credentials_stored=True +- **VMS-Server**: type="vms", boundary="Management Network", credentials_stored=True +- **RTSP-Relay**: type="rtsp-server", boundary="Camera Network" +- **Cloud-Archive**: type="backup", boundary="Cloud Storage" +- **Mobile-App-Server**: type="api-gateway", boundary="Mobile Access" + +## Dataflows +- **Cam-to-NVR-01**: from="Cam-Entrance-01", to="NVR-Main", protocol="RTSP", encrypted=False, authenticated=False +- **Cam-to-NVR-PTZ**: from="Cam-Parking-PTZ", to="NVR-Main", protocol="RTSP", encrypted=False, authenticated=False +- **Cam-to-NVR-Server**: from="Cam-Server-Room", to="NVR-Main", protocol="RTSP", encrypted=False, authenticated=False +- **Thermal-to-NVR**: from="Cam-Thermal-Perimeter", to="NVR-Main", protocol="RTSP", encrypted=False, authenticated=True +- **NVR-to-VMS**: from="NVR-Main", to="VMS-Server", protocol="HTTPS", encrypted=True, authenticated=True +- **NVR-to-RTSP**: from="NVR-Main", to="RTSP-Relay", protocol="RTSP", encrypted=False, authenticated=False +- **VMS-to-Cloud**: from="VMS-Server", to="Cloud-Archive", protocol="HTTPS", encrypted=True, authenticated=True +- **VMS-to-PTZ-Control**: from="VMS-Server", to="Cam-Parking-PTZ", protocol="ONVIF", encrypted=False, authenticated=True +- **Operator-to-VMS**: from="Security Operator", to="VMS-Server", protocol="HTTPS", encrypted=True, authenticated=True +- **Mobile-to-API**: from="Mobile Viewer", to="Mobile-App-Server", protocol="HTTPS", encrypted=True, authenticated=True +- **API-to-RTSP**: from="Mobile-App-Server", to="RTSP-Relay", protocol="RTSP", encrypted=False, authenticated=False +- **Attacker-to-Thermal**: from="External Attacker", to="Cam-Thermal-Perimeter", protocol="RTSP", encrypted=False, authenticated=False + +## Protocol Styles +- **RTSP**: color=orange, line_style=dashed +- **ONVIF**: color=darkorange, line_style=dashed +- **HTTPS**: color=darkgreen, line_style=solid + +## Severity Multipliers +- **NVR-Main**: 2.5 +- **VMS-Server**: 2.0 +- **Cam-Server-Room**: 2.0 +- **RTSP-Relay**: 1.8 +- **Cam-Thermal-Perimeter**: 1.5 +- **Cam-Parking-PTZ**: 1.3 diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/ADCS.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/ADCS.yaml new file mode 100644 index 0000000..8d7b485 --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/ADCS.yaml @@ -0,0 +1,21 @@ +asset: ADCS +type: onboard-computer +os: RTEMS 5.1 (RTOS) +firmware_version: "2.2.0" +software: + - name: ADCS Control Loop + version: "2.2.0" + - name: MIL-STD-1553 Bus Controller + version: "1.3.0" +known_cves: [] +detection_level: none +patch_level: never +notes: > + Attitude and Orbit Control System connected to OBC via MIL-STD-1553 bus. + No command authentication between OBC and ADCS — unauthorized commands from + compromised OBC can desaturate reaction wheels, modify orbit parameters, or + cause uncontrolled satellite tumbling. Second attack path via on-board GPS + receiver: ADCS ingests GNSS navigation solutions without integrity checking. + A spoofed GPS signal providing false position/velocity data triggers erroneous + orbit correction manoeuvres without OBC involvement. No anomaly detection on + navigation data quality. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/EGSE.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/EGSE.yaml new file mode 100644 index 0000000..afb934d --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/EGSE.yaml @@ -0,0 +1,26 @@ +asset: EGSE +type: server +os: Windows 10 Pro (engineering laptop) +firmware_version: "N/A" +software: + - name: SCOS-2000 (Spacecraft Control and Operations System) + version: "5.1" + - name: JTAG Debugger (Lauterbach TRACE32) + version: "2022.09" + - name: Satellite Test Framework (vendor-specific) + version: "3.4.2" +known_cves: + - CVE-2021-34527 + - CVE-2022-30190 +detection_level: low +patch_level: ad-hoc +notes: > + Electrical Ground Support Equipment used during satellite AIT (Assembly, Integration + and Test) phase. Connected to OBC via JTAG and serial debug interfaces for full + read/write access to all memory regions, firmware, and configuration registers. + This is the only phase where OBC firmware is physically accessible without a ground + uplink session. CVE-2021-34527 (PrintNightmare) and CVE-2022-30190 (Follina) are + unpatched on the AIT laptop. A supply chain attacker with access to the clean room + can implant malicious firmware at this stage — persistence survives integration, + testing, and launch, and is undetectable after fairing closure. EGSE is removed + before launch and not present in the operational segment. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/GPS-Receiver.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/GPS-Receiver.yaml new file mode 100644 index 0000000..c023f1c --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/GPS-Receiver.yaml @@ -0,0 +1,20 @@ +asset: GPS-Receiver +type: onboard-computer +os: Bare-metal (FPGA + GPS chipset) +firmware_version: "3.1.0" +software: + - name: GNSS Navigation Processor + version: "3.1.0" + - name: Orbit Determination Filter + version: "2.0.1" +known_cves: [] +detection_level: none +patch_level: never +notes: > + On-board GNSS receiver providing GPS position and timing data to the ADCS orbit + determination subsystem. Receives civilian L1 C/A GPS signal — no authentication + (GPS civilian signal is open and unencrypted). Susceptible to RF spoofing attacks + (false GPS signal injection) causing ADCS to compute incorrect orbit parameters, + potentially triggering erroneous thruster firings or orbit correction manoeuvres. + No anomaly detection on GPS data quality — ADCS trusts all navigation solutions + within plausible bounds. Physical replacement requires satellite deorbit. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/Ground-Station.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/Ground-Station.yaml new file mode 100644 index 0000000..8efa70f --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/Ground-Station.yaml @@ -0,0 +1,20 @@ +asset: Ground-Station +type: ground-station +os: Linux 5.15 (Ubuntu 22.04 LTS) +firmware_version: "N/A" +software: + - name: SLE API Gateway + version: "3.2.1" + - name: OpenSCEM ground software + version: "2.8.0" + - name: Apache HTTP Server + version: "2.4.54" +known_cves: + - CVE-2021-41773 + - CVE-2022-22963 +detection_level: medium +patch_level: quarterly +internet_facing: true +notes: > + Internet-connected ground station running SLE protocol gateway. + No MFA on remote operator access. TT&C uplink not authenticated at protocol level. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/Mission-Control-Server.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/Mission-Control-Server.yaml new file mode 100644 index 0000000..6ba1437 --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/Mission-Control-Server.yaml @@ -0,0 +1,18 @@ +asset: Mission-Control-Server +type: server +os: Windows Server 2019 +firmware_version: "N/A" +software: + - name: GMAT (General Mission Analysis Tool) + version: "R2022a" + - name: Microsoft IIS + version: "10.0" +known_cves: + - CVE-2021-34527 +detection_level: medium +patch_level: monthly +notes: > + Mission planning and analysis server accessible from ground station. + PrintNightmare (CVE-2021-34527) unpatched at last audit. Lateral movement + target from compromised ground station — grants mission planning capabilities + and historical telemetry access. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/Mission-Payload.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/Mission-Payload.yaml new file mode 100644 index 0000000..333c77b --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/Mission-Payload.yaml @@ -0,0 +1,15 @@ +asset: Mission-Payload +type: leo-satellite +os: Bare-metal FPGA +firmware_version: "1.0.3" +software: + - name: Payload Control Software + version: "1.0.3" +known_cves: [] +detection_level: none +patch_level: never +notes: > + Mission payload instruments controlled via SpaceWire bus from OBC. + No independent authentication between OBC and payload bus. + Compromise of OBC grants full control over payload instruments. + Physical replacement requires satellite deorbit or in-orbit servicing. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/OBC.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/OBC.yaml new file mode 100644 index 0000000..5693ce5 --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/OBC.yaml @@ -0,0 +1,23 @@ +asset: OBC +type: onboard-computer +os: VxWorks 7.0 (RTOS) +firmware_version: "1.4.2" +software: + - name: CCSDS Telecommand Decoder + version: "2.1.0" + - name: Flight Software Stack + version: "3.0.1" +known_cves: + - CVE-2019-12255 + - CVE-2019-12260 +detection_level: low +patch_level: ad-hoc +notes: > + On-Board Computer running VxWorks RTOS. Telecommand authentication disabled + for compatibility with legacy ground software. Firmware updates require ground + uplink session — no secure boot enforcement. CVE-2019-12255 and CVE-2019-12260 + (URGENT/11 VxWorks TCP/IP stack vulnerabilities) apply ONLY if the OBC exposes + a network management interface (Ethernet, JTAG-over-IP) — not via the CCSDS + command interface. Primary attack surface in this model is the unauthenticated + CCSDS uplink, not the TCP/IP stack. During AIT phase, EGSE JTAG access provides + a second firmware injection path independent of the uplink. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/TLM-Receiver.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/TLM-Receiver.yaml new file mode 100644 index 0000000..291d345 --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/TLM-Receiver.yaml @@ -0,0 +1,22 @@ +asset: TLM-Receiver +type: ttc-link +os: Embedded Linux (Raspberry Pi CM4 / custom SDR) +firmware_version: "1.2.0" +software: + - name: CCSDS TM Frame Decoder + version: "1.2.0" + - name: SDR Receiver (GNU Radio) + version: "3.10.4" +known_cves: + - CVE-2023-38408 +detection_level: low +patch_level: rarely +notes: > + Ground-based downlink telemetry receiver decoding CCSDS TM frames from satellite + on the publicly filed ITU downlink frequency. Frames are unencrypted and + unauthenticated — any observer with a suitable dish antenna and SDR can receive + and decode full telemetry. This is a passive-intercept attack surface: the attacker + does not need to interact with the legitimate ground station. CVE-2023-38408 + (ssh-agent remote code execution) applies if the receiver host is remotely managed + via SSH. Detection level is low — no monitoring of who is receiving the downlink + frequency. diff --git a/threatModel_Template/Satellite_Ground_Segment/BOM/TTC-Frontend.yaml b/threatModel_Template/Satellite_Ground_Segment/BOM/TTC-Frontend.yaml new file mode 100644 index 0000000..fd2519f --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/BOM/TTC-Frontend.yaml @@ -0,0 +1,16 @@ +asset: TTC-Frontend +type: ttc-link +os: Embedded FPGA (Xilinx Zynq) +firmware_version: "2.0.0" +software: + - name: CCSDS SLE Provider + version: "1.5.0" + - name: RF Modem Firmware + version: "4.1.2" +known_cves: [] +detection_level: low +patch_level: rarely +notes: > + CCSDS SLE-compliant RF frontend. Uplink frequency publicly documented in ITU filing. + No authentication on CCSDS TC frames (SDLS not enabled). Vulnerable to replay attacks + and RF spoofing. Frequency and modulation parameters recoverable via SDR scanning. diff --git a/threatModel_Template/Satellite_Ground_Segment/context/satellite_context.yaml b/threatModel_Template/Satellite_Ground_Segment/context/satellite_context.yaml new file mode 100644 index 0000000..32ee253 --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/context/satellite_context.yaml @@ -0,0 +1,300 @@ +project_description: > + Low Earth Orbit satellite ground segment based on the Thales DEF CON 2023 demonstration. + The system operates a commercial LEO satellite with an unauthenticated TT&C uplink, + a ground station running SLE (Space Link Extension) protocol, and an On-Board Computer + accessible via CCSDS telecommands. No cryptographic authentication is enforced on the + uplink chain. The ground station is internet-connected for remote operations. + Reference: SPARTA framework, PWNSAT Attack Flow Builder. + +# ── STRIDE Prompt Enrichment ─────────────────────────────────────────────────── +# These fields are injected into every AI STRIDE analysis prompt for this model. + +sector: > + Space Systems / Critical National Infrastructure. Commercial LEO satellite operator. + Space segment assets (OBC, ADCS, Payload) are physically unreachable once launched — + there is no patch deployment path short of a ground uplink session or satellite deorbit. + RF interfaces are broadcast by nature: any observer with suitable hardware can receive + or transmit on documented frequencies. CCSDS is the dominant protocol stack; + SPARTA (Space Attack Research and Tactic Analysis) is the applicable threat framework + alongside MITRE ATT&CK. + +threat_actor_profiles: > + - RF Attacker (advanced): equipped with SDR (HackRF, USRP), directional dish antenna, + and signal replay tools. Operates from ground or near-ground position during satellite + pass windows (~10 min for LEO). Targets unauthenticated CCSDS uplink and unencrypted + downlink telemetry. Known TTPs: IA-0006 (RF Jamming), EX-0009.01 (CCSDS TC Replay), + EX-0012.06 (RF Spoofing), EX-0009.02 (passive TLM eavesdropping). + + - Nation State Cyber Actor (expert): combines cyber (spear-phishing, CVE exploitation) + and RF capabilities. Primary target is long-term persistence on OBC via firmware + backdoor. Entry path: internet-facing ground station (Apache CVE-2021-41773, + Spring4Shell CVE-2022-22963) → Mission Control Server (PrintNightmare CVE-2021-34527) + → CCSDS uplink. Known TTPs: IA-0001, PER-0002.02, LM-0002, EX-0012.08. + + - Supply Chain / Insider (advanced): physical access during AIT phase (clean room). + JTAG access to OBC via EGSE. Implants firmware backdoor before launch — undetectable + post-fairing-closure. Known TTPs: EX-0012.09, PER-0002.02, RD-0004. + + - Hacktivist (intermediate): open-source tools, opportunistic CVE exploitation, passive + TLM interception using cheap SDR hardware. Low barrier to downlink eavesdropping + (published ITU frequencies). Known TTPs: REC-0005, EX-0012, IMP-0003. + +business_goals_to_protect: + - Satellite mission continuity (payload uptime, instrument data integrity) + - Orbit control integrity (ADCS commands, propellant budget) + - Operator command authority (no unauthorized telecommand execution) + - Telemetry confidentiality (mission data, bus state, orbit ephemeris) + - Ground segment integrity (no pivot from internet to mission network) + - AIT supply chain integrity (firmware authenticity before launch) + +data_sensitivity: > + Critical. Telecommands directly control satellite attitude, propellant, and payload — + unauthorized commands can cause permanent mission loss or satellite destruction. + Telemetry frames expose operational state and may contain cryptographic material. + GPS navigation data, if spoofed, triggers erroneous physical manoeuvres. + +deployment_environment: > + Hybrid: space segment (LEO orbit, physically unreachable, no patch path post-launch) + + ground segment (internet-connected Linux/Windows hosts) + RF channel (broadcast, + publicly documented ITU frequencies) + AIT integration environment (clean room, + pre-launch only). Space segment components run VxWorks, RTEMS, and bare-metal FPGA — + no standard OS security controls apply. Detection capability is near-zero on-orbit. + +integrations: + - CCSDS SLE (Space Link Extension) — ground-to-TTC interface, unauthenticated + - ITU-documented uplink/downlink frequencies — publicly available + - GPS L1 C/A civilian signal — no authentication, open to spoofing + - MIL-STD-1553 internal bus — OBC to ADCS, no command authentication + - SpaceWire — OBC to payload bus, no independent authentication + - JTAG/UART (AIT phase only) — full OBC memory read/write access + +user_base: > + Mission operators (Mission Control Server access), ground station engineers (GS + administration), AIT technicians (pre-launch only), and potentially hostile RF + observers within line-of-sight of the satellite pass. + +# ── Attack Objectives ───────────────────────────────────────────────────────── +attack_objectives: + - id: "obj-payload-disruption" + name: "Payload Disruption" + description: > + Attacker injects malicious CCSDS telecommands through the unauthenticated uplink + to disable or corrupt mission payload instruments, permanently denying the + satellite's primary mission. Physical replacement requires deorbit or in-orbit + servicing — mission loss is effectively permanent. + target_asset_names: ["Mission-Payload"] + target_types: ["leo-satellite"] + business_impact: "Mission loss — payload instruments permanently disabled, no recovery without deorbit" + mitre_final_tactic: "impact" + + - id: "obj-adcs-spoofing" + name: "ADCS Spoofing / Orbit Manipulation" + description: > + Modify ADCS parameters via OBC pivot: desaturate reaction wheels or alter thruster + firing sequences to cause uncontrolled tumbling, deorbit, or collision risk. + No authentication between OBC and ADCS over MIL-STD-1553 bus. + target_asset_names: ["ADCS"] + target_types: ["onboard-computer"] + business_impact: "Satellite loss — uncontrolled deorbit or space debris generation" + mitre_final_tactic: "impact" + + - id: "obj-ttc-jamming" + name: "TT&C Denial of Service" + description: > + RF jamming or replay attack on the publicly documented uplink frequency denies + legitimate operator command access, rendering the satellite uncontrollable during + the jamming window. Frequency recoverable via SDR scanning of ITU filing. + target_asset_names: ["TTC-Frontend"] + target_types: ["ttc-link"] + business_impact: "Temporary loss of satellite control — mission continuity degraded" + mitre_final_tactic: "impact" + + - id: "obj-telemetry-exfiltration" + name: "Telemetry Exfiltration" + description: > + Intercept unencrypted CCSDS downlink telemetry frames to extract operational + parameters, bus encryption keys (if any), payload data, and orbit ephemeris + for intelligence collection without active interference. + target_asset_names: ["OBC", "TTC-Frontend"] + target_types: ["onboard-computer", "ttc-link"] + business_impact: "Intelligence loss — mission data and operational parameters compromised" + mitre_final_tactic: "collection" + + - id: "obj-obc-persistence" + name: "OBC Firmware Persistence" + description: > + Upload malicious firmware image to OBC non-volatile memory via CCSDS uplink. + Firmware update requires no secure boot enforcement — persistence survives power + cycles and establishes long-term undetected access to the satellite bus. + target_asset_names: ["OBC"] + target_types: ["onboard-computer"] + business_impact: "Long-term mission compromise — attacker retains persistent access to satellite bus" + mitre_final_tactic: "persistence" + + - id: "obj-ground-pivot" + name: "Ground Station Network Pivot" + description: > + Compromise internet-facing ground station software (Apache HTTPD CVE-2021-41773, + Spring4Shell CVE-2022-22963) to gain operator-level access, then pivot to Mission + Control Server via PrintNightmare (CVE-2021-34527) for mission planning access. + target_asset_names: ["Mission-Control-Server"] + target_types: ["server"] + business_impact: "Operator access compromise — attacker gains mission planning and telemetry access" + mitre_final_tactic: "lateral-movement" + + - id: "obj-downlink-eavesdrop" + name: "Passive Downlink Telemetry Interception" + description: > + Attacker deploys a rogue TLM receiver (SDR + directional dish) aimed at the + satellite during a pass window. Unencrypted CCSDS TM frames are decoded without + any interaction with the legitimate ground station. Extracted data includes + bus state, payload instrument readings, orbit ephemeris, and potentially + session tokens or diagnostic credentials transmitted in clear. + target_asset_names: ["TLM-Receiver"] + target_types: ["ttc-link"] + business_impact: "Intelligence collection — mission data and operational state compromised without triggering any alarms" + mitre_final_tactic: "collection" + + - id: "obj-gnss-spoofing" + name: "GNSS Spoofing — False Orbit Injection" + description: > + Attacker transmits a stronger-than-authentic GPS L1 C/A signal providing false + position and velocity data to the on-board GPS receiver. The ADCS orbit + determination filter accepts the spoofed navigation solution and computes + erroneous attitude correction manoeuvres. This can trigger unintended thruster + firings, attitude destabilization, or fuel depletion — without any uplink + interaction and without triggering CCSDS command counters. + target_asset_names: ["ADCS"] + target_types: ["onboard-computer"] + business_impact: "Attitude/orbit loss — unintended manoeuvres drain propellant budget or destabilize satellite" + mitre_final_tactic: "impact" + + - id: "obj-supply-chain-firmware" + name: "Supply Chain Firmware Implant via EGSE" + description: > + Attacker with physical access to the AIT clean room compromises the EGSE + engineering laptop (via CVE-2022-30190 / Follina on a malicious email or + USB drive), then uses the JTAG connection to the OBC to flash a modified + firmware image containing a backdoor command handler. Implant survives + integration testing (functional tests do not verify firmware integrity), + launch, and orbital operations indefinitely. + target_asset_names: ["OBC"] + target_types: ["onboard-computer"] + business_impact: "Persistent mission compromise — backdoor active from launch day with no remediation path short of deorbit" + mitre_final_tactic: "persistence" + +# ── Threat Actors ───────────────────────────────────────────────────────────── +threat_actors: + - id: "rf-attacker" + name: "RF Attacker" + sophistication: "advanced" + objectives: + - "obj-payload-disruption" + - "obj-ttc-jamming" + - "obj-adcs-spoofing" + - "obj-telemetry-exfiltration" + - "obj-downlink-eavesdrop" + - "obj-gnss-spoofing" + entry_preference: "external" + known_ttps: + - "IA-0006" # RF Jamming / GNSS spoofing + - "EX-0009" # Uplink interception + - "EX-0009.01" # CCSDS TC replay + - "EX-0009.02" # TLM eavesdropping (passive downlink) + - "IMP-0003" # Denial of Service (jamming) + - "IMP-0004" # Payload manipulation + - "EX-0012.06" # RF spoofing (GPS / uplink) + - "REC-0003" # Orbital parameter reconnaissance + - "REC-0003.04" # ITU filing / frequency enumeration + capable_tactics: + - "reconnaissance" + - "initial-access" + - "execution" + - "impact" + - "collection" + + - id: "nation-state-cyber" + name: "Nation State Cyber Actor" + sophistication: "expert" + objectives: + - "obj-ground-pivot" + - "obj-obc-persistence" + - "obj-telemetry-exfiltration" + - "obj-payload-disruption" + entry_preference: "external" + known_ttps: + - "IA-0001" # Spear phishing / credential access + - "IA-0001.02" # Phishing for access credentials + - "PER-0002" # Establish persistent presence + - "PER-0002.02" # Implant firmware backdoor + - "LM-0002" # Lateral movement to space segment + - "EX-0012" # Exploit ground segment software + - "EX-0012.08" # Exploit unpatched CVE (PrintNightmare) + - "EX-0012.09" # Supply chain compromise + - "IMP-0001" # Destroy mission data + - "IMP-0005" # Exfiltrate mission payload data + - "REC-0003" # Gather satellite orbital parameters + - "REC-0003.04" # ITU filing / frequency enumeration + capable_tactics: + - "reconnaissance" + - "initial-access" + - "execution" + - "persistence" + - "lateral-movement" + - "collection" + - "impact" + + - id: "hacktivist" + name: "Hacktivist" + sophistication: "intermediate" + objectives: + - "obj-ttc-jamming" + - "obj-ground-pivot" + - "obj-downlink-eavesdrop" + entry_preference: "external" + known_ttps: + - "REC-0005" # Open-source reconnaissance + - "REC-0003.04" # ITU filing / frequency enumeration + - "IA-0001" # Credential stuffing / phishing + - "EX-0012" # Exploit known CVEs + - "IMP-0003" # Denial of service + - "EX-0009.02" # Passive TLM eavesdropping (low barrier to entry) + capable_tactics: + - "reconnaissance" + - "initial-access" + - "execution" + - "collection" + - "impact" + + - id: "supply-chain-attacker" + name: "Supply Chain / Insider Attacker" + sophistication: "advanced" + objectives: + - "obj-supply-chain-firmware" + - "obj-obc-persistence" + entry_preference: "insider" + known_ttps: + - "EX-0012.09" # Supply chain compromise + - "PER-0002" # Establish persistent presence + - "PER-0002.02" # Implant firmware backdoor via JTAG + - "RD-0004" # Alter ground systems / EGSE + - "IMP-0001" # Destroy mission capability (dormant backdoor) + capable_tactics: + - "initial-access" + - "execution" + - "persistence" + - "impact" + +# ── Risk Criteria ────────────────────────────────────────────────────────────── +risk_criteria: + max_hops: 6 + max_paths_per_objective: 3 + gdaf_min_technique_score: 0.6 + acceptable_risk_threshold: 6.0 + +compliance_requirements: + - NIST SP 800-53 Rev 5 (SC, SI, AU controls) + - CCSDS Security Architecture (SDLS protocol, 350.0-G-3) + - SPARTA Space Attack Research and Tactic Analysis framework + - ESA Space System Cybersecurity Requirements (ECSS-E-ST-10-04C draft) + - ITU Radio Regulations (interference / jamming prohibition) diff --git a/threatModel_Template/Satellite_Ground_Segment/model.md b/threatModel_Template/Satellite_Ground_Segment/model.md new file mode 100644 index 0000000..5f6c32c --- /dev/null +++ b/threatModel_Template/Satellite_Ground_Segment/model.md @@ -0,0 +1,86 @@ +# Threat Model: Satellite Ground Segment (Thales Demo Scenario) + +## Description +Low Earth Orbit satellite system modelling the attack surface demonstrated by Thales +at DEF CON 2023 and documented in the SPARTA/PWNSAT attack flow. Three independent +attack paths are modelled: (1) RF attacker replays unauthenticated CCSDS uplink +commands, pivots OBC → ADCS/Payload; (2) RF attacker passively intercepts unencrypted +CCSDS downlink telemetry via a rogue TLM receiver; (3) RF attacker spoofs GNSS signals +to feed false orbit data to the ADCS GPS receiver; (4) cyber attacker pivots through +internet-facing ground station to Mission Control Server; (5) supply chain attacker +implants malicious firmware on OBC during AIT phase via EGSE. + +## Context +gdaf_context = context/satellite_context.yaml +bom_directory = BOM + +## Boundaries +- **Space Segment**: isTrusted=False, traversal_difficulty=high +- **Ground Segment**: isTrusted=False, traversal_difficulty=medium +- **TT&C Channel**: isTrusted=False, traversal_difficulty=low +- **Mission Network**: isTrusted=True, traversal_difficulty=high +- **Integration Network**: isTrusted=True, traversal_difficulty=high + +## Actors +- **RF Attacker**: boundary="TT&C Channel" +- **Cyber Attacker**: boundary="Ground Segment" +- **Mission Operator**: boundary="Mission Network" +- **Supply Chain Attacker**: boundary="Integration Network" + +## Servers +- **Ground-Station**: type="ground-station", boundary="Ground Segment", internet_facing=True, credentials_stored=True +- **TTC-Frontend**: type="ttc-link", boundary="TT&C Channel", internet_facing=True +- **TLM-Receiver**: type="ttc-link", boundary="TT&C Channel", internet_facing=True +- **OBC**: type="onboard-computer", boundary="Space Segment", credentials_stored=True +- **GPS-Receiver**: type="onboard-computer", boundary="Space Segment" +- **ADCS**: type="onboard-computer", boundary="Space Segment" +- **Mission-Payload**: type="leo-satellite", boundary="Space Segment" +- **Mission-Control-Server**: type="server", boundary="Mission Network", credentials_stored=True +- **EGSE**: type="server", boundary="Integration Network", credentials_stored=True + +## Dataflows +- **Operator-to-GS**: from="Mission Operator", to="Ground-Station", protocol="HTTPS", encrypted=True, authenticated=True +- **GS-to-TTC**: from="Ground-Station", to="TTC-Frontend", protocol="SLE", encrypted=False, authenticated=False +- **RF-Uplink**: from="TTC-Frontend", to="OBC", protocol="CCSDS", encrypted=False, authenticated=False +- **RF-Downlink**: from="OBC", to="TLM-Receiver", protocol="CCSDS", encrypted=False, authenticated=False +- **TLM-to-GS**: from="TLM-Receiver", to="Ground-Station", protocol="SLE", encrypted=False, authenticated=False +- **GPS-Signal**: from="GPS-Receiver", to="ADCS", protocol="NMEA", encrypted=False, authenticated=False +- **OBC-to-ADCS**: from="OBC", to="ADCS", protocol="MIL-STD-1553", encrypted=False, authenticated=False +- **OBC-to-Payload**: from="OBC", to="Mission-Payload", protocol="SpaceWire", encrypted=False, authenticated=False +- **Attacker-RF-Uplink**: from="RF Attacker", to="TTC-Frontend", protocol="RF", encrypted=False, authenticated=False +- **Attacker-RF-Downlink**: from="RF Attacker", to="TLM-Receiver", protocol="RF", encrypted=False, authenticated=False +- **Attacker-GNSS-Spoof**: from="RF Attacker", to="GPS-Receiver", protocol="GNSS", encrypted=False, authenticated=False +- **Attacker-GS-Exploit**: from="Cyber Attacker", to="Ground-Station", protocol="HTTPS", encrypted=False, authenticated=False +- **GS-to-MCS**: from="Ground-Station", to="Mission-Control-Server", protocol="HTTPS", encrypted=True, authenticated=True +- **Supply-Chain-EGSE**: from="Supply Chain Attacker", to="EGSE", protocol="USB", encrypted=False, authenticated=False +- **EGSE-to-OBC**: from="EGSE", to="OBC", protocol="JTAG", encrypted=False, authenticated=False + +## Data +- **Telecommands**: format="CCSDS", classification="sensitive" +- **Telemetry**: format="CCSDS", classification="internal" +- **Payload-Data**: format="raw", classification="confidential" +- **GPS-Signal**: format="NMEA", classification="public" +- **Firmware-Image**: format="binary", classification="confidential" + +## Protocol Styles +- **CCSDS**: color=purple, line_style=dashed +- **SpaceWire**: color=darkorchid, line_style=dashed +- **MIL-STD-1553**: color=darkviolet, line_style=dashed +- **SLE**: color=slateblue, line_style=dashed +- **RF**: color=crimson, line_style=dotted +- **GNSS**: color=goldenrod, line_style=dotted +- **NMEA**: color=gold, line_style=dashed +- **JTAG**: color=chocolate, line_style=dashed +- **USB**: color=darkorange, line_style=solid +- **HTTPS**: color=darkgreen, line_style=solid + +## Severity Multipliers +- **OBC**: 3.0 +- **Mission-Payload**: 3.0 +- **ADCS**: 2.5 +- **TTC-Frontend**: 2.0 +- **TLM-Receiver**: 1.8 +- **GPS-Receiver**: 2.0 +- **Ground-Station**: 2.0 +- **Mission-Control-Server**: 1.8 +- **EGSE**: 2.5 diff --git a/threat_analysis/config/asset_types_community.yaml b/threat_analysis/config/asset_types_community.yaml index 3aae880..ba12927 100644 --- a/threat_analysis/config/asset_types_community.yaml +++ b/threat_analysis/config/asset_types_community.yaml @@ -210,6 +210,136 @@ asset_types: fuzzy_matches: [siem, log] icon_url: "" + # ── IoT & Video Surveillance ──────────────────────────────────────────────── + + # NOTE: ip-camera must appear before ptz-camera and thermal-camera. + # "ptz" and "thermal" do not contain "cam", so ordering is not critical here, + # but ip-camera is the most common fallback for any generic camera match. + ip-camera: + description: "IP surveillance camera (indoor/outdoor, fixed lens)" + category: iot + platforms: [Linux, Embedded] + tactics: [initial-access, execution, collection, impact, command-and-control] + key_techniques: [T1190, T1110, T1040, T1056, T1059, T1498, T1583] + fuzzy_matches: [camera, ipcam, cctv, cam, ip cam] + icon_url: "/static/resources/icons/ip-camera.svg" + + ptz-camera: + description: "Pan-Tilt-Zoom motorized surveillance camera (ONVIF-controlled)" + category: iot + platforms: [Linux, Embedded] + tactics: [initial-access, execution, collection, impact] + key_techniques: [T1190, T1059, T1040, T1565, T1110] + fuzzy_matches: [ptz, pan-tilt, pan tilt] + icon_url: "/static/resources/icons/ptz-camera.svg" + + thermal-camera: + description: "Thermal / infrared imaging camera for perimeter detection" + category: iot + platforms: [Linux, Embedded] + tactics: [initial-access, collection, exfiltration] + key_techniques: [T1190, T1040, T1048] + fuzzy_matches: [thermal, infrared, ir-camera, ir camera] + icon_url: "/static/resources/icons/thermal-camera.svg" + + nvr: + description: "Network Video Recorder — stores and indexes IP camera feeds" + category: iot + platforms: [Linux, Windows, Embedded] + tactics: [initial-access, collection, exfiltration, impact] + key_techniques: [T1190, T1078, T1005, T1048, T1485] + fuzzy_matches: [nvr, dvr, video recorder, network video] + icon_url: "/static/resources/icons/nvr.svg" + + # NOTE: vms (Video Management System) must appear before any entry whose + # fuzzy_matches contain "video" to avoid shadowing. + vms: + description: "Video Management System — centralized camera lifecycle platform" + category: server + platforms: [Windows, Linux] + tactics: [initial-access, privilege-escalation, collection, lateral-movement] + key_techniques: [T1190, T1078, T1003, T1021] + fuzzy_matches: [vms, video management, surveillance platform] + icon_url: "/static/resources/icons/vms.svg" + + rtsp-server: + description: "RTSP / ONVIF streaming media relay server" + category: server + platforms: [Linux] + tactics: [collection, exfiltration, defense-evasion] + key_techniques: [T1040, T1048.002, T1071.001] + fuzzy_matches: [rtsp, onvif, streaming, media relay] + icon_url: "/static/resources/icons/rtsp-server.svg" + + iot-gateway: + description: "IoT protocol gateway (MQTT broker, CoAP proxy, Zigbee/Z-Wave hub)" + category: iot + platforms: [Linux, Embedded] + tactics: [initial-access, execution, lateral-movement, command-and-control] + key_techniques: [T1190, T1059, T1021, T1071.001] + fuzzy_matches: [iot gateway, mqtt broker, zigbee hub, zwave, coap proxy] + icon_url: "/static/resources/icons/iot-gateway.svg" + + smart-lock: + description: "Smart lock or electronic access control device" + category: iot + platforms: [Embedded, Android, iOS] + tactics: [initial-access, impact, credential-access] + key_techniques: [T1190, T1110, T1552, T1531] + fuzzy_matches: [smart lock, access control, badge reader, electronic lock] + icon_url: "/static/resources/icons/smart-lock.svg" + + smart-meter: + description: "Smart utility meter (electricity, gas, water) with AMI radio" + category: iot + platforms: [Embedded] + tactics: [initial-access, collection, impact] + key_techniques: [T1190, T1040, T1565.001] + fuzzy_matches: [smart meter, meter, amr, ami] + icon_url: "/static/resources/icons/smart-meter.svg" + + # ── Space Segment ──────────────────────────────────────────────────────────── + + # NOTE: leo-satellite must appear before ground-station to avoid fuzzy-match + # shadowing on "satellite" vs "ground". + leo-satellite: + description: "Low Earth Orbit (or GEO/MEO) satellite — bus and payload" + category: space + platforms: [Embedded, Space Systems] + tactics: [execution, persistence, impact, collection, lateral-movement] + key_techniques: [T1059, T1542.003, T1485, T1040, T1021] + fuzzy_matches: [satellite, leo, geo, meo, cubesat, smallsat, spacecraft, sat] + icon_url: "/static/resources/icons/leo-satellite.svg" + + ground-station: + description: "Satellite ground station with TT&C and mission data uplink/downlink" + category: space + platforms: [Windows, Linux] + tactics: [reconnaissance, initial-access, execution, lateral-movement, exfiltration] + key_techniques: [T1590, T1190, T1059, T1021, T1048] + fuzzy_matches: [ground station, ground segment, teleport, gcs, gsoc, scc] + icon_url: "/static/resources/icons/ground-station.svg" + + ttc-link: + description: "Telemetry, Tracking and Command RF link (uplink / downlink)" + category: space + platforms: [Network Devices, Space Systems] + tactics: [initial-access, execution, defense-evasion, impact] + key_techniques: [T1200, T1059, T1565.001, T1498] + fuzzy_matches: [ttc, tt&c, uplink, downlink, telecommand, telemetry link, rf link] + icon_url: "/static/resources/icons/ttc-link.svg" + + onboard-computer: + description: "On-Board Computer (OBC) — satellite flight management system" + category: space + platforms: [Embedded, Space Systems] + tactics: [execution, persistence, impact, lateral-movement] + key_techniques: [T1059, T1542.003, T1485, T1021] + fuzzy_matches: [obc, flight computer, onboard computer, cdh, adcs, eps] + icon_url: "/static/resources/icons/onboard-computer.svg" + + # ── Default ───────────────────────────────────────────────────────────────── + default: description: "Fallback for unknown asset types" category: server diff --git a/threat_analysis/config/prompts.yaml b/threat_analysis/config/prompts.yaml index cc7aa6d..c5709ba 100644 --- a/threat_analysis/config/prompts.yaml +++ b/threat_analysis/config/prompts.yaml @@ -72,6 +72,30 @@ stride_analysis: If Technology Tags is "N/A", infer only from the component Type and Name — never add technologies beyond what can be reasonably inferred from those two fields alone. + SPACE SYSTEMS DOMAIN RULES (apply when Sector contains "Space" or component Type is + one of: onboard-computer, leo-satellite, ttc-link, ground-station): + - Apply SPARTA (Space Attack Research and Tactic Analysis) techniques in addition to + ATT&CK. SPARTA tactic IDs: ST0001 (Reconnaissance), ST0002 (Resource Development), + ST0003 (Initial Access), ST0004 (Execution), ST0005 (Persistence), + ST0006 (Defense Evasion), ST0007 (Collection), ST0008 (Exfiltration), + ST0009 (Impact). + - CCSDS telecommand interfaces without SDLS authentication are directly exploitable + via replay attack — always model this as a concrete Tampering/Spoofing threat. + - On-orbit assets have NO patch path post-launch. "Unpatched firmware" is a permanent + condition, not a temporary gap. Weight persistence threats at Critical severity. + - RF broadcast interfaces (CCSDS downlink, GPS L1 C/A) are passive-intercept surfaces + accessible to any observer with SDR hardware — model eavesdropping as a realistic, + low-barrier threat regardless of authentication status. + - MIL-STD-1553 and SpaceWire internal buses have no native authentication — any + component compromise grants full bus control without privilege escalation. + - A firewall is NOT running Kubernetes, Docker, or any application framework. + - A physical machine tagged [cisco-asa] generates Cisco ASA / network-level threats. + - A virtual machine tagged [apache, centos] generates Apache / Linux threats only. + - An auth-server tagged [windows-server] generates Active Directory / LDAP threats. + - An on-prem component with machine=physical generates NO cloud-native (AWS/GCP/Azure/k8s) threats. + If Technology Tags is "N/A", infer only from the component Type and Name — never add + technologies beyond what can be reasonably inferred from those two fields alone. + You are an elite threat modeling expert with deep mastery of: - STRIDE methodology applied to real-world architectures - MITRE ATT&CK Enterprise v14+ (tactics, techniques, sub-techniques) diff --git a/threat_analysis/config/protocols_community.yaml b/threat_analysis/config/protocols_community.yaml index 12033ee..34cf538 100644 --- a/threat_analysis/config/protocols_community.yaml +++ b/threat_analysis/config/protocols_community.yaml @@ -76,3 +76,29 @@ protocols: syslog: tactic_boost: [collection, defense-evasion] key_techniques: [] + + # ── IoT & Video Surveillance protocols ────────────────────────────────────── + + rtsp: + tactic_boost: [collection, exfiltration] + key_techniques: [T1040, T1048.002] + + onvif: + tactic_boost: [initial-access, execution, collection] + key_techniques: [T1190, T1059, T1040] + + mqtt: + tactic_boost: [command-and-control, initial-access] + key_techniques: [T1071.001, T1190] + + coap: + tactic_boost: [command-and-control, initial-access] + key_techniques: [T1071.001, T1190] + + zigbee: + tactic_boost: [initial-access, lateral-movement] + key_techniques: [T1190, T1021] + + zwave: + tactic_boost: [initial-access, lateral-movement] + key_techniques: [T1190, T1021] diff --git a/threat_analysis/core/asset_technique_mapper.py b/threat_analysis/core/asset_technique_mapper.py index 5cabfd3..3bc5baf 100644 --- a/threat_analysis/core/asset_technique_mapper.py +++ b/threat_analysis/core/asset_technique_mapper.py @@ -34,7 +34,6 @@ logger = logging.getLogger(__name__) -_PROJECT_ROOT = Path(__file__).resolve().parents[2] _PKG_ROOT = Path(__file__).resolve().parents[1] _ASSET_TYPES_PATH = _PKG_ROOT / "config" / "asset_types_community.yaml" _PROTOCOLS_PATH = _PKG_ROOT / "config" / "protocols_community.yaml" @@ -50,10 +49,26 @@ class ScoredTechnique: url: str = "" +SPARTA_TACTIC_IDS: Dict[str, str] = { + "reconnaissance": "ST0001", + "resource-development": "ST0002", + "initial-access": "ST0003", + "execution": "ST0004", + "persistence": "ST0005", + "privilege-escalation": "ST0006", + "lateral-movement": "ST0007", + "collection": "ST0008", + "impact": "ST0009", + "exfiltration": "ST0010", + "command-and-control": "ST0011", +} + + class AssetTechniqueMapper: """Maps asset characteristics to relevant MITRE ATT&CK techniques.""" _raw_techniques: Optional[List[Dict]] = None # class-level cache + _raw_sparta_techniques: Optional[List[Dict]] = None # class-level cache for SPARTA _asset_types: Optional[Dict] = None _protocols: Optional[Dict] = None _scoring_config: Optional[Dict] = None @@ -119,6 +134,24 @@ def _load_raw(cls) -> List[Dict]: cls._raw_techniques = [] return cls._raw_techniques + @classmethod + def _load_raw_sparta(cls) -> List[Dict]: + if cls._raw_sparta_techniques is not None: + return cls._raw_sparta_techniques + sparta_path = Path(__file__).resolve().parents[1] / "external_data" / "sparta-attack.json" + try: + with open(sparta_path, "r", encoding="utf-8") as f: + data = json.load(f) + cls._raw_sparta_techniques = [ + obj for obj in data.get("objects", []) + if obj.get("type") == "attack-pattern" + ] + logger.info("AssetTechniqueMapper: loaded %d SPARTA techniques", len(cls._raw_sparta_techniques)) + except Exception as exc: + logger.error("AssetTechniqueMapper: cannot load sparta-attack.json: %s", exc) + cls._raw_sparta_techniques = [] + return cls._raw_sparta_techniques + @classmethod def _load_asset_types(cls) -> Dict: if cls._asset_types is not None: @@ -167,8 +200,6 @@ def get_techniques( "target" → favor collection, exfiltration, impact """ raw = self._load_raw() - if not raw: - return [] boosts = self._get_boosts() min_score = self._get_minimum_score() @@ -177,6 +208,18 @@ def get_techniques( resolved_type = self._normalize_type(asset_type) asset_types = self._load_asset_types() entry = asset_types.get(resolved_type, asset_types.get("default", {})) + + # Use SPARTA for space assets + if entry.get("category") == "space": + return self._get_sparta_techniques( + resolved_type, asset_attrs, hop_position, + actor_known_ttps, actor_capable_tactics, top_k, + services, credentials_stored, entry, boosts, min_score + ) + + if not raw: + return [] + platforms = set(entry.get("platforms", ["Windows", "Linux"])) primary_tactics = entry.get("tactics", ["initial-access", "execution", "lateral-movement"]) key_techniques = set(entry.get("key_techniques", [])) @@ -303,6 +346,111 @@ def get_techniques( scored.sort(key=lambda t: t.score, reverse=True) return scored[:top_k] + def _get_sparta_techniques( + self, + resolved_type: str, + asset_attrs: Dict[str, Any], + hop_position: str, + actor_known_ttps: Optional[List[str]], + actor_capable_tactics: Optional[List[str]], + top_k: int, + services: Optional[Set[str]], + credentials_stored: bool, + entry: Dict, + boosts: Dict[str, float], + min_score: float, + ) -> List[ScoredTechnique]: + """Return SPARTA techniques for space-category assets.""" + raw_sparta = self._load_raw_sparta() + if not raw_sparta: + return [] + + primary_tactics = set(entry.get("tactics", [])) + key_techniques = set(entry.get("key_techniques", [])) + known_ttp_set = set(actor_known_ttps) if actor_known_ttps else set() + capable_tactic_set = set(actor_capable_tactics) if actor_capable_tactics else None + + hop_tactic_boost = { + "entry": {"initial-access", "execution", "reconnaissance"}, + "intermediate": {"lateral-movement", "persistence", "execution"}, + "target": {"impact", "collection", "exfiltration", "command-and-control"}, + }.get(hop_position, set()) + + no_auth = not asset_attrs.get("is_authenticated", False) and asset_attrs.get("authentication", "none") in ("none", "", None) + no_encryption = not asset_attrs.get("is_encrypted", False) + + scored: List[ScoredTechnique] = [] + + for tech in raw_sparta: + ext_refs = tech.get("external_references", []) + tech_id = next((r["external_id"] for r in ext_refs if r.get("source_name") == "sparta"), None) + tech_url = next((r.get("url", "") for r in ext_refs if r.get("source_name") == "sparta"), "") + if not tech_id: + continue + + # Collect tactic slugs from kill_chain_phases + tech_tactics = set() + for phase in tech.get("kill_chain_phases", []): + if phase.get("kill_chain_name") == "sparta": + tech_tactics.add(phase.get("phase_name", "")) + + if capable_tactic_set and not tech_tactics.intersection(capable_tactic_set): + continue + + score = 0.0 + reasons = [] + + # Platform always matches for space assets + score += boosts.get("platform_match", 0.5) + reasons.append("space platform") + + if tech_tactics.intersection(primary_tactics): + score += boosts.get("primary_tactic", 0.4) + reasons.append("primary tactic") + + if tech_tactics.intersection(hop_tactic_boost): + score += boosts.get("hop_position", 0.3) + reasons.append("hop position") + + if tech_id in key_techniques: + score += boosts.get("key_technique", 0.6) + reasons.append("key technique") + + if tech_id in known_ttp_set: + score += boosts.get("actor_known_ttp", 0.5) + reasons.append("actor TTP") + + if no_auth and tech_tactics.intersection({"initial-access", "lateral-movement"}): + score += boosts.get("no_auth", 0.3) + reasons.append("no-auth") + + if no_encryption and tech_tactics.intersection({"collection", "exfiltration"}): + score += boosts.get("no_encryption", 0.2) + reasons.append("cleartext") + + if score < min_score: + continue + + # Attach sparta_tactic_id to tactics list for AttackFlowBuilder + tactic_ids = [] + for phase in tech.get("kill_chain_phases", []): + if phase.get("kill_chain_name") == "sparta": + tid = phase.get("sparta_tactic_id", "") + if tid: + tactic_ids.append(tid) + + scored.append(ScoredTechnique( + id=tech_id, + name=tech.get("name", ""), + tactics=tactic_ids if tactic_ids else list(tech_tactics), + score=round(score, 2), + rationale=", ".join(reasons), + url=tech_url, + )) + + scored.sort(key=lambda t: t.score, reverse=True) + return scored[:top_k] + def _normalize_type(self, asset_type: str) -> str: if not asset_type: return "default" diff --git a/threat_analysis/core/data_loader.py b/threat_analysis/core/data_loader.py index 54975d9..d555bdb 100644 --- a/threat_analysis/core/data_loader.py +++ b/threat_analysis/core/data_loader.py @@ -307,4 +307,32 @@ def load_cis_to_mitre_mapping() -> Dict[str, Dict[str, List[str]]]: return {} +def load_sparta_techniques() -> Dict[str, Dict[str, Any]]: + """Load SPARTA techniques indexed by technique ID (e.g. 'IA-0006').""" + path = Path(__file__).parent.parent / 'external_data' / 'sparta-attack.json' + try: + with open(path, 'r', encoding='utf-8') as f: + data = json.load(f) + result = {} + for obj in data.get('objects', []): + if obj.get('type') != 'attack-pattern': + continue + ext_refs = obj.get('external_references', []) + tech_id = next((r['external_id'] for r in ext_refs if r.get('source_name') == 'sparta'), None) + if tech_id: + result[tech_id] = obj + return result + except Exception as exc: + logging.getLogger(__name__).error("Cannot load sparta-attack.json: %s", exc) + return {} + +def load_stride_to_sparta() -> Dict[str, List[str]]: + """Load STRIDE → SPARTA technique ID mapping.""" + path = Path(__file__).parent.parent / 'external_data' / 'stride_to_sparta.json' + try: + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + except Exception as exc: + logging.getLogger(__name__).error("Cannot load stride_to_sparta.json: %s", exc) + return {} diff --git a/threat_analysis/external_data/sparta-attack.json b/threat_analysis/external_data/sparta-attack.json new file mode 100644 index 0000000..58315c2 --- /dev/null +++ b/threat_analysis/external_data/sparta-attack.json @@ -0,0 +1,553 @@ +{ + "type": "bundle", + "id": "bundle--sparta-v1", + "spec_version": "2.1", + "objects": [ + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-REC-0003", + "name": "Remote System Information Discovery", + "description": "Adversaries gather information about space systems and ground infrastructure through passive or active reconnaissance of remote assets. This includes enumeration of spacecraft frequencies, protocols, and communication windows using publicly available orbital data and signal analysis tools.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "reconnaissance", + "sparta_tactic_id": "ST0001" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "REC-0003", + "url": "https://sparta.aerospace.org/technique/REC-0003" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-REC-0003.04", + "name": "TT&C Frequency Identification", + "description": "Adversaries identify the Telemetry, Tracking, and Command (TT&C) frequencies used by a spacecraft by monitoring amateur radio reports, ITU filings, satellite tracking databases, and signal intelligence. Knowledge of TT&C frequencies enables targeted jamming, spoofing, or eavesdropping on spacecraft communications.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "reconnaissance", + "sparta_tactic_id": "ST0001" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "REC-0003.04", + "url": "https://sparta.aerospace.org/technique/REC-0003.04" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-REC-0005", + "name": "Non-Standard Protocol Scanning", + "description": "Adversaries scan ground station networks and uplink/downlink infrastructure using non-standard or space-specific protocols (CCSDS, SLE, KISS, AX.25) to discover accessible services, equipment types, and system configurations. Unlike conventional TCP/IP scanning, this technique targets software-defined radios, modems, and mission control systems.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "reconnaissance", + "sparta_tactic_id": "ST0001" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "REC-0005", + "url": "https://sparta.aerospace.org/technique/REC-0005" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-RD-0003", + "name": "Develop Capabilities", + "description": "Adversaries build custom tools and malicious software tailored to target space systems. This includes developing firmware implants, software exploits for mission control applications, and RF signal manipulation tools that are not available commercially.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "resource-development", + "sparta_tactic_id": "ST0002" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "RD-0003", + "url": "https://sparta.aerospace.org/technique/RD-0003" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-RD-0003.01", + "name": "Malicious Software Development", + "description": "Adversaries develop malicious software specifically designed to compromise space mission systems, including ground station software implants, spacecraft on-board computer (OBC) firmware rootkits, and tools that exploit CCSDS or proprietary space communication protocols. These tools are purpose-built for the target mission profile.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "resource-development", + "sparta_tactic_id": "ST0002" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "RD-0003.01", + "url": "https://sparta.aerospace.org/technique/RD-0003.01" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-RD-0004", + "name": "Obtain Capabilities", + "description": "Adversaries acquire existing tools, exploits, or capabilities from external sources (dark web, exploit brokers, nation-state arsenals) that can be repurposed against space systems. This includes commercial satellite signal analysis software repurposed for offensive operations.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "resource-development", + "sparta_tactic_id": "ST0002" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "RD-0004", + "url": "https://sparta.aerospace.org/technique/RD-0004" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-RD-0004.01", + "name": "Exploit Framework Acquisition", + "description": "Adversaries obtain or adapt existing exploit frameworks (e.g., Metasploit modules, custom CCSDS fuzzing toolkits) to target known vulnerabilities in ground station software, mission control systems, or spacecraft on-board computers. Pre-built frameworks reduce development effort and increase attack speed.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "resource-development", + "sparta_tactic_id": "ST0002" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "RD-0004.01", + "url": "https://sparta.aerospace.org/technique/RD-0004.01" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-RD-0004.02", + "name": "Tool Download", + "description": "Adversaries download publicly available tools (software-defined radio utilities, satellite tracking suites, CCSDS packet analyzers) and configure them for offensive purposes. Open-source tools like GNU Radio, GPredict, or GMAT can be weaponized with minimal modification.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "resource-development", + "sparta_tactic_id": "ST0002" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "RD-0004.02", + "url": "https://sparta.aerospace.org/technique/RD-0004.02" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IA-0001", + "name": "Supply Chain Compromise", + "description": "Adversaries compromise the space system supply chain to insert malicious components or software before they reach the mission operator. Targets include spacecraft component manufacturers, launch integrators, ground station hardware vendors, and software library maintainers.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "initial-access", + "sparta_tactic_id": "ST0003" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IA-0001", + "url": "https://sparta.aerospace.org/technique/IA-0001" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IA-0001.02", + "name": "Supply Chain Software Compromise", + "description": "Adversaries insert malicious code into software used in space missions — flight software, ground station applications, mission planning tools, or CCSDS libraries — during development, build, or distribution. Compromised software may include backdoors, logic bombs, or capability-degrading bugs that activate under specific orbital or operational conditions.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "initial-access", + "sparta_tactic_id": "ST0003" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IA-0001.02", + "url": "https://sparta.aerospace.org/technique/IA-0001.02" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IA-0006", + "name": "RF Jamming / Spoofing", + "description": "Adversaries use radio frequency (RF) attacks to disrupt or deceive space system communications. Jamming floods the target frequency with noise to prevent legitimate commands or telemetry from being received. Spoofing transmits counterfeit signals that the spacecraft or ground station accepts as legitimate, potentially issuing false commands, injecting fake telemetry, or overriding GPS/GNSS position data.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "initial-access", + "sparta_tactic_id": "ST0003" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IA-0006", + "url": "https://sparta.aerospace.org/technique/IA-0006" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-EX-0009", + "name": "Telecommand Injection", + "description": "Adversaries inject unauthorized telecommands into the spacecraft command uplink. Without proper authentication of command sources, an attacker with access to the uplink frequency and knowledge of the command format can issue arbitrary commands to the spacecraft — including attitude control maneuvers, payload activation, or safe-mode transitions.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "execution", + "sparta_tactic_id": "ST0004" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "EX-0009", + "url": "https://sparta.aerospace.org/technique/EX-0009" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-EX-0009.01", + "name": "Telecommand Replay Attack", + "description": "Adversaries capture legitimate telecommand frames and retransmit them to the spacecraft at a later time to trigger repeated or unintended actions. Without sequence counters or time-bound authentication (e.g., CCSDS Space Data Link Security), replayed commands are indistinguishable from fresh commands.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "execution", + "sparta_tactic_id": "ST0004" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "EX-0009.01", + "url": "https://sparta.aerospace.org/technique/EX-0009.01" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-EX-0009.02", + "name": "Unauthorized Command Execution", + "description": "Adversaries who have gained access to ground station systems or communication channels execute unauthorized commands against the spacecraft. This may occur through compromised operator workstations, misconfigured command routing, or exploitation of ground software vulnerabilities that bypass command authorization checks.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "execution", + "sparta_tactic_id": "ST0004" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "EX-0009.02", + "url": "https://sparta.aerospace.org/technique/EX-0009.02" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-EX-0012", + "name": "Command Sequence Exploitation", + "description": "Adversaries craft or manipulate command sequences to exploit logic flaws in spacecraft flight software or ground station command processors. By sending commands in unexpected sequences or with boundary-condition parameters, attackers can trigger undefined behavior, memory corruption, or unintended mode transitions.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "execution", + "sparta_tactic_id": "ST0004" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "EX-0012", + "url": "https://sparta.aerospace.org/technique/EX-0012" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-EX-0012.06", + "name": "CCSDS Protocol Exploitation", + "description": "Adversaries exploit implementation vulnerabilities in CCSDS (Consultative Committee for Space Data Systems) protocol stacks used for spacecraft telemetry and telecommand. Malformed CCSDS packets, invalid Transfer Frame headers, or crafted Space Packet sequences can trigger buffer overflows, denial of service, or command injection in flight software or ground station decoders.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "execution", + "sparta_tactic_id": "ST0004" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "EX-0012.06", + "url": "https://sparta.aerospace.org/technique/EX-0012.06" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-EX-0012.08", + "name": "SLE API Exploitation", + "description": "Adversaries exploit vulnerabilities in the Space Link Extension (SLE) API implementation used between ground stations and mission control centers. SLE services (Return All Frames, Forward Communications Link, etc.) may contain authentication weaknesses, buffer overflows, or logic errors that allow unauthorized command injection or data interception.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "execution", + "sparta_tactic_id": "ST0004" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "EX-0012.08", + "url": "https://sparta.aerospace.org/technique/EX-0012.08" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-EX-0012.09", + "name": "Ground Station Software Exploitation", + "description": "Adversaries exploit vulnerabilities in ground station software — mission control applications, antenna controllers, modems, satellite communications management systems — to execute arbitrary code or commands. These systems often run legacy software with unpatched vulnerabilities and may have privileged access to spacecraft command uplinks.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "execution", + "sparta_tactic_id": "ST0004" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "EX-0012.09", + "url": "https://sparta.aerospace.org/technique/EX-0012.09" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-PER-0002", + "name": "Firmware Modification", + "description": "Adversaries modify the firmware of spacecraft on-board computers, payload instruments, or ground station hardware to establish persistent access or degrade mission capabilities. Firmware modifications survive power cycles and may be extremely difficult to detect without hardware-level integrity verification.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "persistence", + "sparta_tactic_id": "ST0005" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "PER-0002", + "url": "https://sparta.aerospace.org/technique/PER-0002" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-PER-0002.02", + "name": "Non-Volatile Memory Corruption", + "description": "Adversaries corrupt the non-volatile memory (EEPROM, Flash, MRAM) of spacecraft computers or embedded systems to inject persistent malicious code, overwrite configuration tables, or corrupt flight software parameters. On-orbit memory modification can be performed via unauthorized telecommand if memory write protections are absent or bypassable.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "persistence", + "sparta_tactic_id": "ST0005" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "PER-0002.02", + "url": "https://sparta.aerospace.org/technique/PER-0002.02" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-LM-0002", + "name": "Internal Spacecraft Bus Lateral Movement", + "description": "Adversaries who have compromised one subsystem of a spacecraft (e.g., the communication subsystem) leverage internal data buses (MIL-STD-1553, SpaceWire, CAN, I2C) to move laterally to other subsystems such as attitude control, power management, or payload instruments. Internal buses often lack authentication and bus isolation, enabling cross-subsystem command injection.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "lateral-movement", + "sparta_tactic_id": "ST0007" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "LM-0002", + "url": "https://sparta.aerospace.org/technique/LM-0002" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IMP-0001", + "name": "Payload Instrument Disruption", + "description": "Adversaries disrupt or damage spacecraft payload instruments through unauthorized commands, power cycling attacks, or thermal management manipulation. Targets may include imaging sensors, scientific instruments, communications transponders, or electronic warfare payloads. Disruption can range from temporary outage to permanent mission failure.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "impact", + "sparta_tactic_id": "ST0009" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IMP-0001", + "url": "https://sparta.aerospace.org/technique/IMP-0001" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IMP-0002", + "name": "Telemetry Data Manipulation", + "description": "Adversaries alter telemetry data transmitted from the spacecraft to the ground, causing operators to receive false readings about spacecraft health, position, attitude, or payload status. Manipulated telemetry can mask ongoing attacks, cause operators to issue incorrect corrective commands, or undermine mission data integrity.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "impact", + "sparta_tactic_id": "ST0009" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IMP-0002", + "url": "https://sparta.aerospace.org/technique/IMP-0002" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IMP-0003", + "name": "Denial of Control (DoS)", + "description": "Adversaries prevent ground operators from sending commands to the spacecraft or receiving telemetry, effectively denying control of the mission. Methods include RF jamming of TT&C frequencies, flooding ground station networks with traffic, compromising command routing infrastructure, or triggering safe-mode transitions that disable the command receiver.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "impact", + "sparta_tactic_id": "ST0009" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IMP-0003", + "url": "https://sparta.aerospace.org/technique/IMP-0003" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IMP-0004", + "name": "ADCS Manipulation", + "description": "Adversaries manipulate the Attitude Determination and Control System (ADCS) of a spacecraft by injecting false sensor data or unauthorized control commands. ADCS manipulation can cause antenna mispointing (loss of communication), solar panel misalignment (power failure), thermal control failure, or orbital maneuver errors potentially leading to collision or deorbit.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "impact", + "sparta_tactic_id": "ST0009" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IMP-0004", + "url": "https://sparta.aerospace.org/technique/IMP-0004" + } + ] + }, + { + "type": "attack-pattern", + "id": "attack-pattern--sparta-IMP-0005", + "name": "Signal Interception / Exfiltration", + "description": "Adversaries intercept spacecraft telemetry, mission data downlinks, or inter-ground-station communications to exfiltrate sensitive information. Targets include raw sensor data, encryption keys transmitted in-the-clear, mission planning data, or operator communications. Ground station downlinks may be intercepted using high-gain antennas without requiring access to any networked system.", + "x_mitre_platforms": ["Space Systems", "Embedded"], + "kill_chain_phases": [ + { + "kill_chain_name": "sparta", + "phase_name": "impact", + "sparta_tactic_id": "ST0009" + } + ], + "external_references": [ + { + "source_name": "sparta", + "external_id": "IMP-0005", + "url": "https://sparta.aerospace.org/technique/IMP-0005" + } + ] + } + ] +} diff --git a/threat_analysis/external_data/stride_to_sparta.json b/threat_analysis/external_data/stride_to_sparta.json new file mode 100644 index 0000000..9c4e623 --- /dev/null +++ b/threat_analysis/external_data/stride_to_sparta.json @@ -0,0 +1,8 @@ +{ + "Spoofing": ["IA-0001.02", "IA-0006"], + "Tampering": ["EX-0009", "EX-0009.01", "EX-0012.06", "IMP-0004"], + "Repudiation": ["REC-0005", "EX-0009.02"], + "Information Disclosure": ["REC-0003.04", "IMP-0005", "EX-0009.02"], + "Denial of Service": ["IA-0006", "IMP-0003"], + "Elevation of Privilege": ["PER-0002.02", "LM-0002", "EX-0012.09"] +} diff --git a/threat_analysis/generation/attack_flow_builder.py b/threat_analysis/generation/attack_flow_builder.py index 3b057ea..cebb7bd 100644 --- a/threat_analysis/generation/attack_flow_builder.py +++ b/threat_analysis/generation/attack_flow_builder.py @@ -224,16 +224,33 @@ def _make_anchors(self) -> tuple: return anchors, anchor_objs def _make_action_node(self, tech) -> Dict: - """Create an AFB action node from a ScoredTechnique.""" + """Create an AFB action node from a ScoredTechnique. + + Detects SPARTA technique IDs (e.g. IA-0006, EX-0009.01) vs ATT&CK (T1059) + and emits the correct tactic field format for each framework. + """ + import re instance_id = str(uuid.uuid4()) anchors, anchor_objs = self._make_anchors() - tactic_slug = tech.tactics[0] if tech.tactics else "unknown" + + is_sparta = bool(re.match(r'^[A-Z]{2,4}-\d{4}', tech.id)) + + if is_sparta: + # SPARTA format: tactic is the ST00xx code stored in the tactics list + # by _get_sparta_techniques (e.g. ["ST0004"]) + tactic_id = tech.tactics[0] if tech.tactics else "ST0000" + ttp_value = [["tactic", tactic_id], ["technique", tech.id]] + else: + # ATT&CK format: tactic is the phase slug (e.g. "execution") + tactic_slug = tech.tactics[0] if tech.tactics else "unknown" + ttp_value = [["tactic", tactic_slug], ["technique", tech.id]] + node = { "id": "action", "instance": instance_id, "properties": [ ["name", tech.name], - ["ttp", [["tactic", tactic_slug], ["technique", tech.id]]], + ["ttp", ttp_value], ["description", f"{tech.name} ({tech.id}) — {tech.rationale}"], ], "anchors": anchors, diff --git a/threat_analysis/generation/diagram_generator.py b/threat_analysis/generation/diagram_generator.py index 597ffd8..598ef77 100644 --- a/threat_analysis/generation/diagram_generator.py +++ b/threat_analysis/generation/diagram_generator.py @@ -309,7 +309,14 @@ def _get_node_attributes(self, element, node_type: str) -> str: # 3. Handle icon and label generation ICON_MAPPING = CONFIG_DATA["ICON_MAPPING"] lookup_key = element_type if element_type else node_type - icon_relative_path = ICON_MAPPING.get(lookup_key) + # Try key variants: raw → hyphen-to-underscore → fully stripped (matches config_generator.py output) + icon_relative_path = None + if lookup_key: + icon_relative_path = ( + ICON_MAPPING.get(lookup_key) + or ICON_MAPPING.get(lookup_key.replace('-', '_')) + or ICON_MAPPING.get(lookup_key.replace('-', '').replace('_', '')) + ) filesystem_icon_path = None if icon_relative_path: filesystem_icon_path = PROJECT_ROOT / 'threat_analysis' / 'server' / icon_relative_path.lstrip('/') diff --git a/threat_analysis/server/static/js/NodeManager.js b/threat_analysis/server/static/js/NodeManager.js index 2a2b095..497f8c1 100644 --- a/threat_analysis/server/static/js/NodeManager.js +++ b/threat_analysis/server/static/js/NodeManager.js @@ -67,7 +67,7 @@ class NodeManager { const fill = properties.color; const stroke = colors.stroke; const textColor = colors.text; - const iconPath = ThreatModelConfig.ICON_MAPPING[type.toLowerCase().replace('_', '')]; + const iconPath = ThreatModelConfig.ICON_MAPPING[type.toLowerCase().replace(/[_-]/g, '')]; const group = new Konva.Group({ x: x, @@ -266,21 +266,31 @@ class NodeManager { x: 0, y: 0, width: width, height: height, fill: fill, stroke: fill, strokeWidth: 2, name: 'shape', }); - text = new Konva.Text({ - x: PADDING, y: (height - TEXT_HEIGHT) / 2, text: name, fontSize: TEXT_HEIGHT, fill: textColor, - width: width - 2 * PADDING, align: 'center', verticalAlign: 'middle', - fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', name: 'label', - listening: false, - }); if (iconPath) { + const iconSize = Math.min(Math.floor(height * 0.55), 22); + const iconY = Math.floor((height - iconSize - 4 - TEXT_HEIGHT) / 2); + text = new Konva.Text({ + x: PADDING, y: iconY + iconSize + 4, text: name, fontSize: TEXT_HEIGHT, fill: textColor, + width: width - 2 * PADDING, align: 'center', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', name: 'label', + listening: false, wrap: 'none', + }); Konva.Image.fromURL(iconPath, (image) => { image.setAttrs({ - x: width - 32, y: height - 32, width: 24, height: 24, - listening: false, name: 'icon', + x: Math.floor((width - iconSize) / 2), y: iconY, + width: iconSize, height: iconSize, + listening: false, name: 'image', }); group.add(image); this.layer.draw(); }); + } else { + text = new Konva.Text({ + x: PADDING, y: (height - TEXT_HEIGHT) / 2, text: name, fontSize: TEXT_HEIGHT, fill: textColor, + width: width - 2 * PADDING, align: 'center', verticalAlign: 'middle', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', name: 'label', + listening: false, + }); } break; } @@ -302,22 +312,33 @@ class NodeManager { x: 0, y: 0, width: width, height: height, fill: fill, stroke: fill, strokeWidth: 2, name: 'shape', }); - text = new Konva.Text({ - x: PADDING, y: (height - TEXT_HEIGHT) / 2, text: name, fontSize: TEXT_HEIGHT, fill: textColor, - width: width - 2 * PADDING, align: 'center', verticalAlign: 'middle', - fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', name: 'label', - listening: false, - wrap: 'none' - }); if (iconPath && type !== 'ACTOR') { + // Icon + text centered vertically inside the shape. + const iconSize = Math.min(Math.floor(height * 0.55), 22); + const iconY = Math.floor((height - iconSize - 4 - TEXT_HEIGHT) / 2); + const textY = iconY + iconSize + 4; + text = new Konva.Text({ + x: PADDING, y: textY, text: name, fontSize: TEXT_HEIGHT, fill: textColor, + width: width - 2 * PADDING, align: 'center', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', name: 'label', + listening: false, wrap: 'none', + }); Konva.Image.fromURL(iconPath, (image) => { image.setAttrs({ - x: width - 32, y: height - 32, width: 24, height: 24, - listening: false, name: 'icon', + x: Math.floor((width - iconSize) / 2), y: iconY, + width: iconSize, height: iconSize, + listening: false, name: 'image', }); group.add(image); this.layer.draw(); }); + } else { + text = new Konva.Text({ + x: PADDING, y: (height - TEXT_HEIGHT) / 2, text: name, fontSize: TEXT_HEIGHT, fill: textColor, + width: width - 2 * PADDING, align: 'center', verticalAlign: 'middle', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', name: 'label', + listening: false, wrap: 'none', + }); } break; } diff --git a/threat_analysis/server/static/js/config.js b/threat_analysis/server/static/js/config.js index b98096a..472cd72 100644 --- a/threat_analysis/server/static/js/config.js +++ b/threat_analysis/server/static/js/config.js @@ -1,5 +1,5 @@ // Threat Model Configuration - Web UI -// Generated on: 2026-05-15 21:17:23 +// Generated on: 2026-05-18 21:30:14 // This file contains configuration for the threat model web interface const ThreatModelConfig = { @@ -17,7 +17,31 @@ const ThreatModelConfig = { "api_gateway": "/static/resources/icons/api-gateway.svg", "apigateway": "/static/resources/icons/api-gateway.svg", "load_balancer": "/static/resources/icons/load_balancer.svg", - "loadbalancer": "/static/resources/icons/load_balancer.svg" + "loadbalancer": "/static/resources/icons/load_balancer.svg", + "ip_camera": "/static/resources/icons/ip-camera.svg", + "ipcamera": "/static/resources/icons/ip-camera.svg", + "ptz_camera": "/static/resources/icons/ptz-camera.svg", + "ptzcamera": "/static/resources/icons/ptz-camera.svg", + "thermal_camera": "/static/resources/icons/thermal-camera.svg", + "thermalcamera": "/static/resources/icons/thermal-camera.svg", + "nvr": "/static/resources/icons/nvr.svg", + "vms": "/static/resources/icons/vms.svg", + "rtsp_server": "/static/resources/icons/rtsp-server.svg", + "rtspserver": "/static/resources/icons/rtsp-server.svg", + "iot_gateway": "/static/resources/icons/iot-gateway.svg", + "iotgateway": "/static/resources/icons/iot-gateway.svg", + "smart_lock": "/static/resources/icons/smart-lock.svg", + "smartlock": "/static/resources/icons/smart-lock.svg", + "smart_meter": "/static/resources/icons/smart-meter.svg", + "smartmeter": "/static/resources/icons/smart-meter.svg", + "leo_satellite": "/static/resources/icons/leo-satellite.svg", + "leosatellite": "/static/resources/icons/leo-satellite.svg", + "ground_station": "/static/resources/icons/ground-station.svg", + "groundstation": "/static/resources/icons/ground-station.svg", + "ttc_link": "/static/resources/icons/ttc-link.svg", + "ttclink": "/static/resources/icons/ttc-link.svg", + "onboard_computer": "/static/resources/icons/onboard-computer.svg", + "onboardcomputer": "/static/resources/icons/onboard-computer.svg" }, "DEFAULT_PROPERTIES": { "BOUNDARY": { diff --git a/threat_analysis/server/static/resources/icons/ground-station.svg b/threat_analysis/server/static/resources/icons/ground-station.svg new file mode 100644 index 0000000..da7d182 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/ground-station.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/iot-gateway.svg b/threat_analysis/server/static/resources/icons/iot-gateway.svg new file mode 100644 index 0000000..0fe1ea9 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/iot-gateway.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/ip-camera.svg b/threat_analysis/server/static/resources/icons/ip-camera.svg new file mode 100644 index 0000000..2dfd0f6 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/ip-camera.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/leo-satellite.svg b/threat_analysis/server/static/resources/icons/leo-satellite.svg new file mode 100644 index 0000000..01c5343 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/leo-satellite.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/nvr.svg b/threat_analysis/server/static/resources/icons/nvr.svg new file mode 100644 index 0000000..11e1686 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/nvr.svg @@ -0,0 +1,21 @@ + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/onboard-computer.svg b/threat_analysis/server/static/resources/icons/onboard-computer.svg new file mode 100644 index 0000000..9a505a8 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/onboard-computer.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/ptz-camera.svg b/threat_analysis/server/static/resources/icons/ptz-camera.svg new file mode 100644 index 0000000..b6ec722 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/ptz-camera.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/rtsp-server.svg b/threat_analysis/server/static/resources/icons/rtsp-server.svg new file mode 100644 index 0000000..1273852 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/rtsp-server.svg @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/smart-lock.svg b/threat_analysis/server/static/resources/icons/smart-lock.svg new file mode 100644 index 0000000..29b4895 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/smart-lock.svg @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/smart-meter.svg b/threat_analysis/server/static/resources/icons/smart-meter.svg new file mode 100644 index 0000000..5bb1770 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/smart-meter.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/thermal-camera.svg b/threat_analysis/server/static/resources/icons/thermal-camera.svg new file mode 100644 index 0000000..75d7509 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/thermal-camera.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/ttc-link.svg b/threat_analysis/server/static/resources/icons/ttc-link.svg new file mode 100644 index 0000000..8506626 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/ttc-link.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/threat_analysis/server/static/resources/icons/vms.svg b/threat_analysis/server/static/resources/icons/vms.svg new file mode 100644 index 0000000..29b3675 --- /dev/null +++ b/threat_analysis/server/static/resources/icons/vms.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/threat_analysis/server/templates/simple_mode.html b/threat_analysis/server/templates/simple_mode.html index 1b574ce..69cbf73 100644 --- a/threat_analysis/server/templates/simple_mode.html +++ b/threat_analysis/server/templates/simple_mode.html @@ -661,7 +661,7 @@ border-left-color: #42a5f5; color: #90caf9; } - .btn-draft-discard { + .btn-draft-restore, .btn-draft-discard { padding: 2px 8px; font-size: 11px; border: 1px solid currentColor; @@ -671,7 +671,8 @@ cursor: pointer; white-space: nowrap; } - .btn-draft-discard:hover { opacity: 0.75; } + .btn-draft-restore { font-weight: 600; } + .btn-draft-restore:hover, .btn-draft-discard:hover { opacity: 0.75; } /* DSL validation banner */ #dsl-validation-banner { @@ -1168,12 +1169,24 @@

Generate Threat Model with AI

let draft = null; try { draft = localStorage.getItem(DRAFT_KEY); } catch(e) {} if (draft && draft.trim() && draft !== content) { - editor.setValue(draft); const banner = document.createElement('div'); banner.className = 'draft-restore-banner'; - banner.innerHTML = '📄 Draft from last session restored. '; - banner.querySelector('.btn-draft-discard').addEventListener('click', () => { - editor.setValue(content); + const bannerText = document.createTextNode('📄 Unsaved draft from last session. '); + const restoreBtn = document.createElement('button'); + restoreBtn.className = 'btn-draft-restore'; + restoreBtn.textContent = 'Restore'; + const discardBtn = document.createElement('button'); + discardBtn.className = 'btn-draft-discard'; + discardBtn.textContent = 'Discard'; + banner.appendChild(bannerText); + banner.appendChild(restoreBtn); + banner.appendChild(document.createTextNode(' ')); + banner.appendChild(discardBtn); + restoreBtn.addEventListener('click', () => { + editor.setValue(draft); + banner.remove(); + }); + discardBtn.addEventListener('click', () => { try { localStorage.removeItem(DRAFT_KEY); } catch(e) {} banner.remove(); }); @@ -1448,9 +1461,13 @@

Generate Threat Model with AI

document.getElementById('tab-bar').innerHTML = ''; document.getElementById('tab-content-area').innerHTML = ''; - // Add tabs for .md files + // Add tabs for .md files. + // Prefix path with the directory name so the draft key + // is unique per project (avoids cross-template autosave collisions + // when different projects share the same filename, e.g. "model.md"). models.forEach((model, index) => { - this.addTab(model.path, model.content, index !== 0 || models.length > 1); + const tabPath = dirHandle.name + '/' + model.path; + this.addTab(tabPath, model.content, index !== 0 || models.length > 1); }); if (models.length > 0) this.switchTab(0);