⚠️ Before proceeding to read the documentation, please verify that you are on the correct branch for your Yocto release, as the feature set and default configurations may vary!
meta-cyclonedx is a Yocto meta-layer which
produces CycloneDX Software Bill of Materials
(aka SBOMs) from your target root filesystem.
This layer generates CycloneDX compliant SBOMs with the following features:
- Currently, support for CycloneDX specification 1.6 and 1.4
- Support for multiple supported Yocto (LTS) releases.
- Improved package matching against the NIST NVD by fixing CPE generation process.
- Included purl package URLs.
- Added generation of an additional CycloneDX VEX file which contains information on patched and ignored CVEs from within the OpenEmbedded build system.
- Component scopes to differentiate between runtime (
required) and build-time (optional) dependencies, enabling per-use-case SBOM filtering. - Include component licenses.
- Added option to reduce the SBOM size by limiting SBOM collection to run-time packages (which might potentially come at some expense)
This repository was originally forked from the BG Networks repository.
To install this meta-layer simply clone the repository into the sources
directory, check out the corresponding branch for your Yocto release
(e.g. scarthgap, kirkstone, ...)
and add it to your build/conf/bblayers.conf file:
cd sources
git clone https://github.com/iris-GmbH/meta-cyclonedx.git
cd meta-cyclonedx
git checkout <YOCTO_RELEASE>and in your bblayers.conf file:
BBLAYERS += "${BSPDIR}/sources/meta-cyclonedx"To enable and configure the layer simply inherit the cyclonedx-export class
in your local.conf file:
INHERIT += "cyclonedx-export"By default, meta-cyclonedx generates CycloneDX 1.6 format SBOMs. If you need compatibility with tools that only support CycloneDX 1.4, you can configure:
CYCLONEDX_SPEC_VERSION = "1.4"Version differences:
- 1.4: Legacy format for compatibility with older tools
- 1.6: Modern format with enhanced metadata and timestamps (default)
By default, meta-cyclonedx will only include run-time packages in the SBOM, which drastically reduces the number of potentially irrelevant packages. However, this can lead to valid packages being omitted from the SBOM (see here).
If preferred, you can add the following configuration setting (e.g in your local.conf), which will cause meta-cyclonedx to include all build-time packages as well:
CYCLONEDX_RUNTIME_PACKAGES_ONLY = "0"When including both runtime and build-time packages, meta-cyclonedx uses CycloneDX component scopes to differentiate between them:
- Runtime packages are marked with
"scope": "required" - Build-time only packages are marked with
"scope": "optional"
This allows tools to filter components based on their use case:
- CVE matching: Focus on components with
"scope": "required" - License compliance: Include all components regardless of scope
- Supply chain tracking: Include all components regardless of scope
Component scopes are enabled by default and available in both CycloneDX 1.4 and 1.6 specifications. If you need to disable them (e.g., for compatibility with certain SBOM profiles or tools):
CYCLONEDX_ADD_COMPONENT_SCOPES = "0"By default, vulnerability analysis records include firstIssued and lastUpdated
timestamps when using CycloneDX 1.6. To generate minimal VEX documents without timestamps:
CYCLONEDX_ADD_VULN_TIMESTAMPS = "0"By default, component licenses are included in the SBOM.
You may choose to exclude license information from your SBOM:
CYCLONEDX_ADD_COMPONENT_LICENSES = "0"The licenses data is taken from the component recipe (see LICENSE. Single licenses are matched against a list of known SPDX licenses where possible.
If multiple licenses are specified using & or |, the license is converted
into a SPDX license expression.
Additionally, simple expressions (only containing "AND" operators) are split into multiple license entries by default, improving the SBOM data quality. Note however, that this might not be supported by your SBOM consuming tool of choice (e.g. DependencyTrack).
To disable this feature you can set
CYCLONEDX_SPLIT_LICENSE_EXPRESSIONS = "0"# Specification version (default: "1.6")
CYCLONEDX_SPEC_VERSION = "1.6" # or "1.4"
# Include build-time packages (default: "1" = runtime only)
CYCLONEDX_RUNTIME_PACKAGES_ONLY = "1"
# Add component scopes (default: "1")
CYCLONEDX_ADD_COMPONENT_SCOPES = "1"
# Add vulnerability timestamps in 1.6 (default: "1")
CYCLONEDX_ADD_VULN_TIMESTAMPS = "1"
# Add component licenses (default: "1")
CYCLONEDX_ADD_COMPONENT_LICENSES = "1"
# split license expressions into multiple license entries
# when possible (default: "1")
CYCLONEDX_SPLIT_LICENSE_EXPRESSIONS = "1"Once everything is configured simply build your image as you normally would.
By default the final CycloneDX SBOMs are saved to the folder
${DEPLOY_DIR}/${PN}/cyclonedx-export as bom.json and vex.json
respectively.
While this layer does not offer a direct integration with DependencyTrack (we consider that a feature, since it removes dependencies to external infrastructure in your build), it is perfectly possible to use the produced SBOMs within DependencyTrack.
At the time of writing DependencyTrack does not support uploading component
and vulnerability information in one go (which is why we currently create a
separate vex.json file). The status on supporting this may be tracked
here.
- Go into an existing project in your DependencyTrack instance or create a new one.
- Go to the Components tab and click Upload BOM.
- Select the
bom.jsonfile from your deploy directory. - Wait for the vulnerability analysis to complete.
- Go to the Audit Vulnerabilities tab and click Apply VEX.
- Select the
vex.jsonfile from your deploy directory.
You may want to script the upload of the SBOM files to DependencyTrack, e.g. as part of a CI job that runs after your build is complete.
This is possible by leveraging DependencyTracks REST API.
At the time of writing this can be done by leveraging the following API endpoints:
/v1/bomfor uploading thebom.json./v1/event/token/{uuid}for checking the status on thebom.jsonprocessing./v1/vexfor uploading thevex.json.
Please refer to DependencyTracks REST API documentation regarding the usage of these endpoints as well as the required token permissions.
In the future we might include an example script in this repository.
We use the image_list_installed_packages function from upstream
OpenEmbedded as a means to reduce the SBOM contents to packages that are added
to the final resulting rootfs. This drastically reduces the "noise" generated
by CVEs in build-time dependencies. This however comes with some potential
downsides (i.e. Missing some packages), as discussed
here.
OpenEmbedded and its core mechanisms work best with "traditional" programming languages such as C and C++, as these are the languages that they were initially designed around. For instance, a core-assumption prevalent in many OE mechanisms (including those we depend on in meta-cyclonedx) is that each library is described in its own OE recipe. This however does not work well with many modern programming languages, which often come with their own package managers (e.g. NPM, Cargo, Go Modules, ...), which do not necessarily integrate well into the OpenEmbedded ecosystem and depend of potentially hundreds of external dependencies (good luck writing a separate OE recipe for each dependency in a small-medium sized Node.js project).
Thus, if you rely on packages written in programming languages that come with their own package managers, you might be better off with a divide and conquer approach for covering their packages as well (your mileage may vary):
- Use this meta-layer to generate a CycloneDX SBOM which covers your OE-based operating system, system libraries, etc.
- Use tools designed explicitly for generating CycloneDX SBOMs for these languages (e.g. Rust, NPM, Golang, ...)
- Optionally, use some glue code to merge the SBOMs together (cyclonedx-cli offers merge functionality)