Software Bill of Materials (SBOM) has been around for some time now. In this post we are going to look into (open source) tools that can be used to work with SBOMs. Unfortunately, we will identify common limitations and problems when working with SBOM related tools.

For some general information on what SBOM is all about, there is a Wired article on "the Modern Day SBOM" that describes the use cases. Summarized in one sentence, an SBOM is a machine-readable document that contains different information elements that contains information related to a (released) software package. The primary security use case for the SBOM (in the context of this blog post) is the identification of known vulnerabilities and risks in the software supply chain. This allows tracking whether 3rd party libraries that are included in a software package contain vulnerabilities (CVEs).

But let’s start from the beginning.

SBOM Basics

SBOMs have been around for some time, for example to convey software license information. But the recent "hype" started with the Executive Order on Improving the Nation’s Cybersecurity (EO 14028) published on November 2021. Shortly summarized: if you sell software to US federal agencies then you have to comply with what is mandated in this order.

NIST & NTIA Minimum Elements

The EO 14028 itself does not define requirements. Instead it references requirements from another document, NIST SP 800-161: Cybersecurity Supply Chain Risk Management Practices for Systems and Organizations.

Of particular interest for this blog post is requirement PS.3.2 from this document:

Collect, safeguard, maintain, and share provenance data for all components of each software release (e.g., in a software bill of materials [SBOM])

This sounds rather high-level and is not further specified in the NIST document. Instead, as can be seen in the quote above, the NIST document references another document that specifies what SBOM is actually really about: National Telecommunications and Information Administration (NTIA) (2021) The Minimum Elements For a Software Bill of Materials (SBOM).

This document, as the title implies, defines the mandatory elements that have to be included in an SBOM (to comply with the EO). Those elements are also called the NTIA minimum elements. Those are:

Data Field Description Example

Supplier Name

The name of an entity that creates, defines, and identifies components

ACME Corporation.

Component Name

Designation assigned to a unit of software defined by the original supplier.

ACME App

Version of the Component

Identifier used by the supplier to specify a change in software from a previously identified version.

1.2.3

Other Unique Identifiers

Other identifiers that are used to identify a component, or serve as a look-up key for relevant databases.

Dependency Relationship

Characterizing the relationship that an upstream component X is included in software Y. These are the 3rd party dependencies, e.g. libraries, that are included in your software package.

ACME App uses OpenSSL

Author of SBOM Data

The name of the entity that creates the SBOM data for this component. This is the name of the tool that was used to generate the SBOM.

Timestamp

Record of the date and time of the SBOM data assembly.

2024-12-19T11:43:57+01:00

Ignore the missing examples for now. This will become more clear in the next sections.

The core part of SBOM are the fields Component Name and Version of the Component. We will use an example application (ACME App) that includes the library openssl, version 3.1.5 in the remainder of this post.

Depdendency Description

OpenSSL is a well known library that everybody knows. It is immediately clear what dependency you are referring to. But what if your application is using a Kafka Client library? There is a Kafka Java client library implementation, but there are also Python, C/C++ and many other variants.

There is therefore a need to disambiguate and use an identifier that uniquely and reliably identifies a dependency. This is the "Unique Identifier" element of the NTIA minimum elements.

The NTIA document does not specify how these identifiers should look like. Instead, it references other standards like Common Platform Enumeration (CPE), Software Identification (SWID) tags and Package Uniform Resource Locators (PURL).

Using OpenSSL from our example application, the dependency can be specified in any of the following ways:

  • Using CPE: cpe:2.3:a:openssl:openssl:3.1.5:*:*:*:*:*:*:* [1]

  • Using PURL: pkg:github.com/openssl/openssl@3.1.5 [2]

Both options uniquely identify the OpenSSL package and version our application is using.

CycloneDX, SPDX & Co

So how does a standardized machine readable SBOM document containing all of this information look like? The NTIA document itself does not specify how the encoding looks like. It instead references other standards:

Any of these standards can be used to generate SBOMs that comply with NTIA minimum elements and the EO.

For the remainder of this post, we are going to use the CycloneDX standard, with JSON encoding. An example for our application is provided below:

sbom.json
{
  "serialNumber": "urn:uuid:a12bc345-01ab-01a2-98a7-98765abc9d8ef",
  "version": 1,
  "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
  "bomFormat": "CycloneDX",                                          1
  "specVersion": "1.6",
  "metadata": {
   "component": {
      "bom-ref": "acme-app",                                         2
      "type": "application",
      "name": "ACME App",
      "version": "1.2.3",
      "supplier": {
        "name": "ACME Corporation",
        "url": [
          "http://acme-corp.com"
        ]
      }
    },
    "authors": [
      {
        "email": "info@acme-corp.com",
        "name": "ACME Corp"
      }
    ],
    "timestamp": "2024-12-19T11:43:57+01:00"
  },
  "components": [
    {
      "bom-ref": "openssl-v3.1.5",                               3
      "type": "library",
      "name": "openssl",
      "version": "3.1.5",
      "purl": "pkg:github.com/openssl/openssl@3.1.5",
      "cpe": "cpe:2.3:a:openssl:openssl:3.1.5:*:*:*:*:*:*:*",
      "supplier": {
          "name": "openssl"
       }
    }
  ],
  "dependencies": [
    {
      "ref": "acme-app",
      "dependsOn": [                                              4
        "openssl-v3.1.5"
      ]
    },
    {"ref": "openssl-v3.1.5"}
  ]
}
1 This SBOM is using version 1.6 of the CycloneDX JSON standard
2 Metadata for our application that this SBOM is describing
3 The only dependency of our application is OpenSSL
4 This explicitly references OpenSSL as a direct dependency of our application

For additional details on the document format, refer to the CycloneDX 1.6 JSON standard.

SBOM Format Conversion

When a customer asks you to provide them with an SBOM, they might ask for it in a particular format. Luckily, there are tools for converting between different SBOM formats. Some examples:

My experience with these tools has been that some information elements might be lost during the conversion process.

Warning

For this reason, these conversion tools are not fully production ready yet.

Using cyclonedx-cli, we can convert Cyclone DX based SBOM to SPDX as follows: cyclonedx convert --input-file sbom.json --output-file sbom.spdx.json --output-format spdxjson.

The generated output looks as follows (click to expand):

SPDX SBOM
sbom.spdx.json
{
  "SPDXID": "SPDXRef-DOCUMENT",
  "spdxVersion": "SPDX-2.2",
  "creationInfo": {
    "comment": "This SPDX document has been converted from CycloneDX format.",
    "created": "2024-12-19T10:43:57Z",
    "creators": [
      "Person: ACME Corp (info@acme-corp.com)"
    ]
  },
  "name": "ACME App-1.2.3",
  "dataLicense": "CC0-1.0",
  "documentNamespace": "http://spdx.org/spdxdocs/ACME App-1.2.3-a12bc345-01ab-01a2-98a7-98765abc9d8ef",
  "packages": [
    {
      "SPDXID": "SPDXRef-openssl-v3.1.5",
      "copyrightText": "NOASSERTION",
      "downloadLocation": "NOASSERTION",
      "homepage": "NOASSERTION",
      "licenseConcluded": "NOASSERTION",
      "licenseDeclared": "NOASSERTION",
      "licenseInfoFromFiles": [
        "NOASSERTION"
      ],
      "name": "openssl",
      "originator": "NOASSERTION",
      "supplier": "Person: openssl ()",
      "versionInfo": "3.1.5"
    }
  ]
}

SBOM Validation

So how can you validate that a generated SBOM complies with the NTIA minimum elements and therefore with EO 14028?

Unfortunately, there are currently not many (open source) options available. The first one is the NTIA conformance checker that is available in two variants:

This is a strict checker that returns a passed/not-passed output when validating an SBOM document. Unfortunately, this tool can only validate SPDX based SBOMs.

Let’s use the Python CLI to validate our previously generated SPDX SBOM:

$ ntia-checker --file sbom.spdx.json

Is this SBOM NTIA minimum element conformant? False

Individual elements                            | Status
-------------------------------------------------------
All component names provided?                  | True
All component versions provided?               | True
All component identifiers provided?            | True
All component suppliers provided?              | True
SBOM author name provided?                     | True
SBOM creation timestamp provided?              | True
Dependency relationships provided?             | False

We have failed one test. However, when looking at the original CycloneDX file, you can see that a dependency relationship is present (dependencies section).

Unfortunately, this element was lost in the translation to SPDX. This is the problem mentioned in SBOM Format Conversion: those tools are not yet reliable enough.

Another tool is sbomqs that can validate different SBOM formats for compliance with NTIA minimum elements. The report can be seen below.

$ sbomqs compliance --ntia sbom.cdx.json
NTIA Report
Compliance score by Interlynk Score:7.1 RequiredScore:9.2 OptionalScore:5.0 for sbom.cdx.json
* indicates optional fields
+--------------------+------------+--------------------------------+--------------------------------------+-------+
|     ELEMENT ID     | SECTION ID |     NTIA MINIMUM ELEMENTS      |                RESULT                | SCORE |
+--------------------+------------+--------------------------------+--------------------------------------+-------+
| ACME App-1.2.3     |        2.4 | Package Name                   | ACME App                             |  10.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.5 | Dependencies on other          | openssl                              |  10.0 |
|                    |            | components                     |                                      |       |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.6 | Package Supplier               | http://acme-corp.com                 |  10.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.7 | Package Version                | 1.2.3                                |  10.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.8 | Other Uniq IDs                 |                                      |   0.0 |
+--------------------+------------+--------------------------------+--------------------------------------+-------+
| Automation Support |        1.1 | Machine-Readable Formats       | cyclonedx, json                      |  10.0 |
+--------------------+------------+--------------------------------+--------------------------------------+-------+
| SBOM Data Fields   |        2.1 | Author                         | info@acme-corp.com                   |  10.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.2 | Timestamp                      | 2024-12-19T11:43:57+01:00            |  10.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.3 | Dependencies                   | doc has 1 dependencies               |  10.0 |
+--------------------+------------+--------------------------------+--------------------------------------+-------+
| openssl-3.1.5      |        2.4 | Package Name                   | openssl                              |  10.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.5 | Dependencies on other          | included-in                          |  10.0 |
|                    |            | components                     |                                      |       |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.6 | Package Supplier               |                                      |   0.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.7 | Package Version                | 3.1.5                                |  10.0 |
+                    +------------+--------------------------------+--------------------------------------+-------+
|                    |        2.8 | Other Uniq IDs                 | pkg:github.com/openssl/openssl@3.1.5 |  10.0 |
+--------------------+------------+--------------------------------+--------------------------------------+-------+

THe component supplier object (.components.supplier.name) that actually exists in the SBOM is not properly detected. Hence the score of 0.0 for the Package Supplier field.

This tool can also calculate a more generic SBOM quality score. This is independent of EO 14028/NTIA minimum elements compliance. It also checks for other elements, e.g. license information or presence of a digital signature.

This report looks as follows (click to expand):

sbomqs score report
$ sbomqs score sbom.spdx.json
SBOM Quality by Interlynk Score:6.5	components:2 sbom.spdx.json
+-----------------------+--------------------------------+-----------+--------------------------------+
|       CATEGORY        |            FEATURE             |   SCORE   |              DESC              |
+-----------------------+--------------------------------+-----------+--------------------------------+
| NTIA-minimum-elements | comp_with_name                 | 10.0/10.0 | 2/2 have names                 |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_supplier             | 10.0/10.0 | 2/2 have supplier names        |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_uniq_ids             | 10.0/10.0 | 2/2 have unique ID's           |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_version              | 10.0/10.0 | 2/2 have versions              |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_authors                   | 10.0/10.0 | doc has 1 authors              |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_creation_timestamp        | 10.0/10.0 | doc has creation timestamp     |
|                       |                                |           | 2024-12-19T11:43:57+01:00      |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_dependencies              | 10.0/10.0 | doc has 1 dependencies         |
+-----------------------+--------------------------------+-----------+--------------------------------+
| Quality               | comp_valid_licenses            | 0.0/10.0  | 0/2 components with valid      |
|                       |                                |           | license                        |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_any_vuln_lookup_id   | 5.0/10.0  | 1/2 components have any lookup |
|                       |                                |           | id                             |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_deprecated_licenses  | 0.0/10.0  | no licenses found              |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_multi_vuln_lookup_id | 5.0/10.0  | 1/2 components have multiple   |
|                       |                                |           | lookup id                      |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_primary_purpose      | 10.0/10.0 | 2/2 components have primary    |
|                       |                                |           | purpose specified              |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_restrictive_licenses | 0.0/10.0  | no licenses found              |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_with_creator_and_version  | 0.0/10.0  | 0/0 tools have creator and     |
|                       |                                |           | version                        |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_with_primary_component    | 10.0/10.0 | primary component found        |
+-----------------------+--------------------------------+-----------+--------------------------------+
| Semantic              | comp_with_checksums            | 0.0/10.0  | 0/2 have checksums             |
+                       +--------------------------------+-----------+--------------------------------+
|                       | comp_with_licenses             | 0.0/10.0  | 0/2 have licenses              |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_required_fields           | 10.0/10.0 | Doc Fields:true Pkg            |
|                       |                                |           | Fields:true                    |
+-----------------------+--------------------------------+-----------+--------------------------------+
| Sharing               | sbom_sharable                  | 0.0/10.0  | doc has a sharable license     |
|                       |                                |           | free 0 :: of 0                 |
+-----------------------+--------------------------------+-----------+--------------------------------+
| Structural            | sbom_parsable                  | 10.0/10.0 | provided sbom is parsable      |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_spec                      | 10.0/10.0 | provided sbom is in a          |
|                       |                                |           | supported sbom format of       |
|                       |                                |           | spdx,cyclonedx                 |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_spec_file_format          | 10.0/10.0 | provided sbom should be in     |
|                       |                                |           | supported file format for      |
|                       |                                |           | spec: json and version:        |
|                       |                                |           | json,xml                       |
+                       +--------------------------------+-----------+--------------------------------+
|                       | sbom_spec_version              | 10.0/10.0 | provided sbom should be        |
|                       |                                |           | in supported spec version      |
|                       |                                |           | for spec:1.6 and versions:     |
|                       |                                |           | 1.0,1.1,1.2,1.3,1.4,1.5,1.6    |
+-----------------------+--------------------------------+-----------+--------------------------------+

Identifying Dependencies in Applications

We now know what has to be inside an SBOM document. But how can we automatically generate a compliant SBOM from a software application, assuming that we have access to the source code of this application? How can you reliably identify the dependencies of an application?

Depending on your application’s programming language and ecosystem, there is an easy and a difficult approach. We will look into both.

Standard Approach and Available Tools

Many "modern" languages allow to explicitly specify an application’s dependencies via a package manager. Some examples:

The package manager will then retrieve the dependencies, as specified in the package manager files, when building the application.

It is important to mention that some ecosystems differentiate between "normal" dependencies and development/build dependencies (e.g. devDependencies in nodejs). Only "normal" dependencies are relevant for the SBOM, as the compiler used to build your application or your test framework are usually not included in your application’s software release.

When your application uses a package manager, generating an SBOM is straightforward: the dependency information is specified in these package manager files. You only need a tool that is able to identify these files, parse them and then generate an SBOM from this information.

There are open source tools available that can do this. Some popular examples:

These tools can generate SBOMs in different output formats, including CycloneDX and SPDX.

Special Note on Container SBOMs

It is also possible to generate an SBOM for a container image. The relevant dependencies for a container image is every software package installed inside the container image.

That includes software installed via OS package manager (e.g. apt-get, yum, etc.). These package managers keep a file inside the container file system that contains information on installed packages. The previously mentioned tools are also able to identify packages installed via package managers from well known Linux distributions, like Debian, Rocky Linux, Arch Linux, etc.

Some tools are able to identify additional software artifacts that exist inside a container image. This is achieved by identifying the previously mentioned package manager files inside the container filesytem, e.g. a Python requirements.txt file that has been copied into the container image.

Some tools are also able to identify application binaries that have been directly copied into a container image, without using a package manger (using Dockerfile COPY instruction). To my knowledge, this identification only works reliably for golang binaries, because starting from Go version 1.18 the Go version and the build dependencies are embedded into the binary at build time.

Non-Standard Approach

We have covered the easy case, fetching dependency information from package managers. But what if your application doesn’t use a package manager?

This is very common for C/C++ based applications. The Conan package manager provides package manager functionality for the C/C++ ecosystem, but is not part of the C/C++ standard and therefore not something you can assume to be available. In fact, many applications do not use it.

And this is where things start to become complicated.

Dependency Types

So how are dependencies retrieved or included in a C/C++ application when you don’t use a package manager? There are multiple options, as shows in the table below.

Dependency Type Description Identification

Source code: Git submodule

