[{"data":1,"prerenderedAt":1104},["ShallowReactive",2],{"blog-cra-sbom-firmware":3},{"id":4,"title":5,"body":6,"date":1086,"description":1087,"extension":1088,"image":1089,"keywords":1090,"meta":1098,"navigation":457,"path":1099,"readTime":1100,"seo":1101,"stem":1102,"__hash__":1103},"blog/blog/cra-sbom-firmware.md","Generating a CRA-Compliant SBOM for Embedded Firmware",{"type":7,"value":8,"toc":1071},"minimark",[9,18,21,29,35,38,43,46,51,54,59,62,65,70,73,78,81,106,109,113,116,119,124,127,130,153,160,163,167,170,173,184,188,202,205,226,245,249,257,263,267,270,403,408,419,422,428,434,566,569,577,583,591,606,612,711,715,718,724,750,753,768,773,796,799,813,816,820,823,828,859,864,891,896,926,931,958,963,984,995,998,1004,1008,1067],[10,11,12,13,17],"p",{},"If you ask a firmware team about SBOMs, you'll get one of two responses: either they've been generating them via Yocto's ",[14,15,16],"code",{},"create-spdx"," class for years and treat this as solved, or they have almost no idea where to start because half their firmware is binary blobs from chipset vendors.",[10,19,20],{},"The reality for most embedded product companies is somewhere in the middle—some components are tracked, others aren't, and the tracking that exists isn't machine-readable in any standard format.",[10,22,23,24,28],{},"The CRA is going to force resolution of this ambiguity. ",[25,26,27],"strong",{},"Annex I Part II, point 1"," requires manufacturers to:",[30,31,32],"blockquote",{},[10,33,34],{},"Identify and document vulnerabilities and components contained in the product with digital elements, including by drawing up a software bill of materials in a commonly used and machine-readable format covering at the very least the top-level dependencies of the products.",[10,36,37],{},"This post breaks down what the regulation actually requires, why firmware SBOM is harder than application SBOM, and how to implement it for Yocto-based and non-Yocto firmware builds.",[39,40,42],"h2",{"id":41},"what-the-regulation-actually-requires","What the Regulation Actually Requires",[10,44,45],{},"The CRA text is more permissive than many summaries suggest. Key points:",[10,47,48],{},[25,49,50],{},"1. Machine-readable format, but no mandatory standard",[10,52,53],{},"The regulation says \"commonly used and machine-readable format.\" It does not mandate SPDX or CycloneDX specifically. Both are acceptable; either is compliant. The format decision is yours to make based on your tooling ecosystem.",[10,55,56],{},[25,57,58],{},"2. \"At the very least top-level dependencies\"",[10,60,61],{},"The SBOM must cover top-level dependencies as a minimum. However, ENISA guidance (2025) interprets \"top-level dependencies\" broadly for firmware: all libraries, packages, and components that are linked into or bundled with your firmware image count as top-level dependencies, regardless of whether they're direct or transitive.",[10,63,64],{},"The practical implication: you can't get away with listing only the packages in your CMakeLists.txt or Makefile and ignoring transitive dependencies. If a library you link pulls in three others and those are compiled into your binary, they need to be in the SBOM.",[10,66,67],{},[25,68,69],{},"3. The SBOM is part of your technical documentation",[10,71,72],{},"Article 31 and Annex VII require the SBOM to be kept in the technical documentation file. It doesn't need to be published publicly, but it must be available to market surveillance authorities upon request. Many manufacturers are choosing to publish SBOMs alongside their products anyway (for supply chain transparency), but this isn't a CRA mandate.",[10,74,75],{},[25,76,77],{},"4. NTIA minimum elements apply as a baseline",[10,79,80],{},"ENISA has cross-referenced the NTIA minimum SBOM elements (2021) as a baseline for what constitutes an adequate SBOM. These are:",[82,83,84,88,91,94,97,100,103],"ul",{},[85,86,87],"li",{},"Supplier name",[85,89,90],{},"Component name",[85,92,93],{},"Version of the component",[85,95,96],{},"Other unique identifiers (CPE, PURL)",[85,98,99],{},"Dependency relationships",[85,101,102],{},"Author of SBOM data",[85,104,105],{},"Timestamp",[10,107,108],{},"Your SBOM needs to cover at least these fields per component.",[39,110,112],{"id":111},"why-firmware-sbom-is-harder-than-application-sbom","Why Firmware SBOM Is Harder Than Application SBOM",[10,114,115],{},"For web application or backend service teams, generating an SBOM is relatively straightforward: parse your package manager lock file (package-lock.json, Pipfile.lock, Cargo.lock, go.sum), generate an SPDX or CycloneDX document, done.",[10,117,118],{},"Firmware is different for four structural reasons:",[120,121,123],"h3",{"id":122},"_1-binary-blobs-and-closed-source-components","1. Binary Blobs and Closed-Source Components",[10,125,126],{},"The most common hard problem: your Wi-Fi chipset vendor provides a pre-compiled binary library. You link it into your firmware. You don't have source code. You have a version number and a filename.",[10,128,129],{},"What can you include in the SBOM?",[82,131,132,135,138,141,144,147,150],{},[85,133,134],{},"Component name: yes",[85,136,137],{},"Supplier: yes",[85,139,140],{},"Version: yes (if the vendor documents it, which isn't always the case)",[85,142,143],{},"Hash/checksum: yes (SHA-256 of the binary blob)",[85,145,146],{},"PURL: sometimes (if the vendor has a published CPE or package registry entry)",[85,148,149],{},"License: sometimes (if the vendor's distribution terms are documented)",[85,151,152],{},"Transitive dependencies of the blob: generally no",[10,154,155,156,159],{},"For binary blobs, you create an SBOM entry with what you know and mark the license and dependency information as ",[14,157,158],{},"NOASSERTION"," in SPDX or equivalent in CycloneDX. This is incomplete but defensible if you've made reasonable efforts to gather information.",[10,161,162],{},"The better long-term answer: contractual requirements on your chipset and SDK vendors to provide SBOMs for their binary deliverables. This is becoming a procurement requirement in the embedded industry.",[120,164,166],{"id":165},"_2-vendor-sdks-with-unreleased-patches","2. Vendor SDKs with Unreleased Patches",[10,168,169],{},"Many embedded product teams use a vendor SDK that's built on top of an open-source project (FreeRTOS, lwIP, mbedTLS, etc.) but with proprietary patches applied. The vendor ships a versioned SDK, but that version doesn't correspond to any public release of the underlying component.",[10,171,172],{},"This creates an SBOM problem: your SBOM should reference the vendor SDK version (since that's what's actually in your firmware), but you also want to capture the underlying component versions for vulnerability matching.",[10,174,175,176,179,180,183],{},"The SPDX approach: include both entries—the vendor SDK package and the upstream component it's derived from, with a ",[14,177,178],{},"CONTAINS"," or ",[14,181,182],{},"DYNAMIC_LINK"," relationship. Many scanners won't generate this automatically; you'll need to add it manually or via tooling customization.",[120,185,187],{"id":186},"_3-rtos-forks-and-custom-modifications","3. RTOS Forks and Custom Modifications",[10,189,190,191,196,197,201],{},"If your team has forked FreeRTOS, Zephyr, or another open-source RTOS and applied custom patches, your firmware contains a component that doesn't match any public release. This is common for embedded teams that need to backport security fixes or add hardware-specific support. For RTOS-specific SBOM guidance, see our ",[192,193,195],"a",{"href":194},"/blog/cra-compliance-zephyr-rtos/","Zephyr CRA compliance guide"," (which covers west manifest parsing) and ",[192,198,200],{"href":199},"/blog/cra-compliance-freertos/","FreeRTOS CRA compliance guide"," (which covers the vendor SDK dependency challenge).",[10,203,204],{},"For SBOM purposes:",[82,206,207,214,220,223],{},[85,208,209,210,213],{},"List the upstream project as your component (e.g., ",[14,211,212],{},"FreeRTOS-Kernel",")",[85,215,216,217,213],{},"Record your fork's base version (e.g., ",[14,218,219],{},"v11.0.1",[85,221,222],{},"Add the patch commit hash or your internal version identifier",[85,224,225],{},"Mark it as a modified version",[10,227,228,229,232,233,236,237,240,241,244],{},"In SPDX: use the ",[14,230,231],{},"PackageComment"," field or the ",[14,234,235],{},"ExternalRef"," field to document the modification. In CycloneDX: use the ",[14,238,239],{},"pedigree"," element with ",[14,242,243],{},"patches"," to describe modifications.",[120,246,248],{"id":247},"_4-bootloaders-and-secure-boot-chains","4. Bootloaders and Secure Boot Chains",[10,250,251,252,256],{},"The full firmware image often includes components that aren't part of the main application firmware build: bootloaders (U-Boot, MCUboot, proprietary), secure boot verification code, trust anchor certificates, and provisioning tools. These need to be in your SBOM too. For details on which bootloader components to document and how secure boot chains work, see our ",[192,253,255],{"href":254},"/blog/cra-secure-boot-firmware-signing/","secure boot and firmware signing guide",".",[10,258,259,260,262],{},"For Yocto-based builds, the ",[14,261,16],{}," class captures most of this if configured correctly. For custom build systems, you may need to add these components manually.",[39,264,266],{"id":265},"spdx-vs-cyclonedx-which-to-choose","SPDX vs. CycloneDX: Which to Choose",[10,268,269],{},"Both formats are CRA-compliant. Choose based on your tooling ecosystem:",[271,272,273,289],"table",{},[274,275,276],"thead",{},[277,278,279,283,286],"tr",{},[280,281,282],"th",{},"Feature",[280,284,285],{},"SPDX (ISO/IEC 5962:2021)",[280,287,288],{},"CycloneDX (OWASP, v1.7+)",[290,291,292,304,315,326,337,352,379,393],"tbody",{},[277,293,294,298,301],{},[295,296,297],"td",{},"Standardisation",[295,299,300],{},"ISO/IEC international standard",[295,302,303],{},"OWASP community standard",[277,305,306,309,312],{},[295,307,308],{},"Formats",[295,310,311],{},"Tag-value, JSON, RDF, YAML, XML",[295,313,314],{},"JSON, XML, Protobuf",[277,316,317,320,323],{},[295,318,319],{},"Tooling ecosystem",[295,321,322],{},"Strong in Linux/Yocto world",[295,324,325],{},"Strong in DevSecOps/cloud world",[277,327,328,331,334],{},[295,329,330],{},"License expression",[295,332,333],{},"Rich SPDX license syntax",[295,335,336],{},"SPDX license identifiers supported",[277,338,339,342,345],{},[295,340,341],{},"VEX support",[295,343,344],{},"SPDX VEX (OpenVEX)",[295,346,347,348,351],{},"Native VEX via ",[14,349,350],{},"vulnerabilities"," component",[277,353,354,356,368],{},[295,355,99],{},[295,357,358,361,362,361,364,367],{},[14,359,360],{},"DESCRIBES",", ",[14,363,178],{},[14,365,366],{},"DEPENDS_ON",", etc.",[295,369,370,361,373,361,376],{},[14,371,372],{},"dependsOn",[14,374,375],{},"provides",[14,377,378],{},"runtimeDependency",[277,380,381,384,390],{},[295,382,383],{},"Yocto native support",[295,385,386,387,389],{},"Yes (",[14,388,16],{}," class)",[295,391,392],{},"Via external tools",[277,394,395,398,401],{},[295,396,397],{},"Vulnerability matching",[295,399,400],{},"Via PURL, CPE",[295,402,400],{},[10,404,405],{},[25,406,407],{},"Recommendation for embedded teams:",[82,409,410,413,416],{},[85,411,412],{},"If you're using Yocto: use SPDX (native support, lower effort)",[85,414,415],{},"If you need rich VEX integration for vulnerability triage: consider CycloneDX",[85,417,418],{},"If you need to consume SBOMs from your chipset vendors: match their format",[10,420,421],{},"You can generate both—SPDX as your authoritative SBOM for the technical documentation file, CycloneDX for integration with vulnerability scanning tools.",[39,423,425,426],{"id":424},"yocto-setting-up-create-spdx","Yocto: Setting Up ",[14,427,16],{},[10,429,430,431,433],{},"The Yocto Project's ",[14,432,16],{}," class (available since Kirkstone) generates SPDX 2.3 or SPDX 3.0 documents for your image. Here's a minimal configuration:",[435,436,441],"pre",{"className":437,"code":438,"language":439,"meta":440,"style":440},"language-bash shiki shiki-themes github-light github-dark","# In your local.conf or distro.conf\n\n# Enable SPDX generation\nINHERIT += \"create-spdx\"\n\n# Output format (spdx-json, spdx-tag-value, or spdx-rdf)\nSPDX_PRETTY = \"1\"\n\n# Include security metadata (CVE checkpoints)\nSPDX_INCLUDE_SOURCES = \"1\"\n\n# Namespace for your SBOM (use your company domain)\nSPDX_NAMESPACE_PREFIX = \"https://sbom.yourcompany.com\"\n\n# Optional: generate per-package SBOMs in addition to image-level\nSPDX_INCLUDE_PACKAGED = \"1\"\n","bash","",[14,442,443,452,459,465,479,484,490,502,507,513,523,528,534,545,550,556],{"__ignoreMap":440},[444,445,448],"span",{"class":446,"line":447},"line",1,[444,449,451],{"class":450},"sJ8bj","# In your local.conf or distro.conf\n",[444,453,455],{"class":446,"line":454},2,[444,456,458],{"emptyLinePlaceholder":457},true,"\n",[444,460,462],{"class":446,"line":461},3,[444,463,464],{"class":450},"# Enable SPDX generation\n",[444,466,468,472,476],{"class":446,"line":467},4,[444,469,471],{"class":470},"sScJk","INHERIT",[444,473,475],{"class":474},"sZZnC"," +=",[444,477,478],{"class":474}," \"create-spdx\"\n",[444,480,482],{"class":446,"line":481},5,[444,483,458],{"emptyLinePlaceholder":457},[444,485,487],{"class":446,"line":486},6,[444,488,489],{"class":450},"# Output format (spdx-json, spdx-tag-value, or spdx-rdf)\n",[444,491,493,496,499],{"class":446,"line":492},7,[444,494,495],{"class":470},"SPDX_PRETTY",[444,497,498],{"class":474}," =",[444,500,501],{"class":474}," \"1\"\n",[444,503,505],{"class":446,"line":504},8,[444,506,458],{"emptyLinePlaceholder":457},[444,508,510],{"class":446,"line":509},9,[444,511,512],{"class":450},"# Include security metadata (CVE checkpoints)\n",[444,514,516,519,521],{"class":446,"line":515},10,[444,517,518],{"class":470},"SPDX_INCLUDE_SOURCES",[444,520,498],{"class":474},[444,522,501],{"class":474},[444,524,526],{"class":446,"line":525},11,[444,527,458],{"emptyLinePlaceholder":457},[444,529,531],{"class":446,"line":530},12,[444,532,533],{"class":450},"# Namespace for your SBOM (use your company domain)\n",[444,535,537,540,542],{"class":446,"line":536},13,[444,538,539],{"class":470},"SPDX_NAMESPACE_PREFIX",[444,541,498],{"class":474},[444,543,544],{"class":474}," \"https://sbom.yourcompany.com\"\n",[444,546,548],{"class":446,"line":547},14,[444,549,458],{"emptyLinePlaceholder":457},[444,551,553],{"class":446,"line":552},15,[444,554,555],{"class":450},"# Optional: generate per-package SBOMs in addition to image-level\n",[444,557,559,562,564],{"class":446,"line":558},16,[444,560,561],{"class":470},"SPDX_INCLUDE_PACKAGED",[444,563,498],{"class":474},[444,565,501],{"class":474},[10,567,568],{},"After a full image build, Yocto outputs SPDX documents to:",[435,570,575],{"className":571,"code":573,"language":574},[572],"language-text","tmp/deploy/spdx/\u003Cmachine>/\u003Cimage>/\n","text",[14,576,573],{"__ignoreMap":440},[10,578,579,580,582],{},"The top-level SPDX document describes the full image and includes ",[14,581,360],{}," relationships to all packages. Each package document includes license information, source URIs, and checksums.",[10,584,585],{},[25,586,587,588,590],{},"What ",[14,589,16],{}," doesn't handle automatically:",[82,592,593,596,603],{},[85,594,595],{},"Binary blob components (you'll need to add these manually or via a custom bbclass)",[85,597,598,599,602],{},"Components fetched via custom ",[14,600,601],{},"do_fetch"," scripts outside of Yocto's standard fetchers",[85,604,605],{},"Bootloader components if they're built outside the Yocto image (e.g., a separate U-Boot build)",[10,607,608,609,611],{},"For binary blobs, write a custom bbclass that adds them as SPDX packages with ",[14,610,158],{}," for license fields:",[435,613,617],{"className":614,"code":615,"language":616,"meta":440,"style":440},"language-python shiki shiki-themes github-light github-dark","# In your custom bbclass (e.g., wifi-blob-spdx.bbclass)\npython do_spdx_wifi_blob() {\n    import json\n    spdx_pkg = {\n        \"SPDXID\": \"SPDXRef-wifi-firmware\",\n        \"name\": d.getVar(\"WIFI_BLOB_NAME\"),\n        \"version\": d.getVar(\"WIFI_BLOB_VERSION\"),\n        \"supplier\": \"Organization: \" + d.getVar(\"WIFI_BLOB_VENDOR\"),\n        \"downloadLocation\": \"NOASSERTION\",\n        \"filesAnalyzed\": False,\n        \"checksums\": [{\"algorithm\": \"SHA256\", \"checksumValue\": d.getVar(\"WIFI_BLOB_SHA256\")}],\n        \"licenseConcluded\": \"NOASSERTION\",\n        \"licenseDeclared\": \"NOASSERTION\",\n        \"copyrightText\": \"NOASSERTION\",\n    }\n    # Append to image SPDX document\n    # (implementation depends on your Yocto version and SPDX class structure)\n}\n","python",[14,618,619,624,629,634,639,644,649,654,659,664,669,674,679,684,689,694,699,705],{"__ignoreMap":440},[444,620,621],{"class":446,"line":447},[444,622,623],{},"# In your custom bbclass (e.g., wifi-blob-spdx.bbclass)\n",[444,625,626],{"class":446,"line":454},[444,627,628],{},"python do_spdx_wifi_blob() {\n",[444,630,631],{"class":446,"line":461},[444,632,633],{},"    import json\n",[444,635,636],{"class":446,"line":467},[444,637,638],{},"    spdx_pkg = {\n",[444,640,641],{"class":446,"line":481},[444,642,643],{},"        \"SPDXID\": \"SPDXRef-wifi-firmware\",\n",[444,645,646],{"class":446,"line":486},[444,647,648],{},"        \"name\": d.getVar(\"WIFI_BLOB_NAME\"),\n",[444,650,651],{"class":446,"line":492},[444,652,653],{},"        \"version\": d.getVar(\"WIFI_BLOB_VERSION\"),\n",[444,655,656],{"class":446,"line":504},[444,657,658],{},"        \"supplier\": \"Organization: \" + d.getVar(\"WIFI_BLOB_VENDOR\"),\n",[444,660,661],{"class":446,"line":509},[444,662,663],{},"        \"downloadLocation\": \"NOASSERTION\",\n",[444,665,666],{"class":446,"line":515},[444,667,668],{},"        \"filesAnalyzed\": False,\n",[444,670,671],{"class":446,"line":525},[444,672,673],{},"        \"checksums\": [{\"algorithm\": \"SHA256\", \"checksumValue\": d.getVar(\"WIFI_BLOB_SHA256\")}],\n",[444,675,676],{"class":446,"line":530},[444,677,678],{},"        \"licenseConcluded\": \"NOASSERTION\",\n",[444,680,681],{"class":446,"line":536},[444,682,683],{},"        \"licenseDeclared\": \"NOASSERTION\",\n",[444,685,686],{"class":446,"line":547},[444,687,688],{},"        \"copyrightText\": \"NOASSERTION\",\n",[444,690,691],{"class":446,"line":552},[444,692,693],{},"    }\n",[444,695,696],{"class":446,"line":558},[444,697,698],{},"    # Append to image SPDX document\n",[444,700,702],{"class":446,"line":701},17,[444,703,704],{},"    # (implementation depends on your Yocto version and SPDX class structure)\n",[444,706,708],{"class":446,"line":707},18,[444,709,710],{},"}\n",[39,712,714],{"id":713},"vex-reducing-triage-noise","VEX: Reducing Triage Noise",[10,716,717],{},"An SBOM listing all your firmware components will generate a large number of CVE matches — many of which aren't actually exploitable in your firmware configuration. Research on SBOM-based vulnerability scanning suggests that the vast majority of CVE matches against firmware SBOMs are false positives — studies have shown baseline false-positive rates as high as 95%+ for downstream vulnerability scanners. The most common false-positive categories are: CVEs affecting a feature you don't use, a code path you've compiled out, or a library version your vendor has backported a fix into.",[10,719,720,723],{},[25,721,722],{},"VEX (Vulnerability Exploitability eXchange)"," is a specification for expressing whether a known CVE is exploitable in a specific product configuration. A VEX document says, for each CVE matched against your SBOM:",[82,725,726,732,738,744],{},[85,727,728,731],{},[25,729,730],{},"Not Affected:"," The CVE affects this component, but not in the way your firmware uses it",[85,733,734,737],{},[25,735,736],{},"Affected:"," The CVE is exploitable in your firmware",[85,739,740,743],{},[25,741,742],{},"Fixed:"," The CVE was exploitable but has been addressed in the current version",[85,745,746,749],{},[25,747,748],{},"Under Investigation:"," Not yet determined",[10,751,752],{},"For CRA compliance, VEX serves two functions:",[754,755,756,762],"ol",{},[85,757,758,761],{},[25,759,760],{},"Internal triage:"," Filter your CVE matches to the ones that actually require action (Article 14 obligations only apply to actually-exploitable vulnerabilities)",[85,763,764,767],{},[25,765,766],{},"Customer communication:"," Share VEX documents with your customers so they can make accurate risk assessments without relying on raw CVE matches against your SBOM",[10,769,770],{},[25,771,772],{},"VEX formats:",[82,774,775,781,787],{},[85,776,777,780],{},[25,778,779],{},"OpenVEX:"," Simple JSON-LD format, good for standalone VEX documents",[85,782,783,786],{},[25,784,785],{},"SPDX VEX:"," Part of the SPDX 3.0 specification",[85,788,789,792,793,795],{},[25,790,791],{},"CycloneDX VEX:"," Embedded in CycloneDX documents via the ",[14,794,350],{}," element",[10,797,798],{},"For embedded product companies, the minimum viable VEX process:",[754,800,801,804,807,810],{},[85,802,803],{},"Run vulnerability scanning against your SBOM (using tools like Grype, OSV-Scanner, or ENISA's planned tooling)",[85,805,806],{},"For each CVE match with CVSS ≥ 7.0: triage whether it's actually exploitable in your firmware",[85,808,809],{},"Document the triage decision in a VEX document with justification",[85,811,812],{},"Update the VEX document with each new firmware release",[10,814,815],{},"The VEX process naturally feeds into your Article 14 obligation: if your triage shows a CVE is actively exploited and affects your firmware, the 24-hour notification clock starts.",[39,817,819],{"id":818},"pre-deadline-checklist","Pre-Deadline Checklist",[10,821,822],{},"Use this checklist to assess your current SBOM posture:",[10,824,825],{},[25,826,827],{},"Foundation",[82,829,832,841,847,853],{"className":830},[831],"contains-task-list",[85,833,836,840],{"className":834},[835],"task-list-item",[837,838],"input",{"disabled":457,"type":839},"checkbox"," Complete inventory of all components in your firmware image (open source, commercial, binary blobs)",[85,842,844,846],{"className":843},[835],[837,845],{"disabled":457,"type":839}," Version numbers recorded for all components including binary blobs (where available)",[85,848,850,852],{"className":849},[835],[837,851],{"disabled":457,"type":839}," Hash/checksum recorded for all binary blobs",[85,854,856,858],{"className":855},[835],[837,857],{"disabled":457,"type":839}," Supplier information recorded for all components",[10,860,861],{},[25,862,863],{},"Generation tooling",[82,865,867,873,879,885],{"className":866},[831],[85,868,870,872],{"className":869},[835],[837,871],{"disabled":457,"type":839}," Automated SBOM generation integrated into your build pipeline (not a one-time manual exercise)",[85,874,876,878],{"className":875},[835],[837,877],{"disabled":457,"type":839}," SBOM updated with every firmware release (not just when you remember to)",[85,880,882,884],{"className":881},[835],[837,883],{"disabled":457,"type":839}," SBOM format is SPDX or CycloneDX (not a custom spreadsheet or proprietary format)",[85,886,888,890],{"className":887},[835],[837,889],{"disabled":457,"type":839}," NTIA minimum elements present for all components",[10,892,893],{},[25,894,895],{},"Hard cases addressed",[82,897,899,908,914,920],{"className":898},[831],[85,900,902,904,905,907],{"className":901},[835],[837,903],{"disabled":457,"type":839}," Binary blobs documented with ",[14,906,158],{}," for unknown fields (not omitted)",[85,909,911,913],{"className":910},[835],[837,912],{"disabled":457,"type":839}," Vendor SDKs with upstream components documented at both levels",[85,915,917,919],{"className":916},[835],[837,918],{"disabled":457,"type":839}," RTOS forks documented with base version and modification indicator",[85,921,923,925],{"className":922},[835],[837,924],{"disabled":457,"type":839}," Bootloader and secure boot components included",[10,927,928],{},[25,929,930],{},"VEX integration",[82,932,934,940,946,952],{"className":933},[831],[85,935,937,939],{"className":936},[835],[837,938],{"disabled":457,"type":839}," CVE monitoring against SBOM running continuously",[85,941,943,945],{"className":942},[835],[837,944],{"disabled":457,"type":839}," VEX triage process defined and documented",[85,947,949,951],{"className":948},[835],[837,950],{"disabled":457,"type":839}," VEX documents generated and versioned alongside firmware releases",[85,953,955,957],{"className":954},[835],[837,956],{"disabled":457,"type":839}," VEX documents available to customers on request",[10,959,960],{},[25,961,962],{},"Compliance documentation",[82,964,966,972,978],{"className":965},[831],[85,967,969,971],{"className":968},[835],[837,970],{"disabled":457,"type":839}," SBOM included in technical documentation file (Annex VII)",[85,973,975,977],{"className":974},[835],[837,976],{"disabled":457,"type":839}," SBOM generation process documented as part of your security processes",[85,979,981,983],{"className":980},[835],[837,982],{"disabled":457,"type":839}," Retention process in place (10 years from product placement on market)",[10,985,986,987,989,990,994],{},"If you're working through Yocto's ",[14,988,16],{}," class and are dealing with binary blob gaps, the ",[192,991,993],{"href":992},"/","Stack Canary assessment tool"," will help identify which Annex I requirements remain open and prioritise your remediation work.",[996,997],"hr",{},[10,999,1000],{},[1001,1002,1003],"em",{},"Based on Regulation EU 2024/2847 Annex I Part II, NTIA Minimum Elements for a Software Bill of Materials (2021), Yocto Project documentation, SPDX ISO/IEC 5962:2021, and OWASP CycloneDX specification v1.7. ENISA SBOM guidance for the CRA (2025). This does not constitute legal advice.",[39,1005,1007],{"id":1006},"sources","Sources",[82,1009,1010,1018,1025,1032,1039,1046,1053,1060],{},[85,1011,1012],{},[192,1013,1017],{"href":1014,"rel":1015},"https://eur-lex.europa.eu/eli/reg/2024/2847/oj/eng",[1016],"nofollow","Regulation (EU) 2024/2847 — Cyber Resilience Act (full text)",[85,1019,1020],{},[192,1021,1024],{"href":1022,"rel":1023},"https://www.ntia.gov/sites/default/files/publications/sbom_minimum_elements_report_0.pdf",[1016],"NTIA — Minimum Elements for a Software Bill of Materials (2021)",[85,1026,1027],{},[192,1028,1031],{"href":1029,"rel":1030},"https://spdx.dev/specifications/",[1016],"SPDX Specification — ISO/IEC 5962:2021",[85,1033,1034],{},[192,1035,1038],{"href":1036,"rel":1037},"https://cyclonedx.org/specification/overview/",[1016],"OWASP CycloneDX Specification",[85,1040,1041],{},[192,1042,1045],{"href":1043,"rel":1044},"https://www.enisa.europa.eu/publications/cyber-resilience-act-requirements-standards-mapping",[1016],"ENISA — Cyber Resilience Act Requirements Standards Mapping",[85,1047,1048],{},[192,1049,1052],{"href":1050,"rel":1051},"https://docs.yoctoproject.org/dev/ref-manual/classes.html#create-spdx",[1016],"Yocto Project — create-spdx class documentation",[85,1054,1055],{},[192,1056,1059],{"href":1057,"rel":1058},"https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847",[1016],"CRA full text (HTML) — see Annex I Part II",[85,1061,1062],{},[192,1063,1066],{"href":1064,"rel":1065},"https://arxiv.org/pdf/2511.20313",[1016],"A Reality Check on SBOM-based Vulnerability Management (arXiv, 2025)",[1068,1069,1070],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":440,"searchDepth":454,"depth":454,"links":1072},[1073,1074,1080,1081,1083,1084,1085],{"id":41,"depth":454,"text":42},{"id":111,"depth":454,"text":112,"children":1075},[1076,1077,1078,1079],{"id":122,"depth":461,"text":123},{"id":165,"depth":461,"text":166},{"id":186,"depth":461,"text":187},{"id":247,"depth":461,"text":248},{"id":265,"depth":454,"text":266},{"id":424,"depth":454,"text":1082},"Yocto: Setting Up create-spdx",{"id":713,"depth":454,"text":714},{"id":818,"depth":454,"text":819},{"id":1006,"depth":454,"text":1007},"2025-12-18","Generate a CRA-compliant SBOM for embedded firmware: handle binary blobs, vendor SDKs, and RTOS forks that break standard SBOM tools.","md","/images/blog/previews/sbom-firmware.svg",[1091,1092,1093,1094,1095,1096,1097],"CRA SBOM","firmware SBOM","SPDX","CycloneDX","Yocto SBOM","CRA Annex I","software bill of materials",{},"/blog/cra-sbom-firmware","11 min",{"title":5,"description":1087},"blog/cra-sbom-firmware","kEsSR7UST9DSLykkIj-SYSeVz-J0ck80Ul_TPb9nBtE",1775939691377]