Skip to content

Build your first threat model

This is the shortest path to something real: one command that reads a repository’s annotations and produces its threat model, a dashboard, and a SARIF export. It sends no traffic, needs no target, and needs no coding agent.

By the end you can read the three numbers that matter and — more usefully — see which threats a probe would never have been able to test.

  • bugb on your PATH and guardlink installed — see What bugb needs.
  • A repository that already carries guardlink annotations. If yours does not, bugb annotate writes them.
  1. From the repository root:

    Terminal window
    bugb model .
    run 20260818-202612-demo · branch bugb/run-20260818-202612-demo
    663 annotations · 119 exposures · 124 threats tracked
    report /Users/you/.bugb/runs/20260818-202612-demo/rounds/1/model-report/threat-model.md
    report (json) /Users/you/.bugb/runs/20260818-202612-demo/rounds/1/model-report/threat-model.json
    dashboard /Users/you/.bugb/runs/20260818-202612-demo/rounds/1/model-report/threat-dashboard.html
    sarif /Users/you/.bugb/runs/20260818-202612-demo/rounds/1/findings.sarif
    MODEL READY — open /Users/you/.bugb/runs/20260818-202612-demo/rounds/1/model-report/threat-dashboard.html
    → see it in the Bugb dashboard: bugb dashboard

    It takes about a second. The run id is the timestamp plus the directory name.

  2. Check what it did to your repository.

    Terminal window
    git status --short
    git branch --show-current
    bugb/run-20260818-202612-demo

    Your working tree is untouched — git status prints exactly what it printed before. What changed is the current branch: bugb checked out bugb/run-<run-id> so that anything a later phase writes lands somewhere revertible. bugb model . --no-checkpoint skips the branch; either way, the model tier commits nothing.

  3. Read the three numbers.

    663 annotations · 119 exposures · 124 threats tracked
    Number Means
    annotations every guardlink annotation in the repository — assets, threats, flows, mitigations, and exposures together
    exposures the @exposes subset: a threat hypothesised against an asset at a line
    threats tracked rows in this run’s ledger

    Threats tracked exceeds exposures here because the ledger also carries the exposures that guardlink’s export drops. The next step is where those show up.

bugb model tells you what is there. bugb inspect tells you where the view is lossy — which is the part worth your attention.

Terminal window
bugb inspect .
/Users/you/demo
guardlink 2.0.0 · cxg pins 6 files
sarif: /Users/you/demo/whitebox/findings.sarif
SUMMARY
exposures 118 (18 critical, 50 high, 45 medium, 5 low)
confirmed 7
unique pairs 69 ← what `guardlink diff` can distinguish
annotations 663 assets 25 threats 29 flows 44

Truncated: the full output lists all 118 exposures between this block and the next.

unique pairs 69 against exposures 118 is the first honest number on the page. guardlink identifies an exposure by its asset and threat alone, so 118 findings collapse into 69 identities — and a second, different weakness in a pair that is already known cannot be distinguished from the first.

Then the section that decides what a run can prove:

SUPPRESSED FROM EXPORT (8)
A @mitigates or @accepts anywhere in the repo removes every exposure sharing that
pair from the SARIF export. cxg never sees these and cannot test them.
critical app.benefits::missing-authz ×4 via @mitigates
high app.memos::xss ×3 via @mitigates
high app.profile::xss ×3 via @mitigates
high app.session::session-fixation ×1 via @mitigates
medium app.session::xss ×1 via @mitigates
critical data.allocationsdao::nosql-injection ×1 via @mitigates
high data.userdao::nosql-injection ×2 via @mitigates
low ops.securitytest::hardcoded-secret ×1 via @mitigates
REACHABILITY
file-anchored 0 usable for goal synthesis
asset-guessed 0 route inferred, often wrong
no route 118 needs agent enrichment
CXG CORRELATION
hypotheses 118
correlated 118/118 ok

Three things to take from it:

  • Suppressed (8 pairs, 16 exposures). One @mitigates anywhere removes every exposure sharing that asset/threat pair from the export. A control that is 90% correct is more dangerous than none, because it ends the investigation silently. The mitigation-audit lens exists to attack exactly these.
  • Reachability. No annotation here carries a route, so every goal will need an agent to work out what to send. That is the slow part of a verify run.
  • Correlation. Every hypothesis can be traced back from a cxg finding to its exposure. When this is short of 118/118, findings arrive that cannot be attributed, and an unattributable finding is never written back.
Terminal window
bugb dashboard

A local page over every run on this machine, on http://127.0.0.1:8787 by default. It is read-only: it displays runs and hands you commands, and it is not a control plane — nothing in it starts a run. --port moves it; --no-open stops it launching a browser.

  • A threat model report and dashboard, under $BUGB_HOME/runs/<run-id>/.
  • A SARIF export — the file cxg consumes.
  • A ledger of every tracked threat, all of it still unverified.

Every threat in that ledger is a hypothesis. Nothing has been tested. Making them true or false is the verify tier’s job, and it sends real exploit traffic: