diff --git a/docs/SUPPLY-CHAIN.md b/docs/SUPPLY-CHAIN.md index aa2e794f..ca6208d6 100644 --- a/docs/SUPPLY-CHAIN.md +++ b/docs/SUPPLY-CHAIN.md @@ -14,7 +14,7 @@ decision record is [ADR 0149](adr/0149-multi-ecosystem-sbom-vex-and-sbom-quality |---|---|---| | `messagefoundry-*.whl` / `*.tar.gz` | The Python engine (wheel + sdist) | GitHub release + PyPI | | `messagefoundry-sbom.cdx.json` | **CycloneDX SBOM** of the engine — license-complete, from the hash-locked core runtime, lifecycle = `build` | GitHub release | -| `messagefoundry-vex.openvex.json` | **OpenVEX** — our per-CVE exploitability assessments | GitHub release | +| `messagefoundry-vex.openvex.json` | **OpenVEX** — the document carrying our exploitability assessment for a CVE, once one has been made | GitHub release | | `*.sigstore*` bundles | Sigstore signatures for the wheel, sdist, **SBOM, and VEX** | GitHub release | | PEP 740 attestations | PyPI-side provenance (Trusted Publishing) | PyPI | | SLSA build provenance | in-toto attestation binding each artifact (incl. SBOM + VEX) to the source commit | GitHub attestations / Sigstore bundle | @@ -59,12 +59,15 @@ Attestations, and our releases, if you standardize on one tool across ecosystems ## Using the SBOM + VEX -The SBOM (CycloneDX 1.6) is a machine-readable inventory: components, versions, PackageURLs, hashes, and -**licenses**. Feed it to your own tooling: +The SBOM (CycloneDX 1.6) is a machine-readable inventory carrying at least a name, version, PackageURL and +**license** for every component. It does **not** carry per-component file hashes — the generator we run does +not emit them (see [How the SBOMs are generated](#how-the-sboms-are-generated-for-auditors)) — so use it as an +inventory, not as an integrity check on the components it lists. "Hash-locked" elsewhere on this page refers +to the lock file the inventory is built from, not to a field inside the SBOM. Feed it to your own tooling: ```bash -# Scan the SBOM for known CVEs, applying our VEX to suppress vulnerabilities we've assessed as -# not-affected/fixed — so you triage real risk, not unreachable CVEs: +# Scan the SBOM for known CVEs. --vex applies whatever assessments our VEX carries; --show-suppressed +# lists what was suppressed, so a run with nothing to apply is visibly a no-op: trivy sbom messagefoundry-sbom.cdx.json --vex messagefoundry-vex.openvex.json --show-suppressed # Or score the SBOM's completeness (0-10, NTIA minimum elements): @@ -72,8 +75,11 @@ sbomqs score -b messagefoundry-sbom.cdx.json ``` **Do not demand a zero-CVE "clean scan."** Per CISA's *Minimum Requirements for VEX* and NTIA's -*Software Consumers Playbook*, the correct posture is to accept a valid VEX assessment. Our VEX -(`security/vex/README.md`) records, per CVE, whether the vulnerable code is reachable in MessageFoundry. +*Software Consumers Playbook*, the correct posture is to accept a valid VEX assessment. Our VEX is the +`messagefoundry-vex.openvex.json` release asset above. Where we have assessed a CVE, its statement records +whether the vulnerable code is reachable in MessageFoundry and carries an OpenVEX `justification`. Where we +have not, the document says nothing about that CVE and your scanner's finding stands unsuppressed — see +[`security/vex/README.md`](../security/vex/README.md) for the assessment process and when a statement is added. ## How the SBOMs are generated (for auditors) diff --git a/scripts/security/sbom_finalize.py b/scripts/security/sbom_finalize.py index 3832e1ea..2ba0ec01 100644 --- a/scripts/security/sbom_finalize.py +++ b/scripts/security/sbom_finalize.py @@ -6,8 +6,11 @@ WHY THIS EXISTS (ADR 0149). The SBOM generators we run — ``cyclonedx-py environment`` (Python), ``@cyclonedx/cyclonedx-npm`` (the VS Code extension), and ``trivy image`` (the container) — emit a -CycloneDX BOM with components, hashes, licenses, and ``metadata.tools`` (the generating tool, i.e. -the draft-2025 CISA "Tool Name" minimum element). Two gaps remain that this closes: +CycloneDX BOM carrying at least components, licenses, and ``metadata.tools`` (the generating tool, i.e. +the draft-2025 CISA "Tool Name" minimum element). Per-component *hashes* are NOT among them for the +Python SBOM: ``cyclonedx-py environment`` emits none at all, so do not describe the finalized artifact +as hash-bearing (docs/SUPPLY-CHAIN.md says so to operators, and the two must not drift apart again). +Two gaps remain that this closes: 1. None of them set ``metadata.lifecycles`` — the CycloneDX field that records WHERE in the SDLC the BOM was produced. That maps to CISA's "Build" SBOM Type and the draft-2025 CISA "Generation