Aller au contenu principal
euvex

Le SBOM d'une chaîne de firmware, lisible par euvex

euvex lit un SBOM CycloneDX en JSON, ou un SBOM SPDX. Yocto écrit du SPDX par défaut, Buildroot du CycloneDX avec un script qu'il livre, Zephyr du SPDX avec west spdx. Les trois chemins, puis ce qu'euvex fait du fichier, sans rien promettre de plus.

Ce qu'euvex lit

CycloneDX JSON 1.2 à 1.6 · SPDX 2.2, 2.3 et 3.0

Le format.

CycloneDX en JSON, versions 1.2 à 1.6, dont le champ bomFormat vaut CycloneDX. Ou SPDX : 2.2 et 2.3 en JSON ou en tag-value, 3.0 en JSON-LD. Le nom du fichier importe peu : euvex reconnaît le contenu. Quatre mégaoctets au plus.

Pas lu.

Le CycloneDX en XML, et l'archive .spdx.tar.zst de Yocto 5.0. Le dépôt les refuse avec un message, il ne les convertit pas.

En SPDX.

Le produit est le paquet que le document dit décrire (en SPDX 3.0, son élément racine). Chaque autre paquet devient un composant, avec son purl, ses CPE, sa licence et son empreinte ; les fichiers ne sont pas repris. Plusieurs documents tag-value mis bout à bout se lisent comme un seul, le premier décrivant le produit. Ce qui suit sur CycloneDX vaut pour le reste.

Le produit.

Le nom et la version de metadata.component deviennent ceux du scan. Si la chaîne y met son propre nom, c'est lui que vous verrez.

Chaque composant.

Son nom (avec son group), sa version, son purl, son cpe, ses licences, son empreinte la plus forte (SHA-512, puis SHA-256, puis SHA-1) et son scope. Les composants imbriqués sont mis à plat.

Le scope.

excluded veut dire « non distribué avec le produit » : le composant sort des règles CRA, de la détection art. 14 et du décompte de couverture. required et optional sont tous deux comptés comme distribués, et un scope absent aussi : c'est la lecture prudente.

Ignoré.

La section vulnerabilities du fichier, donc un VEX ou les CVE que votre chaîne marque comme corrigées. euvex cherche les vulnérabilités lui-même, à sa date : une CVE corrigée par un correctif de votre chaîne peut donc apparaître, et se traite comme une alerte.

Ce que deviennent les composants d'un firmware

purl, CPE, version

Au dépôt, chaque composant est cherché dans OSV par son purl. OSV n'indexe pas pkg:generic, l'identifiant que ces chaînes écrivent le plus souvent, ni un composant sans purl. Ces composants ressortent « non analysé », et le rapport ne conclut pas à l'absence de vulnérabilité tant qu'il en reste un.

Pendant la surveillance programmée, euvex interroge la NVD pour ces composants-là : par leur cpe quand le SBOM en porte un, sinon par leur nom et leur version exacte, avec un éditeur inconnu. Un composant sans CPE ni version reste « non analysé », jamais compté comme sain.

La NVD répond un composant à la fois, et euvex borne le temps qu'il lui consacre à chaque revérification, une trentaine de composants environ. Tant qu'un composant n'a jamais été demandé, le scan revient à chaque passe, toutes les dix minutes, pendant les quarante-huit heures qui suivent le dépôt : un firmware de trois cents composants est couvert en deux heures environ, puis revu chaque jour. Le rapport affiche la couverture « N sur M » en attendant, et les alertes par email partent en une fois, à la fin.

Le réglage qui change le plus

Un cpe exact sur chaque composant. C'est lui que la NVD comprend, et c'est le vôtre qui est interrogé, plutôt qu'un nom rapproché. Si votre chaîne sait écrire des purl de distribution (pkg:deb, pkg:rpm), OSV les comprend dès le dépôt. Le détail, et ce qui a été mesuré.

Depuis Yocto

SPDX 3.0 par défaut · meta-cyclonedx sur la 5.0

Yocto écrit par défaut un SBOM au format SPDX, par la classe create-spdx que INHERIT_DISTRO hérite.

Yocto 5.1 et suivants. C'est du SPDX 3.0, en un seul fichier à côté de l'image, dans ${DEPLOY_DIR_IMAGE}, par exemple core-image-minimal-qemux86-64.rootfs.spdx.json. Déposez-le tel quel : le rootfs devient le produit, les paquets installés dans l'image les composants, avec les CPE que Yocto tire de CVE_PRODUCT. Les sources téléchargées et les recettes ne sont pas reprises.

Yocto 5.0 (scarthgap). La sortie par défaut est une archive .spdx.tar.zst, un document par paquet, qu'euvex ne lit pas. La classe create-spdx-3.0 y existe en option depuis yocto-5.0.14 ; sinon, pour du CycloneDX, il faut une couche de plus : meta-cyclonedx, maintenue hors du Yocto Project, avec une branche par version de Yocto (kirkstone, scarthgap et les suivantes).

terminal, dans le dossier des couches
git clone https://github.com/iris-GmbH/meta-cyclonedx.git
cd meta-cyclonedx
git checkout scarthgap
conf/bblayers.conf
BBLAYERS += "${BSPDIR}/sources/meta-cyclonedx"
conf/local.conf
INHERIT += "cyclonedx-export"
CYCLONEDX_SPEC_VERSION = "1.6"

Pourquoi fixer la version. La couche écrit par défaut du 1.4 sur kirkstone et scarthgap, du 1.6 sur les branches suivantes, et du 1.7 sur sa branche principale. euvex lit 1.2 à 1.6 : demander 1.6 marche sur toutes.

Où trouver le fichier. Cela dépend de la branche. Sur scarthgap, bom.json dans ${DEPLOY_DIR}/cyclonedx-export ; sur les branches récentes, à côté de l'image, avec le suffixe .cyclonedx.bom.json. Le README de votre branche fait foi. Déposez le SBOM, pas vex.json : euvex ne lit pas le VEX.

Ce qu'il contient. Par défaut, les paquets installés dans l'image seulement, chacun avec un purl générique tiré de CVE_PRODUCT et un CPE. Si vous incluez les paquets de compilation (CYCLONEDX_RUNTIME_PACKAGES_ONLY = "0"), la couche les marque optional, et euvex les comptera comme distribués. Le réglage par défaut évite ce bruit.

Le nom du produit. Sur les branches qui les proposent, scarthgap comprise, CYCLONEDX_IMAGE_NAME et CYCLONEDX_IMAGE_VERSION fixent le nom et la version écrits dans metadata.component, donc ceux du scan.

Depuis Buildroot

utils/generate-cyclonedx · depuis Buildroot 2025.02

Buildroot livre un script qui transforme la sortie de make show-info en CycloneDX 1.6, une version qu'euvex lit telle quelle.

terminal, à la racine de Buildroot (2025.08 et après)
make show-info | utils/generate-cyclonedx \
  --project-name thermostat-x --project-version 2.1.0 > sbom.cdx.json

Avant 2025.08, les deux options n'existent pas, et le produit s'appelle buildroot, à la version de Buildroot. Le script plus bas corrige le nom.

terminal, à la racine de Buildroot (2025.02 et 2025.05)
make show-info | utils/generate-cyclonedx > sbom.cdx.json

Ce qu'il contient. Chaque paquet avec son nom, sa version, ses licences, et un cpe quand le paquet en déclare un (variables LIBFOO_CPE_ID_*). Aucun purl : OSV ne pourra rien en dire au dépôt, la NVD prend le relais pendant la surveillance programmée.

Les outils de l'hôte. Le fichier liste aussi les paquets construits pour la machine de compilation, repérés par la propriété BR_TYPE à host, sans scope. euvex ne lit pas cette propriété : ils seraient comptés comme distribués. Le script suivant les marque excluded, ce que dit CycloneDX d'un composant absent à l'exécution, et pose le nom et la version du produit.

terminal, Python 3
python3 - sbom.cdx.json sbom-euvex.cdx.json thermostat-x 2.1.0 <<'EOF'
import json, sys
source, cible, nom, version = sys.argv[1:5]
with open(source) as f:
    bom = json.load(f)
bom["metadata"]["component"]["name"] = nom
bom["metadata"]["component"]["version"] = version
for c in bom.get("components", []):
    if any(p.get("name") == "BR_TYPE" and p.get("value") == "host" for p in c.get("properties", [])):
        c["scope"] = "excluded"
with open(cible, "w") as f:
    json.dump(bom, f, indent=2)
EOF

Déposez ensuite sbom-euvex.cdx.json. Les CVE que Buildroot marque comme corrigées par ses correctifs, dans la section vulnerabilities, ne sont pas reprises : voyez « Ignoré », plus haut.

Depuis Zephyr

west spdx · SPDX 2.3 en tag-value

Zephyr produit ses SBOM avec west spdx, après une compilation faite avec CONFIG_BUILD_OUTPUT_META. Il écrit dans BUILD_DIR/spdx/ quatre documents SPDX 2.3 en tag-value : l'application, le noyau Zephyr, la compilation et les modules.

terminal, dans l'espace de travail west
west build -d build
west spdx -d build

Ce qu'il faut déposer. Les trois documents qui disent le produit et ce qu'il embarque, mis bout à bout, l'application en premier : elle devient le produit, Zephyr et les modules ses composants.

terminal
cat build/spdx/app.spdx build/spdx/zephyr.spdx build/spdx/modules-deps.spdx > firmware.spdx

Le CPE et le purl d'un module viennent de son module.yml (security: external-references) : un module qui n'en déclare pas est cherché par son nom et sa version. Le SPDX 3.0 de Zephyr, encore expérimental, n'a pas été essayé.

Déposer et rattacher

Console · Nouveau scan

Dans la console, « Nouveau scan », déposez le fichier. Puis rattachez le scan au produit qu'il documente : c'est le rattachement qui ouvre le volet RED ou Machines, la déclaration UE et la détection art. 14. Rattacher un scan à un produit.

À chaque nouvelle version du firmware, un nouveau SBOM : la version du produit change, la gamme reste la même.

Ce que ce guide ne couvre pas

Où il s'arrête

  • euvex ne lance pas votre compilation. La lecture du SPDX a été écrite d'après la spécification et le code de Yocto et de Zephyr, et éprouvée sur des documents de leur forme, pas encore sur la sortie d'une vraie compilation : un écart se signale à l'adresse de contact.
  • meta-cyclonedx, le script de Buildroot et west spdx appartiennent à leurs auteurs. Les commandes ci-dessus sont celles de leur documentation à la date de relecture indiquée en bas de page ; la documentation de votre version fait foi.
  • Un SBOM complet ne prouve pas la conformité du produit. Il en est une pièce, exigée par le CRA au 11.12.2027 (annexe I, partie II, point 1).

Sources

Relues le 27.10.2026, SPDX le 30.10.2026