Skip to content

feat: /actors.html — threat-actor coverage page#272

Merged
triw0lf merged 1 commit into
mainfrom
feat/actor-coverage-page
May 24, 2026
Merged

feat: /actors.html — threat-actor coverage page#272
triw0lf merged 1 commit into
mainfrom
feat/actor-coverage-page

Conversation

@triw0lf
Copy link
Copy Markdown
Collaborator

@triw0lf triw0lf commented May 24, 2026

Summary

A new top-level page that answers a single question: "Is this threat actor covered by HEARTH?"

For a hunter prepping an engagement, the page returns a curated list of HEARTH hunts relevant to the chosen actor. For a defender doing coverage analysis, it also surfaces a coverage percentage, a tactic-level heatmap, and a gap list of techniques the actor uses for which HEARTH has no hunt yet — with one-click submit links pre-tagged with the target technique.

The substrate already existed: public/context-graph-data.json has 159 MITRE-mapped threat actors (with aliases) and 1,670 EMPLOYS edges to MITRE techniques. This PR is a thin lookup + UI layer on top.

Spec: docs/plans/2026-05-23-actor-coverage-page-design.md

What's in the box

  • /actors.html with three states: empty (search + suggested-actor chips + animated typewriter + showcase boards), result (header, coverage strip, tactic heatmap, matched-hunts list, gap list), and not-found
  • Alias-aware search — typing "Cozy Bear", "Midnight Blizzard", or "G0016" all resolve to APT29
  • Showcase boards on the empty state: Most matched (by raw hunt count, the honest data) and Biggest open gaps (where one new hunt closes coverage)
  • Home-page Section 03Actor coverage with a live mini coverage card that rotates through the top-6 most-matched actors per reload
  • Library deep-link (index.html?hunt=<id>) wired into the React app so matched-hunt rows open the correct entry
  • Submit deep-link (submit.html?technique=<id>) shows a banner + prepends [gap: T1234] to the GitHub issue title so Keepers know what gap a contributor was filling
  • scripts/build_actor_mentions.py — precomputes public/actor-mentions.json for name-mention matching (word-boundary, 4-char minimum, per-actor alias denylist). 13 passing tests. Wired into rebuild_hunts_data.py and the existing update-hunts.yml workflow
  • Nav link added to all five top-bars (home / library / coverage / context / submit)

Architecture notes

  • All matching runs client-side from three JSON files; no backend
  • Coverage definition: union of shared-technique matches AND actor-name/alias mentions in hunt prose (title/why/notes/references)
  • Match-reason badge (TECH / MENTION) on each row so the user knows why a hunt surfaced

Known limitation worth flagging

context-graph-data.json only contains MITRE techniques HEARTH has at least one hunt for, so the per-actor coverage % is technically "% of mapped-and-touched techniques", not "% of the actor's full real-world TTP set." Honest within its frame, but worth a follow-up rename or caveat in the UI ("HEARTH coverage of mapped techniques") when we're ready.

Test plan

  • npm install && npm run dev → open http://localhost:4173/actors.html (port can vary)
  • Empty state: search "APT", autocomplete returns APT-prefixed actors; click a suggested chip; verify showcase boards render
  • Type "S" → no fuzzy-substring noise (only labels starting with S)
  • Result state: ?actor=G0016 → APT29 with 51 matched hunts, 1 gap (T1587.003), heatmap renders
  • Alias resolution: ?q=cozy%20bear → redirects to ?actor=G0016
  • Not-found: ?q=zzznotreal → graceful state with submit CTA
  • Gap link → submit.html?technique=T1587.003 shows banner; submitted issue title starts with [gap: T1587.003]
  • Matched-hunt row → opens index.html?hunt=<id> with that hunt selected in the preview pane
  • Home page: reload a few times, verify the actor preview card rotates and stays in sync with the underlying data
  • pytest scripts/tests/test_build_actor_mentions.py → 13/13 pass
  • npm run type-check → clean

🤖 Generated with Claude Code

New top-level page that answers "is this actor covered by HEARTH?" Search
by canonical name or any of 159 MITRE-mapped actors' aliases (e.g. "Cozy
Bear" resolves to APT29). For each actor, surfaces:

- matched hunts (technique overlap + name mentions in hunt prose)
- coverage percentage and tactic-by-tactic heatmap
- gap list with one-click submit links pre-tagged with the target technique

Also adds an empty-state showcase ("Most matched" / "Biggest open gaps")
and a live mini coverage card on the home page that rotates through the
top-matched actors per reload. Library deep-links (?hunt=<id>) are wired
through so matched-hunt rows open the right entry, and a banner on the
submit page surfaces the target technique when arriving from a gap link.

Data substrate is the existing context-graph + a new precomputed
public/actor-mentions.json (regenerated automatically by
rebuild_hunts_data.py). No backend; all matching runs client-side.

Spec: docs/plans/2026-05-23-actor-coverage-page-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@triw0lf triw0lf requested a review from letswastetimee May 24, 2026 03:13
@triw0lf triw0lf self-assigned this May 24, 2026
@triw0lf triw0lf merged commit fc572f1 into main May 24, 2026
1 check passed
@triw0lf triw0lf deleted the feat/actor-coverage-page branch May 24, 2026 03:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants