32blogby Studio Mitsu

Yocto SBOM and CVE Management: A Practical Guide for Scarthgap

Generate SBOMs with create-spdx and scan CVEs with cve-check in Yocto Scarthgap 5.0 LTS. Covers EU CRA compliance and production workflows.

by omitsu11 min read

This article contains affiliate links.

On this page

An SBOM (Software Bill of Materials) is an inventory listing every software component in a product, and it's becoming a legal requirement for embedded devices. The EU CRA mandates SBOM submission by 2027, the FDA already requires it for medical devices, and Yocto Scarthgap generates SBOMs out of the box.

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)

The CRA (Regulation EU 2024/2847) entered into force on December 10, 2024. Full requirements apply from December 11, 2027 (vulnerability reporting obligations start September 11, 2026). 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 type approval has been mandatory for new vehicle types since July 2022, and since July 2024 it applies to all newly manufactured vehicles including existing types.

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 led by CISA/NSA.

Generating SBOMs with create-spdx

The Yocto SBOM documentation covers the full create-spdx class. 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 in SPDX 2.2 format.

Check if it's enabled

bash
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.

bash
# conf/local.conf — manually enable create-spdx (usually unnecessary)
INHERIT += "create-spdx"
bash
# 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.

bash
# 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.

bash
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.

bash
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).

json
{
  "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. The Yocto vulnerability checking documentation covers the full configuration. Unlike create-spdx, it must be enabled manually.

Enable it

bash
# conf/local.conf — enable CVE checking
INHERIT += "cve-check"

Check a specific recipe

bash
bitbake -c cve_check openssl

Check the entire image

With INHERIT += "cve-check" set, a normal build generates CVE reports automatically.

bash
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.

bash
# 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.

bash
# 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.

bash
# 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.

bash
# 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.

bash
# 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

bash
# 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

#RequirementHow Yocto Handles It
1Machine-readable SBOMcreate-spdx (enabled by default)
2Known vulnerability scanningcve-check
3Vulnerability response documentationCVE_STATUS with reasons
4Regular SBOM updatesRegenerate SBOM with each release build
5Authority submission readinessArchive .spdx.tar.zst as build artifacts

FAQ

What's the difference between SPDX and CycloneDX?

Both are machine-readable SBOM formats. SPDX (ISO/IEC 5962:2021) originated from the Linux Foundation with a focus on license compliance. CycloneDX originated from OWASP with a focus on security and vulnerability tracking. Yocto's create-spdx generates SPDX format. Both formats are accepted by the EU CRA and FDA. If your downstream tooling or customers don't have a preference, SPDX is the natural choice for Yocto projects since it's generated natively.

Does cve-check require an internet connection?

Yes, for the initial NVD database download and subsequent updates. The NIST NVD API is rate-limited, so the first fetch can take 10-30 minutes. After that, only incremental updates are downloaded. For air-gapped environments, you can pre-fetch the NVD data and point cve-check to a local mirror.

How often should I regenerate SBOMs?

At minimum, regenerate with every release build. For products under EU CRA scope, you need to maintain "up-to-date" SBOMs, which means regenerating whenever you update a component. A practical approach: generate as part of your CI/CD pipeline on every release branch build, and archive alongside the image artifacts.

Can I use cve-check in CI without full builds?

Yes. Use bitbake -c cve_check <recipe> to scan individual recipes without building the full image. This is useful for quick checks in merge request pipelines. However, for comprehensive reports, the image-level scan (bitbake <image> with cve-check inherited) gives the complete picture.

What does CVE_STATUS "cpe-incorrect" mean?

CPE (Common Platform Enumeration) is how the NVD identifies affected software. Sometimes the NVD entry's CPE matches your package name but refers to a completely different software. For example, a CVE for "python-json" might match your custom json recipe but actually targets a different project. cpe-incorrect marks these false positives.

How do I handle CVEs in the Linux kernel?

The kernel is the noisiest source of CVE reports — thousands of entries, most irrelevant to your configuration. Start with Poky's cve-extra-exclusions.inc for known non-applicable entries. Then use CVE_STATUS_GROUPS to batch-exclude CVEs that only affect subsystems you've disabled. Document each exclusion with your kernel config rationale. The Yocto vulnerability docs have kernel-specific guidance.

Is SBOM generation adding significant build time?

Minimal. The create-spdx class runs as a post-processing step and adds only seconds to the build. It doesn't re-compile anything — it collects metadata that BitBake already tracks. cve-check has a larger initial cost (NVD download), but subsequent builds add only a few minutes for the database diff and scan.

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.

TaskMethodEffort
SBOM generationcreate-spdx (enabled by default)Near zero
CVE scanningINHERIT += "cve-check"One line in conf/local.conf
Noise filteringCVE_STATUS + documented reasonsOne-time setup effort
OperationsArchive SBOM + CVE report per buildAutomate 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.

Related articles: