On this page
ADR-0039: Source-built components are declared in the SBOM; their scan coverage rests on Grype
Status: Accepted (WP-70f; threat model O13). Implemented in scripts/generate-sbom.sh and scripts/scan.sh (shared template files), the manifest field build.components (schemas/commonlayer-v1.schema.json) and declaration files shipped under /usr/share/commonlayer/sbom/.
Context
CommonLayer images build their main software from upstream source. syft identifies some
of those binaries (nginx, PostgreSQL) and not others (Valkey), so a clean scan of a
Valkey image said nothing about Valkey. CommonLayer's own helpers (cl-probe in every
image, cl-kube-label in Valkey) were not listed at all.
Decision
- A project declares its source-built components in
commonlayer.yaml(build.components: name, purl, CPEs;fromUpstreamtakes the version from the image's upstream label and the source URL and SHA-256 from the matchingversions[]entry). Components that every image derived from the base contains are declared by the image itself (/usr/share/commonlayer/sbom/*.json); the build fails if a declaration's version differs from the binary's--version. generate-sbom.shadds each declaration to both SBOMs (SPDX package with checksums, supplier, license, CPE and purl references and aCONTAINSrelationship; CycloneDX component with the same facts), completes an entry syft already found instead of duplicating it, and fails if a declared component is missing from a written document.scan.shruns Trivy and Grype on the image and on the CycloneDX SBOM; both rows gate.- purls are
pkg:generic/<name>@<version>so that VEX product ids match exactly. The CPEs are the vendor:product pairs the NVD data actually uses (looked up withgrype db search), including several per product where NVD uses several. pkg:bitnami/...purls, which would make Trivy evaluate the components, were rejected: they would claim the software is a Bitnami package.
Consequences
- Grype matches the declared components by CPE. Demonstrated with deliberately old declared versions: nginx 1.25.3 and Valkey 8.0.1 fail the gate; PostgreSQL 16.1 reports high findings (WP-70f).
- Trivy evaluates none of the declared components (it does no CPE matching and ignores generic packages). For nginx, Valkey, PostgreSQL and darkhttpd the scan gate rests on Grype alone, plus the platform's OSV re-matching, which was not checked for these components.
- Still invisible: CommonLayer's own helpers have no vulnerability feed; libraries Valkey
vendors in
deps/are compiled into the binary and surface only as Valkey CVEs; extensions added in derived PostgreSQL images are not declared. NVD data trails upstream advisories, so the projects keep a "watch upstream advisories" duty. - Valkey's 7.2 and 8.0 lines carry VEX statements (
fixed, with evidence) for NVD ranges that ignore backports; they must be re-checked when those lines get a new patch release. - The platform's own static Rust binaries carry no dependency metadata, so neither syft
nor Trivy sees a Rust crate in the platform images; cargo-deny and cargo-audit in CI
cover the Rust dependencies. Building with
cargo auditablewould close this gap and has not been done.