threatmap

March 25, 2026 Β· View on GitHub

2026-03-25_12-36

threatmap

CI Version PyPI Python Docker STRIDE MITRE PASTA GraphQL API License: MIT Offline

Static IaC threat modeler that parses Terraform, CloudFormation, and Kubernetes manifests and produces structured threat model reports using STRIDE, MITRE ATT&CK, or PASTA frameworks. No network calls, no cloud credentials, fully offline. Runs as a CLI, REST API, or containerized service.


Quick Start

CLI:

pip install threatmap
threatmap scan ./examples --output report.md --fail-on HIGH

Docker:

docker run -v $(pwd):/workspace bogdynn/threatmap:2.1.0 threatmap scan /workspace --output /workspace/report.md

REST API Server:

threatmap serve --host 0.0.0.0 --port 8000
# Or via Docker:
docker run -p 8000:8000 bogdynn/threatmap:2.1.0
# API endpoints: /health, /version, /rules, /analyze

GraphQL API:

docker run -p 8000:8000 bogdynn/threatmap:2.1.0
# GraphQL endpoint: http://localhost:8000/graphql
# Queries: health, version, rules
# Mutations: analyze(content, filename, framework)

Supported Formats and Providers

FormatProviderExtension
Terraform HCLAWS, Azure, GCP.tf
CloudFormationAWS.yaml, .yml, .json
Kubernetes manifestsKubernetes.yaml, .yml

Install

Install from PyPI:

pip install threatmap

Or for local development:

git clone https://github.com/bogdanticu88/threatmap.git
cd threatmap
pip install -e .

Usage

Scan a directory and print a Markdown report to stdout:

threatmap scan ./terraform/

Scan multiple paths and write a JSON report to a file:

threatmap scan ./terraform/ ./k8s/ ./cloudformation/ --format json --output report.json

Generate an interactive HTML report or a SARIF report for GitHub Security:

threatmap scan ./infra/ --format html --output report.html
threatmap scan ./infra/ --format sarif --output report.sarif

CI gate β€” exit code 1 if any CRITICAL or HIGH threat is found:

threatmap scan ./infra/ --fail-on HIGH --output threat-report.md

Print a terminal summary table only, without writing a full report:

threatmap scan ./infra/ --summary

Use ASCII-only severity indicators (no emojis) for environments that don't support Unicode:

threatmap scan ./infra/ --ascii --output report.md

Analyze using different threat modeling frameworks:

# STRIDE (default)
threatmap scan ./infra/ --framework stride

# MITRE ATT&CK (maps to tactics and techniques)
threatmap scan ./infra/ --framework mitre --format json

# PASTA (asset-centric threat modeling)
threatmap scan ./infra/ --framework pasta --format json

Threat Modeling Frameworks

STRIDE (73 rules)

  • Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege
  • Threat-centric approach ideal for identifying attack surface
  • Provider-specific: AWS (22 rules), Azure (19 rules), GCP (15 rules), Kubernetes (17 rules)
  • Best for: Traditional threat modeling, security architecture reviews

MITRE ATT&CK (11 rules, 14 tactics)

  • Maps infrastructure threats to real-world adversary tactics and techniques
  • Resource-aware technique selection for accurate TTP mapping
  • Tactics: Reconnaissance, Initial Access, Execution, Persistence, Privilege Escalation, Defense Evasion, Credential Access, Discovery, Collection, Command & Control, Exfiltration, Impact, Lateral Movement
  • Best for: Aligning with threat intelligence, incident response planning, red team exercises

PASTA (12 rules, asset-centric)

  • Process for Attack Simulation and Threat Analysis
  • Asset-centric approach focusing on what needs protection
  • Asset types: Data, Identity, Compute, Network, Infrastructure
  • Threat actors: Internal, External, Misconfiguration, Supply Chain
  • Best for: Risk-based prioritization, asset protection strategies, supply chain threats

Sample Report Output

Running threatmap scan ./examples --output report.md against the bundled examples produces a full Markdown report. Below is a representative excerpt.

STRIDE Threat Table

