CI integration
One step in your pipeline. Every build publishes an SBOM, and the build fails if a component is on the CISA KEV catalog.
GitHub Actions
- uses: crakit/sbom-action@v1
with:
token: ${{ secrets.CRAKIT_TOKEN }}
product: ${{ vars.CRAKIT_PRODUCT }}
release: ${{ github.ref_name }}
fail-on-kev: trueThe build system is detected from the tree. Pass build-system if you want to be explicit, and dir if the build is not at the repository root.
GitLab CI
include:
- remote: 'https://cra.synthworx.com/integrations/crakit.gitlab-ci.yml'
crakit-sbom:
extends: .crakit-sbom
variables:
CRAKIT_DIR: "build"Set CRAKIT_TOKEN and CRAKIT_PRODUCT as masked CI/CD variables. The token is a credential. Mask it and keep it out of the file.
Anything else
The script is one file with no dependencies. A CI step that pulls a dependency tree into a firmware build is a supply-chain problem inside a supply-chain tool, so it does not.
curl -fsSL https://cra.synthworx.com/integrations/crakit-sbom.mjs -o crakit-sbom.mjs
node crakit-sbom.mjs --product $PRODUCT --release 2.4.1 --fail-on-kev
# or write the SBOM to a file and do what you like with it
node crakit-sbom.mjs --build-system yocto --dir ./build --out sbom.jsonWhat it reads
| Build system | File | What comes out |
|---|---|---|
| Zephyr | west.yml | Every project in the manifest with its pinned revision, plus the kernel version from the VERSION file. |
| Yocto | license.manifest | Every recipe that went into the image, with its version and declared licence. This is the richest of the six. |
| Buildroot | .config and package/*.mk | Every selected BR2_PACKAGE, with the version from .config or the package makefile. |
| PlatformIO | platformio.ini | lib_deps entries with their pinned versions, plus the platform and framework. |
| ESP-IDF | dependencies.lock | Every managed component with its version, plus the IDF version itself. |
| STM32Cube | the .ioc project file | The firmware package and version, the HAL family, the MCU part number and the CubeMX version. |
Where a build system records no version for a dependency, the component is published with the version unknown. It is never guessed. Same rule as the binary path.
Failing the build
With --fail-on-kev, the step exits non-zero when a component in the build is on the CISA Known Exploited Vulnerabilities catalog. KEV membership means somebody is exploiting it against real systems, which is the Article 14(1) trigger.
It does not fail on ordinary CVEs. A gate that fires on every new advisory gets disabled in a week, and then it is not there for the one that mattered.
Merging with a binary scan
If you also upload the shipped image, the two lists merge into one canonical SBOM per release. The build manifest wins on versions, because it is ground truth. The image contributes everything the build never saw, which is exactly the ODM blobs you are exposed on. The merge records which side saw what.
The integrations wrap the public API and nothing else. Anything they do, you can do with a key and curl.