Safeguard
Compliance

SBOM Examples: What a Real Software Bill of Materials Looks Like

Concrete SBOM examples in both SPDX and CycloneDX, showing what fields actually go in a software bill of materials and how the two formats differ in practice.

Yukti Singhal
Platform Engineer
5 min read

An SBOM, or software bill of materials, is a structured inventory of every component in a piece of software, and the clearest way to understand one is to read a real example rather than a definition. So this post is built around concrete SBOM examples in the two dominant formats, CycloneDX and SPDX, with each field explained in plain terms. By the end you will be able to read an SBOM someone hands you and generate one for your own project.

The reason SBOMs went from niche to expected is supply-chain risk. When a critical vulnerability lands in a widely used library, the first question is "am I affected?", and an SBOM turns that from a frantic manual audit into a lookup.

The minimum an SBOM must contain

Regardless of format, a useful SBOM records, for each component: a name, a version, a unique identifier, and ideally a supplier and license. Guidance from the US National Telecommunications and Information Administration established a set of minimum data fields that most tooling now follows, covering supplier, component name, version, unique identifiers, dependency relationships, the author of the SBOM, and a timestamp.

Keep those fields in mind as you read the examples; every real SBOM is a repetition of that same handful of facts across every dependency.

A CycloneDX example

CycloneDX is a lightweight format that came out of the OWASP community and is popular for security use cases. Here is a trimmed CycloneDX SBOM in JSON for a small project:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
  "version": 1,
  "metadata": {
    "timestamp": "2025-09-10T14:32:00Z",
    "component": { "type": "application", "name": "my-service", "version": "2.1.0" }
  },
  "components": [
    {
      "type": "library",
      "name": "express",
      "version": "4.19.2",
      "purl": "pkg:npm/express@4.19.2",
      "licenses": [{ "license": { "id": "MIT" } }]
    },
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21",
      "licenses": [{ "license": { "id": "MIT" } }]
    }
  ]
}

Read it top to bottom: the metadata block describes the thing the SBOM is about (the application my-service version 2.1.0) and when it was generated. The components array is the inventory. Each entry has a name, a version, a purl (package URL, a standard identifier that says exactly which package this is and from which ecosystem), and its license. That purl is the field that makes automated vulnerability matching possible: pkg:npm/lodash@4.17.21 is unambiguous.

An SPDX example

SPDX is the older, ISO-standardized format, widely used for license compliance as well as security. The same two dependencies in SPDX's tag-value form look like this:

SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: my-service-2.1.0
Creator: Tool: my-sbom-generator-1.4
Created: 2025-09-10T14:32:00Z

PackageName: express
SPDXID: SPDXRef-Package-express
PackageVersion: 4.19.2
PackageLicenseConcluded: MIT
ExternalRef: PACKAGE-MANAGER purl pkg:npm/express@4.19.2

PackageName: lodash
SPDXID: SPDXRef-Package-lodash
PackageVersion: 4.17.21
PackageLicenseConcluded: MIT
ExternalRef: PACKAGE-MANAGER purl pkg:npm/lodash@4.17.21

It carries the same information, a document header describing what and when, then one block per package with name, version, license, and a package-URL external reference. SPDX also has a JSON form; the tag-value form above is just easier to read line by line.

How the two formats differ in practice

Both are legitimate, and most tooling reads and writes both. The practical differences:

  • CycloneDX leans toward security and supply-chain use cases and is compact. It has first-class support for vulnerability and VEX (Vulnerability Exploitability eXchange) data.
  • SPDX is an ISO/IEC standard (5962) with deep roots in open-source license compliance and very detailed licensing fields.

If your driver is security and vulnerability tracking, CycloneDX is a natural fit. If it is license compliance or you need a formal ISO standard, SPDX fits. When a customer or regulator asks for an SBOM, ask which format they expect; if they do not specify, either is defensible.

How you actually generate these

You do not write SBOMs by hand. Tools read your project's manifests and lockfiles and emit the document. Common generators include Syft (multi-ecosystem, outputs both formats), the CycloneDX family of language-specific plugins, and the SBOM export built into most software composition analysis platforms.

# Generate a CycloneDX SBOM for a container image or directory
syft dir:. -o cyclonedx-json > sbom.cdx.json

Generating the file is the easy part. The value comes from what you do next: feeding the SBOM into vulnerability matching so you learn which of those components carry known flaws. A software composition analysis tool both produces the SBOM and continuously checks it against advisory data, and a platform such as Safeguard can generate the SBOM in either format as a build artifact. For the format-and-fields side, see our SBOM template guide; for the compliance angle, the Safeguard academy covers where SBOMs fit in frameworks.

FAQ

What formats are used for SBOM examples?

The two dominant formats are CycloneDX and SPDX. CycloneDX is compact and security-focused with strong vulnerability and VEX support; SPDX is an ISO standard with deep license-compliance roots. Most tooling can produce and consume both, so pick based on whether your driver is security or licensing.

What is a purl in an SBOM?

A purl, or package URL, is a standardized identifier like pkg:npm/lodash@4.17.21 that unambiguously names a package, its ecosystem, and its version. It is the field that makes automated vulnerability matching reliable, since it removes ambiguity about exactly which component is present.

Do I write an SBOM by hand?

No. SBOMs are generated by tools that read your manifests and lockfiles, such as Syft or the SBOM export in an SCA platform. Writing one manually would be error-prone and would go stale immediately; automated generation keeps it accurate as dependencies change.

What is the minimum an SBOM should include?

At minimum, for each component: supplier, component name, version, a unique identifier, dependency relationships, plus the author of the SBOM and a timestamp. These minimum fields, aligned with NTIA guidance, are what make an SBOM useful for tracing whether you are affected by a given vulnerability.

Never miss an update

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