Safeguard
Compliance

SBOM Template: How to Structure a Software Bill of Materials

A practical SBOM template covering the required fields, a ready-to-adapt CycloneDX skeleton, and the mistake of treating an SBOM as a document you fill in by hand.

Marcus Chen
DevSecOps Engineer
5 min read

An SBOM template is best understood as a checklist of the fields a compliant software bill of materials must contain, not a blank document you type components into by hand. People searching for an "SBOM template" usually want two things: to know what fields are mandatory, and to see a skeleton they can adapt. This post gives you both, then explains why the real deliverable is a generated file, not a filled-in form.

The distinction matters because an SBOM that is out of date is worse than none at all, since it gives false confidence. A template tells you the shape; automation keeps the content true.

The required fields, as a template

Every compliant SBOM, whatever its format, has to carry a consistent set of facts. Treat this as your field-level template. It splits into document-level metadata and per-component data.

Document metadata (about the SBOM itself):

  • Author / creator: the tool or person that produced it.
  • Timestamp: when it was generated.
  • Subject: the application or artifact the SBOM describes, with its version.
  • Format and spec version: e.g. CycloneDX 1.5 or SPDX 2.3.

Per-component fields (repeated for every dependency):

  • Supplier: who produced the component.
  • Component name.
  • Version.
  • Unique identifier: ideally a package URL (purl) or CPE.
  • License.
  • Dependency relationship: how it relates to other components (direct, transitive).
  • Hash / checksum: to verify integrity, where available.

Those fields align with the minimum elements set out in NTIA guidance and adopted by most tooling. If your SBOM carries all of them for every component, it will satisfy the common requirements.

A CycloneDX skeleton to adapt

Here is a minimal CycloneDX JSON skeleton showing the structure. Replace the placeholder values, or better, let a generator fill it:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "version": 1,
  "metadata": {
    "timestamp": "2025-10-01T09:00:00Z",
    "authors": [{ "name": "your-sbom-tool" }],
    "component": {
      "type": "application",
      "name": "YOUR_APP_NAME",
      "version": "YOUR_APP_VERSION"
    }
  },
  "components": [
    {
      "type": "library",
      "name": "COMPONENT_NAME",
      "version": "COMPONENT_VERSION",
      "purl": "pkg:ECOSYSTEM/COMPONENT_NAME@COMPONENT_VERSION",
      "licenses": [{ "license": { "id": "SPDX_LICENSE_ID" } }],
      "hashes": [{ "alg": "SHA-256", "content": "HASH_VALUE" }]
    }
  ]
}

The metadata.component block is the subject. The components array repeats one object per dependency. Notice how mechanical it is: the same five or six fields, over and over. That repetition is exactly why nobody should be typing it.

An SPDX skeleton, for compliance-driven teams

If your requirement points to SPDX, the equivalent skeleton in tag-value form:

SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: YOUR_APP_NAME-YOUR_APP_VERSION
Creator: Tool: your-sbom-tool
Created: 2025-10-01T09:00:00Z

PackageName: COMPONENT_NAME
SPDXID: SPDXRef-Package-COMPONENT_NAME
PackageVersion: COMPONENT_VERSION
PackageSupplier: Organization: SUPPLIER
PackageLicenseConcluded: SPDX_LICENSE_ID
PackageChecksum: SHA256: HASH_VALUE
ExternalRef: PACKAGE-MANAGER purl pkg:ECOSYSTEM/COMPONENT_NAME@COMPONENT_VERSION

Same information, different syntax. Pick the format your consumer expects; if none is specified, either satisfies the field requirements.

Why "template" is slightly the wrong word

Here is the important shift in thinking. A template implies a human fills in blanks. But a real application has hundreds of transitive dependencies, each with its own version, license, and hash, and all of that changes every time you update a package. A hand-maintained SBOM is wrong within a day.

The correct workflow is to generate the SBOM from your actual project state as a build step, so it is always current and always matches what you ship. The template above is useful for understanding the output and for reviewing that a generated file is complete, not for authoring by hand.

# Generate an SBOM from a project directory
syft dir:. -o cyclonedx-json > sbom.cdx.json
# or from a lockfile via a language-specific CycloneDX tool

Putting the template to work

Once generation is wired into your pipeline, three practices make the SBOM valuable rather than ceremonial:

  1. Generate on every build and attach the SBOM as a release artifact, so every version you ship has a matching, accurate bill of materials.
  2. Feed it into vulnerability matching. An SBOM's payoff is answering "am I affected?" instantly when a new CVE drops. That means checking every listed component against advisory data continuously, which is what software composition analysis does.
  3. Store it and version it alongside the release, so you can prove what was in a given build months later for an audit or an incident.

A platform such as Safeguard can emit a compliant SBOM in CycloneDX or SPDX as part of the build and then monitor those components over time, which closes the loop between "we have an SBOM" and "we act on it." For worked examples of complete SBOMs in both formats, see our SBOM examples post, and the Safeguard academy covers where SBOM requirements show up in compliance frameworks.

FAQ

What fields are mandatory in an SBOM template?

Document-level: author, timestamp, the subject application and its version, and the format/spec version. Per component: supplier, name, version, a unique identifier (ideally a purl), license, dependency relationship, and a hash where available. These align with NTIA minimum-element guidance.

Should I fill in an SBOM template by hand?

No. Real projects have hundreds of transitive dependencies that change with every update, so a hand-filled SBOM goes stale immediately. Use the template to understand and review the output, but generate the actual file automatically from your project state on every build.

CycloneDX or SPDX for my template?

Use CycloneDX if your driver is security and vulnerability tracking; it is compact and has strong VEX support. Use SPDX if you need an ISO standard or deep license-compliance detail. Most tools produce both, so match whatever format your customer or regulator expects.

How do I keep my SBOM accurate over time?

Generate it as a build step so it always reflects the code you ship, attach it to each release, and feed it into continuous vulnerability matching. An SBOM that is regenerated automatically and monitored stays useful; one maintained by hand drifts out of date and gives false confidence.

Never miss an update

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