# Yocto Security & CRA Compliance Checklist

> What to verify in a Yocto build before it ships as a regulated product.

Source page: https://mnpsystems.ca/resources/yocto-security-compliance-checklist/
PDF: https://mnpsystems.ca/downloads/yocto-security-compliance-checklist.pdf (free, no email required)

MNP Systems is an engineering consultancy based in Brampton, Ontario, Canada, that takes work from anywhere: remotely, or on site where a job needs hands on the equipment.
Contact: https://mnpsystems.ca/contact/ or contact@mnpsystems.ca.

The EU Cyber Resilience Act treats an unpatched embedded Linux image as a defect in the product, not a detail of the build. This checklist is what to verify in a Yocto build before it ships.

It assumes you already have a build that boots. Nothing here is about getting Yocto working — it is about the gap between "the image boots" and "the image is defensible."

Work through it once per release candidate, not once per project. The answers change every time a layer moves.

## 1. Bill of materials

- [ ] `create-spdx` (or `create-spdx-2.2`) is in `INHERIT`, and the SPDX output lands in `tmp/deploy/spdx/` on every image build
- [ ] The SBOM is archived as a release artefact alongside the image, not left in `tmp/`
- [ ] Every recipe in the image resolves to a real `LICENSE` field — no `CLOSED` placeholders on third-party code
- [ ] `INCOMPATIBLE_LICENSE` is set for the licences your legal position forbids, so a GPL-3.0 recipe fails the build rather than shipping
- [ ] The SBOM lists the kernel and bootloader, not only userspace packages

## 2. Known vulnerabilities

- [ ] `cve-check` is in `INHERIT` and the build emits a CVE report per image
- [ ] The CVE database is refreshed in CI rather than reused from a stale download cache (`DL_DIR/CVE_CHECK`)
- [ ] Every unpatched CVE above your severity threshold has a written disposition: patched, not applicable, or accepted with a reason
- [ ] `CVE_STATUS` entries carry a justification string, so an ignored CVE explains itself to an auditor
- [ ] A CVE report regression fails the pipeline instead of being reviewed by hand

## 3. Attack surface

- [ ] The image is built from a minimal base, not `core-image-full-cmdline` or a vendor demo image
- [ ] `IMAGE_FEATURES` contains no `debug-tweaks`, `tools-debug`, `dbg-pkgs`, or `empty-root-password` in production
- [ ] The serial console is disabled, or gated behind authentication, in production images
- [ ] No development SSH keys, `.netrc`, build-host paths, or CI tokens survive into the rootfs
- [ ] The package list has been read end to end at least once — every package is there because something needs it

## 4. Kernel and boot

- [ ] The kernel `defconfig` is under version control in your own layer, not inherited unseen from the BSP
- [ ] Secure boot is enabled and the chain terminates in a key you control, not a vendor test key
- [ ] The kernel and bootloader track a maintained upstream branch with a known patch cadence, and the recipe pins `SRCREV` to a specific commit on it
- [ ] `PREEMPT_RT` is applied only where a control loop needs it, and the resulting latency has been measured rather than assumed
- [ ] Boot time is measured on the production image, so a regression shows up before a customer finds it

## 5. Root filesystem integrity

- [ ] The root filesystem is read-only, with writable state confined to a named overlay or data partition
- [ ] `dm-verity` or an equivalent protects the root filesystem from offline modification
- [ ] Persistent secrets live in encrypted storage (LUKS, or the SoC's secure element), never in the image
- [ ] Log and cache directories are on a volume that can fill up without bricking the device

## 6. Updates in the field

- [ ] An A/B or dual-bank update mechanism is in place — RAUC, Mender, or swupdate — with a proven rollback
- [ ] Update artefacts are signed, and the device verifies the signature before applying
- [ ] Rollback has been exercised on real hardware, including a power cut mid-write
- [ ] There is a documented path from "CVE published" to "fleet updated," with an owner and a target window
- [ ] The device reports its running image version somewhere you can query across the fleet

## 7. Reproducibility

- [ ] Every layer is pinned to a commit SHA in the manifest, not to a branch name
- [ ] A clean build from a fresh sstate cache produces the same image the release did
- [ ] The build runs in a container with a pinned base, so the host toolchain is not part of the result
- [ ] The exact build inputs — manifest, local.conf, layer SHAs — are archived with the release artefact

## Where this usually goes wrong

Three items on this list account for most of the failures we see.

**`cve-check` enabled but never read.** The report generates, nobody owns it, and the count climbs release over release. A report without a disposition step is decoration.

**Layers pinned to branches.** The build is reproducible until the day it is not, and the day it is not is usually the day you need to rebuild a shipped release to patch it.

**Rollback never tested.** Signed updates and A/B partitions are straightforward to configure and easy to get subtly wrong. The first real test should not be a field failure.

## When this needs outside help

Most teams can work through this list themselves. It is worth bringing in help when the answers are expensive rather than unknown:

- The BSP is a vendor drop with no upstream, so pinning and patching mean taking on maintenance of a kernel fork
- You need a CRA conformity argument, not just a clean CVE report, and someone has to write the technical documentation an assessor will read
- Boot time or latency targets are not being met and the fix requires kernel and device-tree work rather than configuration

If any of those describe where you are, we do this work: **contact@mnpsystems.ca**.
