AI-assisted CTI-to-detection workbench for MITRE ATT&CK mapping and detection-gap analysis.
Current release: v0.9.0 · Live Intelligence Workspace · Documentation & Usage Guide · Medium Walkthrough
ThreatMapper AI is a self-hosted CTI-to-detection workbench for mapping threat reports to MITRE ATT&CK, comparing TTP overlap with known groups and campaigns, identifying detection gaps, and exporting analyst-ready outputs.
Live Web Workspace: https://1200km.com/threat-matrix/
Project Hub: https://1200km.com/threatmapper/
Documentation: https://1200km.com/threatmapper-docs/
Medium Walkthrough: https://medium.com/@1200km/threatmapper-i-built-a-self-hosted-ai-threat-intelligence-platform-heres-how-to-use-it-0aa7673e6bd8
Validation and attribution limitation: ThreatMapper assists analysts but does not replace analyst validation. LLM-generated mappings may contain false positives, false negatives, or ambiguous technique assignments. Group/campaign similarity is based on TTP overlap and is an investigation lead, not attribution proof.
ThreatMapper is pre-v1.0, but the repository now publishes the operational evidence expected from a serious security tool:
| Area | Evidence |
|---|---|
| Installability | Quickstart, Docker Compose deployment, .env.example |
| Analyst documentation | User guide, comparison, limitations |
| Operations | Admin guide, security model, security policy |
| Quality | Backend unit/integration tests, frontend build, CI workflow |
| Reviewability | Demo dataset, expected mappings, sample outputs |
| Validation | Evaluation plan, mapping review rubric |
| Maintenance | Maintainers, roadmap, changelog, contributing guide |
| Production readiness | Production readiness tracker |
Curated-list resubmission should wait until the project has additional release history and external usage evidence. The current documentation is intended to make that path measurable rather than promotional.
The public Web workspace is intended for exploration. Do not upload confidential, customer-sensitive, classified, or internal reports into public demos or third-party environments. Use the self-hosted Docker deployment for private analysis.
ThreatMapper assists analysts but does not replace analyst validation. LLM-generated ATT&CK mappings may include false positives, false negatives, or ambiguous technique assignments. Group and campaign similarity is based on TTP overlap and should be treated as an investigation lead, not attribution proof.
Screenshot evidence is preserved in docs/screenshots/.
The set covers the public ATT&CK matrix workspace, group overlay workflows,
analysis views, report/evidence review, and ecosystem navigation from the
companion walkthrough.
| Matrix and actor workflow | Analysis and review workflow |
|---|---|
![]() |
![]() |
![]() |
![]() |
- Features
- Architecture
- Quick Start
- Usage Guide
- Two-Database Architecture
- API Reference
- Configuration
- Development
- Deployment
- Tech Stack
- Changelog
| Module | Capability |
|---|---|
| Navigator | Full ATT&CK matrix (Enterprise, Mobile, ICS) with D3.js zoom/pan, sub-technique expansion, dual-layer colouring |
| Threat Actor Library | Currently ingested MITRE ATT&CK group profiles, aliases, techniques, and named campaign relationships |
| AI Analysis | Upload PDF/DOCX/TXT or paste text → streamed LLM extraction of ATT&CK mapping candidates; results saved to Reports Library (DB 2) |
| Compare — Groups | Jaccard similarity ranking of your TTPs vs currently ingested group profiles; visual matrix diff, tactic breakdown, gap analysis |
| Compare — Campaigns | Jaccard similarity ranking of your TTPs vs every named MITRE campaign (e.g. SolarWinds C0024, Operation Ghost C0023) |
| Compare — Reports | Browse stored AI analyses (DB 2); re-run group-similarity comparison without re-calling the LLM |
| Export | ATT&CK Navigator JSON layers, PDF threat intelligence reports, plain JSON |
| MITRE Sync | Auto-detects new ATT&CK releases daily (Celery beat), manual sync via API; sidebar shows staleness indicator |
| Anomaly Detection Reference Book | Docker-served, autonomously synchronized reference catalogs with exact paragraph-level links from every mapped matrix TTP |
| Intelligence Pipeline | Scheduled reviewed RSS intake, STIX/TAXII, MISP and ATLAS imports, normalized observables, public enrichment, team audit trail |
| Detection Studio | Versioned Sigma, KQL, SPL and EQL skeleton generation with structural validation and explicit analyst-review placeholders |
| Operations | Investigations, evidence graphs, report intake, tracked actor changes, and detection engineering lifecycle |
ThreatMapper Web is the public browser-native workspace for ATT&CK exploration, manual layers, group overlays and comparisons, local workspaces, ecosystem research, coverage-gap analysis, and browser-generated exports. It does not perform LLM report extraction or backend private-report storage.
ThreatMapper Docker is the full self-hosted platform for provider-configured AI extraction, private PostgreSQL-backed analyses, campaigns, APIs, PDF reports, detection-rule workflows, and scheduled ATT&CK synchronization.
ThreatMapper is self-hosted. In Docker mode, report content is sent only to the LLM provider configured by the operator. For fully private analysis, use a local or private LLM gateway. The public Web workspace does not perform LLM report extraction or backend report storage.
The Docker deployment gives the operator control over storage, networking, and provider configuration. Trusted-header authentication and roles are available when configured, but internet-facing deployments still require TLS, an authenticating reverse proxy, restricted network exposure, backups, retention controls, and secrets management.
┌──────────────────────────────────────────────────────────────────┐
│ Docker Compose │
├────────────────┬───────────────┬──────────────┬─────────────────┤
│ React / Vite │ FastAPI │ PostgreSQL │ Redis + Celery │
│ (port 3000) │ (port 8000) │ 16 │ worker + beat │
│ │ │ │ │
│ Vite proxy │ SQLAlchemy │ DB 1: ATT&CK│ daily MITRE │
│ /api → :8000 │ async ORM │ DB 2: Reports sync job │
└────────────────┴───────────────┴──────────────┴─────────────────┘
Backend — Python 3.12, FastAPI, SQLAlchemy 2.x (async), Celery
Frontend — React 18, TypeScript, Vite, D3.js, Tailwind CSS, Zustand
Database — PostgreSQL 16 with JSONB for ATT&CK STIX data
Queue — Redis + Celery (daily MITRE sync at 03:00 UTC)
User uploads report
│
▼
_read_input() ← stream with 50 MB byte-cap, size-check before buffer
│
▼
LLMAdapter.extract() ← Claude / OpenAI / Gemini
│
▼
_parse_response() ← JSON extraction with raw_decode fallback
│
▼
_rank_apt_groups() ← Jaccard TTP overlap vs group profiles in DB 1
│
▼
AnalysisResult → DB 2 ← session + name, techniques, similarity leads, domain
│
▼
Frontend renders ← techniques table, group similarity ranking, Navigator injection
| Database | What it holds | Key tables |
|---|---|---|
| DB 1 (MITRE) | ATT&CK groups, campaigns, techniques, relationships — ingested from official STIX bundles | apt_groups, campaigns, campaign_techniques, apt_group_campaigns, techniques |
| DB 2 (Reports) | Every AI analysis you run: extracted techniques, summary, group-similarity leads, name, domain | analysis_sessions, analysis_results |
- Docker + Docker Compose (v2)
- API key for at least one LLM provider (Claude, OpenAI, or Gemini)
git clone https://github.com/anpa1200/threatmapper.git
cd threatmapper
cp .env.example .envEdit .env and add your API keys:
# Required: at least one AI provider key
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=AIza...
# Database (defaults are fine for local use)
DB_NAME=threatmapper
DB_USER=tm_user
DB_PASS=changeme_strong_password
# ATT&CK domains to ingest (comma-separated)
ATTCK_DOMAINS=enterprise-attack,mobile-attack,ics-attack
LOG_LEVEL=infoYou only need one LLM key. The others can be left blank — the UI only shows providers that have a key configured.
You must create
.envbefore runningdocker compose up. Without it the API keys are empty and AI Analysis will return 500.
docker compose upFirst startup takes 5–15 minutes. The API container automatically:
- Runs
create_tables()to initialise the PostgreSQL schema - Downloads the latest ATT&CK STIX bundles from MITRE's GitHub (~105 MB total for all three domains)
- Parses the STIX 2.1 JSON directly (no third-party ATT&CK library — Python 3.12 compatible)
- Upserts tactics, techniques, groups, campaigns, and all relationships into PostgreSQL
The atlas-builder container builds the embedded reference-book snapshot immediately,
then synchronizes it with the standalone Anomaly Detection Atlas repository every hour.
Set ATLAS_SYNC_INTERVAL=0 to disable remote synchronization. Run make sync-atlas
to import unpushed changes from a local sibling anomaly-detection-atlas checkout.
Watch progress:
docker compose logs -f apiExpected output (v19.1):
Parsing enterprise-attack-19.1.json ...
Parsed: current tactics, techniques, groups, campaigns, and relationships
Ingested 15 tactics
Ingested techniques from the selected ATT&CK release
Ingested groups from the selected ATT&CK release
Ingested campaigns from the selected ATT&CK release
Ingested campaign-technique and group-campaign attribution links
Finished ingesting enterprise-attack v19.1
| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Reference book | http://localhost:3001/anomaly-detection-atlas/ |
| API docs | http://localhost:8000/docs |
| Health | http://localhost:8000/api/health |
The central workspace. The full ATT&CK matrix renders as a colour-coded heatmap.
| Action | How |
|---|---|
| Zoom / pan | Scroll to zoom, drag to pan. Double-click resets view. |
| Select a technique | Click any cell — turns red, added to your TTP layer |
| Expand sub-techniques | Click the ▶ arrow on any parent cell |
| Open detail panel | Click a cell to open the right-side panel (full description, detection notes, data sources, exact reference paragraphs, AI assistant) |
| Search | Type in the search box to filter by name or ATT&CK ID |
| Filter by platform | Use the platform dropdown (Windows, Linux, macOS, Cloud, etc.) |
| Filter by tactic | Use the tactic dropdown to focus on a specific kill-chain phase |
| Button | Action |
|---|---|
| ↑ Import layer | Load an existing ATT&CK Navigator .json layer |
| ↓ Navigator layer | Export your TTPs + overlay as ATT&CK Navigator JSON |
| Export current layer as a formatted PDF report | |
| Expand all / Collapse all | Toggle sub-technique visibility |
| Clear my TTPs | Reset your selection |
| Clear overlay | Remove the group-profile overlay |
| Colour | Meaning |
|---|---|
Red #e94560 |
In your TTP layer |
Blue #3b82f6 |
In the group-profile overlay only |
Amber #f59e0b |
In both layers (shared TTPs) |
| Dark | Not selected |
Click any technique to open the detail panel with an embedded chat. The full ATT&CK description for the selected technique is already in context. Example prompts:
- "What are the most common detections for this technique?"
- "Write a SIGMA rule skeleton for T1059.001"
- "What ATT&CK groups use this in combination with lateral movement?"
ThreatMapper integrates the complete Anomaly Detection Atlas as an autonomous Docker service. Click Reference Book in the sidebar to open the full documentation site.
Each matrix technique panel loads ttp-reference-index.json and shows links only to exact matching
paragraphs or table rows. A technique can link to multiple relevant activity descriptions, basic
detection rules, and statistical-anomaly mappings. The links use stable generated anchors such as:
http://localhost:3001/anomaly-detection-atlas/attack-basic-detection-rule-catalog/#ttp-t1059-001
http://localhost:3001/anomaly-detection-atlas/attack-statistical-anomaly-mapping/#ttp-t1030
The embedded snapshot makes the book usable without a successful remote synchronization. The
atlas-builder service checks the standalone atlas repository every hour, regenerates exact TTP
anchors and the crosslink index, then atomically publishes the updated Docusaurus build.
Manual local synchronization:
make sync-atlas
docker compose up -d --build atlas-builder atlas-docs frontendAnalyse threat intelligence documents and automatically map every observable behaviour to ATT&CK. Each completed analysis is saved to the Reports Library (DB 2).
- Click Analyze in the sidebar
- Select an LLM provider and optionally specify a model
- Choose a domain (
enterprise-attack,mobile-attack, orics-attack) - Optionally enter a name for this analysis (shown in the Reports Library later)
- Either paste text or upload a file (PDF, DOCX, TXT — up to 50 MB)
- Click Analyse with AI
- Watch the live SSE token stream as the model thinks
- Review the three tabs:
| Tab | Content |
|---|---|
| Techniques | ATT&CK technique mappings with confidence score, tactic, and evidence snippet |
| Group Similarity Leads | Top group profiles ranked by Jaccard TTP overlap |
| Raw Response | Full LLM JSON output for debugging |
- Click → Inject into Navigator to push all extracted techniques into your layer
| Score | Meaning |
|---|---|
| 90–100 % | Explicitly stated in the text |
| 70–89 % | Strongly implied |
| 40–69 % | Weakly implied or inferred from context |
| < 40 % | Speculative — treat with caution |
| Type | Notes |
|---|---|
.pdf |
Text extraction via PyMuPDF |
.docx |
Paragraphs and table cells extracted via python-docx |
.txt / plain text |
UTF-8, latin-1, CP1252 auto-detected |
Files are truncated at 120,000 characters before being sent to the LLM.
Browse the threat groups in the currently ingested ATT&CK release. Each group has two tabs:
- All known techniques with ATT&CK IDs, tactic, and use description
- Add all to my TTPs — bulk-load every technique into your Navigator layer
- Overlay on Navigator — show the group's TTPs as a blue overlay on the matrix
Named operations attributed to this group, parsed from MITRE ATT&CK STIX data.
Each campaign card shows:
- ATT&CK campaign ID (e.g. C0024), name, and date range
- Technique count for that specific operation
- Expand to see the full technique list with tactic tags
- Add to my TTPs — load this campaign's specific TTP fingerprint into Navigator
Why campaigns matter: A group's aggregate profile is the union of all operations over years. A campaign profile is specific to one attack. Matching against C0024 (SolarWinds Compromise) at 45% similarity is a more specific lead than matching against G0016 (APT29) at 15%.
Rank ATT&CK groups and campaigns against your TTPs using Jaccard similarity.
Jaccard similarity = |shared techniques| / |union of all techniques|
Use the mode switcher at the top of the Compare page to choose what to compare against:
Rank every ATT&CK group against your current Navigator selection.
| Detail tab | Content |
|---|---|
| Overview | Similarity score, shared techniques (amber chips), your-only techniques |
| Tactic Breakdown | Stacked bar per kill-chain phase: shared / user-only / group-profile-only |
| Visual Diff | Compact matrix colour-strip showing the full overlap |
| Gap Analysis | Every technique in the group's profile not in your layer — your detection backlog |
Actions:
- Overlay in Navigator — visualise the overlap on the full matrix
- ↓ PDF Report — export a formatted comparison report
Rank named campaigns from the currently ingested ATT&CK release against your current Navigator selection.
The detail panel shows:
- Similarity score, shared techniques highlighted in purple
- Full campaign technique list with overlap indicators
- Attribution (which group conducted this campaign)
- Date range of the campaign
Browse your stored AI analysis sessions. Click any report body to see which group and campaign profiles have the strongest TTP overlap with its extracted profile — without re-running the expensive LLM call.
Use cases:
- Retrospective TTP-overlap review after a new ATT&CK version is released
- Cross-incident correlation across multiple saved reports
- Environmental profiling — which groups keep appearing across your incident set
Per-session actions:
- ↓ PDF — download the full analysis PDF for that session at any time
- ✕ Remove — delete the session from DB 2 (browser confirm required; list refreshes automatically)
From Analyze, click Download PDF on any completed analysis. Includes:
- Cover page with provider, model, domain, session ID, timestamp
- Executive summary (AI-generated)
- Extracted techniques table sorted by confidence
- Group-similarity section with top Jaccard-overlap leads
- Tactic coverage breakdown
From Navigator, click ↓ PDF in the toolbar. Lists all techniques in your current layer with tactics and platforms.
Click ↓ Navigator layer to download a .json file compatible with MITRE ATT&CK Navigator.
ThreatMapper tracks new ATT&CK releases automatically.
A Celery Beat scheduler runs check_and_sync every day at 03:00 UTC. It queries MITRE's GitHub repository for new bundle versions and ingests updates without downtime.
The sidebar footer shows:
- Green dot — all domains are on the latest version
- Amber pulse — at least one domain has a newer version available
# Via API
curl -X POST http://localhost:8000/api/sync/trigger
# Check status
curl http://localhost:8000/api/sync/statusPopulated from MITRE's official STIX 2.1 bundles on startup and on each sync. Contains:
- Groups — named threat actors with aggregate TTP profiles from the ingested release
- Campaigns — named operations with per-operation TTP profiles from the ingested release
- Attribution links — which group conducted which campaign (
attributed-torelationships) - Technique usage — the specific techniques observed in each group/campaign with use descriptions
Built on the currently ingested MITRE ATT&CK dataset. Counts depend on the selected ATT&CK domain and ATT&CK release.
| Domain | Groups | Campaigns | Techniques |
|---|---|---|---|
| Enterprise | Dynamic | Dynamic | Dynamic |
| ICS | Dynamic | Dynamic | Dynamic |
| Mobile | Dynamic | Dynamic | Dynamic |
Created by every AI Analysis you run. Each session stores:
name— the label you gave when uploading (or the filename)domain— which ATT&CK domain was usedprovider/model— which LLM was usedextracted_techniques— JSON array of{attack_id, name, tactic, confidence, evidence}apt_matches— JSON array of the Jaccard ranking computed at analysis timesummary— the AI-generated summarystatus—processing/completed/failed
Sessions in DB 2 can be re-compared at any time via POST /api/analyze/sessions/{id}/compare — this re-runs Jaccard against the current DB 1 (useful after a new ATT&CK version has been ingested).
Full interactive documentation at http://localhost:8000/docs.
All 21 registered routes:
GET /api/attack/versions
GET /api/attack/tactics?domain=enterprise-attack[&version=19.1]
GET /api/attack/techniques?domain=enterprise-attack[&tactic=initial-access&search=phish&platform=Windows&subtechniques=true]
GET /api/attack/techniques/{attack_id}?domain=enterprise-attack
GET /api/apt/groups?domain=enterprise-attack[&search=APT29&version=19.1]
GET /api/apt/groups/{attack_id}?domain=enterprise-attack
POST /api/apt/compare?domain=enterprise-attack[&top_n=10&version=19.1]
body: { "technique_ids": ["T1566", "T1059", "T1078"] } ← max 500 IDs
GET /api/apt/campaigns?domain=enterprise-attack[&group_id=G0016&search=solar&version=19.1]
GET /api/apt/campaigns/{attack_id}?domain=enterprise-attack
POST /api/apt/campaigns/compare?domain=enterprise-attack[&top_n=20&version=19.1]
body: { "technique_ids": ["T1566", "T1059", "T1078"] } ← max 500 IDs
POST /api/analyze
multipart: provider={claude|openai|gemini}, domain=enterprise-attack,
name="My Report", text=... | file=@report.pdf
POST /api/analyze/stream ← Server-Sent Events (same fields)
GET /api/analyze/sessions[?limit=50&offset=0] ← DB 2 report library
POST /api/analyze/sessions/{session_id}/compare[?top_n=10] ← re-run Jaccard
DELETE /api/analyze/sessions/{session_id} ← remove from DB 2
GET /api/analyze/{session_id} ← returns AnalysisOut or 202 if processing
POST /api/analyze/chat
body: { "message": "...", "provider": "claude", "model": "...", "context": "..." }
Route order note:
GET /analyze/sessionsmust be registered beforeGET /analyze/{session_id}in the router — it is. Do not reorder these routes or the literal stringsessionswill be treated as a UUID and return 400.
Event type |
Payload | Meaning |
|---|---|---|
token |
{"content": "..."} |
LLM token streamed in real-time |
result |
{"data": AnalysisOut} |
Final parsed result |
error |
{"message": "..."} |
LLM or DB failure |
done |
— | Chat stream completed |
GET /api/export/analysis/{session_id} → PDF download
POST /api/export/layer
body: { "technique_ids": ["T1059"], "domain": "enterprise-attack" }
→ PDF download
GET /api/sync/status
POST /api/sync/trigger
GET /api/sync/task/{task_id}
GET /api/health
All configuration is via environment variables in .env.
| Variable | Default | Description |
|---|---|---|
DB_NAME |
threatmapper |
PostgreSQL database name |
DB_USER |
tm_user |
Database user |
DB_PASS |
changeme |
Database password — change this |
ANTHROPIC_API_KEY |
— | Anthropic / Claude API key |
OPENAI_API_KEY |
— | OpenAI API key |
OPENAI_MODEL |
gpt-4.1 |
OpenAI model used when no request-level model is provided |
GEMINI_API_KEY |
— | Google Gemini API key |
ATTCK_DOMAINS |
enterprise-attack,mobile-attack,ics-attack |
Comma-separated domains to ingest |
LOG_LEVEL |
info |
debug / info / warning / error |
To ingest only Enterprise (faster first start):
ATTCK_DOMAINS=enterprise-attackthreatmapper/
├── backend/
│ ├── app/
│ │ ├── api/routes/
│ │ │ ├── analyze.py # /analyze, /analyze/stream, /analyze/sessions, /analyze/chat
│ │ │ ├── attack.py # /attack/tactics, /attack/techniques
│ │ │ ├── apt.py # /apt/groups, /apt/compare, /apt/campaigns, /apt/campaigns/compare
│ │ │ ├── export.py # /export/analysis, /export/layer
│ │ │ └── sync.py # /sync/status, /sync/trigger
│ │ ├── core/
│ │ │ ├── config.py # Pydantic Settings from .env
│ │ │ └── database.py # async engine, session factory, create_tables
│ │ ├── models/
│ │ │ ├── analysis.py # AnalysisSession (name, domain), AnalysisResult
│ │ │ └── attack.py # AttackVersion, Tactic, Technique, AptGroup,
│ │ │ # Campaign, CampaignTechnique, AptGroupCampaign
│ │ ├── services/
│ │ │ ├── ai/
│ │ │ │ ├── base.py # LLMAdapter ABC, SYSTEM_PROMPT, _parse_response (raw_decode)
│ │ │ │ ├── claude.py # Anthropic adapter (cached client)
│ │ │ │ ├── openai.py # OpenAI adapter (cached client)
│ │ │ │ ├── gemini.py # Gemini adapter (cached genai instance)
│ │ │ │ └── factory.py # get_adapter(provider, model)
│ │ │ ├── attck/
│ │ │ │ ├── downloader.py # Fetch STIX bundles from MITRE GitHub
│ │ │ │ ├── ingestor.py # Parse STIX 2.1, upsert groups+campaigns
│ │ │ │ └── version_checker.py
│ │ │ ├── file_parser.py # PDF / DOCX / TXT text extraction
│ │ │ └── report_generator.py # fpdf2 PDF builder
│ │ └── tasks/
│ │ ├── celery_app.py # Celery + Redis config
│ │ └── sync.py # check_and_sync Celery beat task
│ ├── main.py
│ └── requirements.txt
├── frontend/
│ └── src/
│ ├── api/client.ts # attackApi, aptApi, reportsApi, analyzeApi, exportApi
│ ├── types/attack.ts # TS interfaces for all DB 1 + DB 2 types
│ ├── components/
│ │ ├── Navigator/ # AttackMatrix, LayerControls, TechniquePanel, LLMChat
│ │ └── Compare/ # MatrixDiff, TacticBreakdown
│ ├── pages/
│ │ ├── Navigator.tsx
│ │ ├── APTLibrary.tsx # Groups list + Techniques tab + Campaigns tab
│ │ ├── Analyze.tsx
│ │ └── Compare.tsx # Mode switcher: Groups / Campaigns / Reports
│ ├── hooks/ # useAttackMatrix, useSseStream
│ └── store/ # Zustand global state (domain, version, selectedTechniques)
├── docker-compose.yml
├── Makefile
└── .env.example
make up # docker compose up --build
make down # stop and remove containers
make logs # follow api + worker logs
make shell-api # bash into the api container
make shell-db # psql into postgres
make ingest # trigger ATT&CK re-ingestion manually
make reset # tear down volumes and rebuild from scratch (destructive)# Inside the api container (real PostgreSQL):
docker compose exec api pytest tests/ -v
# Locally (unit tests, no DB):
cd backend
pip install -r requirements.txt
PYTHONPATH=. pytest tests/unit/ -vThreatMapper uses SQLAlchemy create_all() on startup — no migration framework required for a fresh install.
If upgrading an existing deployment, apply these ALTER TABLE statements manually:
-- v0.2.1: stores the ATT&CK domain used for each analysis session
ALTER TABLE analysis_sessions
ADD COLUMN IF NOT EXISTS domain VARCHAR(50) NOT NULL DEFAULT 'enterprise-attack';
-- v0.3.0: user-defined label for each report session
ALTER TABLE analysis_sessions
ADD COLUMN IF NOT EXISTS name VARCHAR(255);
-- v0.3.0: campaigns, campaign-technique links, campaign-group attribution
-- (created automatically by create_all() if tables don't exist)
-- If upgrading a v0.2.x DB, restart the API container and it will create them.- Create
backend/app/services/ai/myprovider.pyextendingLLMAdapter:
class MyProviderAdapter(LLMAdapter):
def __init__(self, model: str = "my-model") -> None:
self._model = model
self._api_client = MySDK(api_key=settings.my_provider_api_key)
@property
def provider(self) -> str: return "myprovider"
@property
def model(self) -> str: return self._model
async def _raw_complete(self, system: str, user: str) -> str: ...
async def _stream_complete(self, system: str, user: str) -> AsyncIterator[str]: ...- Register in
factory.py - Add to
ALLOWED_PROVIDERSinanalyze.py - Add to the frontend provider dropdowns in
Analyze.tsxandLLMChat.tsx
ThreatMapper is suitable for local labs, private analyst workstations, internal CTI workflows, and controlled self-hosted deployments. Internet-facing deployments require additional access control and hardening.
- Set a strong
DB_PASSin.env - Never commit
.envto git (it is in.gitignore) - Do not expose PostgreSQL publicly; bind it to an internal network or localhost only
- Protect the API with a VPN, SSO, OAuth proxy, authenticating reverse proxy, or internal network controls
- Use TLS for browser and API traffic
- Restrict CORS to approved origins
- Use strong, unique secrets and rotate LLM API keys regularly
- Configure PostgreSQL backups, restore testing, retention, and deletion controls
- For any internet-facing deployment, place ThreatMapper behind nginx or Caddy with TLS and authentication
- Review trusted-header authentication and role configuration before team deployment
- API keys are read from environment variables and never stored in the database
- The API runs a single uvicorn worker by default. For concurrent users add
--workers 4indocker-compose.yml. - Celery workers scale horizontally — add additional
workerservice instances. - The PostgreSQL connection pool is set to 10 connections (max 20 overflow).
Use DISCOVERY.md for canonical links, community-specific launch
copy, newsletter pitch text, and current external submission tracking.
| Layer | Technology | Notes |
|---|---|---|
| Backend framework | FastAPI 0.115 | Async, OpenAPI auto-docs |
| ORM | SQLAlchemy 2.x (async) | asyncpg driver |
| Database | PostgreSQL 16 | JSONB for STIX arrays |
| Task queue | Celery 5.4 + Redis 7 | Daily ATT&CK sync |
| ATT&CK parsing | stdlib json only |
No mitreattack-python; Python 3.12-compatible |
| AI — Claude | anthropic SDK |
Cached async client in __init__ |
| AI — OpenAI | openai SDK |
Cached client; JSON mode on non-streaming |
| AI — Gemini | google-generativeai |
configure() called once in __init__ |
| File parsing | PyMuPDF (PDF), python-docx (DOCX) | Streamed with 50 MB hard cap |
| PDF reports | fpdf2 | Multi-page with tactic coverage chart |
| Frontend framework | React 18 + TypeScript | |
| Build tool | Vite 6 | |
| Visualisation | D3.js 7 | ATT&CK matrix heatmap |
| Styling | Tailwind CSS 3 | |
| State | Zustand 5 | |
| Data fetching | TanStack Query 5 | |
| Testing | pytest, pytest-asyncio, httpx |
Operational intelligence workbench:
- Persistent campaign/investigation workspaces containing actors, TTPs, reports, evidence graph nodes/relationships, and timelines
- Analyst-reviewed CTI/IR report intake queue with source reliability and promotion/rejection states
- Tracked-actor snapshots with explainable added/removed TTP change logs
- Detection engineering lifecycle from idea and hunt through validation, production, and retirement
- Unified Operations UI and API endpoints under
/api/operations - Direct actor-library action to snapshot and monitor actor behavior changes
Web-workspace parity plus AI:
- Added intelligence discovery dashboard and global actor/TTP/report search
- Bundled the same correlated CTI/IR report and 1200km resource indexes used by ThreatMapper Web
- Added actor report tabs and technique-level reports, practical resources, detection logic, mitigation guidance, threat-hunting hypotheses, and hunt-plan export
- Added persistent evidence, source, confidence, mapping quality, notes, and coverage maturity assessments
- Added local investigation workspaces, coverage import/visualization, detection-backlog export, shareable deep links, and investigation-report export
- Preserved Docker-only AI analysis, LLM technique assistant, private report sessions, campaigns, saved server layers, API workflows, PDF export, and automated ATT&CK synchronization
Known parity gap: native MITRE ATLAS matrix ingestion remains separate from the embedded Anomaly Detection Atlas reference-book integration. Enterprise, Mobile, and ICS ATT&CK domains have full Docker support.
Public intelligence and ecosystem release:
- ThreatMapper Web promoted as the public intelligence workspace and primary ecosystem entry point
- Permanent crawlable actor and technique pages generated from the current public-workspace dataset
- Global actor, alias, technique, report, publisher, and evidence search
- Intelligence discovery dashboard, shareable deep links, report filtering, and event-level Google Analytics
- Correlated CTI/IR reports, defensive guidance, threat hunting, evidence assessment, and detection coverage workflows
- Strong cross-links to ThreatMapper docs, CTI Analyst Field Manual, Israel Threat Actors CTI, Anomaly Detection Atlas, ITDR Handbook, and 1200km Medium research
Group vs Group comparison:
- New Group vs Group page (sidebar → ◉ Group vs Group): compare up to 6 ATT&CK group profiles simultaneously
- Overlap Matrix tab — N×N Jaccard similarity table; pairwise shared-technique cards with amber badges
- ATT&CK View tab — compact combined matrix filtered to techniques used by ≥ 1 selected group; each cell shows coloured dots for every group that uses the technique
- Technique Table tab — sortable by ID or per-group column, filterable (All / Shared 2+ / Exclusive), ✓ checkmarks per group, count column
Clickable TTP detail panels:
- Every technique ID throughout the UI is now a clickable link — click to open a slide-in detail panel
- Panel shows: technique name and ID, tactics, platforms, full description, detection guidance, Anomaly Detection Atlas cross-references, Ecosystem Resources section
- Ecosystem Resources links: Anomaly Detection Atlas (per-technique deep links), ITDR Handbook (auto-linked for identity techniques: T1078, T1098, T1110, T1111, T1136, T1531, T1539, T1550, T1552, T1555, T1556, T1558, T1606, T1621), CTI Analyst Field Manual, ThreatMapper Web Tool
- Wired in: Navigator matrix cells, ATT&CK Group Library technique list, Compare (shared/gap/overview badges), Group vs Group (overlap badges and technique table)
- Close with Esc or click outside
Ecosystem integration:
- Sidebar links added: ThreatMapper Web Tool (no-Docker browser version), CTI Knowledge Base, 1200km.com
- Anomaly Detection Atlas links now point to
https://1200km.com/anomaly-detection-atlas(previouslylocalhost) — works without running the full Docker stack
Two-database architecture:
- Added
Campaign,CampaignTechnique,AptGroupCampaignSQLAlchemy models - STIX ingestor parses
campaignobjects andattributed-to/usesrelationships from the selected ATT&CK release - New API endpoints:
GET /api/apt/campaigns— list all campaigns, filterable by domain, group, search, versionGET /api/apt/campaigns/{attack_id}— full campaign detail with technique listPOST /api/apt/campaigns/compare— Jaccard ranking vs all campaigns (body:{technique_ids: [...]})GET /api/analyze/sessions— DB 2 report library (all completed analysis sessions)POST /api/analyze/sessions/{id}/compare— re-run Jaccard for a stored report
AnalysisSessiongainsname VARCHAR(255)column; name is set from thenameform field or filename- ATT&CK Group Library — Campaigns tab per group: expandable campaign cards with technique list, date range, "Add to my TTPs" action
- Compare page — three-mode switcher: Groups (DB 1) / Campaigns (DB 1) / Reports (DB 2)
Bug fix:
GET /api/analyze/sessionswas shadowed byGET /api/analyze/{session_id}because the wildcard route was registered first; fixed by reordering routes so static paths precede parameterised ones
Security fixes:
- File uploads streamed with 64 KB chunk reader; 50 MB guard fires before entire body is buffered
/apt/compareenforces max 500 technique IDsChatRequest.contextcapped at 8,000 characters;modelvalidated against pattern_parse_responseusesjson.JSONDecoder().raw_decode()— greedy regex replaced
Correctness fixes:
- Failed post-stream DB writes now set
status=failedcorrectly AnalysisSessiongainsdomaincolumn; PDF exports show the actual analysis domainGET /analyze/{id}returnsJSONResponse(status_code=202)instead of raisingHTTPException(202)- Layer PDF technique count derived from DB results, not raw client-supplied IDs
Performance:
- Anthropic, OpenAI, and Gemini SDK clients cached in
__init__; connection pools reused across requests
- Initial release: Navigator, AI Analysis, ATT&CK Group Library, Compare, Export, MITRE Sync
- STIX bundle parsing rewritten to use stdlib
jsononly (Python 3.12, dropsmitreattack-python) - Fixed tactic matrix ordering: canonical ATT&CK kill-chain sort
MIT — see LICENSE.
ATT&CK® is a registered trademark of The MITRE Corporation. This project is not affiliated with or endorsed by MITRE.



