Code Security

SBOM in Practice: Formats, Tools, and What Your SBOM Is Missing

Amartya | CodeAnt AI Code Review Platform
Sonali Sood

Founding GTM, CodeAnt AI

Most SBOM guidance stops at the point where you have generated a file. That is the easy part, and it is not the part that determines whether the document is worth anything.

An SBOM is a claim about what is inside your software. The useful question is whether that claim is true, and for a large share of generated SBOMs it is only partially true.

This covers the formats, where in the pipeline to generate, which tools to use, and the specific categories of component that a build-generated SBOM structurally cannot see.

Where the gap gets closed: CodeAnt AI works from the artifact as well as the source, mining shipped bundles directly and correlating findings against the National Vulnerability Database, the CISA Known Exploited Vulnerabilities catalog, and EPSS. The declared-versus-actual gap described below is the reason that matters.

What is a software bill of materials?

A software bill of materials, usually shortened to SBOM, is a formal, machine-readable inventory of the components that make up a piece of software, along with the relationships between them.

The analogy to a physical bill of materials is exact and worth holding onto.

A manufacturer knows every part in a product, who supplied it, and which sub-assembly it belongs to. When a part is recalled, they can identify every affected unit within hours.

Software historically had no equivalent. When a widely used library turned out to be critically vulnerable, the answer to "are we affected" required a manual audit at most organisations.

The NTIA minimum elements

Following United States Executive Order 14028 in May 2021, the National Telecommunications and Information Administration published a set of minimum elements.

A document must contain all of them to be considered an SBOM.

Element

What it records

Supplier name

Who produced the component

Component name

What the component is called

Version

Which version is included

Other unique identifiers

CPE, PURL, or similar for machine matching

Dependency relationship

Which component includes which

Author of SBOM data

Who generated this document

Timestamp

When it was generated

Two of those do more work than the rest.

Unique identifiers are what make automated vulnerability matching possible at all, and dependency relationships are what let you distinguish a direct dependency you chose from a transitive one you inherited.

An SBOM listing component names and versions with no identifiers is a document a human can read and a machine cannot use.

CycloneDX vs SPDX, which format to use

Two formats dominate. A third exists and is rarely the right answer for new work.


CycloneDX

SPDX

Steward

OWASP

Linux Foundation

Standardised as

ECMA-424

ISO/IEC 5962:2021

Original focus

Security and vulnerability use cases

Licence compliance

Encodings

JSON, XML, Protobuf

JSON, YAML, RDF, tag-value, spreadsheet

Vulnerability data

Native support, integrated VEX

Handled through separate documents

Typical feel

Compact, security-first

Comprehensive, compliance-first

The practical guidance is short.

  • Choose CycloneDX if your primary use case is security, if you want vulnerability and exploitability data in the same document, or if you want the smallest workable file.

  • Choose SPDX if licence compliance is the driver, if you are in a procurement chain that specifies ISO conformance, or if a regulator or customer asks for it by name.

  • Choose both if you are shipping to enterprise customers, because conversion between them is a solved problem and receiving teams have preferences you cannot predict.

The format argument is genuinely less important than the content argument. A complete SBOM in either format beats an incomplete one in your preferred format.

Where VEX fits

VEX stands for Vulnerability Exploitability eXchange. Always write it out, because the three-letter abbreviation collides badly with an unrelated robotics brand in search.

A VEX document answers a question an SBOM cannot. Your SBOM says a vulnerable library version is present. VEX says whether that vulnerability actually affects your product.

Four statuses carry the meaning.

Not affected means the vulnerable code path is not reachable in your product, and the statement should carry a justification such as the vulnerable function never being called.

Affected means it is exploitable and remediation is required. Fixed means it was addressed in this release. Under investigation means you have not yet determined which.

VEX matters because SBOM adoption creates a downstream problem.

Once customers can see your dependencies, they will file tickets about every CVE affecting any listed version, regardless of reachability. VEX is how you answer at scale rather than one email at a time.

How SBOMs are generated, and at which build stage

Generation point determines completeness, and this is the decision that most affects SBOM quality.

Stage

Sees

Misses

Source

Declared manifests and lockfiles

Anything resolved or injected during build

Build

Resolved dependency graph, build-time inputs

Prebuilt binaries pulled in as opaque artifacts

Binary

What is actually in the shipped artifact

Nothing structurally, but detection is probabilistic

Runtime

What is actually loaded and executed

Code paths not exercised during observation

Source-stage generation reads package.json, pom.xml, go.mod, Cargo.toml, or the equivalent. It is easy, fast, and it describes intent rather than outcome.

Build-stage generation hooks into the build system and records what was actually resolved and linked. This is the highest-fidelity option for the code you compile, and it is the recommended default.

Binary-stage generation derives the inventory from the finished artifact. It catches everything that reached the artifact regardless of how, which is exactly what the earlier stages miss.

Runtime observation tells you what is actually loaded, which is a different and complementary signal. A component present but never loaded is a lower risk than one on your primary request path.

The mature position is to generate at build stage and validate against a binary-derived inventory, because the difference between the two is a list of things you did not know you shipped.

SBOM tools compared

The tooling has consolidated considerably. These are the options worth knowing.

Tool

Type

Formats

Best for

Syft

Open source

CycloneDX, SPDX

Containers and filesystems, the common default

Trivy

Open source

CycloneDX, SPDX

Combined SBOM generation and vulnerability scanning

cdxgen

Open source

CycloneDX

Broad language coverage, many ecosystems

CycloneDX plugins

Open source

CycloneDX

Native build integration for Maven, Gradle, npm

SPDX tools

Open source

SPDX

Validation and format conversion

Tern

Open source

SPDX, CycloneDX

Container layer inspection specifically

ORT

Open source

SPDX, CycloneDX

Licence compliance workflows

Dependency-Track

Open source

Consumes both

Continuous monitoring of SBOMs over time

CodeAnt AI

Commercial

One-click export, PDF for audits

Build-stage generation plus continuous re-evaluation against NVD, KEV, and EPSS

One distinction is worth drawing clearly. Most of that list generates SBOMs once, as a standalone artifact. Dependency-Track and CodeAnt both consume and monitor them continuously against new vulnerability data.

That second capability is the one teams skip and then regret. An SBOM generated at release time and filed away is a snapshot.

New vulnerabilities are disclosed against components in it every week, and something has to re-evaluate it.

CodeAnt AI dependency inventory view, showing every detected package and version across branches with language-wise totals

What a build-generated SBOM cannot see

This is the section that matters most, and it is missing from almost all SBOM guidance.

A build-stage SBOM records what the build system knows about. Five categories of component reach your artifact without the build system recording them.

  • Statically linked libraries. A C or C++ dependency compiled directly into your binary leaves no package manager record. The code is present, it may be vulnerable, and no manifest mentions it.

  • Vendored code. Source copied into your repository rather than declared as a dependency. Common for small utilities and for patched forks. It appears to your tooling as first-party code.

  • Files copied into container images. A COPY instruction places a binary in the image with no package metadata. Package-manager-based scanning is blind to it by construction.

  • Build-time injected assets. Bundled JavaScript, embedded resources, and generated code that the bundler pulled in from somewhere your manifest does not describe.

  • Transitive native dependencies. A package that ships prebuilt native binaries brings whatever those binaries were compiled against, and that inner layer is opaque to the outer manifest.

The common property is that all five are present in the artifact and absent from the manifest. A vulnerability in any of them is real, exploitable, and invisible to any tool reading your lockfile.

Binary composition analysis and the binary SBOM

Binary composition analysis derives the component inventory from the compiled artifact rather than from the build metadata. It is the only method that closes the five gaps above.

The detection techniques are probabilistic rather than authoritative, which is the honest trade-off.

  • Version strings. Most libraries embed a version banner in their compiled form. A single string frequently identifies an exact release.

  • Function signatures. A compiled function has a recognisable shape. Matching against a corpus of known library builds identifies components that embed no strings at all.

  • Constant tables. Cryptographic implementations contain distinctive constant arrays. S-boxes and initialisation vectors act as fingerprints.

  • Build artifacts. Compiler version strings, embedded build paths, and residual debug metadata frequently survive and often disclose more than intended.

The workflow follows the same structural-diffing family of techniques used against binary patches, matching on control-flow shape and reference tables rather than on raw bytes.

The output is a binary SBOM, and its relationship to a build-generated one is the point.


Build-generated SBOM

Binary SBOM

Derived from

Build system metadata

The shipped artifact

Accuracy on declared deps

Authoritative

Probabilistic

Statically linked code

Invisible

Detected

Vendored code

Invisible

Detected

Requires build access

Yes

No

Works on third-party software

No

Yes

Neither is a replacement for the other. Build-generated is authoritative for what you declared. Binary-derived is the only option for what you did not declare and for software you did not build.

How to reconcile a declared SBOM against a binary-derived one

The reconciliation is the exercise, and it is more valuable than either document alone.

Step 1. Generate both. Build-stage SBOM from your pipeline, binary-derived inventory from the shipped artifact.

Step 2. Diff them. Three categories of discrepancy emerge, and each means something different.

In binary, not in SBOM. This is the important one. Every entry is a component you ship and did not declare, which means no vulnerability feed has ever been matched against it.

In SBOM, not in binary. Usually benign. Tree-shaking, dead code elimination, or a build-time-only dependency. Worth confirming rather than assuming.

Version mismatch. The manifest says one version and the artifact contains another. Frequently caused by a transitive resolution you did not expect, and always worth investigating.

Step 3. Match the reconciled inventory against vulnerability data. Use the reconciled list, not the declared one, when querying CVE data. CodeAnt's vulnerability database is built for this, keeping the query closer to the vendor advisory than to the slower, enriched NVD record.

Step 4. Prioritise the results. Component presence is not risk. Apply CVSS, EPSS, and CISA KEV together, the way CodeAnt's EPSS scoring does for both SAST and SCA findings, rather than triaging on severity alone.

Step 5. Publish VEX for the false positives. Once you have established that a listed vulnerable component is not reachable, say so formally rather than answering the same customer question repeatedly.

SBOM requirements under current regulation

Three regimes drive most SBOM demand. Verify current status directly, since requirements have been evolving quickly.

United States federal procurement. Executive Order 14028, issued May 2021, directed the establishment of SBOM requirements for software sold to the federal government, and the NTIA minimum elements followed in July 2021.

Medical devices. The United States Food and Drug Administration requires cybersecurity information including a software bill of materials in premarket submissions for devices with software.

European Union. The Cyber Resilience Act entered into force in December 2024 and introduces obligations for products with digital elements, including maintaining a software bill of materials. Its main obligations phase in over subsequent years.

The common direction of travel is toward SBOM as a procurement precondition rather than a security nicety.

Practically, that means the document will be read by people who did not write it, so accuracy has an external consequence.

How CodeAnt AI Fits Into the Stack

Two capabilities, on two different sides of the declared-versus-actual gap.

Generating the declared side properly. CodeAnt's SBOM feature is build-integrated rather than a one-off script: it detects every package and version across branches, flags restrictive or unknown licences before they become a compliance problem, and exports an audit-ready PDF on demand.

That covers the source and build stages described above, done continuously rather than at release time, which is what makes reconciliation against a binary inventory possible in the first place instead of comparing a fresh artifact scan against a stale document.

CodeAnt AI license and copyleft analysis view, identifying license types and compliance levels and flagging restrictive or unknown licenses

Closing the artifact-side gap. The AI penetration testing pipeline performs artifact-level analysis on the web surface, mining production JavaScript bundles for API keys, internal hostnames, OAuth client identifiers, and source-map artifacts.

That is the same declared-versus-actual gap this article describes, applied to the bundled surface and run continuously rather than at release time. It does not replace the build-stage SBOM above, it is the binary-side check the reconciliation section describes, run against your own shipped frontend.

Which listed components actually matter. Attack surface management correlates the reconciled inventory against the National Vulnerability Database, the CISA Known Exploited Vulnerabilities catalog, and EPSS scoring, refreshed daily.

An SBOM produces a list of components. That correlation is what turns the list into a shortlist, using EPSS to filter the improbable mass and the CISA KEV catalog to define urgency.

On the value of looking beyond declared dependencies, the record is public.

CodeAnt's research team has disclosed 100+ CVEs, including CVE-2026-29000, a CVSS 10.0 authentication bypass in pac4j-jwt, and CVE-2026-28292, a CVSS 9.8 remote code execution bug in simple-git that had survived two earlier patch attempts, across packages that together sit in the dependency tree behind a share of the 1.85B+ monthly downloads CodeAnt's research has audited. For as long as either bug went unfixed, any SBOM listing that library reported it clean, because no advisory had been filed against it yet. That is exactly the gap a document, on its own, cannot close.

The SBOM completeness checklist

Generation

  • Generate at build stage, not from source manifests, so the resolved graph is captured.

  • Include unique identifiers such as PURL or CPE for every component. Names and versions alone are not machine-matchable.

  • Record dependency relationships, so direct and transitive components are distinguishable.

Completeness

  • Audit for statically linked libraries that carry no package record.

  • Audit for vendored source copied into the repository rather than declared.

  • Audit container images for files added by COPY with no package metadata.

  • Check packages shipping prebuilt native binaries for what those binaries were compiled against.

Reconciliation

  • Derive a binary inventory from the shipped artifact and diff it against the declared SBOM.

  • Investigate every in-binary-not-in-SBOM entry. Each is an undeclared shipped component.

  • Investigate version mismatches before assuming the manifest is correct.

Ongoing use

  • Monitor SBOMs continuously, not at release only, since new vulnerabilities land against existing components weekly.

  • Publish VEX statements for components where the vulnerable path is not reachable.

  • Match against the reconciled inventory, never against the declared one alone.

Where this leaves you

Generating an SBOM is now a solved problem with good free tooling. Generating an accurate one is not.

The distinction that matters is between the components you declared and the components you shipped.

Those two lists differ at almost every organisation, and the difference is precisely the set of components no vulnerability feed has ever examined.

Generate at build stage. Reconcile against the artifact. Then treat the difference as your actual finding list.

Related reading on CodeAnt AI: Zero-Day vs N-Day, and the Patch Window for what happens after a component you tracked turns out to have a public fix you have not deployed, and the IDOR guide for the authorization-layer bug class that sits alongside dependency risk in the same shipped artifact.

FAQs

What is the difference between CycloneDX and SPDX?

What does an SBOM not include?

What is a binary SBOM?

What is VEX and why do I need it alongside an SBOM?

Are SBOMs legally required?

Start Your 14-Day Free Trial

AI code reviews, security and quality trusted by modern engineering teams.

Table of Content
No headings found on page
Ship clean & secure code faster

Get Pentest Report

NO CC REQUIRED