Countermap
Countermap puts the Fight Fraud Framework (F3) in one local knowledge graph with MITRE ATT&CK® (Enterprise, Mobile, ICS), D3FEND and ATLAS, and serves it read-only to AI agents over MCP and to people from the command line. Every cross-framework link says whether MITRE published it or Countermap derived it, and one small scenario spec renders validated D3FEND CAD graphs, Attack Flow 2.0 bundles, ATT&CK Navigator layers and Mermaid diagrams.
- Fraud and intrusion in one graph. F3 techniques sit beside ATT&CK, D3FEND and ATLAS, with F3's reuse of ATT&CK IDs and ATLAS's ATT&CK references kept as MITRE publishes them.
- Provenance on every link. Each link is
official(published by MITRE),derived(with the path it was composed through) oranalyst, and an empty D3FEND result says why it is empty. - Schema-validated diagrams. The D3FEND CAD graph and the Attack Flow bundle are checked against their published schemas before they are returned.
- Telemetry and rule coverage. What your log sources and detection rules cover against MITRE's analytics, with the gaps ranked by how many groups ATT&CK links to them.
- Offline and air-gapped builds. One SQLite file and no network at query time; builds pin each upstream release by SHA-256 and can read every source from a mirror or a local file.
Status: alpha (0.2.1). Built against ATT&CK v20.0, F3 v1.2, ATLAS 2026.09 and D3FEND 1.6.0. See the architecture document for the design and the provenance and coverage rules, the changelog for each release, SECURITY.md to report a vulnerability and CONTRIBUTING.md to contribute.
Quick start
pipx install countermap # or pip install countermap; Python 3.10 or newer
countermap build # about 150 MB of MITRE data, downloaded once
countermap gaps T1566.002 T1539 F1005.006 # mitigations, detections and D3FEND per technique
claude plugin install countermap --marketplace elchacal801/countermap # the MCP server and skills for Claude Code
Example
flowchart LR
%% AiTM phishing to payment redirection
s1["<b>T1566.002</b><br/>Spearphishing Link<br/><i>Initial Access</i>"]:::step
s2["<b>T1539</b><br/>Steal Web Session Cookie<br/><i>Credential Access</i>"]:::step
s3["<b>F1005.006</b><br/>Account Manipulation: Change of Payment Details<br/><i>Positioning, Defense Impairment</i><br/>[no D3FEND mapping: F3-native technique]"]:::step
s1 --> s2
s2 --> s3
c1{{"D3-HD<br/>Homoglyph Detection<br/><i>Detect</i>"}}:::counter
a1[/"URL"/]:::artifact
s1 -- "produces" --> a1
c1 -. "analyzes" .-> a1
c2{{"D3-SMRA<br/>Sender MTA Reputation Analysis<br/><i>Detect</i>"}}:::counter
a2[/"Email"/]:::artifact
s1 -- "produces" --> a2
c2 -. "analyzes" .-> a2
c3{{"D3-CCSA<br/>Credential Compromise Scope Analysis<br/><i>Detect</i>"}}:::counter
a3[/"Session Cookie"/]:::artifact
a4[/"Credential"/]:::artifact
s2 -- "accesses" --> a3
c3 -. "analyzes" .-> a4
a3 --- a4
classDef step fill:#fde8e8,stroke:#b42318,color:#1f1f1f
classDef artifact fill:#eef2f6,stroke:#475467,color:#1f1f1f
classDef counter fill:#e6f4ea,stroke:#137333,color:#1f1f1f
classDef derived fill:#fff4e5,stroke:#b54708,color:#1f1f1f,stroke-dasharray: 4 3
Rendered by countermap render examples/aitm-cookie-to-payment-change.json --format mermaid against the releases in sources.lock.json, with automatic countermeasures limited to D3FEND's Detect tactic ("d3fend_tactics": ["Detect"]). Each official D3FEND countermeasure (green) connects to its attack step through digital artifacts (gray), as D3FEND's published mappings link them, and the F3 step says why it has none. The same spec also renders the CAD graph, the Attack Flow bundle and the Navigator layers.
Why this exists
When an agent says "D3-MFA counters this F3 technique", a reader needs to know whether MITRE published that link or whether it was stitched together. Countermap enforces three rules:
- Stored links are only what MITRE publishes. ATT&CK relationships, ATLAS's ATT&CK references, F3's reuse of ATT&CK IDs, and D3FEND's published inference output. Nothing inferred is stored.
- Derived links are labeled and carry their path. For example
F3 reuses ATT&CK ID attack:T1056 -> sub-technique attack:T1056.001. Agents are told never to present them as MITRE's. - Gaps are reported as gaps, with the reason. An empty D3FEND result carries a
gap_reason: D3FEND maps nothing to the technique (no_mapping), the loaded D3FEND release predates it (predates; ATT&CK v20's new AI techniques are not in D3FEND 1.6.0 yet), it is an F3-native technique (f3_native), or none is in the requested tactic (none_in_tactic). When the D3FEND data is not loaded at all, the reason isunavailable, which is never counted as a gap.
It also reports upstream data problems the frameworks do not flag themselves. Against the current releases it reports, for example, that F3 still reuses T1672 (revoked in ATT&CK, now T1684.002) and two sub-technique IDs that ATT&CK does not contain, that ATLAS AML.T0073 references the revoked T1656, and that ATT&CK ICS publishes a deprecated object under an ID Enterprise uses for different malware.
Install
pip install countermap # into the current environment
pipx install countermap # as an isolated command-line tool
uvx countermap --help # run it without installing (requires uv)
With uv, prefix any command below with uvx, for example uvx countermap build. Dependencies: mcp (2.x), rdflib, jsonschema, referencing, pydantic, PyYAML.
From source:
git clone https://github.com/elchacal801/countermap && cd countermap
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest -q # offline test suite
Build the database
countermap build # latest upstream releases
countermap build --lock sources.lock.json # exactly the releases in a lock file, verified by SHA-256 where pinned
countermap build --attack-version 19.2 # pin ATT&CK
countermap diff ~/.cache/countermap/countermap.prev.db ~/.cache/countermap/countermap.db
A build takes a few seconds after download. Each rebuild keeps the previous snapshot so version_diff can show what changed.
A build fails, and leaves the existing database as it was, when a required source (every source except d3fend-mappings) cannot be fetched or parsed. countermap build --allow-partial builds without it instead: the database is marked degraded, countermap info and the datasets tool show degraded: true and the missing sources, and without D3FEND, countermeasure results say unavailable rather than empty.
A lock file pins each upstream release by URL and SHA-256. The repository's sources.lock.json pins the releases the project is currently tested against; download it to build exactly those, or write a lock for the latest releases with countermap lock -o sources.lock.json.
A lock pins each source's primary URL, and a build from it never falls back to another location. The D3FEND file in the tagged GitHub release is not byte-identical to the release file d3fend.mitre.org serves, so it could not pass the pinned hash anyway. On a network that blocks an upstream host, build from a mirror or a local copy instead (below).
Sources and where they come from:
| Key | Upstream |
|---|---|
attack-enterprise, attack-mobile, attack-ics |
mitre-attack/attack-stix-data (STIX 2.1, resolved through its index.json) |
f3 |
center-for-threat-informed-defense/fight-fraud-framework public/f3-stix.json |
atlas |
mitre-atlas/atlas-data dist/manifest.yaml, newest v6 YAML |
d3fend-ontology |
d3fend.mitre.org/ontologies/d3fend/<version>/d3fend.ttl for the release d3fend.mitre.org/api/version.json reports, falling back (without a lock) to the tagged release in d3fend/d3fend-ontology |
d3fend-mappings |
d3fend.mitre.org/ontologies/d3fend/<version>/d3fend-full-mappings.json (optional; countermeasure queries report "unavailable" without it) |
Mirrors and air-gapped use. Every source accepts a URL or a local path:
countermap build --source attack-enterprise=/mirror/enterprise-attack-20.0.json --source d3fend-mappings=/mirror/d3fend-full-mappings.json
# or per source: COUNTERMAP_URL_ATTACK_ENTERPRISE=https://ghe.example.com/raw/... countermap build
With --lock, the COUNTERMAP_URL_<KEY> variables do not apply, and --source replaces that source's locked URL and hash, so the file it names is not verified. To keep the check, edit the entry's url in a copy of the lock (a byte-identical copy keeps the pinned sha256), or write a lock for the mirror with countermap lock --source KEY=PATH_OR_URL.
COUNTERMAP_DB and COUNTERMAP_CACHE move the database and download cache.
Use it from an agent (MCP)
VS Code (Copilot agent mode) reads .vscode/mcp.json in your workspace:
{ "servers": { "countermap": { "type": "stdio", "command": "countermap", "args": ["serve"] } } }
Claude Code: install the plugin, which bundles the MCP server with two skills. countermap teaches Claude the tools and the provenance rules; /countermap:threat-model turns an incident or report into a mapped scenario, countermeasures, gaps and diagram files.
claude plugin install countermap --marketplace elchacal801/countermap
This shell form needs Claude Code v2.1.292 or later; inside a session, /plugin install countermap --marketplace elchacal801/countermap needs v2.1.275 or later. On older versions, add the marketplace first:
claude plugin marketplace add elchacal801/countermap
claude plugin install countermap@countermap
The plugin starts the server from PyPI with uv (uvx countermap@0.2.1 serve), so uv must be on your PATH. On first use Claude offers to build the database (about 150 MB of downloads, once). Without the plugin: claude mcp add --transport stdio countermap -- countermap serve (use the full path to the executable if countermap is not on PATH, or -- uvx countermap serve). Any stdio MCP client works the same way.
Updating the plugin. Auto-update is off by default for third-party marketplaces. To update:
claude plugin marketplace update countermap
claude plugin update countermap@countermap
The new version loads in the next session, or after /reload-plugins. To turn on auto-update, run /plugin, open the Marketplaces tab, select countermap and choose Enable auto-update.
| Tool | What it returns |
|---|---|
datasets |
Loaded versions, SHA-256, licenses, object counts, build warnings, and whether the database is degraded |
search |
Full-text search over names, aliases, IDs, descriptions |
lookup |
Any object by ID, name or alias; revoked IDs followed to their replacement. Detection strategies, analytics and data components also show their analytics, log sources, tunables and techniques; campaigns show their first and last seen dates |
technique_context |
Tactics, sub-techniques, users, mitigations, detection strategies with each analytic's description, tunables and log sources, D3FEND, crosswalk |
countermeasures |
D3FEND countermeasures with artifact paths, official or derived, and a gap_reason when there are none |
crosswalk |
ATLAS and F3 links to ATT&CK, flagged when the target is revoked, deprecated or missing |
actor_techniques |
Techniques for a group, software or campaign (optionally through its software) |
compare_actors |
Shared and unique techniques, pairwise Jaccard |
coverage_gaps |
Mitigation, detection-strategy and D3FEND counts for a technique list, with the reason for each missing D3FEND mapping |
telemetry_coverage |
What your log sources and data components can detect: each technique of a list, an actor or a platform (in one ATT&CK domain or all three) as covered, partial or blind against MITRE's analytics, with the log sources missing; optional Navigator layer, up to 120 techniques over MCP |
rule_coverage |
What your detection rules cover: given the ATT&CK IDs your rules map to, each in-scope technique as covered, partial or uncovered, with gaps ranked by how many groups ATT&CK links to them, directly or through software they use (the latter derived by Countermap); optional Navigator layer, up to 120 techniques over MCP |
version_diff |
Added, removed, revoked, deprecated, renamed, re-tacticed objects since the last build |
navigator_layer |
Navigator layer 4.5 JSON per domain, including F3's Navigator domain |
render_scenario |
CAD, Attack Flow, Mermaid and Navigator output from a scenario spec, plus the mitigations and detection strategies (with their analytics) MITRE links to each step, and why a step has no countermeasures |
The same queries work from the shell: countermap lookup "Cozy Bear", countermap counter T1539, countermap gaps T1566.002 T1539 F1005.006, countermap info.
Coverage from the shell, with an optional Navigator layer file:
countermap coverage --log-source WinEventLog:Sysmon --log-source "WinEventLog:Security EventCode=4688" --platform Windows --actor APT29 --layer apt29-telemetry.json
countermap rules-coverage --sigma ./rules --actor APT29 --layer apt29-rules.json # or --technique T1059.001 --technique T1003 ...
The telemetry and rule coverage classes, and the gap ranking, are Countermap's computations over MITRE's analytics and "uses" relationships; each result says so in its basis. Group counts are the groups ATT&CK links to a technique directly, or through software they use (the latter derived by Countermap), not how common it is in the wild; each count says how many are direct. Over MCP, a coverage layer is returned for up to 120 techniques; for a broader target the result points to the CLI's --layer FILE, which has no limit.
Diagrams: the scenario spec
Agents never hand-write CAD JSON or STIX. They write this, and Countermap checks every ID against the loaded data:
{
"title": "AiTM phishing to payment redirection",
"steps": [
{"id": "s1", "technique": "T1566.002", "description": "Link to an AiTM reverse proxy"},
{"id": "s2", "technique": "T1539", "after": ["s1"], "assets": ["Victim mailbox"]},
{"id": "s3", "technique": "F1005.006", "after": ["s2"]}
],
"countermeasures": "auto",
"max_countermeasures_per_step": 2
}
Save it as scenario.json and render it:
countermap render scenario.json -o out/
This spec is the diagram above. Three complete specs are in the repository's examples/ folder: this one, a longer AiTM phishing to payment fraud case, and an agentic prompt injection.
- D3FEND CAD: attack steps, the digital artifacts they act on, and the countermeasures that act on those artifacts, so every countermeasure connects to the attack through an artifact. Validated against the published CAD schema. Open it in CAD with Load.
- Attack Flow 2.0: validated against the CTID schema; passes CTID's own
af validatewith no warnings. - Navigator: one layer per domain; F3 steps go on an F3 layer that uses the same domain and data URL as the layer CTID publishes for F3.
- Mermaid: renders in GitHub, VS Code and most wikis.
"countermeasures": "auto" picks official D3FEND countermeasures first, one tactic at a time (Harden, Detect, Isolate, Evict, Restore, Deceive, Model), preferring ones that act on the same artifact as the attack. A step that gets none is listed in steps_without_countermeasures with its gap_reason, and every format labels it with the reason. You can also list countermeasures explicitly; ones D3FEND does not map to that step are drawn and labeled as analyst assertions.
CAD calibration
The public CAD schema does not specify edge handle IDs or the current cad_schema_version. Countermap writes what CAD itself writes (handles source and target, schema version 1, checked against CAD 0.26.1 on 2026-10-15). The layout leaves room for the size CAD actually draws each node. If a future CAD release changes these values, export any graph from D3FEND CAD that has at least one connection, then:
countermap calibrate-cad exported-from-cad.json
Keeping current
countermap check-updates --lock FILE compares the latest upstream releases with a lock file and exits with code 10 when any of them has moved past it.
The repository runs that check in a scheduled upstream-sync GitHub Actions workflow (daily at 06:17 UTC, or by hand). When an upstream release version differs from sources.lock.json, the workflow writes a new lock, builds against the new data, runs the offline test suite, the live test and a lookup smoke test, diffs the old and new ATT&CK snapshots, and opens a pull request that changes only the lock file, with the version changes, the ATT&CK diff counts and the build warnings in its body. Nothing merges on its own.
Security and data handling
- The server opens the database read-only and makes no network calls; tools cannot write files or run commands.
- MCP tools take IDs, never file paths. Reading Sigma rules is a CLI-only step (
rules-coverage --sigma), which parses YAML withyaml.safe_load_alland never runs anything in it. It does not follow symlinks or junctions, skips files over 1 MiB or nested more than 100 brackets deep, and counts any file it cannot parse instead of stopping. - Only
build,lockandcheck-updatestouch the network, and only the upstream URLs above (or your overrides). - Tool results contain upstream MITRE text. Treat it as data, not instructions.
- Scenario specs stay local. Nothing is sent anywhere.
To report a vulnerability, see SECURITY.md.
Limitations
- D3FEND's published mappings are the only source of countermeasure links. The parser expects MITRE's SPARQL result columns (
off_tech_id,def_tech,def_artifact_label, ...). If the columns change or no row can be used, the build marks the mappingsfailed, records a warning, and countermeasure queries report "unavailable" rather than "no coverage". - D3FEND moved its published mappings once already: release 1.6.0 dropped the old
api/ontology/inference/path for the versioned release folder. If the location changes again, the build marks the mappingsunavailablewith the HTTP error rather than guessing. - Names are matched exactly (case-insensitive) and by alias. When a name matches several objects (for example "Phishing" exists in ATT&CK Enterprise, Mobile, F3 and ATLAS), ATT&CK wins and the result carries a warning listing the others. Pass IDs when it matters.
- D3FEND lags ATT&CK. D3FEND 1.6.0 aligns to ATT&CK 19.0 (per its CHANGELOG) and has no classes for v20's new techniques, so they have no countermeasures yet. It also still maps 22 techniques that ATT&CK had already revoked, while 20 of their replacements have no mappings of their own; Countermap reaches those replacements only through
derivedlinks that name the revoked ID. - F3 has no published mitigations or data sources yet, so F3-native techniques have no countermeasures.
- CAPEC, CWE and NIST mappings are not included.
Contributing
See CONTRIBUTING.md for the development setup, the tests and the provenance rules every change keeps.
License
Apache-2.0, except the 12 OASIS STIX 2.1 JSON schemas vendored for offline validation, which are BSD-3-Clause; their license text is in LICENSES/BSD-3-Clause-OASIS.txt. NOTICE lists MITRE attributions, trademarks and the license of every vendored schema. Both files also ship inside the package. This project is not affiliated with or endorsed by MITRE.
Metadata
Release files for countermap 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| countermap-0.2.1.tar.gz | 136.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| countermap-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 238.8 kB
Release files / countermap-0.2.1.tar.gz
| Download URL | countermap-0.2.1.tar.gz |
|---|---|
| Size | 136.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7f174798062f0741a1108385f118e53ad91a019224c3190b6341837f7e43acc2
|
|
BLAKE2b-256 checksum How to use checksums |
09e612f485cd1f95fe2d5732d19ad2aab283bdc78f763ee20f26e990a8eb949b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / countermap-0.2.1-py3-none-any.whl
| Download URL | countermap-0.2.1-py3-none-any.whl |
|---|---|
| Size | 102.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5d05a5c6f598e2389fe03a8f070cbe3240efaf993d752a61f65321176b7400d2
|
|
BLAKE2b-256 checksum How to use checksums |
54f913aeaccf61451bfe9e749274d587302bc6226bf92335af546d9ee55774a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|