An SBOM should list all the components, open-source and third-party dependencies, along with their versions and licenses. In this section, we'll explore the key elements of an SBOM, examine different SBOM formats, and look at some examples of how to generate them.
Schema Elements
The first thing to discuss is the schema. The NTIA mandates a minimum set of elements that must be included in an SBOM schema, along with optional fields that consumers of an SBOM can request if needed. Let's illustrate what these elements are with a simple example:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.software</groupId>
<artifactId>MyApp</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>
</project>
The above XML represents the pom
file of a Maven project responsible for managing dependencies and their versions. The table below explains these elements in further detail:
Table 1
Element |
Description |
Example |
Supplier name
|
Name of company that made the software
|
Apache Software Foundation
|
Component name
|
Name of software part
|
MyApp
|
Component version
|
Version of software being used
|
1.0.0
|
Other unique identifiers
|
Ways to identify components and their versions uniquely (e.g., package URLs)
|
pkg:maven/com.fasterxml.jackson.core/[email protected] (using package URL)
|
Dependency relationship
|
Definition of how different components relate to each other
|
MyApp → log4j-core (2.14.1 )
MyApp → jackson-databind (2.10.0 )
|
Author of SBOM data
|
Name of person or group that created the list of an SBOM
|
Apache Software Foundation
|
Timestamp
|
Date and time of when the SBOM was created
|
2025-05-10T12:00:00Z
|
These elements are the minimum set of attributes that every SBOM must contain. Next, we will discuss the optional elements that further enhance the purpose and effectiveness of the bill of materials.
Hash of the Component
A cryptographic hash ensures that the component being used is the same as the intended component. Oftentimes in supply chain attacks, a piece of software may be tampered with to include malicious code and be distributed publicly. To prevent this, users must compare the hash of the downloaded software with the approved one. If they match, the software is considered safe to use. In other words, this is one way to ensure integrity in the software supply chain.
Lifecycle Phase
An SBOM can vary depending on where in the software development lifecycle it was built. For example, in the piece of code below, because the version is specified as latest
, different versions of log4j could be downloaded at different times (on local box, during build). To address this variance, it is recommended to capture the lifecycle phase, whether it is source, build, or post build. The lifecycle phase acts as additional metadata that users can consider.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>latest</version>
</dependency>
Other Component Relationships
Sometimes, it is good to know whether a software dependency in use is a modified version of another base dependency. This is valuable information because if the original dependency has a known vulnerability, all of its modified versions may also be affected.
A perfect example of this can be forked dependencies. For instance, a company might use the Apache Commons Lang library but later fork the repository to create a custom version. Although the code is now maintained separately, any vulnerability in the original Commons Lang library could still impact the forked version. This is what we refer to as derivation, or descendancy dependencies. As a result, capturing the relationship information makes it easy for the security team to identify and mitigate risks inherited from shared code origins.
License Information
License information can help the organization protect itself from compliance issues. For instance, the components licensed under the General Public License (GPL) may require any software that uses it to be open sourced under the same license. Because this requirement can conflict with the company's business model, the organization may choose to avoid adopting such an open-source dependency.
Types of Dependencies
An SBOM typically declares two types of dependencies: direct and transitive (also known as indirect):
- Direct dependencies are components directly used by your software. For example, if your application directly depends on MySQL, Kafka, and Apache Commons, they are all declared as direct dependencies because they are integrated and used within your codebase.
- Indirect dependencies are components used by your direct dependencies but not by your software directly. For instance, MySQL might internally use a library to generate UUIDs. Although your application doesn't call this library itself, it still becomes part of the software supply chain and is considered a transitive dependency.
Formats and Standards
Once the schema and requirements are established, the next step is to develop standardized SBOM formats, which enable seamless exchange between different systems to support automation, thereby promoting strong interoperability. Currently, there are three popular formats: Software Package Data Exchange, CycloneDX, and Software Identification Tag.
Software Package Data Exchange
The Software Package Data Exchange (SPDX) is one of the most widely adopted SBOM standards and is formally recognized as an international standard under ISO/IEC 5962:2021 for software data exchange. It supports multiple representations, including XML, JSON, and plain text, making it flexible for different tooling and ecosystems. The example below demonstrates a plain text version of the SPDX format and includes all the minimum schema elements discussed earlier:
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
DocumentNamespace: http://spdx.org/spdxpackages/io.dapr.spring_dapr-spring-parent-0.16.0-SNAPSHOT
DocumentName: dapr-spring-parent
SPDXID: SPDXRef-DOCUMENT
## Creation Information
Creator: Tool: spdx-maven-plugin
Created: 2025-05-10T23:11:23Z
LicenseListVersion: 3.26.0
## Relationships
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef--4b3779bf0
RelationshipComment: <text></text>
## Package Information
PackageName: dapr-spring-parent
SPDXID: SPDXRef--4b3779bf0
PackageVersion: 0.16.0-SNAPSHOT
PrimaryPackagePurpose: INSTALL
PackageVerificationCode: da39a3ee5e6b4b0d3255bfef95601890afd80709
PackageHomePage: https://dapr.io/dapr-spring-parent
PackageLicenseConcluded: Apache-2.0
PackageLicenseDeclared: Apache-2.0
PackageSummary: <text>SDK extension for Spring and Spring Boot</text>
PackageDescription: <text>SDK extension for Spring and Spring Boot</text>
ExternalRef: PACKAGE-MANAGER purl pkg:maven/io.dapr.spring/[email protected]
## Relationships
Relationship: SPDXRef--4b3779bf0 DYNAMIC_LINK SPDXRef--10b0268e0
RelationshipComment: <text>Relationship based on Maven POM file dependency information</text>
## Package Information
PackageName: JUnit
SPDXID: SPDXRef--286550880
PackageVersion: 3.2.6
PackageOriginator: Organization:VMware, Inc.
PackageHomePage: https://spring.io/projects/spring-boot
PackageLicenseDeclared: Apache-2.0
PackageSummary: <text>Core starter, including auto-configuration support, logging and YAML</text>
PackageDescription: <text>Core starter, including auto-configuration support, logging and YAML</text>
ExternalRef: PACKAGE-MANAGER purl pkg:maven/org.springframework.boot/[email protected]
FilesAnalyzed: false
## Relationships
Relationship: SPDXRef--286550880 TEST_DEPENDENCY_OF SPDXRef--4b3779bf0
RelationshipComment: <text>Relationship created based on Maven POM information</text>
In this example:
SPDXVersion
– Indicates the version of the SPDX format used. In this case, it is 2.3, although the latest version is 3.0, which was released in 2024.
Creation Information
– Since the SBOM was generated using a Maven plugin, the creator field includes Tool: spdx-maven-plugin
, along with the timestamp and other optional license details.
Package Information
– Lists the dependencies used to build the dapr-spring-parent
component. It includes metadata such as the package name, version, and license.
PackageVerificationCode
is used to verify the integrity of the package and ensure the user is downloading the correct version. This is the cryptographic hash that we discussed previously.
ExternalRef
includes a package URL (purl
) that uniquely identifies the package and its version using a package manager reference.
PackageLicenseDeclared
specifies that the components are licensed under Apache 2.0, which is a permissive license with fewer usage restrictions.
Relationships
– Each package is assigned an SPDX Identifier (SPDXID
). These identifiers are used to express relationships between packages. For example, TEST_DEPENDENCY_OF
indicates that JUnit is a test dependency of the dapper-spring
parent project.
SPDX is particularly good at relationships. It offers a rich set of relationship types for describing how components are connected. Examples of other relationship types that SPDX supports include BUILD_DEPENDENCY_OF
, DEPENDS_ON
, and CONTAINED_BY
.
CycloneDX
CycloneDX (CDX) is another popular format developed by the Open Worldwide Application Security Project (OWASP). It especially shines at mapping dependency versions to vulnerabilities coming from vulnerability databases like CVE and CVSS. As a result, if the purpose of creating SBOMs is for identifying vulnerabilities, then CDX suits this use case better.
{
"bomFormat" : "CycloneDX",
"specVersion" : "1.6",
"serialNumber" : "urn:uuid:ed23acb5-17a8-3d29-a340-3f12db890b04",
"metadata" : {
"tools" : {
"components" : [
{
"type" : "library",
"author" : "OWASP Foundation",
"group" : "org.cyclonedx",
"name" : "cyclonedx-maven-plugin",
"version" : "2.9.1",
"description" : "CycloneDX Maven plugin",
"hashes" : [
{
"alg" : "MD5",
"content" : "9c7a565cf28cce58557d0c621c5ea4b1"
}
]
}
]
}
},
"components" : [
{
"type" : "library",
"bom-ref" : "pkg:maven/io.grpc/[email protected]?type=jar",
"group" : "io.grpc",
"name" : "grpc-netty-shaded",
"version" : "1.69.0",
"description" : "gRPC: Netty Shaded",
"hashes" : [
{
"alg" : "MD5",
"content" : "a72607f4fdca24e310ac1e91dc0010fb"
}
],
"licenses" : [
{
"license" : {
"id" : "Apache-2.0"
}
}
],
"purl" : "pkg:maven/io.grpc/[email protected]?type=jar",
}
],
"dependencies" : [
{
"ref" : "pkg:maven/io.grpc/[email protected]?type=jar",
"dependsOn" : [
"pkg:maven/io.grpc/[email protected]?type=jar",
"pkg:maven/io.grpc/[email protected]?type=jar",
]
}
]
}
Similar to SPDX, a CDX document includes metadata such as the following:
specVersion
, which specifies the version of the CycloneDX format being used.
metadata
, which include plugin details used to generate the CDX document, as well as supplies other information like the tool name, version, and even the hashes
of components.
- Package URL (
purl
) uniquely identifies each element along with its version.
- Dependency relationships are represented using a simpler
dependsOn
attribute, unlike SPDX, which supports a broader set of relationship types.
license
information is also included, specifying the license(s) associated with each component — in this case, Apache-2.0
.
Later, in the SBOM consumption section, we will look at how the CDX format can be used to detect vulnerabilities in the dependencies.
Software Identification Tags
Software Identification (SWID) tags are primarily used to identify installed software, firmware, and products for inventory management. While they can describe the basic metadata and optional relationships, they do not fulfill the full requirements of an SBOM as they lack standardized support for transitive dependencies and complex dependency relationships.
There is also very limited tooling available for SWID tags. For example, while the swid-maven-plugin allows you to build a tag by manually specifying metadata, it does not automatically generate a tag from the existing project structure or dependency graph. So in short, SWID is more for inventory management.
To wrap things up, let's compare CycloneDX, SPDX, and SWID to better understand how they differ in scope and purpose.
Table 2
Feature |
SPDX |
CDX |
SWID |
Origin and backing
|
Linux Foundation
|
OWASP Foundation
|
ISO/IEC 19770-2
|
Use case |
Supply chain security, software license compliance and intellectual property management
|
Supply chain security, vulnerability analysis
|
IT asset and inventory management
|
Formats |
Plain text, JSON, YAML, XML
|
JSON, XML
|
XML |
Vulnerability data |
Basic support (e.g., you can reference CVEs but not describe them)
|
Built-in support for VEX (Vulnerability Exploitability eXchange) and VDR (Vulnerability Disclosure Report)
|
No native support for vulnerability data
|
Industry adoption |
Compliance-heavy organizations, OSS tools
|
Security-first organizations, vendors
|
Enterprises, government
|
Open-Source SBOM Generation Tools
With the growing importance of supply chain security and SBOMs, a wide range of tools has emerged in this space over time. These tools can analyze everything from your software components to your infrastructure artifacts, such as Docker images and Kubernetes applications. Following are examples of open-source tools that can help automate the process of generating SBOMs for us.
SPDX and CDX Maven Plugin
SPDX offers a Maven plugin that can be integrated into the build phase. Below is an example of how we could integrate the plugin into the existing pom
.
Step 1: Add the following plugin under the build
tag:
<plugin>
<groupId>org.spdx</groupId>
<artifactId>spdx-maven-plugin</artifactId>
<version>1.0.2</version>
<executions>
<execution>
<goals>
<goal>createSPDX</goal>
</goals>
</execution>
</executions>
</plugin>
Step 2: Run mvn spdx:createSPD
and check the target/site folder. There will be an artifact with the extension .spdx
.
CycloneDX also has a Maven plugin that can generate SBOMs in the CDX format. The command for CDX using the below plugin is mvn cdx:makeAggreagateBom
. Check the Maven repository for the latest versions.
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>makeAggregateBom</goal>
</goals>
</execution>
</executions>
</plugin>
There are plugins for programming languages like Python and Go, too.
Trivy
Trivy is another popular open-source tool that can generate SBOMs for Docker artifacts. Docker images are also formed from a set of dependencies. For example, a Python-based image or a Linux-based image has a ton of libraries that come with them. It is important to inventory them as well because any compliance or vulnerabilities in them will affect your service.
To illustrate how we can scan images with Trivy, let's consider the following example:
Step 1: Pick an image. In this case, let's choose the python:3.13-alpine Docker image.
Step 2: For Windows, download the .exe file from here, and for other platforms, the installation instructions can be found on the Trivy Installation page.
Step 3: Run the following command to output SBOMs in SPDX and CDX formats:
For SPDX:
.\trivy.exe image --format spdx-json --output result.json python:3.13-alpine
For CDX:
.\trivy.exe image --format cyclonedx --output result.json python:3.13-alpine
Kubernetes BOM and Syft are examples of some other open-source tools in the infrastructure space.
Open Source Limitations
Open-source tools typically offer a fragmented view of software risk. For example, Trivy open source only lists open-source vulnerability feeds when using the open-source software version. The vulnerabilities from the commercial vulnerability feed are not provided. Additionally, they don't offer any service-level agreements (SLAs), which might be necessary for companies that provide a strict SLA to their customers.
Support for programming languages is also limited. If we consider the SPDX plugin example that we discussed previously, it supports only a handful of languages (e.g., Java, Python, Go, Haskell, JavaScript).
Attesting SBOM Artifacts
Attestation is a signed, verifiable declaration that asserts a fact or event. For SBOMs, attestation plays a critical role in establishing trust. Without a mechanism to verify an SBOM's authenticity and origin, its integrity cannot be guaranteed. As a result, by validating the signer through cryptographic attestation, SBOM consumers can ensure that it was generated by an authorized source and remains untampered.
Cosign is a widely adopted open-source tool for signing and attesting supply chain artifacts, including SBOMs. It uses a public and private key system to sign documents and verify their authenticity. By using Cosign, producers can digitally sign an SBOM, and consumers can validate its origin and integrity before trusting or integrating it.
To attest an SBOM with cosign
, follow these steps:
Step 1: Generate a public and private key using the command below:
Step 2: Sign the SBOM using the private key:
cosign sign-blob --key cosign.key <SBOM artifact>
This command will generate a signature, for example:
MEUCIQD8VwYQUp9ayJqS2V1bOKzTdr7FK5dHDwBHV+nz0qgeVgIgS8wvUuBQnH3ghfq8NSt+ExO+6WY0lY0w92FnVGn3vb4=
Step 3: Embed this signature alongside the SPDX or CDX SBOM file.
Step 4: Consumers can then verify the signed artifact using the public key and signature:
cosign verify-blob --key cosign.pub --signature MEUCIQD8VwYQUp9ayJqS2V1bOKzTdr7FK5dHDwBHV+nz0qgeVgIgS8wvUuBQnH3ghfq8NSt+ExO+6WY0lY0w92FnVGn3vb4= <SBOM artifact>
If the verification is successful, the output will include:
codeblock: cmd
"Verified OK"