Dependency security clearance
The required Security workflow evaluates JavaScript vulnerability evidence
offline and evaluates Go vulnerabilities against the current source. Before
that gate, the workflow separately prepares the ignored generated Go source
needed by source-aware govulncheck with task db:generate, task config:generate, and task ui-signals:generate; this step does not run a
JavaScript audit or advisory query, or refresh the controlled evidence. The
default task security:dependencies command reads
.security/javascript-vulnerability-evidence.json; it does not refresh that
document or call a live JavaScript audit. The evidence must cover exactly six
JavaScript graphs:
- root
package.json+bun.lock; desktop/package.json+desktop/bun.lock;deploy/compose/qualification/package.json+deploy/compose/qualification/bun.lock;deploy/compose/qualification/package.json+deploy/compose/qualification/package-lock.json;internal/app/testing/maliciousinstance/electron/package.json+internal/app/testing/maliciousinstance/electron/bun.lock;pkg/apigen/typespec/package.json+pkg/apigen/typespec/package-lock.json.
Each manifest and lockfile is bound by a SHA-256 identity in the evidence.
Generation and expiry timestamps are UTC, and the evidence lifetime is bounded
to at most 7 days (168 hours). Future-dated, stale, malformed, missing, or
mismatched evidence fails closed. The same
required task performs a live, source-aware Go govulncheck scan for every
maintained Go module, so a Go feed/tool outage also remains a gate failure.
Before those scans, the evaluator provisions the pinned govulncheck@v1.6.0
binary once into a private temporary directory and verifies its -version
identity. Provisioning accepts only Go module download-progress diagnostics;
the scanner's own stderr remains forbidden.
The evaluator requires a source-mode symbol scan, validates every module-,
package-, and symbol-level record, and blocks every callable symbol finding.
Module-only and import-only records remain non-reachable notices; an ambiguous
trace or mismatched scan precision fails closed.
The evidence names the npm advisory API as its provider and records the exact
Bun or npm scanner identity and version used for each graph.
Treat every evidence change as a security-policy input: review the refresh
command, scanner identities, timestamps, file digests, and normalized findings
alongside the manifest or lockfile change that required the refresh.
JavaScript evidence is refreshed only by the separate task:
task security:dependencies:evidence:refresh
That task invokes the same repository-owned tool with
-refresh-javascript-evidence and is the live registry path. Bun may retry one
process-wide exact recognized transport failure, using a fresh process after a
five-second backoff. Critical findings, malformed or unknown output, another
transport failure, and retry exhaustion fail without another retry. npm audit
has no retry. A complete authoritative refresh, including one that records a
blocking finding, atomically replaces the evidence document; an operationally
failed or unavailable refresh does not replace it. An outage cannot create or
extend evidence, and previously reviewed evidence remains usable only until its
explicit bounded expiry.
The retained compatibility report is separate from the checked-in JavaScript evidence and remains a live scanner path. Its commands still require the scanners, Node, and Task as described below; they must not be treated as an offline version of the required Security workflow. It 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 uses a digest-pinned distroless Debian compatibility base. It supplies glibc, libstdc++, CA certificates, tzdata, and the BusyBox utilities required by release qualification without carrying APT or general-purpose Debian runtime packages. The same immutable multi-platform digest is scanned on both release architectures.
The distroless public-site runtime installs no OS packages. The authoring qualification image is test-only: it copies the exact candidate binaries and evaluation fixture into a digest-pinned Debian client stage that supplies DBus and GNOME Keyring. The malicious-browser proof image is also 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 the published runtime, select and pin a reviewed distroless Debian compatibility base digest containing the intended security updates, build both linux/amd64 and linux/arm64, inspect the supplied runtime components, 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.