Skip to content

Vulnerabilities

Joey King edited this page Oct 5, 2026 · 1 revision

Vulnerabilities: Reporting and Fixing

Repo Steward can tell you which known security advisories affect a repository, and in one mode it can act on them. This page is for someone who wants to run that. The mechanism, for a developer, is in Vulnerability Scan Pipeline.

The short version

  • vulns reports. It changes nothing.
  • maintain -mode baseline -select vulnerable fixes: it picks the smallest upgrade that removes the reported advisory from one dependency, upgrades it under the usual gates, scans the result again, and proposes it only if the advisory is gone and no new one appeared.
  • Only baseline mode fixes by advisory. The scripted and model modes refuse -select vulnerable with one line and an exit of 1. No model is involved in choosing a fix.
  • Findings in Go's standard library are reported but not fixed. They follow the Go version of the toolchain image, and Repo Steward never changes the go directive, so it cannot act on them.
  • The local model has not repaired any of the real breaks it has been given so far (0 of 9 runs, see Evidence and Benchmark Results). Vulnerable selection does not depend on that: it runs the deterministic pipeline, which cannot edit source, so an upgrade that breaks the build ends regressed.

What you need

The same container engine and pinned toolchain image as inspect. Beyond that, the first time each toolchain image is used, network access to proxy.golang.org and sum.golang.org to build the scanner, and network access to https://vuln.go.dev on every run unless you name a database directory with -vulndb. The scan itself runs with no network.

vulns: the report

go build -o ~/tmp/repo-steward ./cmd/repo-steward
~/tmp/repo-steward fixture vulndb -dest ~/tmp/vulndb
~/tmp/repo-steward vulns ~/tmp/patch-safe -fixture-proxy ~/tmp/proxy -vulndb ~/tmp/vulndb

fixture vulndb -dest DIR writes a synthetic database that plants advisories in the fixture modules, so you can try this without the network. DIR must not exist.

vulns prints a JSON report on stdout and a short summary on stderr. The report splits findings into third-party dependencies and the standard library. Each finding has its advisory id and aliases, summary, module, found version, fixed version or none, the level the scanner reached (module, package, or symbol), and for symbol-level findings one call path. The report also names the scanner version and sha256, the database snapshot id and modified time, the image, the tree hash, and the Go version.

Exit codes:

Code Meaning
0 the scan is conclusive and finds nothing in third-party dependencies (standard-library findings alone still exit 0)
1 an error, such as a bad flag, an unreachable engine, or a scanner or database that fails verification
2 the repository is unsupported
3 the scan is inconclusive; the reason is in the report
4 third-party findings are present

An inconclusive scan says nothing about vulnerabilities. It is never read as "none found".

maintain -select vulnerable: the fix

~/tmp/repo-steward maintain ~/tmp/patch-safe -mode baseline -select vulnerable \
  -vulndb ~/tmp/vulndb -author "Your Name <you@example.com>" -fixture-proxy ~/tmp/proxy

On the patch-safe fixture this picks lib v1.2.4, the lowest version that clears GO-TEST-0001, rather than the newest, and proposes it only if a scan of the upgraded tree shows the advisory gone and nothing new. Everything else, the gates, validation, readiness, and the frozen proposal, is the same pipeline as plain baseline mode, and publication works the same way.

How the target is chosen:

  1. For each affected module, the version it needs is the highest fixed version among its findings.
  2. The target is the lowest published version at or above that which your policy allows: no pre-release or pseudo-version, the patch, minor, and major allowances, the deny list, and a -dependency pin (which must name that module and clear the findings).
  3. A finding with no fix does not stop the module being targeted for its other findings.
  4. Modules are ordered by the highest reachability level among their fixable findings (symbol, then package, then module), then direct before indirect, then path. The first is applied; the rest are listed as remaining.
  5. A module the main module requires only indirectly can be the target. In the default selection it never is.

The proposal body gains a section for each fixed advisory: aliases, summary, the reachability level and call path at the base, the found and fixed versions, whether the dependency is direct or indirect, the third-party findings that remain and why, the count of standard-library findings and why they are not acted on, the scanner version, and the database snapshot and its modified time. Text that came from the database is shortened and quoted so it cannot pose as part of the pull request.

Outcomes

Besides proposal_prepared and proposal_published, the new outcomes are explained non-results that exit 4:

Outcome Meaning
scan_inconclusive the base scan did not complete cleanly; the reason is given and nothing is changed
no_vulnerabilities the scan was conclusive and found nothing in third-party dependencies; standard-library findings are listed but not acted on
no_fix_available nothing eligible clears any finding, with a reason per finding; no manifest is touched

If the upgrade that clears the advisory breaks the build the run ends regressed, as in any baseline run. If it clears the advisory but introduces another, readiness refuses it and nothing is frozen.

Flags

Flag Meaning
-select smallest|vulnerable how baseline mode picks the upgrade; default smallest, the behaviour described in Modes
-vulndb DIR use this database directory instead of fetching https://vuln.go.dev; it is checked but never written
-scan-timeout DURATION bound for each scan; 10 minutes by default

vulns takes -vulndb and -scan-timeout too. See Command Reference.

The version cooldown

inspect and maintain apply a cooldown by default: a version published less than -min-age ago (72 hours by default), or whose publish time the module proxy does not give, is not eligible, and its candidate entry says why. -min-age 0 turns it off. This is a change in behaviour from releases before it. In vulnerable selection the cooldown is waived for the version that clears an advisory, because waiting leaves a known hole open; the result records cooldown_waived and the proposal says so. The publish time is whatever the module proxy reports.

What is and is not claimed

Claim Status
vulns reports against a pinned scanner and a verified database snapshot, and a scan that did not complete is never read as clean Verified by named tests, see Security Model
A proposal from vulnerable selection clears the findings the scanner reports in one module, against the snapshot it names Verified on fixtures, and on labstack/echo v4.15.4 in the network smoke test TestSmokeVulnerable
A proposal fixes every vulnerability Not claimed. One module per run, and a module none of whose packages the scanner loads is not reported at all
The database is correct or complete Not claimed
A standard-library finding is fixed Never: none is
The scripted or model modes select by advisory Not implemented. It would change what the model sees, so the recordings would have to be re-recorded
Native Linux Docker behaves as colima does for the scan paths Not yet run; CI's integration job is the proof

Measured on 2026-10-04 on colima: a cold vulns run on patch-safe with the fixture database took 11.9 seconds, 10.6 of them building the scanner; a warm run took 1.0 second. Fetching the public database added about 1.2 seconds per run. On the public database of 2026-10-01, patch-safe has no third-party findings and 52 standard-library findings for go1.22.12.

Problems and their fixes are in Troubleshooting and FAQ.

Clone this wiki locally