IDSeveritySTRIDE CategoryResourceDescription
T-001πŸ”΄ CRITICALInformation DisclosureAuditBucketS3 bucket 'AuditBucket' has no public access block configured β€” bucket may be publicly accessible.
T-002πŸ”΄ CRITICALSpoofingWebSecurityGroupSecurity group 'WebSecurityGroup' exposes SSH/RDP (port 22/3389) to 0.0.0.0/0.
T-003πŸ”΄ CRITICALElevation of Privilegeapp_contributorRole assignment 'app_contributor' grants the privileged role 'Contributor'.
T-006🟠 HIGHInformation DisclosureAuditBucketS3 bucket 'AuditBucket' does not have server-side encryption configured.
T-008🟠 HIGHElevation of PrivilegeapiContainer 'api' in Deployment 'api' may run as root (no runAsNonRoot=true or runAsUser=0).
T-011🟠 HIGHElevation of PrivilegewebEC2 instance 'web' allows IMDSv1 β€” metadata service accessible without session tokens, enabling SSRF-based credential theft.

Mitigation Detail (excerpt)

### T-002 β€” Spoofing (CRITICAL)

Resource:   AWS::EC2::SecurityGroup.WebSecurityGroup
Property:   ingress.ssh_rdp_open
Finding:    Security group 'WebSecurityGroup' exposes SSH/RDP (port 22/3389) to 0.0.0.0/0.
Mitigation: Remove public SSH/RDP access. Use AWS Systems Manager Session Manager
            or a bastion host with IP restrictions.

Data Flow Diagram (Mermaid)

The report appends a Mermaid flowchart LR diagram. Nodes are coloured by worst-case severity (πŸ”΄ red = CRITICAL, 🟠 orange = HIGH). Paste the block into any Mermaid renderer or view it directly on GitHub.

flowchart LR
    Internet((Internet))
    subgraph Networking
        aws_security_group_web_sg{web_sg}
        NetworkPolicy_default_deny{default-deny}
        azurerm_network_security_group_app_nsg{app_nsg}
    end
    subgraph Compute
        aws_instance_web[web]
    end
    subgraph Kubernetes
        Namespace_myapp[myapp]
        Deployment_api[api]
        Service_api_svc[api-svc]
        Ingress_api_ingress[api-ingress]
    end
    subgraph Data
        aws_s3_bucket_app_data[(app_data)]
        aws_db_instance_app_db[(app_db)]
        azurerm_storage_account_app_storage[(app_storage)]
    end
    subgraph Security
        azurerm_key_vault_app_kv[app_kv]
    end
    subgraph Identity
        azurerm_role_assignment_app_contributor[/app_contributor/]
    end
    AWS__S3__Bucket_AppBucket -->|ref| AWS__S3__Bucket_AuditBucket
    AWS__CloudTrail__Trail_AppTrail -->|ref| AWS__S3__Bucket_AuditBucket
    Internet -->|HTTPS| Ingress_api_ingress
    style aws_security_group_web_sg fill:#ff4444,color:#fff
    style aws_s3_bucket_app_data fill:#ff4444,color:#fff
    style aws_instance_web fill:#ff8800,color:#fff
    style Deployment_api fill:#ff8800,color:#fff
    style azurerm_key_vault_app_kv fill:#ffcc00,color:#000
    style azurerm_network_security_group_app_nsg fill:#ff8800,color:#fff
    style azurerm_role_assignment_app_contributor fill:#ff4444,color:#fff

Advanced Features (v2.1.0+)

Graph-based Attack Path Analysis

threatmap now includes Graph Intelligence that traces relationships between resources. It automatically identifies "chained" threats where a compromise of one resource (e.g., an internet-exposed EC2) leads directly to another (e.g., a private S3 bucket), flagging these as Elevation of Privilege attack paths.

Custom YAML Rules

You can define internal security requirements by creating a threatmap_rules.yaml in your project root.

rules:
  - resource_type: "aws_s3_bucket"
    property: "force_destroy"
    expected: false
    stride: "Tampering"
    severity: "MEDIUM"
    description: "Production buckets should not have force_destroy enabled."
    mitigation: "Set force_destroy = false."

Remediation Hints

Most findings now include a remediation field (visible in JSON, HTML, and SARIF reports) that provides the exact code snippet needed to fix the security issue.


Architecture

STRIDE Analyzer β€” Provider-specific threat rules:

