MITRE ATT&CK Coverage¶
Sigma rules carry ATT&CK tags, and Bifract stores them verbatim on the alert:
tags:
- attack.persistence
- attack.privilege-escalation
- attack.t1543.003
- attack.t1068
Alerts > Coverage reads those tags back against an embedded copy of the ATT&CK Enterprise matrix and renders the tactic-column grid, heat-mapped by how many of your rules cover each technique. The page is scoped to the fractal or prism you are in.
Reading the map¶
Each column is a tactic in kill-chain order and each cell is a technique. By default the color says what state the technique is in, which is the question the page exists to answer:
| Cell | Meaning |
|---|---|
| Filled purple, badged with a count | At least one enabled rule maps here. The fill deepens with the rule count |
| Dashed purple outline | Every rule mapped here is switched off. Covered on paper, detecting nothing |
| Amber, badged with a count | No rule here, but a synced feed carries that many rules that were never imported. These are the gaps you can close today |
| Flat, unbadged | No rule, and nothing waiting in a feed. This one needs a new detection |
Switch Color to rule count or highest severity when you are tuning depth rather than looking for holes.
The header carries one number, Coverage, and a chip for each thing outstanding: gaps a feed could close, techniques covered only by disabled rules, rules carrying no ATT&CK tag, and broken tags. A chip is rendered only when its count is not zero, and the first two are filters: click one and the grid narrows to the cells it counted.
Each column header shows that tactic's coverage over a two-part meter: purple for what is covered, amber for the share a feed could close on top of it.
Cells with a chevron have sub-techniques; expand them individually or turn on Sub-techniques to expand every group.
How coverage is counted¶
Two numbers, and the difference between them matters.
Coverage (the headline, and the number in each column header) counts
detectable units:
every sub-technique, plus every technique that has none. Nothing is inherited. A rule
tagged attack.t1547 covers the parent but zero of its 14 autostart sub-techniques,
because the tag does not say which mechanism the rule catches.
Techniques touched counts top-level techniques with at least one rule mapped to them or to any of their sub-techniques. A technique with 14 sub-techniques and 1 covered scores a full point here. It lives in the tooltip on Coverage and on each column header rather than on the page, because it is the flattering number of the two and does not belong in a glance.
The gap between them is usually large:
| Tactic | Coverage | Techniques touched |
|---|---|---|
| Privilege Escalation | 36/85 (42%) | 12/13 (92%) |
| Persistence | 42/99 (42%) | 17/22 (77%) |
| Credential Access | 26/58 (45%) | 14/17 (82%) |
Reading the right-hand column as your coverage overstates it by roughly a factor of two. Use "techniques touched" for breadth, "Coverage" for depth.
Three further counting rules:
- A rule tagged with a sub-technique (
attack.t1543.003) counts as direct coverage of that sub-technique and inherited coverage of its parent. The drawer and tooltip report the two separately. - A rule tagged with only a tactic (
attack.persistence, no technique) is not coverage of anything on the grid. It is counted under "rules mapped" as unmapped, because it does not say which technique it detects. - A rule tagged with a retired technique ID resolves to its replacement where
ATT&CK defines one (
attack.t1086counts toward T1059.001). An ID with no replacement, usually a typo or a technique MITRE removed outright, is invisible to the map; they are counted by the broken tags chip in the header so you can fix the rule.
Deprecated techniques are excluded from every denominator.
Controls¶
| Control | Effect |
|---|---|
| Search | Dims non-matching cells and expands any sub-technique group holding a match. Matches technique IDs too, which are not printed on the cells |
| Color | Status, rule count or highest severity |
| Filters | Show (all / gaps only / covered only), severity, platform, source and enabled-only, folded into one control with a badge showing how many are active. The platform filter narrows the technique universe too, so the percentage stays honest |
| Sub-techniques | Expands every sub-technique group at once |
| Export layer | Downloads your coverage as an ATT&CK Navigator layer (see below) |
Export layer¶
Layer is ATT&CK Navigator's term for a coverage overlay: a JSON file of
{techniqueID, score, comment} entries that MITRE's viewer paints onto the matrix. It
is the interchange format the ATT&CK ecosystem speaks, so exporting one lets you:
- Open Bifract's coverage in MITRE's Navigator and hand the file to people who have no Bifract access
- Diff it against another source. Navigator subtracts one layer from another, so
you can overlay your EDR vendor's claimed coverage, a red team's layer, or a threat
group's technique set (published on
attack.mitre.org) and see where you are exposed - Keep a dated snapshot to show coverage growth over time
The export honours the active filters, so "Windows only" or "critical severity only" can be its own layer. Scores are rule counts, and each entry's comment records the direct/inherited split.
Clicking any cell opens a drawer with the technique's tactics, the rules covering it (click one to open it in the alert editor), its platforms, the telemetry MITRE expects it to be detectable in, and a link to attack.mitre.org.
Closing a gap¶
An amber cell means a feed you have configured carries rules for that technique that were never imported. Open it and the drawer lists them, each with the reason it is not running:
below the feed severity threshold: the rule'slevelis under the feed's Min Levelbelow the feed maturity threshold: the rule'sstatusis under the feed's Min Statuscannot be translated to BQL: the rule parsed but Bifract cannot express its detection logicfailed to import: the translation produced a query Bifract's own parser rejected
The first two are a threshold you chose: lower the feed's Min Level or Min Status to pull those rules in. The last two are translator work, and their counts are the clearest signal of which Sigma constructs to support next. Nothing is imported behind your back.
For a technique with nothing waiting, Write a detection opens the alert editor with
the name, the attack.tNNNN label and MITRE's expected telemetry already filled in, so
the rule that closes the gap is tagged for the map without you having to remember.
The rule catalog¶
The amber cells are backed by feed_rule_catalog, which records every rule a feed's
repository offers, imported or not. It is populated on every feed sync, so a
freshly upgraded install shows no candidates until its feeds sync again. Trigger a
sync from Alerts > Feeds to populate it immediately.
Catalog metadata is read before translation is attempted, which is the point: a rule Bifract cannot translate still has ATT&CK tags, and those are exactly the gaps worth knowing about.
Updating the ATT&CK matrix¶
The Enterprise matrix is embedded in the binary (gzipped, ~17 KB) rather than fetched at runtime, so air-gapped installs work and startup has no dependency on GitHub. Regenerate it when MITRE publishes a new version:
go run ./cmd/bifract-attackgen -out pkg/attack/data/enterprise-attack.json.gz
That downloads MITRE's STIX bundle, slims it, validates the result, and writes the
embedded file; pass -in enterprise-attack.json to use a local copy. Commit the
regenerated file. go test ./pkg/attack/... verifies that it loads, that every tactic
column is populated in kill-chain order, and that no sub-technique has a dangling
parent.
ATT&CK renames tactics between versions (v19 renamed Defense Evasion to Stealth) while
rule sets keep emitting the old slug for years. Bifract resolves both, so
attack.defense-evasion keeps working regardless of the version embedded.