The 3rd party component is included as source code in your own repository as a git submodule (git submodule add).

Relatively easy to identify because git metadata includes information on the 3rd party git repository URL and branch/tag/commit information.

Source code: copy & paste

The source code of the 3rd party component was copied into your own source code repository.

Doable with special tooling. There are some commercial tools that have fingerprint databases for open source software. This then works reliably for as long as the 3rd party code has not been changed. The tool calculates the fingerprint of your local code, compares it against the fingerprints in the (vendor owned) database and then returns the matches.

Source code: download of source package at build time

The source code of the 3rd party component is downloaded during the build process and copied into into the source or build directory.

Same approach as for the copy & paste variant.

Statically linked library

Instead of building the 3rd party component from source, you install the pre-compiled library (in particular it’s static lib .a) in your build environment. You then specify your build/linker settings to statically link this library into your own software.

This requires either analysis of your application binary or monitoring of the build process to identify the 3rd party component. Both approaches can have false positive & false negative problems.

Dynamically linked library

Similar to the previous approach, but instead of actually linking a pre-compiled library into your own software, you are merely referencing a shared library object (.so/.dll).

This should be irrelevant and therefore not listed in the SBOM, as dynamically linked libraries are not part of your software package.[3] When running an application that references dynamically linked libraries, those libraries have to be pre-installed in the OS/container environment where the application is being executed.

Summarized, we have to differentiate between 3rd party components that are available on the source code level vs those available as pre-compiled libraries.

Source code level identification is difficult without metadata from something like git or commercial tooling that can perform lookups against a database with open source software fingerprints. At the same time, just because 3rd party source code is inside your repository doesn’t mean that this code is actually used in the build process, or maybe only in some build processes (e.g. for certain target platforms only). This therefore entails a false positive risk.

While ldd can be used to reliably identify dynamically linked libraries referenced by an application binary, that is not possible for statically linked libraries.

A commercial tool with an interesting solution approach is the Blackduck C/CPP tool: it uses both static analysis and build time information for generating an SBOM. The static analysis performs a scan of the source code in your repository to identify 3rd party components (as described in the table above). During build time, the tool monitors file system access during the build (using Coverity). This allows to identify the (static) libraries that are being accessed during the build process. Combining information from both static and build time analysis then allows to build an SBOM that, while not perfect, has at least good accuracy. The end result might still contain false positives/negatives though.

Conclusion for Dependency Identification

Many languages, or language ecosystems, use package managers that make it easy to identify 3rd party dependencies in a reliable way. Examples for this are Python, Golang, etc.

Without a package manager dependency identification is complex and error prone. An example for this is C/C++, where no standard package manager exists and there are different possibilities for including 3rd party code or pre-compiled libraries into your application. In that case, the solution will usually have to be tailored for the specific application and how it manages dependencies.

Vulnerability Scanning an SBOM

Assuming we have been able to generate a valid SBOM, we can finally move on to the core use case of the NTIA SBOM: vulnerability/CVE scanning.

This ties into the dependency identifiers we discussed before in Depdendency Description: vulnerability/CVE databases provide a list of known CVEs for a given package identifier. In case of the National Vulnerability Database (NVD), the identifier that you have to use for CVE search is the cpe.

The identifier to be used for vulnerability database lookups depends on the database. The cpe is usually supported by every database, whereas a purl with a github repository reference is barely supported at all.

An exception to this is Google’s osv database that also contains repository and commit range information for C/C++ packages. E.g. CVE-2016-6309 on osv contains a reference to the git repository github.com/openssl/openssl and a "last affected" commit hash. Unfortunately this database is not complete and has gaps, so use with caution!

Scanning Tools

Using our existing Cyclone DX based SBOM, we can test some vulnerability scanning tools to check whether our application’s dependencies have CVEs.

We first start with grype:

