# SBOM: what it contains and how to generate, sign and use it

> An SBOM is a machine-readable inventory of the components in software. CISA's 2026 minimum elements, SPDX vs CycloneDX, and how to generate, sign and match one.

- URL: https://computese.com/sbom/
- Author: Duong Quan Nguyen, CEO, Computese
- Published: 2026-10-26
- Updated: 2026-10-26
- Topics: Security, Custom software

## In short
- An SBOM is a machine-readable inventory of the components in software. CISA's 2026 minimum elements (version 2.1, dated 29 July 2026) replace NTIA's 2021 list and ask for all components, including transitive dependencies, plus the generation context.
- Generate the SBOM from the built image in CI on every release. Syft's documentation says an image scan finds installed packages, while a directory scan also finds declared dependencies, which may not identify the exact version that would be installed.
- Sign the SBOM as an attestation on the image digest and verify it at the deploy gate against a named signer identity. A valid signature shows who signed, not that the list is complete, and a missing attestation should block the release.
- Match stored SBOMs against vulnerability data on a schedule with a tool such as Grype, Trivy or OSV-Scanner, and publish VEX statements (OpenVEX, CSAF or CycloneDX VEX) for matches where the product is not affected.
- Rules differ: the EU Cyber Resilience Act applies from 11 December 2027 and asks for at least the top-level dependencies, US agencies may require an SBOM by contract (OMB M-26-05), and the FDA says makers of cyber devices must provide one.

