SBOM (Software Bill of Materials)

Also known as: software bill of materials, SBOM file, dependency inventory, dependency manifest, CycloneDX SBOM, SPDX SBOM

Updated 2026-07-244 questions

An SBOM (software bill of materials) is a machine-readable inventory of every component that ships in a build: direct and transitive dependencies, exact versions, licenses, and cryptographic hashes. Generated at build time and attached to the artifact, it lets teams answer "is this vulnerable dependency in production?" in seconds instead of guessing across services.

How does an SBOM work?

An SBOM is produced by a scanner that walks whatever you feed it - a source tree, a built binary, a container image, a filesystem - and records every component it can identify. For each component the tool captures a small set of fields: a name, a version, a package URL (purl), a license identifier, a supplier, and one or more cryptographic hashes.

The output is a single machine-readable document. The two formats in practical use are CycloneDX and SPDX; both are open standards, both serialize to JSON, and most scanners can emit either. Common generators include Syft, Trivy, Microsoft's sbom-tool, and language-native ones like cyclonedx-npm, cyclonedx-python, and cyclonedx-maven-plugin.

Where you point the scanner matters. Scanning source only sees what your package manager declares; scanning a built binary catches statically-linked libraries; scanning a container image catches everything the base layer dragged in - OpenSSL, glibc, a stray curl. For an artifact that will run in production, scan the built image, not the repo.

Once produced, the SBOM travels with the artifact it describes. You can attach it as a sibling file in artifact storage, push it as an OCI referrer alongside the image, or wrap it in an in-toto attestation signed by cosign. A vulnerability scanner or license auditor later reads the SBOM, joins it against a vulnerability database like the NVD or OSV, and tells you which of the components you shipped are known to be broken.

Why does an SBOM matter?

The honest answer: because most incidents in the last five years - Log4Shell, xz-utils, Codecov, the SolarWinds compromise - hinged on a dependency deep inside somebody's build. When the next CVE lands, the question is always the same: is this library in any of our services, and in which versions? Without SBOMs, teams answer that by grepping repos, re-scanning production, and hoping. With SBOMs stored per artifact, it becomes a database query that returns in a second.

There is a second reason that has stopped being optional: regulation. US Executive Order 14028 pushed federal buyers toward SBOMs; the EU Cyber Resilience Act, phasing in through 2027, makes vendors of software products in the EU legally responsible for tracking known vulnerabilities in their components, and an SBOM is the standard way to prove you can. If you sell software to enterprises or governments, procurement is already asking for one.

The value only lands, though, if the SBOM is accurate and stored. A stale SBOM that was generated once and never regenerated is worse than none, because it lets people believe they know what is running when they do not. Regenerate on every build, sign it, and keep it addressable by the artifact's digest.

SBOM vs lock file vs vulnerability report

Three artifacts often get lumped together. They are not the same:

  • A lock file is a build input. It pins the resolution your package manager will use next time (npm ci, pip install -r requirements.txt, cargo build --locked). It knows nothing about the base OS image, statically-linked C libraries, or files copied in by a Dockerfile.
  • An SBOM is a build output. It describes what actually landed in the artifact, from every layer. It is a snapshot of composition, not a policy.
  • A vulnerability report is a derivation over an SBOM. Feed the SBOM into a scanner plus a vulnerability database, and the report is the intersection: which listed components have known CVEs today. Tomorrow the same SBOM plus a fresher database can produce a new report without another build.

A common failure mode is treating the vulnerability report as the deliverable and throwing the SBOM away. Keep the SBOM: it lets you re-answer the question next month, when a fresh CVE is disclosed, without touching the pipeline.

How do popular CI/CD tools handle SBOMs?

The mechanics are largely the same everywhere - run a scanner (Syft, Trivy, or the language-native generator) in a build step, then archive or attest the output. Where the tools differ is how much of that plumbing is already wired up for you.

  • GitLab CI (Ultimate/Premium) ships Dependency Scanning and CycloneDX SBOM generation as first-party features, wired into the MR UI, the security dashboard, and the Dependency List. If your organisation already pays for GitLab Ultimate, this is the shortest path to a compliant SBOM workflow - the tooling and reporting are already there and you would be paying to reproduce it elsewhere.
  • GitHub Actions has strong ecosystem coverage: the anchore/sbom-action drops a Syft-generated SBOM into a workflow in three lines, Dependency Review blocks risky PRs, and Dependency Graph + Dependabot close the loop on known CVEs. Great if the repo already lives on GitHub and you want SBOM plus alerting without buying a platform.
  • Jenkins has no built-in SBOM step, but the plugin ecosystem is wide: the OWASP Dependency-Track plugin submits CycloneDX SBOMs to a central Dependency-Track server that many mature security programmes already run. A pragmatic choice when you need SBOMs across dozens of repos and want one dashboard rather than per-project reports.
  • Argo CD / Kubernetes-native stacks don't generate the SBOM themselves - the CI system does that upstream - but they close the loop at admission time: sigstore/cosign can sign the image and its SBOM, and a policy engine like Kyverno or Sigstore Policy Controller can refuse to admit an image whose SBOM/signature is missing or fails policy.
  • Buddy is one solid option when you want the same Syft-or-Trivy scan tucked directly beside the build step, with the SBOM stored as a pipeline artifact and forwarded to an external system on success. A BUILD action can run any container image, so anchore/syft and aquasec/trivy slot in the same way they would in a raw Docker CI. Buddy's role is to make the plumbing (build -> scan -> upload -> gate) one linear pipeline you can read on one screen; the scanners and the vulnerability database stay best-of-breed.

