Générer un SBOM CycloneDX dans un pipeline CI
Section intitulée « Générer un SBOM CycloneDX dans un pipeline CI »Une nomenclature logicielle, ou SBOM (Software Bill of Materials), doit être produite à chaque build et rattachée exactement à l’artefact déployé. Le compromis est net : on paie quelques minutes de pipeline, du stockage et une discipline de versions, mais on obtient enfin un inventaire exploitable quand une vulnérabilité connue tombe sur une dépendance déjà en production.
Une nomenclature logicielle ressemble à la liste complète des ingrédients d’un plat industriel : elle indique ce qui se trouve réellement dans le paquet, pas ce que la recette promettait. Quand un ingrédient devient dangereux ou interdit, cette liste permet de retrouver les produits concernés avant que quelqu’un ne découvre le problème à sa place.
→ Lire l’explication complète de la marmotte
Préparer la chaîne SBOM, scan et distribution
Section intitulée « Préparer la chaîne SBOM, scan et distribution »Le périmètre minimal comprend un runner d’intégration continue, ou CI (Continuous Integration), capable de construire l’image livrable, un registre d’images ou un dépôt d’artefacts, et un gestionnaire de vulnérabilités recevant les SBOM. Syft génère l’inventaire, Grype le compare à une base de vulnérabilités, et Dependency-Track conserve cet inventaire pour détecter les nouvelles vulnérabilités après le déploiement.
Le format retenu ici est CycloneDX en JavaScript Object Notation, ou JSON. CycloneDX transporte bien les composants, leurs versions, leurs licences, leurs relations et leurs identifiants de paquets. SPDX (Software Package Data Exchange) convient mieux lorsqu’un processus licence ou une exigence contractuelle impose ce format. Produire les deux n’apporte généralement rien : choisir CycloneDX, sauf obligation SPDX explicite. Deux fichiers divergents ne constituent pas une défense en profondeur, seulement deux causes de tickets.
L’image doit être construite avant la génération du SBOM. Scanner le répertoire Git ne décrit ni les paquets ajoutés par le gestionnaire de distribution, ni les bibliothèques copiées durant un build multi-étapes, ni les binaires récupérés hors du gestionnaire de dépendances. Le SBOM pertinent est celui de l’image par digest, pas celui de l’intention déclarée dans package.json, pom.xml ou requirements.txt.
Prévoir les éléments suivants avant d’écrire le pipeline :
- Syft et Grype dans des versions épinglées et validées sur l’environnement de build.
- Une image construite avec un tag immuable, idéalement le hash du commit Git.
- Un accès sortant contrôlé à la base de vulnérabilités de Grype, ou un miroir interne pour les environnements isolés.
- Un projet Dependency-Track distinct par application et par ligne de produit maintenue.
- Un secret de téléversement Dependency-Track stocké dans le coffre de secrets du forgeur, jamais dans le dépôt.
- Une rétention des SBOM au moins égale à la durée de rétention des images déployables.
La génération ne garantit pas qu’un composant est supporté, configuré sans danger ou réellement chargé à l’exécution. Elle ne prouve pas non plus l’absence de code malveillant dans une dépendance. Un SBOM répond à « de quoi est fait cet artefact ? », pas à « cet artefact est-il digne de confiance ? ». Pour l’intégrité de la chaîne, il faut compléter avec des signatures d’images, des droits minimaux du runner et des revues des dépendances, sujets liés aux fondations de l’architecture cybersécurité et aux failles connues.
Générer un SBOM CycloneDX avec Syft
Section intitulée « Générer un SBOM CycloneDX avec Syft »Syft inspecte un système de fichiers, une archive ou une image de conteneur et produit une liste de composants. Pour une image, l’entrée doit être le digest publié ou l’image locale tout juste construite. Un tag mutable tel que latest ne constitue pas une identité : il désigne surtout l’artefact qui aura gagné la dernière course au tag.
L’exemple suivant suppose une image locale construite par Docker. L’image doit porter le hash du commit, transmis par la variable CI_COMMIT_SHA. En environnement sans démon Docker, remplacer docker: par une référence de registre avec digest.
Exemple reproductible : build, SBOM et scan Grype
Section intitulée « Exemple reproductible : build, SBOM et scan Grype »#!/usr/bin/env bashset -euo pipefail
# Construire l'artefact dont le SBOM doit décrire le contenu réel.docker build \ --tag "registry.example.invalid/paiement:${CI_COMMIT_SHA}" \ .
# Générer CycloneDX depuis l'image construite, jamais depuis le seul dépôt Git.syft "docker:registry.example.invalid/paiement:${CI_COMMIT_SHA}" \ --output cyclonedx-json=sbom.cdx.json
# Conserver le rapport brut afin de justifier un blocage ou une exception.grype sbom:sbom.cdx.json \ --output json \ --file grype-report.json \ --fail-on high
# Afficher une synthèse lisible dans les journaux du pipeline.grype sbom:sbom.cdx.json \ --output table \ --fail-on high--fail-on high bloque sur les vulnérabilités de sévérité haute et critique. Ce seuil est un point de départ raisonnable pour les builds de livraison. Le baisser à medium augmente la détection mais produit souvent un volume d’exceptions ingérable si les faux positifs, composants non atteignables et correctifs non disponibles ne sont pas traités. Le laisser à critical revient fréquemment à déclarer que les vulnérabilités hautes sont une décoration de tableau de bord.
Grype associe les composants par nom, version, écosystème et parfois par identifiant de paquet. Cette corrélation peut échouer : un binaire compilé localement, un paquet renommé, une version corrigée par le distributeur sans changement visible de version, ou une dépendance vendue sous un nom commercial peuvent échapper à la correspondance. Un résultat vide n’est pas une attestation de sécurité. C’est une réponse de base de données à un inventaire donné, à une date donnée.
Conserver trois artefacts de pipeline : sbom.cdx.json, grype-report.json et le digest de l’image. Sans ce triplet, une alerte future ne permet pas de déterminer précisément quel binaire était déployé. Les captures d’écran de scanner vieillissent très vite, avec une constance presque artistique.
Intégrer le SBOM dans le pipeline CI
Section intitulée « Intégrer le SBOM dans le pipeline CI »Le pipeline doit produire le SBOM dans le même job ou immédiatement après le build, avant la promotion de l’image. Une génération après déploiement laisse une fenêtre où l’artefact est en service sans inventaire fiable. Le job publie ensuite le SBOM comme artefact CI et le transmet à Dependency-Track.
L’extrait suivant utilise une syntaxe compatible GitLab CI. Le principe reste identique sur un autre forgeur : construire, récupérer le digest, générer le SBOM, analyser, puis téléverser.
sbom-et-vulnerabilites: stage: verify image: alpine:3.21 dependencies: - build-image script: # Installer les binaires depuis un dépôt interne validé ou une image outillée épinglée. - ./ci/install-syft-grype.sh - export IMAGE_REF="${CI_REGISTRY_IMAGE}@${IMAGE_DIGEST}" - syft "$IMAGE_REF" --output cyclonedx-json=sbom.cdx.json - grype sbom:sbom.cdx.json --fail-on high --output json --file grype-report.json # DEPENDENCY_TRACK_API_KEY est un secret masqué du forgeur. - > curl --fail --silent --show-error -X POST "${DEPENDENCY_TRACK_URL}/api/v1/bom" -H "X-Api-Key: ${DEPENDENCY_TRACK_API_KEY}" -F "project=${DEPENDENCY_TRACK_PROJECT_UUID}" -F "[email protected]" artifacts: when: always expire_in: 180 days paths: - sbom.cdx.json - grype-report.jsonLe script install-syft-grype.sh doit vérifier les sommes de contrôle des binaires ou, mieux, installer une image de build interne dont le digest est épinglé. Télécharger un binaire à chaque pipeline depuis une URL flottante transforme le contrôle de chaîne logicielle en loterie avec journalisation. Documenter les versions retenues dans le dépôt et planifier leur mise à jour : le scanner est lui-même un composant de la chaîne d’approvisionnement.
Le digest IMAGE_DIGEST doit venir de l’étape qui pousse réellement l’image. Ne pas le recalculer depuis un tag. Un tag peut être réassigné entre deux jobs, notamment lors de builds parallèles. Le digest lie cryptographiquement le SBOM à un contenu immuable, à condition que le registre applique l’immuabilité ou que les règles de promotion l’imposent.
Exploiter Dependency-Track après le déploiement
Section intitulée « Exploiter Dependency-Track après le déploiement »Dependency-Track reçoit le SBOM, conserve la liste des composants et la réévalue lorsqu’une nouvelle CVE (Common Vulnerabilities and Exposures) apparaît dans ses sources de renseignement. C’est la différence décisive entre un scan de pipeline et une surveillance : le scan répond au moment du build, la surveillance répond aussi six mois après, quand une bibliothèque déjà déployée devient publiquement vulnérable.
Créer un projet par application et versionner ses livraisons avec le champ de version CycloneDX ou avec des projets distincts selon la politique de cycle de vie. Ne pas écraser indistinctement le même projet avec les SBOM de production, préproduction et branches de développement : une alerte ne permettra plus de savoir où se trouve le composant vulnérable.
Configurer ensuite dans Dependency-Track :
- Un éditeur de notification de type courriel ou webhook vers l’outil de gestion d’incidents.
- Une règle de notification au niveau projet pour les événements
NEW_VULNERABILITYetNEW_VULNERABLE_DEPENDENCY. - Un routage séparant les alertes de production des branches éphémères.
- Une règle d’escalade pour les vulnérabilités critiques exposées par un service accessible depuis Internet.
- Une revue périodique des alertes supprimées, car une suppression sans justification est une exception cachée sous le tapis.
L’alerte doit inclure le composant, la version, la CVE, le projet, le digest de l’image et le lien vers le SBOM. Sans digest, l’équipe reçoit le nom d’une dépendance mais pas l’artefact à corriger. Sans propriétaire de service, l’alerte rejoint le cimetière des boîtes partagées, où les incidents vont méditer.
Les exceptions doivent être temporaires, liées à une CVE et révisées à échéance. Une exception portant sur une famille entière de bibliothèques masque les vulnérabilités futures. Pour les composants non exploitables, documenter la justification technique : fonction désactivée, code non appelé, port non exposé, contrôle compensatoire vérifiable. « Pas utilisé normalement » n’est pas une justification, c’est une invitation au rapport d’incident.
Surface d’attaque, exploitation et reprise
Section intitulée « Surface d’attaque, exploitation et reprise »Un SBOM expose des informations sensibles sur les composants et versions. Un attaquant qui l’obtient peut accélérer son ciblage, surtout si le document révèle des bibliothèques obsolètes ou des chemins internes. Distribuer le SBOM dans un dépôt d’artefacts avec contrôle d’accès, mais ne pas le cacher comme un secret absolu : la sécurité par ignorance ne survit pas au premier scan externe.
Le secret DEPENDENCY_TRACK_API_KEY autorise l’envoi de données et, selon les droits attribués, des actions d’administration. Lui attribuer uniquement la permission de téléversement sur les projets concernés, le stocker dans le gestionnaire de secrets du forgeur, le faire tourner périodiquement et révoquer immédiatement les clés d’un runner compromis. Le contenu du SBOM ne contient pas de secret par conception, mais un paquet peut embarquer accidentellement un fichier de configuration : exclure les chemins contenant des secrets du contexte de build reste obligatoire.
Sauvegarder la base de données Dependency-Track, sa configuration de notifications et l’association entre projets et propriétaires. Restaurer seulement les SBOM sans les règles d’alerte rétablit un inventaire muet, ce qui est une forme particulièrement coûteuse de décoration. Tester la restauration et l’émission d’un webhook sur un environnement isolé.
Enfin, mesurer trois signaux : pourcentage d’images déployées avec SBOM associé, délai entre publication d’une CVE et création d’alerte, délai entre alerte critique et décision documentée. La conformité sans ces mesures ressemble beaucoup à une présentation de vendeur : elle contient des rectangles colorés, mais aucune preuve que le mécanisme fonctionne.
🐝 Ce qui arrive si on s’en passe
Section intitulée « 🐝 Ce qui arrive si on s’en passe »Sans nomenclature produite à chaque build, la question « quelles versions déployées contiennent ce composant ? » se règle par reconstruction manuelle, image par image, à un moment où les couches de base ont souvent été réétiquetées et où le contexte de build d’origine n’est plus reproductible. Le délai de réponse se compte alors en jours, pendant lesquels les décisions de correction et de retrait reposent sur des suppositions de dépendances déclarées plutôt que sur le contenu réel de l’artefact. La chronologie d’un incident devient invraisemblable à documenter : impossible d’attester à quelle date une version vulnérable est entrée en service, ni quand elle en est sortie. Les demandes de composants et de licences émanant de clients ou d’auditeurs se traitent par inventaire ad hoc, jamais rejouable, et les écarts entre deux réponses successives restent inexplicables.
🦫 Explication de la marmotte 🍫
Section intitulée « 🦫 Explication de la marmotte 🍫 »Une application utilise souvent beaucoup de morceaux de logiciels écrits par d’autres personnes. Certains sont ajoutés directement par les développeurs, d’autres arrivent avec le système qui sert à faire fonctionner l’application. La nomenclature logicielle est un fichier qui liste ces morceaux, leur nom et leur version.
Le fichier doit être créé juste après la fabrication de l’application, car il doit décrire exactement ce qui sera envoyé sur les serveurs. Un outil lit alors le contenu de l’application fabriquée et écrit cette liste dans un format normalisé. Un second outil compare cette liste avec une base qui recense les défauts de sécurité connus. Si un défaut assez grave est trouvé, la fabrication peut être arrêtée avant l’envoi.
Le fichier est aussi transmis à un service de suivi. Ce service garde la liste des composants présents dans chaque version déployée. Lorsqu’un nouveau défaut de sécurité est découvert plus tard, il compare ce défaut avec les listes déjà reçues et envoie une alerte aux personnes responsables.
Le processus échoue si la liste est créée une seule fois, si elle décrit les fichiers du dépôt plutôt que l’application fabriquée, ou si elle n’est jamais envoyée au service de suivi. Il faut également protéger la clé permettant d’envoyer les listes, conserver les rapports et pouvoir restaurer le service de suivi après une panne.
À lire aussi
- SBOM pour inventorier les composants réellement exécutésUne SBOM permet d’identifier les composants qui composent réellement un logiciel, à condition de la produire au bon endroit et de l’exploiter.
- Écrire un Dockerfile durci avec condensé, multi-étapes et non rootGuide pas à pas pour durcir un Dockerfile naïf avec image de base épinglée par condensé, construction multi-étapes, utilisateur non root, secrets de construction BuildKit et contrôle en intégration continue.
- L'attaque de la chaîne d'approvisionnement logicielle en une pageComment une dépendance piégée traverse le build, reçoit une signature valide et s'installe chez tous les clients, en cinq étapes animées, avec SolarWinds, xz-utils, event-stream et les parades qui coupent la chaîne.
- Gérer les risques tiers et la chaîne logicielleCette page traite de la gestion des risques liés aux fournisseurs, dépendances et composants de la chaîne d'approvisionnement logicielle.
Pages voisines
La citent
- Écrire un Dockerfile durci avec condensé, multi-étapes et non root
- CERT-FR alertes et avis de sécurité
- Désérialisation non sécurisée et chaîne de gadgets
- Exigences CRA pour produits connectés sécurisés
- La chaîne d'approvisionnement logicielle
- Maîtriser le risque des dépendances transitives (npm, pip, Maven, Go)
- Sécuriser les fonctions serverless FaaS
Générer un SBOM CycloneDX dans un pipeline CI
Elle cite
: les deux pages se citent l’une l’autre.
