Safeguard
SBOM

The SBOM Your Customer Wants Is Not the One You Generated

An SBOM is a statement about a specific artifact. Generate it from the repository and you have an accurate document about something nobody runs, missing the base image where most of your published CVEs live.

Sofia Marchetti
Open Source Program Lead
6 min read

Your customer asked for an SBOM. You generated one from the repository, sent it, and it passed their check. It also does not describe the thing you shipped them.

An SBOM is a statement about a specific artifact. Generate it from a different artifact and you have produced an accurate document about something nobody is running, which is worse than no document, because it will be trusted. This post is about which artifact to generate from, what each choice leaves out, and how to tell which one you have.

Four places you can generate, and what each misses

From the source repository. Reads manifests: package.json, pom.xml, requirements.txt, and the lockfile if there is one.

Misses: anything installed by the Dockerfile rather than the package manager, the base image entirely, native libraries, anything vendored by path, and any difference between what the manifest permits and what the build resolved. If your manifest says ^4.17.0 and the build resolved 4.17.21, an SBOM from the manifest describes a range rather than a fact.

This is the most commonly produced SBOM and the least accurate one.

From the build, at build time. Instrument the build and record what it actually resolved and linked.

Misses: very little about dependencies, which is why this is the best source for application components. It still does not know about the base image or anything added after the build step.

From the container image. Scan the final image: operating system packages, application dependencies, binaries present on the filesystem.

Misses: nothing that is present, which is its strength. What it cannot tell you is provenance. It sees a file and identifies it, so it will attribute something by fingerprint and occasionally get it wrong, and it cannot distinguish a dependency you use from one that happens to be in the base image.

From the running system. Inspect what is actually loaded.

Misses: anything not loaded during observation. Best for answering what is in use, worst for completeness, and not what a customer means when they ask for an SBOM.

The gap that causes the trouble

The base image. An SBOM generated from source describes your application's dependencies and says nothing about the several hundred operating system packages underneath them.

Those packages are where most of your published CVEs live. A customer running a scanner against your image will find them, compare against your SBOM, and see a document that omits the majority of what they found. From their side that looks like concealment, and explaining that your generator simply reads manifests is not a good conversation to have during a security review.

The second gap is build-time additions: a curl of a binary in the Dockerfile, a vendored library copied in, a plugin fetched by the build tool. None of these appear in any manifest, all of them ship, and a source-level generator cannot see any of them.

What to actually do

Generate from the image, and merge in a build-time SBOM. The image scan gives completeness. The build-time SBOM gives accurate provenance and relationships for your own dependencies. Together they describe the artifact and how it came to be. Both SPDX and CycloneDX support merging.

Generate per artifact, per release, in the pipeline. Not on request, not by hand. An SBOM produced by a person in response to a customer email is stale the moment the next release ships, and it is the version that ends up in a procurement folder for three years.

Attach it to the artifact, not to a folder. As an attestation alongside the image in your registry, so the SBOM and the thing it describes travel together and cannot drift apart. This is the single change that most reduces the chance of sending the wrong one.

Record what the generator could not see. If your build pulls a binary the tool cannot identify, that belongs in the document as a known gap. A customer who receives an SBOM with an explicit "these components could not be automatically identified" section trusts it more, not less, because it is the first one they have seen that admits a limit.

Checking which one you have

Take your current SBOM and your shipping image, and compare counts:

# components your SBOM claims
jq '[.components[]] | length' sbom.json

# packages actually in the image, as a rough independent count
docker run --rm --entrypoint sh <your-image> -c \
  'command -v dpkg >/dev/null && dpkg -l | tail -n +6 | wc -l ||
   command -v apk  >/dev/null && apk info | wc -l'

If the second number is large and the first does not account for it, your SBOM is source-derived and omits the base image. That is the common case, and it takes about a minute to establish.

Then check a specific thing you know you ship. Pick a binary added by your Dockerfile and search the SBOM for it. If it is absent, you now have a concrete example to show whoever owns the pipeline, which is more persuasive than an argument about methodology.

The concession

A source-derived SBOM is not useless. For a library published to a package registry, where there is no image and no base layer, it is exactly right. For answering "which of my dependencies are affected by this advisory", it is often sufficient and much cheaper to produce.

The claim here is narrower: if you distribute a container image or an installable application, an SBOM that omits the base image is not a description of your product, and it will be read as one. Match the artifact to the question. If the customer is asking what they will be running, generate from what they will be running.

The implication

The value of an SBOM is entirely in whether it corresponds to a real artifact. A document that is internally perfect and describes the wrong thing gives everyone downstream a false position, and it fails in the specific direction of understating what you ship.

So the useful question is not whether you have an SBOM. It is: which artifact was this generated from, and is that the artifact the customer received. If you cannot answer the first half quickly, that is the work.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.