SBOM is everywhere in embedded Linux discussions now. The EU's new regulation mandates SBOM submission by 2027. The FDA already requires it for medical devices.
This guide walks through generating SBOMs and scanning for CVE vulnerabilities using Yocto Scarthgap 5.0 LTS (BitBake 2.8) built-in tools. From the regulatory background to practical configuration and production workflows — everything in one article.
Why SBOM Is Becoming Mandatory for Embedded
An SBOM (Software Bill of Materials) is an inventory of all software components in a product. Which libraries are included, what versions they are, and what licenses they use — recorded in a machine-readable format.
You might think "we're too small for this to matter." But if you ship products to the EU, US, or automotive markets, your team is already affected.
EU Cyber Resilience Act (CRA)
Entered into force in December 2024. Full requirements apply from December 11, 2027. It covers every business that manufactures, sells, or imports products with digital elements into the EU market. Japanese companies exporting to the EU are included.
Specific SBOM requirements:
- Create and maintain SBOMs in machine-readable format (SPDX / CycloneDX)
- Must be available for market surveillance authorities on request
- Penalties up to €15 million or 2.5% of global annual revenue
Medical Devices (FDA)
The US FDA has required SBOM submission since March 2023 under FD&C Act Section 524B (PATCH Act). SBOMs are mandatory for premarket submissions. The FDA can refuse to accept applications without one.
Automotive (UN-R155 / R156)
UN-R155 doesn't explicitly mandate SBOMs. However, operating a Cyber Security Management System (CSMS) requires knowing your software composition, and vulnerability management doesn't work without it. In Japan, UN-R155 compliance has been mandatory for all new vehicle types since July 2024.
Japan
No legal mandate yet. However, METI (Ministry of Economy, Trade and Industry) has published the "Guide to Introducing SBOMs" (version 2.0, August 2024), widely seen as groundwork for future legislation. In September 2025, Japan co-signed a 15-nation international SBOM guidance document.
Generating SBOMs with create-spdx
In Scarthgap, the default Poky distro configuration (poky.conf) includes INHERIT += "create-spdx", so it's enabled by default. A normal build automatically generates SBOMs.
Check if it's enabled
bitbake-getvar -r core-image-minimal INHERIT
If the output includes create-spdx, you're set. If not, add the following to conf/local.conf.
# conf/local.conf — manually enable create-spdx (usually unnecessary)
INHERIT += "create-spdx"
Recommended settings
# conf/local.conf — recommended SBOM settings
SPDX_PRETTY = "1"
SPDX_PRETTY adds indentation to the JSON output, making it human-readable. File size increases, but it's worth it for debugging and review.
If you need source code traceability, add these as well.
# conf/local.conf — include source info in SBOM (optional)
SPDX_INCLUDE_SOURCES = "1"
SPDX_ARCHIVE_SOURCES = "1"
Build and check the output
Run a normal build.
bitbake core-image-minimal
After the build completes, the SBOM is at:
tmp/deploy/images/<MACHINE>/core-image-minimal-<MACHINE>.spdx.tar.zst
Extract and inspect the contents.
cd tmp/deploy/images/qemux86-64/
mkdir spdx-output && cd spdx-output
tar -I zstd -xf ../core-image-minimal-qemux86-64.spdx.tar.zst
ls *.spdx.json | head -10
Understanding the SBOM Output
After extraction, you'll find one SPDX file per recipe. Each file is in SPDX 2.2 JSON format.
File structure
spdx-output/
├── recipe-core-image-minimal.spdx.json ← entire image
├── recipe-busybox.spdx.json ← individual recipe
├── recipe-glibc.spdx.json
├── recipe-openssl.spdx.json
└── ...
Key JSON fields
Opening an individual SPDX file shows a structure like this (simplified).
{
"spdxVersion": "SPDX-2.2",
"dataLicense": "CC0-1.0",
"name": "recipe-openssl",
"packages": [
{
"name": "openssl",
"versionInfo": "3.2.1",
"supplier": "Organization: OpenSSL",
"downloadLocation": "https://www.openssl.org/source/openssl-3.2.1.tar.gz",
"licenseConcluded": "Apache-2.0",
"licenseDeclared": "Apache-2.0"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-Recipe",
"relationshipType": "GENERATES",
"relatedSpdxElement": "SPDXRef-Package-openssl"
}
]
}
The key fields for compliance:
- packages: Package name, version, license, and download location
- relationships: Dependency chain between recipes and packages
- licenseConcluded / licenseDeclared: License information (always reviewed in audits)
Scanning Vulnerabilities with cve-check
The cve-check class cross-references built packages against the NIST NVD (National Vulnerability Database) to detect known vulnerabilities. Unlike create-spdx, it must be enabled manually.
Enable it
# conf/local.conf — enable CVE checking
INHERIT += "cve-check"
Check a specific recipe
bitbake -c cve_check openssl
Check the entire image
With INHERIT += "cve-check" set, a normal build generates CVE reports automatically.
bitbake core-image-minimal
Report locations
tmp/deploy/cve/
├── openssl ← per-recipe text
├── openssl_cve.json ← per-recipe JSON
└── ...
tmp/deploy/images/<MACHINE>/
├── core-image-minimal-<MACHINE>.cve ← image-wide text
└── core-image-minimal-<MACHINE>.cve.json ← image-wide JSON
Both text and JSON formats are generated by default.
Reading the report
The text report lists each CVE with its status.
PACKAGE NAME: openssl
PACKAGE VERSION: 3.2.1
CVE: CVE-2024-XXXXX
CVE STATUS: Patched
CVE SUMMARY: ...
CVE: CVE-2024-YYYYY
CVE STATUS: Unpatched
CVE SUMMARY: ...
There are three status values:
- Patched: Fixed by a Yocto patch
- Unpatched: Not fixed. Needs attention
- Ignored: Intentionally skipped (set via
CVE_STATUS)
Filtering CVE Report Noise
Enabling CVE checks can flood you with results. The Linux kernel alone has thousands of registered CVEs, and the vast majority don't apply to your specific architecture or kernel configuration.
Mark individual CVEs with CVE_STATUS
When a specific CVE doesn't apply to your platform, exclude it with CVE_STATUS.
# In a recipe or bbappend
CVE_STATUS[CVE-2024-12345] = "not-applicable-platform: Only affects Windows"
CVE_STATUS[CVE-2024-67890] = "not-applicable-config: Feature disabled in our config"
Status values are predefined in cve-check-map.conf. Common ones include not-applicable-platform, not-applicable-config, cpe-incorrect, and upstream-wontfix. The text after the colon documents the specific reason, making exclusions auditable. You can manage these settings in a bbappend without modifying the original recipe. If migrating from Kirkstone, you'll need to rewrite CVE_CHECK_IGNORE to CVE_STATUS — see the migration checklist.
Batch-manage with CVE_STATUS_GROUPS
When many CVEs share the same exclusion reason, group them for easier management.
# conf/local.conf or a custom inc file
CVE_STATUS_GROUPS = "CVE_STATUS_WIN CVE_STATUS_ARM32"
CVE_STATUS_WIN = "CVE-2024-11111 CVE-2024-22222 CVE-2024-33333"
CVE_STATUS_WIN[status] = "not-applicable-platform: Windows-only vulnerability"
CVE_STATUS_ARM32 = "CVE-2024-44444 CVE-2024-55555"
CVE_STATUS_ARM32[status] = "not-applicable-platform: ARM32-only, we target ARM64"
Limit scan scope by layer
Useful when you only want to check your own layer.
# conf/local.conf — check only your layer
CVE_CHECK_LAYER_INCLUDELIST = "meta-mylayer"
# or exclude specific layers
CVE_CHECK_LAYER_EXCLUDELIST = "meta-poky"
Use the official exclusion list
Poky ships with a pre-defined CVE exclusion list.
# conf/local.conf
include conf/distro/include/cve-extra-exclusions.inc
Production Workflow
Here's how to integrate SBOM and CVE checking into your daily build flow.
Recommended conf/local.conf
# conf/local.conf — SBOM + CVE recommended settings
# SBOM generation (enabled by default, but make it explicit)
INHERIT += "create-spdx"
SPDX_PRETTY = "1"
# CVE checking
INHERIT += "cve-check"
# Official CVE exclusion list
include conf/distro/include/cve-extra-exclusions.inc
Per-build flow
1. bitbake <image>
↓ automatic
2. SBOM generated → tmp/deploy/images/MACHINE/IMAGE.spdx.tar.zst
↓ automatic
3. CVE report generated → tmp/deploy/images/MACHINE/IMAGE.cve
↓
4. Review Unpatched CVEs
↓
5. Apply patches or exclude with CVE_STATUS (with documented reasons)
↓
6. Archive SBOM + CVE report in version control
Extract Unpatched CVEs
# Extract Unpatched entries from the text report
grep -B 3 "CVE STATUS: Unpatched" \
tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.cve
For debugging build issues that arise from patching CVEs, the build error debugging guide covers devshell and log analysis techniques.
EU CRA Compliance Checklist
| # | Requirement | How Yocto Handles It |
|---|---|---|
| 1 | Machine-readable SBOM | create-spdx (enabled by default) |
| 2 | Known vulnerability scanning | cve-check |
| 3 | Vulnerability response documentation | CVE_STATUS with reasons |
| 4 | Regular SBOM updates | Regenerate SBOM with each release build |
| 5 | Authority submission readiness | Archive .spdx.tar.zst as build artifacts |
Wrapping Up
SBOM and CVE management is no longer a "nice to have." The EU CRA deadline in 2027, the existing FDA mandate, the practical requirements of UN-R155 — regulation is closing in.
The good news: compliance with Yocto Scarthgap is simpler than you might think.
| Task | Method | Effort |
|---|---|---|
| SBOM generation | create-spdx (enabled by default) | Near zero |
| CVE scanning | INHERIT += "cve-check" | One line in conf/local.conf |
| Noise filtering | CVE_STATUS + documented reasons | One-time setup effort |
| Operations | Archive SBOM + CVE report per build | Automate via CI/CD |
SBOMs are already being generated by default. CVE checking takes one line to enable. The first step toward regulatory compliance is easier than you think.