You have an SBOM. Congratulations. Now tell me, with a straight face, that it lists every package your image actually contains, at the versions your build actually resolved, tied to the digest your registry actually holds. Still confident?
On June 25, Docker published a guide on generating SBOMs for container workflows. The choice it forces on your pipeline is: bake the bill of materials at build time, or scan the finished image and reconstruct it after the fact. Docker's post lands firmly on one side.
The two places an SBOM can be born
At build time. The generator runs inside the build system with access to the resolved dependency tree, the lock files, and the full build context. For containers, that means BuildKit's native attestation output: the SBOM comes out as an in-toto attestation, gets bound to the image digest, and is pushed to the registry in one operation. Every transitive dependency the build resolved is in the file because the build resolved it.
After the fact. A scanner walks the finished image, matches package-manager metadata and file signatures against a database of known patterns, and infers what is inside. It works on any OCI image, which is useful when you did not build the image yourself. It also misses statically linked binaries, vendored dependencies, and OS packages that lived in intermediate stages. Heuristics, not build graph.
Both produce a file that says "SBOM" at the top. Only one can honestly claim to know what got compiled in.
What Docker says a good SBOM has to clear
The guide names five bars: completeness across every layer and package type, accuracy on resolved versions (not declared ranges), freshness pinned to a specific build, verifiability via a signed attestation bound to the artifact digest, and format compliance against SPDX or CycloneDX. Read them together and post-build scanning looks thin: heuristic detection struggles on completeness and accuracy, and a scan run last Tuesday is not fresh next Tuesday.
Docker's recipe: BuildKit attestation flags for the image layer, language-specific plugins (CycloneDX for Maven and Gradle, the npm or yarn plugins for Node) for the application layer, generate from the final stage on multi-stage builds, validate the output before publishing, and gate deployment on presence-plus-severity. SPDX if you care about license work; CycloneDX if you correlate against a vulnerability database.
Why a pipeline person should care
This is a CI/CD workflow problem wearing an SBOM hat. Executive Order 14028 already pushed SBOMs onto anything sold to US federal agencies, and the EU Cyber Resilience Act extends similar duties to products with digital elements sold in the EU. Whichever regulation reaches your team first, someone will ask: prove the file describes the artifact. If your SBOM was scanned out of the image a week after publish, the "prove" part gets awkward. If it was emitted alongside the digest, signed and stored as an attestation, it becomes a policy check the release stage can enforce.
Concretely, your pipeline gets two new jobs. Generate and attach on every build, including rebuilds. Then verify on every deploy: no image ships without a valid attestation, none deploys with a component above your severity threshold. The interesting engineering is on the verification side, which is the part everyone conveniently skips.
How popular pipelines handle it
-
BuildKit /
docker buildx: native SPDX attestation as part of the build. Closest to Docker's guide because it is Docker's guide. If your images build with Buildx, this is the least code. - Syft (Anchore): mature post-build scanner, emits SPDX and CycloneDX. If you do not control the build (vendor image, third-party base), Syft is honestly the right answer; reverse-engineering the artifact is the whole point.
- Trivy (Aqua): SBOM plus vulnerability scan in one binary, easy to drop into any CI step. Convenient when you want one tool for both jobs and can accept scanner-style completeness limits.
-
Sigstore
cosign: not an SBOM generator; the thing you use to sign the attestation Buildx (or anyone) produced. Serious setups end up pairing an SBOM tool withcosign attestandcosign verify. - GitHub Actions artifact attestations: first-party attestations for artifacts built inside GitHub-hosted runners, tied to workflow identity. If your builds already live there, the trust boundary is short.
- Buddy: pipeline runner where the SBOM step is one action among your usual build stages. Useful when you want attestation, signature and deploy gate wired in the same YAML instead of glued across three tools. Not the choice if you are all-in on GitHub-native attestations; that trust chain is shorter there.
A skeletal Buddy step, for shape:
- action: "Attest SBOM"
type: "BUILD"
docker_image_name: "library/docker"
execute_commands:
- "docker buildx build --sbom=true --provenance=true --push -t $REGISTRY/app:$BUDDY_EXECUTION_REVISION ."
- "cosign attest --predicate sbom.spdx.json --type spdxjson $REGISTRY/app@sha256:<digest>"
Placeholders are placeholders on purpose. Do not paste a real-looking digest here or anywhere else. The guide is not going to save you from copying someone else's SHA.
The part everyone skips
Generation is the easy half. Verification decides whether any of this actually protects you. A signed SBOM that nobody downstream checks is a compliance artifact, not a security control. Docker's guide is a reasonable starting point because it treats generation as one step of a workflow that ends at a deploy gate. Whether you enforce that gate on every rebuild, including the ones you would rather forget, is the question your pipeline has to answer.
Attest early. Verify later. Or skip both and file it under "we have SBOMs now."
Top comments (0)