A Software Bill of Materials is one of those requirements in the Cyber Resilience Act that sounds straightforward until you try to actually produce one for an embedded product. In enterprise software, SBOM tooling is mature and well-integrated into CI/CD pipelines. In embedded and IoT development, the dependency graph is messier, the build toolchains are non-standard, and the components include things like vendor SDKs, RTOS kernels, and hardware abstraction layers that do not always surface cleanly in automated scans.

This article covers what CRA actually requires from an SBOM, what formats satisfy the requirement, and how to approach SBOM generation for firmware in practice - not just for web applications or server software. If you need broader context on the full CRA requirement set, start with our complete guide for IoT manufacturers.


What CRA requires

Annex I Part 2 of the CRA regulation requires manufacturers to "identify and document components contained in the product with digital elements." The SBOM is the formal output of that identification.

The regulation does not require you to publish your SBOM publicly. You are required to have one, keep it current, and make it available to market surveillance authorities on request. In practice this means it needs to be accurate at the time of product launch, updated when the software changes, and stored as part of your technical documentation for the full retention period - a minimum of 10 years after the product is placed on the market.

What the SBOM must cover: every software component in the product, including your own application code components, open source libraries, third-party modules, the RTOS or kernel if applicable, vendor SDKs, and bootloader components. Each entry needs at minimum a component name, version, and a unique identifier - typically a CPE (Common Platform Enumeration) or PURL (Package URL).

Why this matters operationally: the SBOM is not a compliance checkbox. It is the data source that makes everything else possible. Without an accurate component inventory, you cannot run systematic CVE checks against your firmware. Without CVE monitoring, you cannot meet the September 2026 Article 14 reporting obligation - because you cannot know which CVEs affect your product. Read more about that obligation in our Article 14 vulnerability reporting guide. The SBOM is foundational infrastructure, not documentation.


The two accepted formats

CRA does not mandate a specific format, but aligns with the two formats that have become the industry standard for machine-readable SBOMs.

SPDX (Software Package Data Exchange) - developed by the Linux Foundation and now an ISO standard (ISO/IEC 5962:2021). SPDX is strong on licence tracking and is widely used in open source ecosystems. Supported output formats include JSON, YAML, RDF, and a tag-value text format. If your product uses significant open source components and licence compliance is a priority alongside security, SPDX is a natural fit.

CycloneDX - developed by OWASP, specifically designed with security use cases in mind. CycloneDX supports JSON and XML, has strong tooling integration for vulnerability correlation, and is the format better supported by CVE scanning tools like Dependency-Track and Grype. For IoT security and CRA compliance, CycloneDX is typically the more practical choice.

Both formats satisfy the CRA requirement. The decision between them usually comes down to which tooling you are integrating with downstream. If you are feeding the SBOM into a vulnerability management pipeline, check which format your scanning tool prefers.


The embedded firmware problem

Standard SBOM generation tools - Syft, cdxgen, Trivy - work well for software where the dependency management is done through package managers: npm, pip, Maven, Cargo. They read package manifests, lock files, and known dependency structures.

Embedded firmware does not look like this. A typical firmware project pulls in components through a mix of:

  • A vendor SDK (Nordic nRF Connect SDK, STM32CubeIDE HAL, ESP-IDF) that bundles its own version of libraries
  • An RTOS (FreeRTOS, Zephyr, ThreadX) with version pinning that may differ from the upstream repository
  • Open source libraries added as submodules or copied directly into the source tree
  • Compiled-in components from chip vendor libraries with no published version string
  • Your own application code layered on top

None of this produces a package manifest that standard tools can read automatically. The SBOM for firmware often requires a combination of automated scanning and manual inventory.

Practical approach for firmware SBOM:

First, extract what automated tools can find. Running Syft or cdxgen over your source tree will capture components it recognizes - open source libraries with identifiable headers, CMakeLists or Makefile fragments with version references, Zephyr or nRF Connect module definitions. This gets you 60-70% of the picture.

Second, manually document the vendor components. Your chip vendor SDK, HAL, and any closed-source components need to be added manually. Check the SDK release notes or version headers for the version string. For Nordic, this is the nRF Connect SDK version and the Zephyr RTOS version it bundles. For STM32, the STM32Cube version and the specific HAL driver versions.

Third, verify against your build output. The most reliable way to confirm what is actually in your binary is to cross-reference your component list against what the build system links. Build logs and map files show you what made it into the final image.

The result is an SBOM that reflects what is actually in the firmware, not just what automated tools could detect.


Keeping it current

A launch-time SBOM that is never updated is a compliance liability. If you release a firmware update in March 2027, your SBOM needs to reflect the component versions in that update - not the ones from the original release.

This means SBOM generation needs to be part of your release process, not a one-time project. The practical implementation:

  • Automate what can be automated. Integrate SBOM generation into your CI pipeline so it runs with every firmware build. Even if the output requires manual review, automating the extraction step saves time.
  • Version the SBOM alongside the firmware. Each firmware release tag should have a corresponding SBOM. When you ship firmware v2.1.0, the v2.1.0 SBOM goes into your documentation archive.
  • Review manually on each release for components that automation misses. This takes 30 minutes for an experienced engineer who knows the codebase.

The SBOM also feeds your ongoing vulnerability monitoring. Once it is in machine-readable format, you can load it into a tool like Dependency-Track and receive automated alerts when new CVEs appear for your component versions. This is the mechanism that makes the Article 14 24-hour reporting window achievable - you find out about vulnerabilities through your monitoring, not from customers.


What goes into each SBOM entry

For each component, a complete SBOM entry should include:

  • Name - the component name as it is commonly identified (e.g. "FreeRTOS", "mbedTLS", "nRF Connect SDK")
  • Version - the exact version string, not "latest" or "recent"
  • PURL or CPE - a unique identifier that vulnerability databases can match against. PURL format for an open source library: pkg:github/FreeRTOS/FreeRTOS-Kernel@10.6.1. CPE format: cpe:2.3:a:freertos:freertos:10.6.1:*:*:*:*:*:*:*
  • Supplier - who provides or maintains the component
  • Licence - the licence under which the component is distributed
  • Hash - a checksum of the component (SHA-256 of the source archive or binary) for integrity verification

A minimal CycloneDX JSON entry for a firmware component looks like this:

{
  "type": "library",
  "name": "FreeRTOS-Kernel",
  "version": "10.6.1",
  "purl": "pkg:github/FreeRTOS/FreeRTOS-Kernel@10.6.1",
  "supplier": { "name": "Amazon Web Services" },
  "licenses": [{ "license": { "id": "MIT" } }],
  "hashes": [{ "alg": "SHA-256", "content": "a1b2c3d4..." }]
}

Not every field will be available for every component - some vendor libraries do not have a published CPE or a PURL-compatible identifier. Document what you have and note what is unavailable. The goal is a complete and accurate inventory, not a formally perfect one.


Common mistakes

Using the wrong version string. Vendor SDKs often have a top-level version (nRF Connect SDK 2.5.0) that bundles components at different versions internally. The SBOM needs to reflect the actual component versions, not just the SDK version. A CVE against Zephyr 3.4.0 will not match against an SBOM entry that says "nRF Connect SDK 2.5.0."

Not including the bootloader. MCUboot, the ST secure boot implementation, or a custom bootloader are software components in your product with their own CVE history. They belong in the SBOM.

Generating the SBOM from source rather than from the build output. Source trees often contain components that are not linked into the final binary. The SBOM should reflect what is actually in the shipped firmware, which requires cross-referencing against your build output.

Treating the SBOM as a one-time deliverable. An SBOM that does not get updated with each firmware release is out of date as soon as you ship an update. Component versions change, new libraries get added, dependencies get updated. The SBOM process needs to be part of your release workflow.


FAQ

Does the SBOM need to be publicly available? No. CRA requires you to have an SBOM and to make it available to market surveillance authorities on request. There is no requirement to publish it. You may choose to publish it - some manufacturers do as a transparency measure - but it is not mandated.

How detailed does the SBOM need to be? Do I need to list every transitive dependency? CRA requires identification of components "contained in the product." The regulation does not specify a depth requirement explicitly. Best practice is to include at minimum all direct dependencies and the most significant transitive ones - particularly open source libraries with their own CVE history. A flat list of top-level dependencies is a starting point, not a complete SBOM.

What tools do you recommend for embedded SBOM generation? Syft (by Anchore) and cdxgen are useful starting points for automated extraction. Dependency-Track (OWASP) is the standard tool for loading SBOMs and running ongoing CVE correlation. For manual documentation of vendor SDK components, a simple spreadsheet that feeds into a CycloneDX template is a practical approach.

We use a proprietary RTOS from our chip vendor. Does it go in the SBOM? Yes. If it is software running on your device, it belongs in the SBOM. Include whatever version information the vendor provides in the SDK documentation or release notes. If the vendor does not publish CVEs, that is worth noting in your risk assessment - it means your monitoring coverage for that component is limited.

How long do we need to keep the SBOM? CRA requires technical documentation to be retained for a minimum of 10 years after the product is placed on the market. The SBOM is part of your technical documentation.


Sources: CRA Regulation (EU) 2024/2847 - Annex I Part 2; CycloneDX specification - OWASP; SPDX ISO/IEC 5962:2021; NTIA minimum elements for SBOM


Denys Zaliznyak is the CTO of Platanor Technologies. SBOM generation and vulnerability monitoring setup are part of the CRA compliance package Platanor delivers to IoT and hardware manufacturers. platanor.com/contact