threatmap/analyzers/
β”œβ”€β”€ aws.py         # 22 rules β€” S3, IAM, EC2, RDS, EKS, CloudTrail, KMS, Lambda
β”œβ”€β”€ azure.py       # 19 rules β€” Storage, Key Vault, NSG, RBAC, AKS, ACR, SQL
β”œβ”€β”€ gcp.py         # 15 rules β€” GCS, Firewall, Compute, Cloud SQL, GKE, IAM, KMS
└── kubernetes.py  # 17 rules β€” workloads, RBAC, network, secrets

MITRE ATT&CK Analyzer β€” 11 rules mapped to MITRE tactics:

  • Resource-aware technique selection
  • Supports: IAM, Storage, Network, Compute, Kubernetes, Databases
  • Provides tacticβ†’technique mapping for threat intelligence alignment

PASTA Analyzer β€” 12 rules with asset-centric focus:

  • Classifies resources by asset type (data, identity, compute, network, infrastructure)
  • Identifies threat actors (internal, external, misconfiguration, supply chain)
  • Assigns attack scenarios for each threat

APIs:

  • REST API: /health, /version, /rules, /analyze, /analyze/file
  • GraphQL API: /graphql with Query (health, version, rules) and Mutation (analyze)

Each rule is a function that receives a Resource object (normalised from whatever source format was parsed) and returns a Threat if the condition is met. Rules are plain Python conditionals β€” no DSL, no regex engine, no external ruleset files.

How severities are assigned

Severity reflects both exploitability and blast radius:

SeverityMeaning
CRITICALDirectly exploitable with no additional preconditions (e.g. SSH open to 0.0.0.0/0, wildcard IAM policy, cluster-admin binding to anonymous)
HIGHSignificant risk requiring one additional step (e.g. unencrypted RDS with public access, IMDSv1 on an EC2 instance)
MEDIUMDefence-in-depth controls missing β€” lower immediate risk but violates security baselines (e.g. no versioning, no logging, no resource limits)
LOWBest-practice gaps with limited standalone exploitability (e.g. Lambda not in VPC)

How false positives are avoided

  • No heuristics or ML β€” every rule fires on a concrete, unambiguous property value (e.g. publicly_accessible = true, Principal: "*").
  • Conservative defaults β€” if a property is absent, the rule assumes the insecure default (e.g. no metadata_options block on an EC2 instance means IMDSv1 is active, because that is AWS's default).
  • No cross-account or runtime state β€” the tool only looks at what is declared in the template. It does not attempt to infer what SCPs, permission boundaries, or runtime configs might mitigate a finding.
  • Deduplication in the engine β€” findings are keyed on (stride_category, resource_name, trigger_property) so the same logical issue is never reported twice even if it appears across multiple file formats.

CI Integration

# .github/workflows/threat-model.yml
name: Threat Model

on: [pull_request]

jobs:
  threatmap:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install threatmap
        run: pip install threatmap

      - name: Run threat model scan
        run: |
          threatmap scan ./infra/ \
            --format markdown \
            --output threat-report.md \
            --fail-on HIGH

      - name: Upload threat report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: threat-report
          path: threat-report.md

The --fail-on HIGH flag makes the job exit with code 1 if any HIGH or CRITICAL threat is found, blocking the PR merge. The uploaded artifact gives reviewers the full report without leaving the pull request.


STRIDE Rule Coverage

ProviderRules
AWS (Terraform + CloudFormation)22
Azure (Terraform)19
GCP (Terraform)15
Kubernetes17
Total73

Categories covered per provider:

ProviderSTRIDE
AWSβœ“βœ“βœ“βœ“βœ“βœ“
Azureβœ“βœ“βœ“βœ“β€”βœ“
GCPβœ“βœ“βœ“βœ“β€”βœ“
Kubernetesβœ“βœ“β€”βœ“βœ“βœ“

(S=Spoofing, T=Tampering, R=Repudiation, I=Information Disclosure, D=Denial of Service, E=Elevation of Privilege)


Development

Run tests:

pytest tests/ -v

Run with coverage:

pytest tests/ --cov=threatmap --cov-report=term-missing

Contributing

  1. Fork the repository
  2. Add rules in threatmap/analyzers/<provider>.py following the existing pattern
  3. Add a fixture in tests/fixtures/ that triggers the new rule
  4. Add assertions in tests/test_analyzers.py
  5. Open a pull request