Skip to content

Read a run

A finished run leaves a ledger, a set of reports, and one advisory per confirmed finding. This page reads all three, and spends most of its time on the part that most often gets skipped: the threats the run never tested, and why.

  • A completed or interrupted run on this machine. bugb resume lists them.

Every command here is read-only. None of them sends traffic or touches your repository.

Terminal window
bugb ledger --run 20260818-120316-nodegoat
124 threats tracked · 4 settled · 105 outstanding
UNVERIFIED (97)
critical App.Allocations #nosql-injection app/views/allocations.html:22
the probe produced no evidence for this threat, so nothing was proven or disproven; the gap stays in the backlog to be re-tested (probe note: no probe ran for this threat: cxg generated no template carrying gl-4dfb3fd2c18d, so nothing in this run tested it; the templates that did run tested other threats (nosql-injection-allocations-threshold-anon) — cxg reported: 4 requests, nothing confirmed)
critical App.Config #hardcoded-secret config/env/all.js:8
critical App.Contributions #ssjs-injection app/routes/contributions.js:32

Truncated after three rows of the first section — the full output runs through every verdict group and ends with 16 suppressed exposures.

The header is the whole run in one line: 124 tracked, 4 settled, 105 outstanding. Settled means a probe reached a conclusion. Outstanding means it did not, whatever the reason.

Then one section per verdict:

CONFIRMED (3)
critical App.Allocations #idor app/routes/index.js:63
IDOR CONFIRMED (authenticated): a single logged-in session (profiles[1]) retrieved allocations for 4 distinct account holder(s) — "Node Goat Admin", "John Doe", "Will Smith", "Pen Test" — by varying the path userId across ids [1, 2, 3, 4]. displayAllocations (allocations.js:16-18) reads req.params.userId with the session-scoped variant (line 14) commented out, so any authenticated user reads any user's allocations. Cross-verified: Record 4 rendered account holder "Pen Test" to BOTH the attacker
REFUTED (1)
critical Data.AllocationsDAO #idor app/data/allocations-dao.js:57
2 requests, nothing confirmed
ERROR (7)
critical App.Allocations #idor app/routes/allocations.js:16
zero requests were issued; the target was never reached
not tested — error_no_identities
high App.Views #xss app/routes/session.js:58
session expired for np-admin
not tested — error_dead_profile

Truncated: ERROR has seven rows and SUPPRESSED sixteen.

Two lines to notice. 2 requests, nothing confirmed is a refutation — probes ran and the threat did not hold up. zero requests were issued; the target was never reached is not: nothing was learned, so the threat stays in the backlog. Verdicts and the ledger is the full taxonomy.

Filter to one verdict:

Terminal window
bugb ledger --run 20260818-120316-nodegoat --verdict confirmed

--all stops the truncation. --json emits the records. bugb ledger markdown --run <id> -o ledger.md writes the table form, and bugb ledger rebuild reconstructs the ledger from the run’s artifacts.

Each run writes a report directory:

$BUGB_HOME/runs/<run-id>/report/
├── summary.md
├── ledger.md
├── triage.json
├── finding-artifacts.json
└── advisories/
├── idor-app-allocations.md
├── idor-app-allocations.json
└── …

summary.md opens with the outcome and — the useful part — why the run stopped:

# Threat coverage — 20260818-120316-nodegoat
- **Codebase** — `/Users/you/nodegoat`
- **Target** — https://web.nodegoat.orb.local
- **Rounds** — 2
- **Status** — halted (2 consecutive degraded rounds; results are not trustworthy until the underlying failure is fixed)
## Outcome
- 124 threats tracked
- **3 confirmed exploitable**
- 4 settled, 120 outstanding
- 1 threats carried from run `20260817-221148-nodegoat`, 4 re-verified, 0 now refuted

A run that halts on degraded rounds says so in the status line rather than presenting its findings as a complete picture. Three confirmed findings out of 124 tracked threats is not the same claim as “121 threats are fine”, and the report does not make it.

Below the outcome, summary.md lists the coverage gaps by cause — threats hidden from the export by a @mitigates, and threats whose probing failed.

One markdown file and one JSON file per confirmed finding. The markdown reads like a GitHub security advisory:

# NodeGoat: Insecure Direct Object Reference in GET /allocations/:userId lets any authenticated user read every user's asset allocations
**Severity** — High · **CVSS v4.0** — 8.0 · **CWE** — CWE-639, CWE-285, CWE-200
**Location** — `app/routes/index.js:63`
## Summary

The summary names the exact route, the middleware that failed to check ownership, the line where the session-scoped variant sits commented out, and what the probe actually retrieved — four named account holders by varying one path segment. Under it, a triage assessment argues the finding the way a maintainer would argue back. See Triage and entitlements.

The probe that produced the verdict is kept too, under the run’s round directory, so an advisory can show the exact code that proved it:

$BUGB_HOME/runs/<run-id>/rounds/<n>/goals/<goal>/attempt-1/
├── cxg.log
├── session/
│ ├── audit.jsonl
│ └── report.json
└── templates/
├── idor-2402ed654130.js
└── …

--no-local-session aside, those templates are also mirrored into <codebase>/.bugb/, which is what bugb replay re-runs after you fix something.

Terminal window
bugb dashboard

The same material as a local page on http://127.0.0.1:8787: runs with their rounds and event log, findings per repository split into guardlink exposures and cxg-verified advisories, and each advisory with the probe that produced it.

It is loopback-only and read-only — it derives, displays, and hands you a CLI command. Nothing in it starts a run, so there is no second execution path that can drift from the CLI.