Dependency security clearance

The nightly dependency lane records one normalized, content-bound evidence document for the root Bun graph, Desktop Bun graph, APIGen npm graph, and Go graph. The report is generated by the repository-native tool:

go run ./internal/app/tools/dependencyreport report \
  --output dependency-security-report.json
go run ./internal/app/tools/dependencyreport check \
  --input dependency-security-report.json

The equivalent task security:report and task security:report:check tasks may be used in CI. Generation requires a clean checkout and all four scanners, Node, and Task. A missing tool, scanner error, malformed result, missing lock graph, edited graph, commit mismatch, or expired waiver fails closed. Scanner errors and vulnerable graphs replace any older report with explicit uncleared diagnostics while still returning non-zero. When a failure occurs before complete evidence can be assembled, generation removes an older report rather than leaving stale clearance. The --allow-dirty flag is reserved for diagnostics and still cannot produce cleared evidence.

Each report includes its schema version and UTC generation time, source commit and dirty state, Node and Task versions, scanner/runtime versions and exact normalized commands, the Go vulnerability database timestamp when supplied by govulncheck, scanner environment controls, SHA-256 digests of all required module lock/manifests, the presence and SHA-256 digest of the repository waiver policy, normalized results, and severity/package counts. The Go scanner uses a 4 GiB soft memory limit so the full source analysis remains viable on standard CI runners. check recomputes the digests, toolchain, scans, summaries, and clearance before accepting the artifact.

For Go, module-only and import-only advisories are retained as non-reachable notices, deduplicated by advisory and module. They do not satisfy the scanner's definition of affected code and therefore do not require a waiver. A finding with a symbol-level call trace is recorded as reachable and blocks clearance.

Runtime OS packages

The production server image combines a digest-pinned Debian base with the dated sources in deploy/container/debian-bookworm.sources. Both the Debian and Debian security suites use one immutable snapshot timestamp. APT binds each source to the Debian archive keyring, verifies signed repository metadata and package hashes, and therefore resolves direct and transitive packages from a frozen package universe on both release architectures. The bootstrap CA bundle comes from the separately digest-pinned Go builder before APT connects to the HTTPS snapshot.

The distroless public-site runtime installs no OS packages. The authoring qualification image derives from the server runtime and inherits its frozen sources. The malicious-browser proof image is test-only, and the Ubuntu host bootstrap is an installation-time patching boundary: it intentionally consumes the current signed Ubuntu 24.04 repositories and enables unattended upgrades rather than becoming part of the immutable application image.

To refresh runtime packages, choose a reviewed snapshot containing the intended security updates, update the snapshot timestamp and the pinned runtime base digest together, build both linux/amd64 and linux/arm64, inspect the installed package inventory, and rerun container vulnerability and provenance checks. Do not point a release build back at a moving mirror.

Waivers

Waivers are optional JSON in security/dependency-waivers.json. Reports bind the exact repository policy, including its absence; alternate or report-only waiver sources are rejected. Every waiver must match an observed advisory and dependency and must include an owner, reachability assessment, compensating control, creation time, and a future expiry. Unused, malformed, future-dated, or expired waivers are rejected.

[
  {
    "advisory": "GHSA-example",
    "dependency": "example-package",
    "owner": "security@example.com",
    "reachability": "The vulnerable code path is not shipped.",
    "compensating_control": "Pinned transitive version and regression test.",
    "created": "2026-08-01T00:00:00Z",
    "expiry": "2026-09-01T00:00:00Z"
  }
]

The report is cleared only when every scan passes or every observed finding is covered by a valid waiver. Do not hand-edit a report; check compares its normalized scan results with a fresh scan of the same checkout.

Verification

Run the report and then its check from the same clean commit. Both commands must exit successfully, and the JSON must contain "clearance": {"cleared": true}. Review the uploaded artifact's commit and lockfile digests before retaining it as release evidence. If a scan, digest, commit, graph, or waiver check fails, fix the dependency or waiver and regenerate the artifact; do not reuse a previous report.