Expand description
Build-time SBOM generation (issue #457).
Emits a CycloneDX 1.5 JSON Software Bill of Materials at the
root of the generated site (sbom.cdx.json) and links to it
from every HTML page via <link rel="sbom" type="application/vnd.cyclonedx+json">.
§Why ship an SBOM with the static site?
Procurement teams in regulated industries (finance, healthcare,
government) increasingly require SBOMs for any deployed software
— including the build pipeline that produced static assets. The
scheduled.yml workflow already generates a CycloneDX SBOM via
cargo cyclonedx and attaches a Sigstore provenance attestation,
but those artifacts live in CI; they’re not discoverable from
the deployed site. This plugin fixes that gap by embedding
the SBOM into every site, making the supply chain machine-
introspectable from the consumer’s browser.
§Format
Minimal CycloneDX 1.5 (the JSON Schema is documented at
https://cyclonedx.org/docs/1.5/json/). The component list
covers the SSG package itself; transitive Cargo dependencies
are out of scope here (they’re in the CI-generated SBOM
published as a release artifact). The rendered SBOM declares:
bomFormat: “CycloneDX”specVersion: “1.5”serialNumber:urn:uuid:…, derived from the build (see below)version: 1metadata.timestamp: build time (ISO 8601, UTC)metadata.tools[]: SSG name + versionmetadata.component: the site itself (type: “application”)components[]: SSG generator
§Discoverability
Every HTML page emitted by the build receives a
<link rel="sbom" type="application/vnd.cyclonedx+json" href="<base-url-path>/sbom.cdx.json"> element in <head>.
This is the
IANA-registered link relation for SBOM discovery (registered
2023; see https://www.iana.org/assignments/link-relations/).
§Serial number
serialNumber is optional in the CycloneDX schema, but consumers
use it to tell the format apart from SPDX. GitHub’s attestation
action, for one, recognises CycloneDX only when bomFormat,
serialNumber and specVersion are all present, and otherwise
fails a release with “Unsupported SBOM format”. An SBOM without it
is therefore valid but not attestable.
The value is derived from the build rather than drawn at random, so
SOURCE_DATE_EPOCH pins it along with the timestamp and the
determinism gate still holds. Two builds that differ only in wall
clock still get different serials, which is what CycloneDX asks for.
§Idempotency
The HTML transform is idempotent — pages that already contain
rel="sbom" are left unchanged. The JSON file is rewritten on
every build (so timestamps stay current).
Structs§
- Sbom
Plugin - Plugin that emits a
CycloneDXSBOM and links to it from every HTML page.