A software bill of materials (SBOM) is a machine-readable inventory of the components in software: names, versions, producers, identifiers and relationships. [CISA calls it "a nested inventory, a list of ingredients that make up software applications and systems."](https://www.cisa.gov/sites/default/files/2026-07/2026_cisa_sbom_minimum_elements_508c.pdf) It lets you check whether a newly reported vulnerability involves a component you ship.

This guide is for the security lead, DevOps engineer or software vendor who has been asked for an SBOM. Tools, standards and dates are as published on 9 October 2026. For the build stages around the SBOM, see our CI/CD pipeline guide. If you run a CMS website instead, start with our [website security checklist](https://computese.com/essential-tips-to-secure-your-website/).

## What must an SBOM contain?

The baseline is CISA's [2026 Minimum Elements for a Software Bill of Materials](https://www.cisa.gov/sites/default/files/2026-07/2026_cisa_sbom_minimum_elements_508c.pdf), version 2.1, dated 29 July 2026. It "updates and replaces" the [2021 NTIA minimum elements](https://www.ntia.gov/files/ntia/publications/sbom_minimum_elements_report.pdf), whose data fields were supplier name, component name, component version, other unique identifiers, dependency relationship, author of SBOM data and timestamp. CISA says its elements "do not create new requirements; they refine how organizations should generate and request SBOMs".

| What the recipient needs | NTIA 2021                                                                                      | CISA 2026                                                                                                                |
| ------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Who made the component   | Supplier Name                                                                                  | Component Producer, because Supplier Name "has proven ambiguous in practice"                                             |
| Which version            | Version of the Component                                                                       | Component Version, or "unknown" if the producer gives none                                                               |
| How to look it up        | Other Unique Identifiers                                                                       | Component Identifiers: at least one, such as CPE or purl                                                                 |
| How much is listed       | Depth: all top-level dependencies at a minimum, with enough detail to find the transitive ones | Coverage: all components, "including transitive dependencies. There is no minimum depth."                                |
| How it can be checked    | Hash of the Component, recommended only                                                        | Component Hash Value and Hash Algorithm, plus SBOM Author Signature                                                      |
| Which licence applies    | License Information, recommended only                                                          | Component License                                                                                                        |
| How the SBOM was made    | Author of SBOM Data and Timestamp; Lifecycle Phase, recommended only                           | SBOM Author, Timestamp (RFC 9557), Tool Name and Version, SBOM Version, Data Format Name and Version, Generation Context |

For vulnerability checks, CISA gives coverage an explicit test: the recipient "should be able to conclude that a newly reported vulnerability does not affect them if the SBOM does not list the component associated with the vulnerability." By that test, an SBOM that stops at the top-level dependencies cannot clear a product, because the affected component may sit deeper.

![A tree of grey boxes crossed by a dashed line under its second row. Arrows copy the boxes above the line into a document of grey bars; one orange box just below the line has no arrow.](https://computese.com/images/blog/sbom/dependency-depth.260834fdc1-1536.webp)

*Stopping at the top-level dependencies hides the component a vulnerability report names.*

Generation context is a new element. Component data "may differ based on the phase of the software lifecycle during which the SBOM was generated", so the SBOM now says which phase it came from. CISA's examples: "before build" for an SBOM generated from source code, "after build" for binary analysis tools.

## Which laws and customers require an SBOM?

Each row follows the wording of its primary text.

| Rule                                                                                                                                                      | What the text says                                                                                                                                                                                                                               | Dates                                                                                                 |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| US [Executive Order 14028](https://www.govinfo.gov/content/pkg/FR-2021-05-17/html/2021-10460.htm)                                                         | NIST's software supply chain guidance must include standards, procedures or criteria on "providing a purchaser a Software Bill of Materials (SBOM) for each product directly or by publishing it on a public website"                            | Signed 12 May 2021                                                                                    |
| US [OMB memo M-26-05](https://www.whitehouse.gov/wp-content/uploads/2026/01/M-26-05-Adopting-a-Risk-based-Approach-to-Software-and-Hardware-Security.pdf) | Rescinds M-22-18 and M-23-16; agencies "may also choose to adopt contractual terms that require a software producer to provide a current software bill of materials (SBOM) upon request"                                                         | 23 January 2026                                                                                       |
| EU [Cyber Resilience Act](https://eur-lex.europa.eu/eli/reg/2024/2847/oj/eng)                                                                             | Manufacturers must identify and document the components of a product "including by drawing up a software bill of materials in a commonly used and machine-readable format covering at the very least the top-level dependencies of the products" | Applies from 11 December 2027; Article 14 from 11 September 2026                                      |
| US [FDA guidance](https://www.fda.gov/media/119933/download) on medical device cybersecurity                                                              | Section 524B(b)(3) of the FD&C Act "requires manufacturers of cyber devices to provide an SBOM, including commercial, open-source, and off-the-shelf software components"                                                                        | Guidance issued 3 February 2026; section 524B effective for premarket submissions since 29 March 2023 |

Four details matter. The CRA names no format beyond "a commonly used and machine-readable format", and Article 13(24) says the Commission "may" specify the format and elements by implementing act. Annex VII has the SBOM, "where applicable", go to a market surveillance authority "further to a reasoned request", and Annex II lets the manufacturer decide whether users get it. The CRA's floor, the top-level dependencies, is lower than CISA's coverage. And FDA's guidance, issued before CISA's update, recommends machine-readable SBOMs "consistent with the minimum elements" that NTIA identified in October 2021.

For a contract, CISA advises that an organization "should explicitly address these elements in any policy, contract, or arrangement to ask for or provide SBOMs". Specify the format and version (CISA says to avoid accepting SBOMs for new software in deprecated versions of any format), how often SBOMs arrive, how they are delivered and how unknown fields are marked.

## Which format should you use: SPDX or CycloneDX?

CISA names two data formats "widely used by software ecosystem stakeholders to generate and consume SBOMs": [SPDX](https://spdx.dev/use/specifications/) and [CycloneDX](https://cyclonedx.org/specification/overview/). It advises organizations to "accept any widely used, interoperable, and machine-processable SBOM format", so let your customer and your toolchain decide. SPDX describes its specification as an international open standard, ISO/IEC 5962:2021, and lists 3.0 as the current document version. CycloneDX lists 1.7 (released 21 October 2025) as current, and the Ecma International standard ECMA-424 as published on 10 December 2025.

CISA's Component Identifiers element asks for "at least one software identifier" that serves as a look-up key for databases, and points to two: [CPE](https://nvd.nist.gov/products/cpe), "a structured naming scheme for information technology systems, software, and packages", and the [Package URL (purl)](https://github.com/package-url/purl-spec), a URL of seven components, `scheme:type/namespace/name@version?qualifiers#subpath`. Ecma International standardized purl as ECMA-427 in December 2025.

Here is a minimal CycloneDX 1.7 document for a container image with two components. All values are illustrative and `sbom-tool` is a made-up tool name. The file has been checked against the [CycloneDX 1.7 JSON schema](https://cyclonedx.org/docs/1.7/json/).

```json
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.7",
  "serialNumber": "urn:uuid:4f4e7a60-b501-413b-a9a6-48f463ac575c",
  "version": 1,
  "metadata": {
    "timestamp": "2026-10-09T15:04:05Z",
    "lifecycles": [{ "phase": "post-build" }],
    "tools": {
      "components": [
        { "type": "application", "name": "sbom-tool", "version": "1.0.0" }
      ]
    },
    "manufacturer": { "name": "Example Corp" },
    "component": {
      "type": "container",
      "bom-ref": "shop-api",
      "name": "shop-api",
      "version": "2.4.0",
      "purl": "pkg:oci/shop-api@sha256:fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
    }
  },
  "components": [
    {
      "type": "library",
      "bom-ref": "pkg:npm/acme-parser@2.1.0",
      "name": "acme-parser",
      "version": "2.1.0",
      "purl": "pkg:npm/acme-parser@2.1.0",
      "licenses": [{ "license": { "id": "MIT" } }],
      "hashes": [
        {
          "alg": "SHA-256",
          "content": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
        }
      ]
    },
    {
      "type": "library",
      "bom-ref": "pkg:deb/debian/libacme1@1.4.2-1?arch=amd64&distro=debian-12",
      "name": "libacme1",
      "version": "1.4.2-1",
      "purl": "pkg:deb/debian/libacme1@1.4.2-1?arch=amd64&distro=debian-12"
    }
  ],
  "dependencies": [
    {
      "ref": "shop-api",
      "dependsOn": [
        "pkg:npm/acme-parser@2.1.0",
        "pkg:deb/debian/libacme1@1.4.2-1?arch=amd64&distro=debian-12"
      ]
    },
    { "ref": "pkg:npm/acme-parser@2.1.0", "dependsOn": [] }
  ]
}
```

In CycloneDX, `purl` carries the identifier, `dependencies` carries the relationships and `lifecycles` carries the generation context. The value `post-build` is the phase for a BOM made after a build process has completed.

## How do you generate an SBOM?

Where you generate the SBOM decides what it can see, which is why CISA added generation context. These commands come from each tool's documentation.

| Generate from        | Command                                                       | What the documentation says                                                                                                                                                                               |
| -------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Source, before build | `cdxgen -o bom.json .`                                        | [cdxgen](https://github.com/cdxgen/cdxgen) generates a CycloneDX BOM "from a local repo, git URL, purl, or container image"                                                                               |
| The built image      | `syft IMAGE -o cyclonedx-json=sbom.cdx.json`                  | [Syft](https://github.com/anchore/syft) generates SBOMs "from container images and filesystems" and takes one or more output formats                                                                      |
| The built image      | `trivy image --format cyclonedx --output sbom.cdx.json IMAGE` | [Trivy](https://github.com/aquasecurity/trivy/blob/v0.75.0/docs/guide/supply-chain/sbom.md) logs that `--format cyclonedx` "disables security scanning"; add `--scanners vuln` to include vulnerabilities |
| The build itself     | `docker buildx build --sbom=true --push -t IMAGE .`           | [BuildKit](https://docs.docker.com/build/metadata/attestations/sbom/) attaches an SPDX document to the image as an in-toto attestation and scans only the final stage by default                          |

If customers ask for different formats, Syft can write several in one run: `syft IMAGE -o spdx-json=./spdx.json -o cyclonedx-json=./cdx.json`.

Scanning an image and scanning a source tree give different answers. Syft's [cataloger documentation](https://oss.anchore.com/docs/guides/sbom/catalogers/) says an image scan finds installed packages, while a directory scan finds both installed packages and declared dependencies. Declared dependencies are weaker evidence: a `requirements.txt` "might specify a package without a version, or with a version range", which most of the time cannot give the exact version that would be installed. After the build tooling has run, an image scan "can provide more accurate version information". What it can miss is the build itself: BuildKit's SBOM leaves out "build-time dependencies installed in earlier stages, or that exist in the build context" unless you set the build arguments `BUILDKIT_SBOM_SCAN_STAGE` and `BUILDKIT_SBOM_SCAN_CONTEXT`.

![Two panels. Left: a tray of file sheets and a lens arrow to a short document of grey bars. Right: a container drawn as four stacked slabs with an orange base slab, and a lens arrow to a much longer document.](https://computese.com/images/blog/sbom/scan-vantage.3aa24e8caa-1536.webp)

*A source folder and the finished image give different component lists.*

Copied code needs deliberate handling: CISA's footnote says it "is effectively a dependency that is better tracked as a fork and a dependency relationship". A workable default: generate from the built image in every CI run and record it as "after build", then add a source-level SBOM ("before build") when a customer wants what the project declares as well as what shipped.

## How do you sign an SBOM and check the signature?

CISA says a recipient "may be concerned about verifying the source of the SBOM data and confirming its integrity". It added an SBOM Author Signature element for that, and says to use "existing software signing infrastructure, tools, and key management".

For container images, one way to do this is an attestation. An [in-toto attestation](https://github.com/in-toto/attestation/blob/main/spec/README.md) is "authenticated metadata about one or more software artifacts". Its predicate holds the metadata, here the SBOM, and its statement binds the attestation to a subject, here the image digest, which identifies the exact image being attested. An envelope handles authentication and serialization: cosign signs with the "DSSE signing spec", as the [Sigstore documentation](https://docs.sigstore.dev/cosign/verifying/attestation/) puts it. The commands follow the [attest](https://github.com/sigstore/cosign/blob/v3.1.3/doc/cosign_attest.md) and [verify-attestation](https://github.com/sigstore/cosign/blob/v3.1.3/doc/cosign_verify-attestation.md) references for cosign v3.1.3:

```bash
cosign attest --yes --type cyclonedx --predicate sbom.cdx.json \
  ghcr.io/example-corp/shop-api@sha256:<digest>

cosign verify-attestation --type cyclonedx \
  --certificate-identity <signer identity> \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  ghcr.io/example-corp/shop-api@sha256:<digest>
```

For keyless flows, `verify-attestation` must be told which identity and which OIDC issuer to expect; the issuer above is the one [GitHub Actions OIDC tokens](https://docs.github.com/en/actions/reference/security/oidc) carry. The Sigstore documentation warns that "Signatures can guarantee a file has not been tampered with, but they can't guarantee the file arrives at all", and that systems should "fail closed rather than open". Design the deploy gate so that a missing attestation blocks a release as surely as a wrong one. How a cluster enforces image signatures differs by platform; our [OpenShift vs Kubernetes](https://computese.com/openshift-vs-kubernetes/) comparison has a row on it.

On GitHub, [actions/attest](https://github.com/actions/attest) does the same job inside a workflow. Passing `sbom-path` makes it create an SBOM attestation from an SPDX or CycloneDX JSON file of up to 16 MB, signed with a short-lived Sigstore certificate. [It recognizes a CycloneDX file](https://github.com/actions/attest/blob/v4.2.2/src/sbom.ts) only if `bomFormat`, `serialNumber` and `specVersion` are all present, and gives it the predicate type `https://cyclonedx.org/bom`. Artifact attestations are available in public repositories on all current GitHub plans; private or internal repositories need GitHub Enterprise Cloud, and GitHub Enterprise Server is not supported.

![Isometric conveyor through three machines: a gear machine pushes a box onto the belt, a printer-like machine puts a document of grey bars into an envelope, and a press sets a round orange seal on the flap.](https://computese.com/images/blog/sbom/build-line-seal.0326117865-1536.webp)

*Signing the SBOM in the pipeline lets a reader check where it came from.*

Check it with [gh attestation verify](https://cli.github.com/manual/gh_attestation_verify). By default the command enforces the `https://slsa.dev/provenance/v1` predicate type, so an SBOM check names its own type with `--predicate-type`. The manual says that ideally the path of the signer workflow is validated too, with `--signer-workflow`:

```bash
gh attestation verify oci://ghcr.io/example-corp/shop-api@sha256:<digest> \
  --owner example-corp \
  --signer-workflow example-corp/shop-api/.github/workflows/release.yml \
  --predicate-type https://cyclonedx.org/bom
```

> [!WARNING]
> A valid signature shows who signed, not that the SBOM is complete. The GitHub CLI manual says only the signing certificate and the timestamps hold values "that cannot be manipulated by the workflow that originated the attestation", so an attacker with access to your workflow's execution context could falsify the predicate. Its mitigation is a "trusted builder": a reusable workflow whose execution cannot be influenced by input from the caller workflow.

[SLSA](https://slsa.dev/), Supply-chain Levels for Software Artifacts, is "a security framework, a checklist of standards and controls to prevent tampering", and it answers a different question from an SBOM. [Version 1.2](https://slsa.dev/blog/2025/11/announce-slsa-v1.2), announced on 24 November 2025, introduced the Source Track alongside the [Build Track](https://slsa.dev/spec/v1.2/build-track-basics). Build levels run from L1, where provenance exists but "is trivial to bypass or forge", to L3, where builds run on "a hardened build platform that offers strong tamper protection". The SLSA [FAQ](https://slsa.dev/spec/v1.2/faq) says SBOMs and provenance "operate at different levels of abstraction": an SBOM describes the components in an artifact, provenance describes the build, and provenance "can increase the trustworthiness of an SBOM by describing how the SBOM was created".

## How do you generate and sign an SBOM in GitHub Actions?

This workflow builds an image, generates a CycloneDX SBOM from the pushed image by digest, and attaches it as a signed attestation. It is adapted from each action's documentation and has not been run end to end for this article, so try it on a test repository first. Each action is pinned to the commit SHA of its latest release as resolved on 9 October 2026.

```yaml
name: release
on:
  push:
    tags: ["v*"]
jobs:
  image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      id-token: write
      attestations: write
      artifact-metadata: write
    env:
      IMAGE: ghcr.io/example-corp/shop-api
    steps:
      - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
      - id: push
        uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
        with:
          push: true
          tags: ${{ env.IMAGE }}:${{ github.ref_name }}
      - uses: anchore/sbom-action@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3
        with:
          image: ${{ env.IMAGE }}@${{ steps.push.outputs.digest }}
          format: cyclonedx-json
          output-file: sbom.cdx.json
      - uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
        with:
          subject-name: ${{ env.IMAGE }}
          subject-digest: ${{ steps.push.outputs.digest }}
          sbom-path: sbom.cdx.json
          push-to-registry: true
```

GitHub's [secure use reference](https://docs.github.com/en/actions/reference/security/secure-use) says pinning to a full-length commit SHA "is currently the only way to use an action as an immutable release". [build-push-action](https://github.com/docker/build-push-action) uses the Git context by default, so no checkout step is needed, and its `digest` output feeds the later steps. The [sbom-action](https://github.com/anchore/sbom-action) fetches the image with the Docker daemon if it is available, "which will use any authentication available to the daemon", so the earlier login applies. With `push-to-registry`, actions/attest needs `subject-name` to be the fully qualified image name: "Do NOT include a tag as part of the image name", because the digest identifies the image.

![Two crates with different square patterns sit on a shelf. A blank tag moves on a dashed arrow from the left crate's peg to the right one, while an envelope stays tied to the left crate by an orange cord.](https://computese.com/images/blog/sbom/digest-not-tag.6f22ffaf49-1536.webp)

*A tag can move to another image; the SBOM attestation stays attached to the digest.*

Docker's [attestation example](https://docs.docker.com/build/ci/github-actions/attestations/) sets up Buildx before the build, so this workflow does too. The same page says build-push-action adds a provenance attestation by default, with `mode=max` in public repositories, and that it then contains the values of build arguments. Pass secrets with secret mounts instead of build arguments.

## How do you use an SBOM to find vulnerable components?

A stored SBOM turns a new advisory into a lookup. CISA says that after learning new vulnerability information, "an organization can assess whether its SBOMs contain or reference a vulnerable component, significantly improving response time compared to organizations without SBOMs." These tools do the matching from a file:

| Tool                                                                                  | Command                                    | Notes from the documentation                                                                                                                                                |
| ------------------------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Grype](https://github.com/anchore/grype)                                             | `grype sbom:./sbom.cdx.json`               | Scans container images, filesystems and SBOMs; filters results with OpenVEX documents                                                                                       |
| [Trivy](https://github.com/aquasecurity/trivy/blob/v0.75.0/docs/guide/target/sbom.md) | `trivy sbom sbom.cdx.json`                 | Detects the format automatically; CycloneDX XML is not supported, and SBOMs from other tools "may result in inaccurate detection" because Trivy relies on custom properties |
| [OSV-Scanner](https://google.github.io/osv-scanner/usage/scan-source)                 | `osv-scanner scan source -L sbom.cdx.json` | Reads SPDX and CycloneDX SBOMs that use Package URLs; recognizes CycloneDX files named `bom.json` or `*.cdx.json`                                                           |

[Dependency-Track](https://dependencytrack.github.io/docs/next/) runs this matching as a service: its version 5 documentation says it is "built around the Bill of Materials (BOM) concept" and surfaces known vulnerabilities "as they emerge". A [finding](https://dependencytrack.github.io/docs/next/concepts/vulnerability-findings/) there is "the association between a component in one of your projects and a vulnerability that affects it".

Matching has to be repeated, because the SBOM stays the same while vulnerability data changes. Re-run it on a schedule against every stored SBOM, and in CI on each pull request. Our [secure coding checklist](https://computese.com/best-practices-for-secure-coding/) covers keeping dependencies under control.

![A container drawn as stacked slabs beside a document of eight grey rows. One row is solid orange and joined by a line to a warning triangle on a database cylinder at the right.](https://computese.com/images/blog/sbom/match-row-warning.96665154cd-1536.webp)

*A stored SBOM matches one advisory to the exact image and component it affects.*

A match is not yet a priority. Our guide to [predicting which vulnerabilities to patch first](https://computese.com/the-future-of-cybersecurity/) covers the signals for ranking them, and our guide to penetration testing vs vulnerability scanning explains what a scanner can and cannot find. Outdated components also count as debt, and our [technical debt](https://computese.com/technical-debt/) guide shows how to measure dependency age and end-of-life dates.

## What is VEX, and how does it cut false alerts?

A match is not proof of exposure: the vulnerable code may not be present in your product, or may never execute. VEX, the Vulnerability Exploitability eXchange, lets the producer say so in a machine-readable way. Several formats carry it. [OpenVEX](https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md) is "designed to be lightweight, and embeddable". [CSAF](https://docs.oasis-open.org/csaf/csaf/v2.0/os/csaf-v2.0-os.html) is an OASIS standard whose VEX profile exists "to state that and why a certain product is, or is not, affected by a vulnerability". [CycloneDX VEX](https://cyclonedx.org/capabilities/vex/) conveys "the exploitability of vulnerable components in the context of the product in which they're used".

An OpenVEX statement has one of four statuses: `not_affected`, `affected`, `fixed` or `under_investigation`. A `not_affected` statement needs one of five fixed justification labels or a free-text impact statement, which the specification calls "highly discouraged for automated systems". This document says the `libacme1` package in the example image does not contain the vulnerable code. `CVE-2023-12345` is the identifier the OpenVEX specification uses in its own samples:

```json
{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "@id": "https://vex.example.com/shop-api/2.4.0/1",
  "author": "Example Corp Product Security",
  "timestamp": "2026-10-09T16:00:00Z",
  "version": 1,
  "statements": [
    {
      "vulnerability": { "name": "CVE-2023-12345" },
      "products": [
        {
          "@id": "pkg:oci/shop-api@sha256:fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
          "subcomponents": [
            {
              "@id": "pkg:deb/debian/libacme1@1.4.2-1?arch=amd64&distro=debian-12"
            }
          ]
        }
      ],
      "status": "not_affected",
      "justification": "vulnerable_code_not_present"
    }
  ]
}
```

Feed it to Grype with `grype IMAGE@sha256:<digest> --vex vex.json`. [Grype](https://oss.anchore.com/docs/guides/vulnerability/filter-results/) accepts OpenVEX and CSAF VEX documents and, by default, moves `not_affected` and `fixed` results to its ignored list; `--show-suppressed` brings them back, marked "(suppressed by VEX)". Its documentation matches a VEX product by image digest (the most reliable form), by tag (less reliable, because tags can move) or by package purl, as the `subcomponents` entry above does. [Dependency-Track](https://dependencytrack.github.io/docs/next/concepts/vex/) "both produces and consumes VEX documents in CycloneDX format", and cosign can attest a VEX document with `--type openvex`.

![A scanner drops grey warning triangles onto a sieve plate linked to a document with a check mark. Most slide off into a bin on the left; one orange triangle falls through a narrow slot into a tray holding a wrench.](https://computese.com/images/blog/sbom/vex-filter.4faf6f4c04-1536.webp)

*A VEX statement removes alerts that do not apply and leaves the ones that do.*

## What goes wrong with SBOMs in practice?

| Pitfall                          | What happens                                                                                                                    | Fix                                                                                                       |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Made once, never refreshed       | The file describes an old build, but CISA says each software version or update "should have an associated SBOM"                 | Generate in CI for every release and store the SBOM with the release                                      |
| One SBOM per repository          | A repository can build several images, and one file describes none of them exactly                                              | One SBOM per artifact and version, tied to the image digest                                               |
| Name and version only            | Matching is ambiguous: CISA replaced Supplier Name with Component Producer because it "has proven ambiguous in practice"        | Require a purl or CPE and a component producer; state whether a missing value is unknown or withheld      |
| Alert flood without VEX          | Matches include components whose vulnerable code the product never runs                                                         | Publish VEX statements with justifications                                                                |
| Stored where nobody can find it  | A customer or incident responder cannot retrieve the SBOM for the release in question                                           | Attach it to the image, or publish it at a version-specific URL, through an API or in a public repository |
| Unsigned, unchecked or tag-based | A swapped file goes unnoticed, a missing attestation passes the gate, or a moved tag leaves the SBOM describing the wrong image | Verify with a named identity, fail closed, and reference digests rather than tags                         |

## What should you do first?

1. Ask each customer or regulator for the format, version and elements they need, and write CISA's 2026 elements into your policy.
2. Generate an SBOM from each image in CI on every release, and record the generation context.
3. Sign it as an attestation on the image digest, and make one pipeline step verify it with the signer identity you expect.
4. Store the SBOMs where security can query them, and re-match them against vulnerability data on a schedule.
5. Publish VEX statements for matches that do not apply.

For help setting this up, Computese's [security testing](https://computese.com/services/security-testing/) service lists Syft and Grype among its code and supply-chain tools, and its [cloud transformation](https://computese.com/services/cloud-transformation/) work covers signed images and SBOMs.

## Key terms
- **SBOM**: A machine-readable inventory of the components in a piece of software: names, versions, producers, identifiers and relationships. CISA calls it a nested inventory, a list of ingredients that make up software applications and systems.
- **Generation context**: The software lifecycle phase and data available when the SBOM was generated, such as before build or after build. CISA's 2026 elements include it because component data may differ by phase.
- **Component producer**: The entity that created a component. CISA's 2026 elements use it in place of Supplier Name, which CISA says has proven ambiguous in practice, particularly around distributors of software.
- **Package URL (purl)**: A URL of seven components, scheme:type/namespace/name@version?qualifiers#subpath, that identifies a package. Ecma International standardized it as ECMA-427 in December 2025.
- **CPE**: Common Platform Enumeration: a structured naming scheme for information technology systems, software and packages, with an official dictionary in NIST's National Vulnerability Database.
- **SPDX**: System Package Data Exchange, an SBOM format that its project describes as an international open standard, ISO/IEC 5962:2021. Its specifications page lists 3.0 as the current document version.
- **CycloneDX**: An SBOM specification developed by the OWASP Foundation and standardized by Ecma International as ECMA-424. Version 1.7 was released on 21 October 2025.
- **Attestation**: In in-toto's terms, authenticated metadata about one or more software artifacts. For an SBOM, the predicate holds the SBOM, the statement binds the attestation to a subject such as an image digest, and the envelope handles authentication and serialization.
- **VEX**: Vulnerability Exploitability eXchange: a machine-readable statement of whether a product is affected by a vulnerability. OpenVEX statuses are not_affected, affected, fixed and under_investigation.
- **SLSA**: Supply-chain Levels for Software Artifacts, a security framework of standards and controls to prevent tampering. Version 1.2 (24 November 2025) introduced a Source Track alongside the Build Track, whose levels run from L1 to L3.

## Common questions

### What is an SBOM?

A software bill of materials is a machine-readable inventory of the components in software, with their versions, producers, identifiers and relationships. CISA describes it as a nested inventory, a list of ingredients that make up software applications and systems. A common use is checking whether a newly reported vulnerability involves a component you ship.

### What is the difference between SPDX and CycloneDX?

Both are widely used, machine-processable SBOM formats, and CISA advises accepting any widely used, interoperable one. SPDX is described as ISO/IEC 5962:2021, and CycloneDX is standardized as ECMA-424. Choose the format your customers and tools support; Syft can write both in one run.

### What are the minimum elements of an SBOM?

CISA's 2026 elements (version 2.1) include data fields such as SBOM author, component producer, name, version, identifiers, dependency relationship, hash and licence, plus tool, timestamp and generation context. They also set practices, such as covering all components with no minimum depth, a new SBOM for each release, and stating when information is unknown. They replace NTIA's 2021 list.

### Is an SBOM required by law?

It depends on the market. The EU Cyber Resilience Act applies from 11 December 2027 and has manufacturers draw up an SBOM covering at least the top-level dependencies. The FDA says section 524B(b)(3) requires makers of cyber devices to provide one. US federal agencies may require one by contract under OMB memo M-26-05.

### How do I generate an SBOM for a container image?

Run a generator such as Syft, Trivy or cdxgen on the built image and choose CycloneDX or SPDX output. An image scan finds installed packages, which can give more accurate version information than declared dependencies. Then sign the SBOM as an attestation on the image digest and store it with the release.

### What is VEX and how does it relate to an SBOM?

VEX, the Vulnerability Exploitability eXchange, is a machine-readable statement of whether a product is affected by a vulnerability. An SBOM lists the components you ship, and a VEX statement says whether a vulnerable component affects the product. OpenVEX, CSAF and CycloneDX can carry it, and Grype moves not_affected and fixed results to its ignored list.

### How often should an SBOM be updated?

CISA says each software version or update should have an associated SBOM, so generate a new one for every build or release. Issue a revised SBOM when you find new details about the components or correct an error. Match stored SBOMs against vulnerability data on a schedule, because that data changes even when the SBOM does not.

## Sources
1. [2026 Minimum Elements for a Software Bill of Materials (SBOM), version 2.1](https://www.cisa.gov/sites/default/files/2026-07/2026_cisa_sbom_minimum_elements_508c.pdf), CISA
2. [The Minimum Elements For a Software Bill of Materials (SBOM)](https://www.ntia.gov/files/ntia/publications/sbom_minimum_elements_report.pdf), NTIA, U.S. Department of Commerce
3. [Executive Order 14028 of May 12, 2021: Improving the Nation's Cybersecurity](https://www.govinfo.gov/content/pkg/FR-2021-05-17/html/2021-10460.htm), Federal Register, via GovInfo
4. [M-26-05: Adopting a Risk-based Approach to Software and Hardware Security](https://www.whitehouse.gov/wp-content/uploads/2026/01/M-26-05-Adopting-a-Risk-based-Approach-to-Software-and-Hardware-Security.pdf), Office of Management and Budget
5. [Regulation (EU) 2024/2847 on horizontal cybersecurity requirements for products with digital elements (Cyber Resilience Act)](https://eur-lex.europa.eu/eli/reg/2024/2847/oj/eng), EUR-Lex
6. [Cybersecurity in Medical Devices: Quality Management System Considerations and Content of Premarket Submissions](https://www.fda.gov/media/119933/download), U.S. Food and Drug Administration
7. [SPDX specifications](https://spdx.dev/use/specifications/), SPDX
8. [CycloneDX specification overview](https://cyclonedx.org/specification/overview/), CycloneDX
9. [Official Common Platform Enumeration (CPE) Dictionary](https://nvd.nist.gov/products/cpe), NIST National Vulnerability Database
10. [Package-URL (PURL) specification](https://github.com/package-url/purl-spec), package-url project on GitHub
11. [CycloneDX v1.7 JSON reference](https://cyclonedx.org/docs/1.7/json/), CycloneDX
12. [cdxgen: CycloneDX BOM generator](https://github.com/cdxgen/cdxgen), cdxgen on GitHub
13. [Syft: generate SBOMs from container images and filesystems](https://github.com/anchore/syft), Anchore on GitHub
14. [Trivy v0.75.0: SBOM generation (supply chain)](https://github.com/aquasecurity/trivy/blob/v0.75.0/docs/guide/supply-chain/sbom.md), Aqua Security on GitHub
15. [SBOM attestations](https://docs.docker.com/build/metadata/attestations/sbom/), Docker Docs
16. [Package catalogers (Syft SBOM generation guide)](https://oss.anchore.com/docs/guides/sbom/catalogers/), Anchore OSS documentation
17. [in-toto Attestation Framework specification](https://github.com/in-toto/attestation/blob/main/spec/README.md), in-toto on GitHub
18. [In-Toto Attestations (cosign)](https://docs.sigstore.dev/cosign/verifying/attestation/), Sigstore documentation
19. [cosign attest (v3.1.3)](https://github.com/sigstore/cosign/blob/v3.1.3/doc/cosign_attest.md), Sigstore on GitHub
20. [cosign verify-attestation (v3.1.3)](https://github.com/sigstore/cosign/blob/v3.1.3/doc/cosign_verify-attestation.md), Sigstore on GitHub
21. [OpenID Connect reference](https://docs.github.com/en/actions/reference/security/oidc), GitHub Docs
22. [actions/attest: generate signed attestations for workflow artifacts](https://github.com/actions/attest), GitHub
23. [actions/attest v4.2.2: src/sbom.ts](https://github.com/actions/attest/blob/v4.2.2/src/sbom.ts), GitHub
24. [gh attestation verify](https://cli.github.com/manual/gh_attestation_verify), GitHub CLI manual
25. [SLSA: Supply-chain Levels for Software Artifacts](https://slsa.dev/), SLSA
26. [Announcing SLSA v1.2](https://slsa.dev/blog/2025/11/announce-slsa-v1.2), SLSA
27. [SLSA v1.2: Build Track basics](https://slsa.dev/spec/v1.2/build-track-basics), SLSA
28. [SLSA v1.2: Frequently asked questions](https://slsa.dev/spec/v1.2/faq), SLSA
29. [Secure use reference](https://docs.github.com/en/actions/reference/security/secure-use), GitHub Docs
30. [docker/build-push-action](https://github.com/docker/build-push-action), Docker on GitHub
31. [anchore/sbom-action](https://github.com/anchore/sbom-action), Anchore on GitHub
32. [Add SBOM and provenance attestations with GitHub Actions](https://docs.docker.com/build/ci/github-actions/attestations/), Docker Docs
33. [Grype: vulnerability scanner for container images and filesystems](https://github.com/anchore/grype), Anchore on GitHub
34. [Trivy v0.75.0: SBOM scanning (target)](https://github.com/aquasecurity/trivy/blob/v0.75.0/docs/guide/target/sbom.md), Aqua Security on GitHub
35. [Project source scanning](https://google.github.io/osv-scanner/usage/scan-source), OSV-Scanner documentation
36. [Dependency-Track documentation (version 5)](https://dependencytrack.github.io/docs/next/), Dependency-Track
37. [About vulnerability findings](https://dependencytrack.github.io/docs/next/concepts/vulnerability-findings/), Dependency-Track documentation
38. [OpenVEX specification](https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md), OpenVEX on GitHub
39. [Common Security Advisory Framework Version 2.0](https://docs.oasis-open.org/csaf/csaf/v2.0/os/csaf-v2.0-os.html), OASIS
40. [Vulnerability Exploitability eXchange (VEX)](https://cyclonedx.org/capabilities/vex/), CycloneDX
41. [Filter scan results (Grype VEX documents)](https://oss.anchore.com/docs/guides/vulnerability/filter-results/), Anchore OSS documentation
42. [About VEX](https://dependencytrack.github.io/docs/next/concepts/vex/), Dependency-Track documentation
