How to Build an SBOM for Embedded Firmware (and Keep It Accurate)
- Last Updated: November 4, 2026
Platanor Technologies
- Last Updated: November 4, 2026



A Software Bill of Materials sounds simple: a list of every software component in your product, with versions. For a web service, it mostly is. Package managers already know your dependencies, and tools like Syft or cdxgen can produce a decent SBOM in a CI step.
Embedded firmware is a different story. The dependency graph is messier, build toolchains are non-standard, and many components (vendor SDKs, RTOS kernels, hardware abstraction layers, bootloaders) never pass through a package manager at all.
Here's a scenario we've seen more than once: a team runs an SBOM tool over their firmware repository, gets a clean-looking file with a dozen entries, and only later realizes that the TLS library, the RTOS, and the bootloader are not in it.
This article covers how to build an SBOM that reflects what is actually inside your firmware image, which format to use, how to keep it current release after release, and how to check that your vulnerability scanner really examined every component.
There are two reasons, one practical and one regulatory.
The practical one: when a new vulnerability lands in a widely used library, the first question is "are we affected, and on which products?" Without a component inventory, answering that means digging through source trees and asking whoever wrote the code years ago. With an SBOM per release, it is a search.
The regulatory one: the EU Cyber Resilience Act (CRA) requires manufacturers of products with digital elements to identify and document the components in their products, including by drawing up an SBOM in a commonly used, machine-readable format that covers at least the top-level dependencies. Manufacturers must keep it as part of their technical documentation and provide it to market surveillance authorities on request. They are not required to publish it.
The SBOM is also what makes ongoing vulnerability monitoring possible. Once your component list is machine-readable, you can automatically match it against vulnerability databases, which is how you learn about a problem through your own monitoring rather than from a customer.
Two formats dominate, and both are widely accepted.
SPDX, maintained by the Linux Foundation, is an international standard (ISO/IEC 5962:2021). It is strong on licence information and common in open source ecosystems. It supports JSON, YAML, RDF, and a tag-value text format.
CycloneDX, maintained by OWASP and standardised as ECMA-424, was designed with security use cases in mind. It supports JSON and XML and is well supported by vulnerability tooling such as Dependency-Track.
The CRA does not prescribe a format today; it asks for a commonly used, machine-readable one, and the European Commission may specify a format later. In practice, the choice usually comes down to your downstream tools. Before standardizing, check which format your scanning and monitoring tools accept; some accept only one of the two.
Standard SBOM generators read package manifests and lock files: npm, pip, Maven, Cargo. A typical firmware project pulls in components through a mix of:
None of this produces a manifest that generic tools understand. So a firmware SBOM is usually a combination of automated extraction and deliberate manual inventory.
1. Extract what automation can find. Run Syft or cdxgen over the source tree. They will pick up components they recognise: libraries with identifiable metadata, version references in CMake or Makefile fragments, Zephyr or nRF Connect module definitions. Treat this as a starting point, not the answer.
2. Add vendor components by hand. Your chip vendor's SDK, HAL, and any closed-source blobs need manual entries. Take version strings from SDK release notes or version headers. For Nordic, that means both the nRF Connect SDK version and the Zephyr version it bundles; for STM32, the STM32Cube package and the individual HAL driver versions.
3. Verify against the build output. Source trees often contain code that never makes it into the binary, and miss code pulled in at build time. Cross-check your list against what the build actually links: build logs and linker map files show what ended up in the final image. The SBOM should describe the shipped firmware, not the repository.
For every component, aim for:
A minimal CycloneDX entry looks like this:
{
"type": "library",
"name": "FreeRTOS-Kernel",
"version": "10.6.1",
"purl": "pkg:github/FreeRTOS/[email protected]",
"cpe": "cpe:2.3:o:amazon:freertos:10.6.1:*:*:*:*:*:*:*",
"supplier": { "name": "Amazon Web Services" },
"licenses": [{ "license": { "id": "MIT" } }],
"hashes": [{ "alg": "SHA-256", "content": "<sha256 of the source archive>" }]
}
Not every component will have every field. Some vendor libraries have neither a PURL nor a CPE. Record what you have and mark what is missing: a complete and honest inventory beats a formally perfect one.
This is the step most teams skip. Vulnerability databases key their records differently. Some match components mainly by PURL, which works well for package ecosystems such as npm, PyPI, or Debian. Others match mainly by CPE, the identifier used by NIST's National Vulnerability Database. Embedded C libraries pulled from GitHub or copied into a source tree often fall between the two.
The result can be a report that says "no vulnerabilities found" when, in fact, several components were never checked. When you review a scan:
An SBOM written once at launch goes stale with the first firmware update. Make it part of the release process:
Recording only the SDK version. An SDK release bundles components at their own versions. A vulnerability in Zephyr 3.4.0 will not match an entry that just says "nRF Connect SDK 2.5.0." List the bundled components individually.
Leaving out the bootloader. MCUboot, a vendor secure-boot implementation, or a custom bootloader is software with its own vulnerability history. If you rely on secure boot, the code doing the verifying belongs in the inventory too.
Generating from source instead of the build. It produces both false positives (code never linked) and gaps (code pulled in at build time).
Treating the SBOM as a one-off deliverable. Component versions change with every release; the SBOM has to change with them.
Does the SBOM have to be public? No. Under the CRA, manufacturers must have one and provide it to market surveillance authorities on request. Publishing it is optional.
How deep does it need to go? The CRA sets the floor at top-level dependencies. For security purposes, going further is worth it, especially for open source libraries with their own vulnerability history, such as TLS stacks, network stacks, and compression libraries.
Which tools should we start with? Syft and cdxgen for automated extraction, Dependency-Track for loading SBOMs and monitoring vulnerabilities over time, and a simple template for documenting vendor components by hand.
Our RTOS is a proprietary one from the chip vendor. Does it belong in the SBOM? Yes. Anything that runs on the device belongs there. If the vendor does not publish vulnerability information, note that in your risk assessment: it means your monitoring coverage for that component is limited.
How long do we need to keep it? Under the CRA, technical documentation, including the SBOM, must be kept for at least 10 years after the product is placed on the market, or for the support period if that is longer.
The Most Comprehensive IoT Newsletter for Enterprises
Showcasing the highest-quality content, resources, news, and insights from the world of the Internet of Things. Subscribe to remain informed and up-to-date.
New Podcast Episode

Related Articles