$ grype sbom:sbom.json
 ✔ Vulnerability DB                [no update available]
 ✔ Scanned for vulnerabilities     [0 vulnerability matches]
   ├── by severity: 1 critical, 2 high, 2 medium, 0 low, 0 negligible (1 unknown
   └── by status:   6 fixed, 0 not-fixed, 0 ignored
NAME     INSTALLED  FIXED-IN                                       TYPE            VULNERABILITY  SEVERITY
openssl  3.1.5      1.0.2zk, 1.1.1za, 3.0.15, 3.1.7, 3.2.3, 3.3.2  UnknownPackage  CVE-2024-5535  Critical
openssl  3.1.5      3.0.15, 3.1.7, 3.2.3, 3.3.2                    UnknownPackage  CVE-2024-6119  High
openssl  3.1.5      1.1.1y, 3.0.14, 3.1.6, 3.2.2, 3.3.1            UnknownPackage  CVE-2024-4741  High
openssl  3.1.5      1.0.2zl, 1.1.1zb, 3.0.16, 3.1.8, 3.2.4, 3.3.3  UnknownPackage  CVE-2024-9143  Medium
openssl  3.1.5      3.0.14, 3.1.6, 3.2.2, 3.3.1                    UnknownPackage  CVE-2024-4603  Medium
openssl  3.1.5      1.1.1y, 3.0.14, 3.1.6, 3.2.2                   UnknownPackage  CVE-2024-2511  Unknown

And this is working as expected: our application is using OpenSSL version 3.1.5 that is one year old and therefore has known CVEs.

Another tool we can use is trivy:

$ trivy sbom sbom.json
2025-01-13T15:14:50+01:00	INFO	[vuln] Vulnerability scanning is enabled
2025-01-13T15:14:50+01:00	INFO	Detected SBOM format	format="cyclonedx-json"
2025-01-13T15:14:50+01:00	WARN	Third-party SBOM may lead to inaccurate vulnerability detection
2025-01-13T15:14:50+01:00	WARN	Recommend using Trivy to generate SBOMs
2025-01-13T15:14:50+01:00	INFO	Number of language-specific files	num=0

This scan does not return any results at all.

This problem is not unusual, as the same issue exists with osv-scanner:

$ osv-scanner  scan --sbom sbom.json
Scanned sbom.json as CycloneDX SBOM and found 1 package
No issues found

And this shows the problem with SBOM vulnerability scanning: certain scanners are unable to provide valid CVE scan results for an SBOM document.

The scanner output looks like there are no CVEs present for the SBOM dependencies. This can be very misleading though, as the OpenSSL dependency in our example does have vulnerabilities.

Summary

As we have seen, the tools used for generating, validating, converting or vulnerability scanning of SBOMs have not yet reached a maturity level where they can be used without the need to be aware of their limitations. Always thoroughly test a tool before using it for production purposes.

When producing an SBOM, take special care of the following things:

  1. If your application is not yet using a package manager, now would be a good time to start doing so

  2. Check whether your SBOM generation tool supports the package manager(s) your application is using

  3. What dependency identifiers (e.g. cpe, purl) are specified in the SBOM and are those supported by (popular) vulnerability scanners?

  4. When converting to a different SBOM format, double check whether the output still complies with the NTIA minimum elements

Except for the last item, these things are usually not a problem when generating an SBOM for an application written in a "modern" language/ecosystem.

When consuming an SBOM, how can you assess it’s fidelity, especially when you don’t have access to the application source code?

  1. When the SBOM document indicates that the application uses a language/framework with a package manager (as indicated by the dependency identifier types), then the SBOM is probably accurate.

  2. If that is not the case, e.g. C/C++ dependencies, you should ask the producer of the SBOM how they generated the SBOM. Explicitly ask how they have handled false positives / false negatives when building the SBOM.

  3. When vulnerability scanning an SBOM: play around with some dependencies, e.g. decrease the version number of a component and check whether your vulnerability scanner returns the expected CVEs [4].

More and more customers are asking for SBOM documents when acquiring software. It will therefore not disappear anytime soon. Unfortunately, as of today, the tooling still has to improve so that we can work with SBOMs in a reliable and accurate way.


1. For an explanation of CPE name encoding, refer to the CPE 2.3 standard
2. For an explanatio of the PURL encoding, refer to the purl specification.
3. The SBOM specifications do not mention dynamic linking and allow conveying information whether a dependency is statically or dynamically linked. For a lot of "modern" languages, this is not relevant, because they usually do static linking, or include the 3rd party source code directly, like in Python. The only exception to this problem in the SBOM specifications is SPDX that provides the relationship type DYNAMIC_LINK.
4. Manually search a vulnerability database for a given component, pick a CVE and determine the version numbers that are affected by the CVE. Then override the version number in the SBOM document.