There is no single right pick on this list. The rule of thumb: match the SBOM tool to where the artifact already lives. If you publish images to a registry, sign and attach the SBOM there; if you publish to a language registry, produce the language-native SBOM alongside; if you already run Dependency-Track or a commercial equivalent, feed it from whichever CI you have.

Example

A Buddy pipeline that builds a container image, generates a CycloneDX SBOM with Syft, scans it for critical vulnerabilities with Grype, and only forwards the SBOM to an external Dependency-Track server when the scan passes. Failure of the scan step short-circuits the pipeline, so a build with a known critical CVE never gets recorded as a shipped artifact.

# .buddy/buddy.yml - build, generate SBOM, scan, upload
- pipeline: "build-and-attest"
  events:
  - type: "PUSH"
    refs:
    - "refs/heads/main"
  variables:
  - key: "IMAGE"
    value: "registry.example.com/checkout:${BUDDY_EXECUTION_REVISION}"
  - key: "DTRACK_URL"
    value: "https://dtrack.example.com"
  - key: "DTRACK_PROJECT"
    value: "checkout-api"
  actions:
  - action: "Build image"
    type: "BUILD"
    docker_image_name: "docker"
    docker_image_tag: "26"
    commands: |-
      docker build -t "$IMAGE" .
      docker save "$IMAGE" -o image.tar

  - action: "Generate CycloneDX SBOM"
    type: "BUILD"
    docker_image_name: "anchore/syft"
    docker_image_tag: "latest"
    commands: |-
      syft docker-archive:image.tar -o cyclonedx-json > sbom.cdx.json
      test -s sbom.cdx.json

  - action: "Scan SBOM for critical CVEs"
    type: "BUILD"
    docker_image_name: "anchore/grype"
    docker_image_tag: "latest"
    commands: |-
      grype sbom:sbom.cdx.json --fail-on critical --output table

  - action: "Upload SBOM to Dependency-Track"
    type: "HTTP"
    method: "POST"
    notification_url: "${DTRACK_URL}/api/v1/bom"
    trigger_time: "ON_SUCCESS"
    headers:
    - name: "X-Api-Key"
      value: "${DTRACK_API_KEY}"
    - name: "Content-Type"
      value: "application/json"
    content: |
      {
        "projectName":    "${DTRACK_PROJECT}",
        "projectVersion": "${BUDDY_EXECUTION_REVISION}",
        "autoCreate":     true,
        "bom":            "@sbom.cdx.json"
      }

The pattern is deliberately linear: the SBOM is produced from the exact image that was built one step earlier, the scan reads the SBOM (not the source, not a re-resolve), and the upload only fires on success. That way the Dependency-Track project always mirrors what actually shipped - which is the whole point of keeping an SBOM in the first place.

Frequently asked questions

What formats does an SBOM come in?

Two open standards dominate in 2026. **CycloneDX** (from OWASP) is security-first, compact, and the default of most scanner tooling. **SPDX** (from the Linux Foundation, ISO/IEC 5962) leans toward license compliance and is the format the US federal government asks vendors for. Both come in JSON, and both can carry the same core inventory. Pick one as house style so downstream tools (scanners, dashboards, registries) only need to speak one dialect.

Is an SBOM the same as a lock file?

No. A lock file (`package-lock.json`, `poetry.lock`, `Cargo.lock`) is a *build input* that pins the exact versions your build should resolve. An SBOM is a *build output* that records what actually ended up in the shipped artifact - including native libraries pulled in by the base image, files installed by `apt-get`, and dependencies that a lock file never sees. They complement each other; neither replaces the other.

Who requires SBOMs today?

A growing list. US federal vendors have been required to attest to a secure SDLC (with SBOMs as one accepted piece of evidence) since Executive Order 14028 and the NIST SSDF. The EU Cyber Resilience Act makes an SBOM effectively mandatory for products with digital elements sold in the EU. Regulated industries (finance, healthcare, critical infrastructure) increasingly ask for one in vendor questionnaires.

When in the pipeline should an SBOM be generated?

Generate it in the same job that produces the artifact, right after the build succeeds and before anything is published. That way the SBOM describes the *exact* bits you are about to sign and ship, not a re-scan of source that may resolve dependencies differently. Attach it to the artifact (as a sibling file, an OCI referrer, or an in-toto attestation) so it travels with what it describes.

Missing a term? Spotted a mistake?

Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.