Skip to content

About

Generates an SBOM (Software Bill of Materials) from a mixed list of packages (RPM, Python, Debian, Java, Go, npm/yarn, Maven, Dart/Flutter), generic archives, or a **container image** (OCI). The tool can also scan SBOMs, container images, etc. for CVEs, using Grype, Trivy and OSV-Scanner.

Topics

Resources

Security policy

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

sbom_generator

🇫🇷 Français · 🇬🇧 English

Génère un SBOM (Software Bill of Materials) à partir d'une liste mixte de paquets (RPM, Python, Debian, Java, Go, npm/yarn/pnpm, Maven, Dart/Flutter, Rust, PHP, Ruby), d'archives génériques, ou d'une image de conteneur (OCI). L'outil est également capable de lancer des scans de CVE sur des SBOM, des conteneurs, etc., en utilisant Grype, Trivy et OSV-Scanner.

IMPORTANT: Je suis développeur dans le monde de la cybersécurité et j'ai besoin de nombreux outils pour mon travail. Jusqu'à maintenant j'utilisais du Bash et Python3 pour "agréger" ces différents outils et il y a quelques mois maintenant, je me suis tourné vers l'IA pour voir si l'IA pouvait m'aider pour me simplifier la vie. Ce repo github (ainsi que plusieurs autres) est le résultat de cette recherche. La totalité des fichiers présents dans cette arborescence est générée par Claude Code en suivant mes directives et mes nombreuses interactions avec l'IA. Le résultat est impressionnant pour moi car je peux désormais faire des choses très vite.


Ce que fait le programme

Entrée (--input) — trois formes possibles :

  1. un fichier texte listant une référence par ligne ;
  2. une archive/un paquet unique (traité directement, sans fichier liste) ;
  3. un dossier, scanné récursivement pour tous les types ci-dessous.

--input est optionnel dès lors que --image (voir plus bas) est fourni ; les deux sources peuvent aussi être combinées.

Type Exemples Outil requis
RPM installé bash, glibc-2.42, openssl-libs-3.0 rpm
Fichier .rpm local /tmp/mypkg-1.0-1.el9.x86_64.rpm rpm
Python wheel .whl /opt/wheels/requests-2.28.0-py3-none-any.whl python3
Python sdist .tar.gz / .tgz /opt/src/Django-4.2.1.tar.gz python3
Archive tar générique .tar.gz / .tgz /opt/3PP/apache-tomcat-10.1.44.tar.gz python3
Archive ZIP générique .zip /opt/3PP/myapp-2.0.0-linux-amd64.zip python3
Paquet Debian .deb /opt/pkgs/libssl3_3.0.1_amd64.deb dpkg-deb
Archive Java .jar /opt/libs/my-lib-1.2.3.jar unzip
Requirements Python .txt /opt/reqs/requirements.txt (aucun)
Modules Go go.sum / go.mod /opt/myapp/go.sum (aucun)
Paquets npm package-lock.json /opt/webapp/package-lock.json (v1/v2/v3) (aucun)
Paquets yarn yarn.lock /opt/webapp/yarn.lock (classique et Berry) (aucun)
Paquets pnpm pnpm-lock.yaml /opt/webapp/pnpm-lock.yaml (v5, v6, v9) (aucun)
Crates Rust Cargo.lock /opt/rustapp/Cargo.lock (crates externes) (aucun)
Paquets PHP composer.lock /opt/phpapp/composer.lock (packages et packages-dev) auteur du paquet
Gems Ruby Gemfile.lock /opt/rubyapp/Gemfile.lock (aucun)
Paquets NuGet packages.lock.json, *.deps.json, *.csproj /opt/dotnetapp/packages.lock.json (SHA-512 inclus) (aucun)
Lockfiles Python uv.lock, poetry.lock, pdm.lock /opt/pyapp/uv.lock (projet et dépendances locales écartés) (aucun)
Swift Package Manager Package.resolved /opt/iosapp/Package.resolved (formats v1, v2, v3) (aucun)
Dépendances Maven pom.xml /opt/javaapp/pom.xml (scope compile) (aucun)
Lockfile Dart/Flutter pubspec.lock /opt/flutterapp/pubspec.lock (versions résolues + transitives) (aucun)
Manifeste Dart/Flutter pubspec.yaml /opt/flutterapp/pubspec.yaml (dépendances directes) (aucun)

Les lignes commençant par # sont ignorées. Les types peuvent être mélangés librement dans un même fichier ou dossier.

Image de conteneur (--image / -I) — alternative ou complément à --input :

Forme Exemple
Référence de registre nginx:latest, ubuntu@sha256:…
Archive tar exportée /path/image.tar, .tar.gz, .tgz (ex. docker save)
Répertoire OCI layout /path/oci_dir/ (contient index.json)

Backend d'analyse au choix (--oci-tool) : syft (défaut, tous écosystèmes), trivy (tous écosystèmes), skopeo (extraction manuelle : dpkg, RPM, APK, Maven JARs, PyPI, npm), ou cdxgen (OWASP CycloneDX Generator, tous écosystèmes ; nécessite Node.js).

Binaire autonome (--binary <fichier>) — variante explicite d'--image pour un exécutable local (ex. un binaire Go lié statiquement) : force --oci-tool syft, seul backend qui sait exploiter les métadonnées embarquées dans un fichier qui n'est pas une image de conteneur (buildinfo Go via go-module-binary-cataloger, classifieur générique syft pour quelques bibliothèques connues — OpenSSL, zlib, sqlite…). Ne récupère pas les dépendances liées statiquement sans métadonnée embarquée (C/C++ « fait maison », Rust sans cargo-auditable). --image <fichier> fonctionne aussi (même mécanisme, syft détecte lui-même une source fichier locale) — --binary documente l'intention et refuse un --oci-tool autre que syft.

Descente dans les objets imbriqués (--depth) — avec --input, descend dans les objets contenus dans l'entrée : jars d'un RPM/deb, paquets et jars d'une archive tar/tgz/zip, jars d'un fat jar (BOOT-INF/lib, WEB-INF/lib), et manifestes rencontrés (package-lock.json, go.sum, pom.xml…). --depth 0 (défaut) analyse l'objet seul ; N descend de N niveaux ; all est sans limite (plafonné à 10). Le SBOM fusionné contient l'objet et tout ce qu'il contient (propriétés sbom_generator:nested:location / depth, dépendances parent → enfant), et un SBOM par objet imbriqué est écrit à côté de -o : <base>.nested-NN-<objet>.<ext> (--no-nested-files pour s'en passer). L'extraction est bornée (taille, nombre de fichiers).

Un SBOM par couche (--per-layer) — avec --image, produit en plus du SBOM global un SBOM par couche de l'image, dans chaque format demandé, à côté de -o : <base>.layer-NN-<digest12>.<ext>. Le SBOM d'une couche décrit son delta : composants ajoutés ou modifiés, composants supprimés listés à part. Le SBOM global indique la couche d'origine de chaque composant et référence chaque SBOM de couche (BOM-Link en CycloneDX). Deux méthodes (--layer-mode) :

Mode Principe Backends Contenu du delta
metadata (défaut) couche d'origine indiquée par le backend (Layer.DiffID de trivy ; couche la plus basse où syft voit le paquet en --scope all-layers) syft, trivy ajouts
rootfs couches appliquées une à une sur un rootfs cumulé (whiteouts compris), rootfs réanalysé après chaque couche les quatre (seul mode pour skopeo/cdxgen) ajouts, modifications (ex. montée de version), suppressions

Le mode rootfs coûte une analyse par couche ; une image de registre est d'abord copiée localement via skopeo (repli sur le stockage local de podman puis de Docker pour une image construite localement).

Traitement :

  • RPM : 3 appels rpm en parallèle par paquet (--queryformat, --requires, --provides)
  • Wheel : lecture du fichier METADATA embarqué dans le ZIP (via python3)
  • Archive tar : détection (Python sdist si PKG-INFO présent, sinon générique), lecture LICENSE/COPYING
  • Archive ZIP : même logique que tar, via python3 zipfile
  • Paquet Debian : extraction du fichier control via dpkg-deb -f, parsing RFC 822
  • Archive .jar : coordonnées Maven lues via pom.properties embarqué, sinon META-INF/MANIFEST.MF (Bundle-SymbolicName…), sinon déduites du nom de fichier ; un jar « shaded »/uber-jar embarquant des dépendances relocalisées (chacune avec son propre pom.properties) produit un composant par dépendance en plus du jar lui-même
  • Requirements.txt : parsing pur Dart (PEP 503), expansion en WheelPackage avant la boucle principale
  • Go (go.sum/go.mod), npm (package-lock.json), yarn (yarn.lock), Maven (pom.xml), Dart/Flutter (pubspec.lock/pubspec.yaml) : parsing pur Dart, expansion en WheelPackage avant la boucle principale (même mécanisme que requirements.txt, cardinalité 1 fichier → N paquets)
    • pubspec.lock : versions résolues + fermeture transitive complète ; toutes les sources (hosted, git, path, sdk). Les dépendances dependency: "direct dev" sont exclues (leurs transitives exclusives restent, faute d'information dans le lockfile).
    • Paquets source: sdk (flutter, sky_engine, flutter_web_plugins…) : le lockfile les note tous version: "0.0.0" (pas de vraie version pour les paquets livrés dans le SDK) — cette version factice est omise (pkg:pub/flutter sans version). --sdk-version flutter=3.47.2 injecte la vraie version du SDK (répétable, ex. --sdk-version dart=3.9.0) et, en plus, inscrit chaque SDK dans la toolchain du SBOM (metadata.tools.components en CycloneDX, creationInfo.creators en SPDX) — c'est là qu'apparaît la version de dart, qui n'a pas de paquet dans le lockfile.
    • Licences : pubspec.lock n'en contient aucune. Le CLI lit le fichier LICENSE de chaque paquet depuis le cache pub ($PUB_CACHE ou ~/.pub-cache, auto-détecté ; --pub-cache <dir> pour forcer, --pub-cache "" pour désactiver) et pour les paquets source: sdk depuis --flutter-root. En pratique la licence est renseignée pour la quasi-totalité des dépendances.
    • pubspec.yaml : dépendances directes de la section dependencies: uniquement (la section dev_dependencies: est ignorée) ; version déduite de la contrainte quand elle est univoque (^1.2.3, 1.2.3, >=1.2.3), sinon vide.
    • Dans un scan de dossier, si pubspec.lock et pubspec.yaml coexistent, seul le .lock est retenu.
  • Image OCI (--image) : analyse via syft, trivy, skopeo ou cdxgen (--oci-tool), en dehors de la boucle concurrente ; combinable avec --input
    • --per-layer : analyse supplémentaire par couche (lib/image_layers.dart) ; les paquets venus de --input ne figurent que dans le SBOM global
    • cdxgen : produit un SBOM CycloneDX complet dont on ne retient que les composants porteurs d'un PURL d'écosystème réel (l'inventaire fichier par fichier de cdxgen et ses actifs cryptographiques sont écartés) ; l'OS de base est reconstitué depuis le qualifiant distro= des PURL système
  • Dossier : parcours récursif (liens symboliques ignorés) ; seuls les fichiers reconnus par extension/nom exact (.rpm, .deb, .whl, .jar, .zip, .tar/.tar.gz/.tgz, requirements.txt, pom.xml, go.sum, go.mod, package-lock.json, yarn.lock, pnpm-lock.yaml, Cargo.lock, composer.lock, Gemfile.lock, uv.lock, poetry.lock, pdm.lock, Package.resolved, packages.lock.json, *.deps.json, *.csproj, pubspec.lock, pubspec.yaml) sont retenus — un .txt quelconque n'est pas traité comme requirements sauf s'il s'appelle exactement requirements.txt

Empreintes cryptographiques : l'empreinte d'un composant est renseignée sans requête réseau quand la source la fournit — integrity des lockfiles npm/yarn, sha256 de pubspec.lock, Digest Trivy / digests Syft (images OCI), SHA-256+SHA-512 calculés pour un fichier .rpm/.deb/.jar/.whl passé en entrée, %{SIGMD5} (MD5) pour un paquet RPM installé interrogé par nom. Émise en hashes (CycloneDX), checksums (SPDX 2.3), verifiedUsing (SPDX 3.0). Le hash de l'en-tête RPM est exposé à part (rpm:header-sha256). Souvent absente pour les paquets système d'une image.

Fournisseur (supplier) des composants : repris de la source quand elle le porte — %{VENDOR} (RPM), Maintainer: (Debian), maintainer/Vendor (images OCI), Author-email (wheels Python), groupId (Maven), et, pour npm, le champ author du package.json de chaque paquet dans node_modules (absent du lockfile). Les lockfiles Go/pip/pub et requirements.txt ne portent aucun éditeur : --supplier "<nom>" fournit une valeur de repli (sans jamais écraser un fournisseur détecté). En SPDX 2.3, un fournisseur inconnu est écrit NOASSERTION (conforme spéc / NTIA / sbomqs) plutôt qu'omis.

Formats de sortie :

-f Standard Contenu
cyclonedx CycloneDX 1.6/1.7 JSON (1.6 par défaut, --cyclonedx-version) components[] + dependencies[] + compositions[]
spdx SPDX 2.3 JSON packages[] + relationships[]
spdx3 SPDX 3.0 JSON-LD graphe @graph (éléments, relations, organisations)
json JSON personnalisé métadonnées complètes + graphe de dépendances résolu
markdown Tableau Markdown tableau Paquet / Version / Architecture / Licence
asciidoc Tableau AsciiDoc idem Markdown, format AsciiDoc
html Rapport HTML rapport autonome filtrable et triable, graphiques par écosystème et licence
csv Fichier CSV (RFC 4180) une ligne par paquet : name,version,architecture,license,type,purl,url,vendor

Interface graphique

Une interface Linux (Flutter, dossier gui/) pilote le CLI et les scanners sans ligne de commande ; elle existe en français et en anglais.

Configuration et progression d'une génération de SBOM

Tableau de bord des vulnérabilités : Grype, OSV-Scanner et Trivy comparés

Scan Grype : tableau des CVE, signaux CISA KEV / EPSS / PoC et détail d'une CVE

Rapport de conformité Cyber Resilience Act

Évaluation de la qualité d'un SBOM (sbomqs)

Ces captures (français dans doc/screenshots/, anglais dans doc/screenshots/en/) se régénèrent avec PATH=<dossier du CLI>:$PATH SCREENSHOT_DIR=../doc/screenshots flutter test test/screenshot_test.dart (depuis gui/) ; les onglets CRA et Qualité lancent le vrai CLI et sbomqs.

Installation

Prérequis

  • Dart SDK ≥ 3.0
  • rpm (pour les paquets RPM)
  • python3 (pour les wheels et archives tar/zip)
  • dpkg-deb (pour les paquets Debian .deb)
  • unzip (pour les archives .jar, et pour --oci-tool skopeo)
  • syft, trivy, skopeo ou cdxgen (uniquement pour --image, selon le backend choisi ; cdxgen requiert Node.js)
  • grype, osv-scanner ou trivy (uniquement pour les sous-commandes scan et cra)
  • accès réseau (uniquement pour l'enrichissement CVE de scan / cra : CISA KEV, EPSS, poc-in-github ; --no-enrich ou SBOMGEN_OFFLINE=1 s'en passe, cache 24 h)
  • asciidoctor-pdf (uniquement pour scan/cra --format pdf ; sinon le .adoc est conservé)
  • sbomqs (uniquement pour --min-quality-score)
  • cosign (uniquement pour --sign)

Compiler l'exécutable natif

dart compile exe bin/sbom_generator.dart -o sbom_generator

Usage

sbom_generator --input <fichier> [options]
sbom_generator --image <image-oci> [options]
sbom_generator --input <fichier> --image <image-oci> [options]
sbom_generator --binary <fichier> [options]
sbom_generator diff <sbom-a> <sbom-b> [--json] [--output <fichier>]
sbom_generator merge <sbom1> <sbom2> ... -o <sortie> [-n <nom>]
sbom_generator convert -i <sbom-source> -f <format> -o <sortie>
sbom_generator validate <sbom1> [<sbom2> ...] [--strict]
sbom_generator scan --sbom <fichier> [options]
sbom_generator scan --image <image> [--per-layer] [options]
sbom_generator scan --package <rpm|deb|tgz|zip|jar…> [--depth N] [options]
sbom_generator cra --sbom <fichier> [options]

Options :
  -i, --input              Fichier d'entrée (requis si --image absent)
  -I, --image              Image de conteneur à analyser : registre, archive
                           tar (.tar/.tar.gz/.tgz) ou répertoire OCI layout
  -b, --binary             Binaire local à analyser directement (ex. exécutable
                           Go lié statiquement) ; force --oci-tool syft
      --oci-tool            Backend d'analyse OCI : syft (défaut) | trivy | skopeo | cdxgen
      --per-layer          Avec --image : un SBOM par couche en plus du global
                           (<base>.layer-NN-<digest12>.<ext>, delta de la couche)
      --layer-mode         Méthode de --per-layer : metadata (défaut, syft/trivy)
                           | rootfs (réanalyse après chaque couche ; forcé pour
                           skopeo et cdxgen)
      --depth              Profondeur de descente dans les objets imbriqués de
                           --input : 0 (défaut, l'objet seul) | N niveaux | all
      --no-nested-files    Avec --depth : pas de SBOM par objet imbriqué
                           (<base>.nested-NN-<objet>.<ext>), fusionné seul
  -o, --output             Fichier de sortie (défaut : sbom.json)
                           Avec plusieurs formats, utilisé comme base de nom
  -f, --format             Format(s), virgule-séparés :
                             cyclonedx | spdx | spdx3 | json | markdown | asciidoc | html | csv
                           Exemple : -f cyclonedx,spdx,html
  -n, --name               Nom du document SBOM / composant racine
      --supplier           Fournisseur par défaut des composants dont la source
                           ne porte aucun éditeur (n'écrase jamais un fournisseur détecté)
  -d, --rpm-dir            Dossier racine où chercher les fichiers .rpm
                           (résout les noms RPM nus ; ne s'applique pas aux .whl/.tar)
  -c, --concurrency        Nombre de tâches traitées en parallèle (0 = illimité, défaut : 4)
  -l, --license-map        Fichier d'override de licences (une ligne "nom: SPDX-expression")
      --deny-license       Refuser les composants dont la licence correspond au motif
                           (répétable, correspondance SPDX partielle) — code retour 2 si violation
      --sdk-version        Vraie version d'un SDK Dart/Flutter (<sdk>=<version>, répétable) :
                           applique la version aux paquets source: sdk correspondants ET
                           ajoute chaque SDK à metadata.tools (CycloneDX) / creators (SPDX)
      --pub-cache          Cache pub pour lire le LICENSE des paquets d'un pubspec.lock
                           (défaut : $PUB_CACHE puis ~/.pub-cache ; "" pour désactiver)
      --flutter-root       Racine du SDK Flutter, pour le LICENSE des paquets source: sdk
      --min-quality-score  Score sbomqs minimum requis (ex. : 7.5) — code retour 2 si insuffisant
      --sign               Signer les fichiers SBOM avec cosign après génération
      --cyclonedx-version  Version CycloneDX générée : 1.6 (défaut) ou 1.7
      --tlp                Classification TLP du BOM (CycloneDX 1.7 uniquement)
      --patent-map         Déclarations de brevets par paquet (CycloneDX 1.7 uniquement)
      --lang               Langue des messages : fr | en (défaut : SBOM_LANG, puis LC_ALL/LANG, sinon fr)
  -v, --verbose            Afficher les détails
      --version            Afficher la version
  -h, --help               Afficher l'aide

Sous-commandes

# Comparer deux SBOMs (ajouts, suppressions, mises à jour de version)
sbom_generator diff avant.cdx.json après.cdx.json
sbom_generator diff avant.cdx.json après.cdx.json --json -o diff.json

# Fusionner plusieurs SBOMs en un seul (déduplication par PURL)
sbom_generator merge base.cdx.json extra.cdx.json -o merged.cdx.json
sbom_generator merge a.cdx.json b.cdx.json c.cdx.json -o merged.cdx.json -n "Système complet"

# Convertir un SBOM existant vers un ou plusieurs formats, sans re-scanner
sbom_generator convert -i sbom.cdx.json -f spdx,csv -o rapport

# Valider la structure d'un ou plusieurs SBOM (0 = tous valides, 1 = au moins un invalide)
sbom_generator validate sbom.cdx.json sbom.spdx.json
sbom_generator validate --strict sbom.cdx.json

# Rechercher les vulnérabilités connues d'un SBOM déjà généré (grype/osv/trivy)
sbom_generator scan --sbom sbom.cdx.json
sbom_generator scan --sbom sbom.cdx.json --scanner all --cve-after 2024-01-01

# Chaque CVE est enrichie (CISA KEV, EPSS, PoC public, exploitabilité CVSS) ;
# filtrer / prioriser :
sbom_generator scan --sbom sbom.cdx.json --only-kev --sort risk
sbom_generator scan --sbom sbom.cdx.json --epss-min 0.1
sbom_generator scan --sbom sbom.cdx.json --no-enrich   # hors-ligne (CI)

# Rapport de synthèse inter-scanners (markdown | asciidoc | pdf), avec section
# « Priorisation par risque » et propriétés kev/epss/poc en SARIF
sbom_generator scan --sbom sbom.cdx.json --scanner all -f pdf -o scan-report.pdf

# Scanner directement un RPM et les jars qu'il contient (2 niveaux) : le SBOM
# est généré (--depth) puis scanné ; chaque CVE indique l'objet qui la contient
sbom_generator scan --package app-1.0-1.x86_64.rpm --depth 2 --scanner all

# CVE par couche d'une image (SBOM par couche générés à la volée) :
# rattachement à la couche d'origine du paquet, ou scan de chaque couche
sbom_generator scan --image ./app.tar --per-layer --scanner all
sbom_generator scan --image ./app.tar --per-layer --layer-scan each --layer-mode rootfs

# Rapport de conformité Cyber Resilience Act (UE 2024/2847) — périmètre
# vérifiable automatiquement : complétude du SBOM (BSI TR-03183-2, éléments
# minimaux NTIA), vulnérabilités connues et correctifs, CVE activement
# exploitées (déclencheur notification ENISA sous 24 h, art. 14)
sbom_generator cra --sbom sbom.cdx.json --config cra.yaml -o rapport-cra.pdf
sbom_generator cra --sbom sbom.cdx.json --no-scan --format json   # exit 2 si non conforme

# Lister les licences d'un SBOM (rapport AsciiDoc / Markdown / HTML / CSV / JSON)
sbom_generator licenses -i sbom.cdx.json -o licences.adoc

# Politique de scan : seuil d'échec, CVE déjà connues, CVE ignorées (avec
# justification et expiration), cache du scanner
sbom_generator scan --sbom sbom.cdx.json --fail-on high
sbom_generator scan --sbom sbom.cdx.json --baseline scan-precedent.json --ignore ignore.txt --cache

# VEX : déclarer une CVE « non affectée » (OpenVEX par défaut, ou -f cyclonedx|csaf),
# puis l'appliquer au scan (OpenVEX, CycloneDX VEX et CSAF 2.0 sont lus)
sbom_generator vex --cve CVE-2024-1234 --product pkg:npm/foo@1.0.0 \
  --status not_affected --justification vulnerable_code_not_in_execute_path -o vex.json
sbom_generator scan --sbom sbom.cdx.json --vex vex.json

# Rapport de vulnérabilités (PDF) à partir d'un résultat `scan --format json`
sbom_generator scan --sbom sbom.cdx.json --format json -o resultats.json
sbom_generator report -i resultats.json -o rapport.pdf

# Signer un SBOM (cosign), puis vérifier sa signature
sbom_generator -i packages.txt -o sbom --sign
sbom_generator verify sbom.cdx.json --key cosign.pub

# Surveiller les nouvelles CVE de SBOM archivés (un passage : code 3 s'il y en a ;
# --interval 6h pour boucler ; --notify-cmd pour alerter)
sbom_generator watch sbom/ --state-dir .watch --scan-arg=--no-enrich
# alertes : --notify-webhook URL, --notify-smtp hote --notify-from … --notify-to … ;
# --interval 6h --print-schedule systemd|cron affiche la planification équivalente

# Note de qualité d'un SBOM (éléments NTIA, empreintes, licences) ; code 2 sous le minimum
sbom_generator quality -i sbom.cdx.json --min-score 80

# Politique de licences en CI (allow / deny / unknown / except) ; code 2 si violée
sbom_generator licenses -i sbom.cdx.json -f json --policy politique.txt

# Attestation in-toto (SBOM ou provenance SLSA) liée à un artefact, signable par cosign
sbom_generator attest -i sbom.cdx.json -s app.bin --sign

# Cache KEV/EPSS/PoC transportable vers une machine sans réseau (SBOMGEN_OFFLINE=1)
sbom_generator offline prepare --from-scan resultats.json
sbom_generator offline export -o offline.json.gz     # puis « offline import » sur la machine isolée

Exemples

# CycloneDX 1.6 (défaut)
./sbom_generator -i packages.txt -o sbom.cdx.json

# SPDX 2.3
./sbom_generator -i packages.txt -f spdx -o sbom.spdx.json

# SPDX 3.0 JSON-LD
./sbom_generator -i packages.txt -f spdx3 -o sbom.spdx3.jsonld

# JSON personnalisé avec nom de document
./sbom_generator -i packages.txt -f json -n "Mon Application" -o sbom.json -v

# Tableau Markdown des licences
./sbom_generator -i packages.txt -f markdown -o licences.md

# Rapport HTML interactif
./sbom_generator -i packages.txt -f html -n "Mon Application" -o sbom.html

# Générer CycloneDX + SPDX + HTML en une seule passe
./sbom_generator -i packages.txt -f cyclonedx,spdx,html -o sbom

# Rapport CSV
./sbom_generator -i packages.txt -f csv -o rapport.csv

# Analyser une image de conteneur (registre distant, via syft par défaut)
./sbom_generator -I nginx:latest -o nginx.cdx.json

# Analyser une archive tar exportée (docker save), backend trivy
./sbom_generator -I ./ubuntu.tar --oci-tool trivy -f spdx -o ubuntu.spdx.json

# Analyser une image via cdxgen (OWASP CycloneDX Generator)
./sbom_generator -I nginx:latest --oci-tool cdxgen -o nginx.cdx.json

# Combiner une image OCI et une liste de paquets supplémentaires
./sbom_generator -I nginx:latest -i extra_pkgs.txt -o sbom.cdx.json

# Un RPM et les jars qu'il contient : SBOM fusionné + un SBOM par jar
./sbom_generator -i app-1.0-1.x86_64.rpm --depth 1 -o out/app.cdx.json
# → out/app.cdx.json, out/app.nested-01-<jar>.cdx.json, …

# Un SBOM par couche de l'image (delta de chaque couche), en plus du global
./sbom_generator -I ./app.tar --per-layer -f cyclonedx,html -o out/app
# → out/app.cdx.json, out/app.layer-01-74d97c428c51.cdx.json, …

# Idem avec modifications et suppressions (réanalyse du rootfs par couche)
./sbom_generator -I ./app.tar --per-layer --layer-mode rootfs -o out/app

# Analyser un binaire lié statiquement (ex. exécutable Go) — dépendances
# embarquées lues via syft, sans registre ni conteneur
./sbom_generator --binary /usr/local/bin/mon-app -o app.cdx.json

# Résoudre les noms RPM depuis un dossier local (pas de rpm installé requis)
./sbom_generator -i packages.txt -d /mnt/repo -o sbom.cdx.json

# --input pointant directement vers un dossier : scan récursif de tous les
# types reconnus (.jar, .rpm, .deb, .whl, archives, manifestes…)
./sbom_generator -i /opt/app/libs -o sbom.cdx.json

# --input pointant directement vers un .jar unique
./sbom_generator -i /opt/app/libs/my-lib-1.2.3.jar -o sbom.cdx.json

# --input pointant directement vers un manifeste unique (lockfile Dart/Flutter,
# requirements.txt, go.sum, package-lock.json, pom.xml…)
./sbom_generator -i /opt/flutterapp/pubspec.lock -o sbom.cdx.json

# Idem, en renseignant la vraie version du SDK Flutter (sinon les paquets
# source: sdk sortent sans version)
./sbom_generator -i /opt/flutterapp/pubspec.lock --sdk-version flutter=3.47.2 -o sbom.cdx.json

# Traitement de 8 entrées en parallèle (liste mixte RPM + .deb + .whl + .txt)
./sbom_generator -i packages.txt -c 8 -o sbom.cdx.json

# Désactiver la limite de parallélisme (toutes les entrées simultanées)
./sbom_generator -i packages.txt -c 0 -o sbom.cdx.json

# Override de licences (fichier texte : une ligne "nom: SPDX-expression")
./sbom_generator -i packages.txt -l overrides.txt -o sbom.cdx.json

# Politiques CI/CD : refuser GPL et AGPL, exiger un score sbomqs ≥ 7.0
./sbom_generator -i packages.txt -o sbom.cdx.json \
  --deny-license GPL --deny-license AGPL \
  --min-quality-score 7.0
# Code retour 2 si violation ; 0 si OK

# Signer les SBOMs avec cosign après génération
./sbom_generator -i packages.txt -f cyclonedx,spdx -o sbom --sign

# CycloneDX 1.7 avec classification TLP et déclarations de brevets
./sbom_generator -i packages.txt --cyclonedx-version 1.7 --tlp AMBER \
  --patent-map patents.txt -o sbom.cdx.json

# Messages et rapports en anglais (défaut : suit SBOM_LANG, LC_ALL, LANG)
./sbom_generator --lang en -i packages.txt -o sbom.cdx.json
SBOM_LANG=en ./sbom_generator scan --sbom sbom.cdx.json -f pdf -o scan-report.pdf

# Comparer deux SBOMs
./sbom_generator diff ancien.cdx.json nouveau.cdx.json
./sbom_generator diff ancien.cdx.json nouveau.cdx.json --json -o diff.json

# Fusionner plusieurs SBOMs
./sbom_generator merge base.cdx.json extra.cdx.json -o merged.cdx.json -n "Système complet"

Langue (FR / EN)

Les messages, l'aide --help et les rapports lisibles (synthèse scan, rapport cra, rapport de licences, tableaux HTML/Markdown/AsciiDoc) existent en français et en anglais. La langue est choisie, dans l'ordre, par --lang fr|en, la variable d'environnement SBOM_LANG, puis LC_ALL / LC_MESSAGES / LANG ; français par défaut. Le contenu des SBOM (noms de propriétés, formats, valeurs) et les sorties lues par des programmes (clés JSON, identifiants de règles SARIF, ligne SBOM written → …) ne sont jamais traduits. La GUI transmet sa propre langue au CLI qu'elle lance.

Exemple de fichier d'override de licences (overrides.txt)

# Overrides de licences — format : nom_paquet: SPDX-expression
libssl3: Apache-2.0
mongodb: SSPL-1.0
my-internal-lib: LicenseRef-Proprietary

Chaque ligne nom: expression remplace la licence détectée automatiquement pour le paquet de ce nom. Les lignes commençant par # sont ignorées.

Exemple de fichier de brevets (patents.txt, CycloneDX 1.7 uniquement)

# Déclarations de brevets — format : nom_paquet: numéro|juridiction|statut|type_assertion
openssl: US1234567|US|granted|license
my-internal-lib: US7654321|US|pending|ownership
  • juridiction : code WIPO ST.3 à 2 lettres (US, EP, JP…)
  • statut : pending, granted, revoked, expired, lapsed, withdrawn, abandoned, suspended, reinstated, opposed, terminated, invalidated, in-force
  • type_assertion : ownership, license, third-party-claim, standards-inclusion, prior-art, exclusive-rights, non-assertion, research-or-evaluation

Nécessite --cyclonedx-version 1.7 (le schéma 1.6 n'a pas de champ patentAssertions).

Exemple de fichier d'entrée

# Noms RPM nus — résolus via --rpm-dir ou interrogés dans la base installée
bash
glibc
openssl-libs

# Fichiers RPM locaux (chemin absolu, toujours utilisé directement)
/mnt/repo/mypkg-1.0-1.el9.x86_64.rpm

# Python wheels
/opt/wheels/requests-2.28.0-py3-none-any.whl
/opt/wheels/numpy-1.24.0-cp311-cp311-linux_x86_64.whl

# Python sdist
/opt/src/Django-4.2.1.tar.gz

# Archives tar binaires tierces (Tomcat, MariaDB, MongoDB…)
/opt/3PP/apache-tomcat-10.1.44.tar.gz
/opt/3PP/mariadb-11.4.8-linux-systemd-x86_64.tar.gz
/opt/3PP/mongodb-linux-x86_64-rhel8-8.0.12.tgz

# Archive ZIP générique
/opt/3PP/myapp-2.0.0-linux-amd64.zip

# Paquets Debian
/opt/pkgs/libssl3_3.0.1_amd64.deb

# Requirements Python (les paquets du fichier sont inclus directement)
/opt/reqs/requirements.txt

Comportement selon le type d'archive (tar et zip)

Contenu de l'archive Résultat
PKG-INFO ou *.dist-info/METADATA Python sdist → PURL pkg:pypi/…, métadonnées complètes
LICENSE / COPYING / LICENSE-* (sans PKG-INFO) Archive générique → PURL pkg:generic/…, licence identifiée depuis le fichier
Ni l'un ni l'autre Archive générique → nom/version déduits du nom de fichier, licence inconnue

S'applique aux formats .tar.gz, .tgz, .tar et .zip. Les archives .zip contenant un METADATA Python sont traitées comme des sdists Python.

Extraction du nom/version depuis le nom de fichier (archives génériques) :

Le premier segment de version (\d+\.\d+) délimite le nom. Les suffixes de plateforme connus (linux, x86_64, rhel8, systemd, etc.) sont retirés du nom.

apache-tomcat-10.1.44.tar.gz           → nom=apache-tomcat    ver=10.1.44  arch=any
mariadb-11.4.8-linux-systemd-x86_64   → nom=mariadb           ver=11.4.8   arch=x86_64
mongodb-linux-x86_64-rhel8-8.0.12     → nom=mongodb           ver=8.0.12   arch=x86_64
mongodb-database-tools-rhel88-x86_64-100.13.0 → nom=mongodb-database-tools  ver=100.13.0
mongosh-2.5.6-linux-x64               → nom=mongosh           ver=2.5.6    arch=x86_64

Structure du projet

sbom_generator/
├── bin/
│   └── sbom_generator.dart      # Point d'entrée CLI + sous-commandes
│                                #   diff/merge/convert/validate/licenses/scan/cra/report/vex/verify/watch/
│                                #   quality/attest/offline
├── lib/
│   ├── i18n.dart                # Localisation FR / EN des messages (tr(), --lang)
│   ├── models.dart              # Package, RpmPackage, WheelPackage, DebPackage,
│   │                            #   OciPackage, PackageHash, PackageDependency, generateUuidV4()
│   ├── hash_utils.dart          # Collecte/normalisation des empreintes (SRI, Digest, fichiers)
│   ├── rpm_parser.dart          # Interrogation rpm (3 appels en Future.wait)
│   ├── wheel_parser.dart        # Lecture des .whl (ZIP + RFC 822)
│   ├── tar_parser.dart          # Lecture des .tar/.tar.gz/.tgz
│   ├── zip_parser.dart          # Lecture des .zip génériques
│   ├── deb_parser.dart          # Lecture des .deb (dpkg-deb -f)
│   ├── jar_parser.dart          # Coordonnées Maven d'un .jar (unzip -p pom.properties)
│   ├── requirements_parser.dart # Parsing requirements.txt Python (pur Dart)
│   ├── go_parser.dart           # Parsing go.sum / go.mod (pur Dart)
│   ├── npm_parser.dart          # Parsing package-lock.json v1/v2/v3 (pur Dart)
│   ├── yarn_parser.dart         # Parsing yarn.lock classique et Berry (pur Dart)
│   ├── maven_parser.dart        # Parsing pom.xml (pur Dart, extraction XML légère)
│   ├── pubspec_parser.dart      # Parsing pubspec.lock / pubspec.yaml (pur Dart)
│   ├── cargo_parser.dart        # Parsing Cargo.lock (Rust, pur Dart)
│   ├── pnpm_parser.dart         # Parsing pnpm-lock.yaml v5/v6/v9 (pur Dart)
│   ├── composer_parser.dart     # Parsing composer.lock (PHP, pur Dart)
│   ├── gem_parser.dart          # Parsing Gemfile.lock (Ruby, pur Dart)
│   ├── nuget_parser.dart        # Parsing packages.lock.json, *.deps.json, *.csproj (.NET, pur Dart)
│   ├── python_lock_parser.dart  # Parsing uv.lock, poetry.lock, pdm.lock (TOML, pur Dart)
│   ├── swift_parser.dart        # Parsing Package.resolved (Swift Package Manager, pur Dart)
│   ├── oci_parser.dart          # Analyse images OCI (syft / trivy / skopeo / cdxgen)
│   │                            #   skopeo : RPM, dpkg, APK, Maven JARs, PyPI, npm
│   │                            #   cdxgen : SBOM CycloneDX natif, filtré aux PURL réels
│   ├── archive_helpers.dart     # Helpers partagés tar/zip (parseFilename, identifyLicense)
│   ├── license_normalizer.dart  # Normalisation SPDX centralisée (LicenseNormalizer)
│   ├── spdx_license_ids.dart    # Instantané SPDX License List (garde-fou id vs name)
│   ├── sbom_diff.dart           # Comparaison de SBOMs (SbomDiffer)
│   ├── sbom_merger.dart         # Fusion de SBOMs (SbomMerger)
│   ├── sbom_reader.dart         # Relecture d'un SBOM existant (sous-commande convert)
│   ├── scan_report_generator.dart # Rapport de synthèse inter-scanners (scan -f md/adoc/pdf)
│   ├── vuln_enrichment.dart     # Enrichissement CVE : CISA KEV, EPSS, PoC, exploitabilité CVSS
│   ├── cra_report.dart          # Rapport de conformité Cyber Resilience Act (sous-commande cra)
│   ├── policy_checker.dart      # Contrôle de licences et score qualité CI/CD
│   ├── nested_archive.dart      # Descente dans les objets imbriqués (--depth)
│   ├── layer_scan.dart          # SBOM et CVE par couche (--per-layer)
│   ├── license_report_generator.dart # Rapport de licences (sous-commande licenses)
│   ├── spdx3_view.dart          # Lecture normalisée d'un graphe SPDX 3.0 (copie dans la GUI)
│   ├── schema_validator.dart    # Validation par les schémas JSON officiels (validate)
│   ├── schemas_data.dart        # Schémas embarqués (généré par tool/embed_schemas.dart)
│   ├── vex.dart                 # Déclarations VEX : OpenVEX, CycloneDX VEX, CSAF 2.0
│   ├── scan_policy.dart         # --ignore, --baseline, --cache, --fail-on
│   ├── remediation.dart         # Version cible minimale par paquet, gain de risque
│   ├── report_input.dart        # Entrée commune des rapports de vulnérabilités
│   ├── signature.dart           # Vérification de signature cosign (sous-commande verify)
│   ├── watch.dart               # Surveillance des nouvelles CVE (sous-commande watch)
│   ├── watch_history.dart       # Historique par SBOM (<état>.history.jsonl)
│   ├── watch_schedule.dart      # Unité/minuterie systemd et ligne cron (--print-schedule)
│   ├── notify.dart              # Alertes watch : webhook JSON et e-mail SMTP (STARTTLS/TLS)
│   ├── offline_bundle.dart      # Bundle du cache d'enrichissement (sous-commande offline)
│   ├── sbom_quality.dart        # Note de qualité NTIA (sous-commande quality)
│   ├── license_policy.dart      # Politique de licences allow/deny (licenses --policy)
│   ├── attestation.dart         # Déclarations in-toto et provenance SLSA (sous-commande attest)
│   ├── tool_runner.dart         # Lancement d'un outil externe (code 127 si absent)
│   ├── cyclonedx_generator.dart # Format CycloneDX 1.6/1.7 JSON
│   ├── spdx_generator.dart      # Format SPDX 2.3 JSON
│   ├── spdx3_generator.dart     # Format SPDX 3.0 JSON-LD
│   ├── simple_json_generator.dart  # Format JSON personnalisé
│   ├── markdown_generator.dart  # Tableau Markdown des licences
│   ├── asciidoc_generator.dart  # Tableau AsciiDoc des licences
│   ├── html_generator.dart      # Rapport HTML interactif (filtrable, triable)
│   └── csv_generator.dart       # Export CSV (RFC 4180)
├── test/
│   ├── unit/                    # Tests sans sous-processus réel (parseurs, générateurs,
│   │                            #   licences, politiques…) — voir doc/developer.adoc
│   └── integration/             # Tests sur archives/outils réels (@TestOn('posix'))
├── example/
│   └── 3PP/                     # Exemples d'archives tierces
├── doc/
│   ├── developer.adoc           # Documentation développeur détaillée (FR)
│   ├── developer.en.adoc        # Documentation développeur détaillée (EN)
│   ├── usage.adoc               # Guide d'utilisation détaillé (FR)
│   └── usage.en.adoc            # Guide d'utilisation détaillé (EN)
├── pubspec.yaml
├── README.md                    # Français
└── README.en.md                 # Anglais

PURLs générés par type de paquet

Type PURL Exemple
RPM pkg:rpm/<name>@<ver>?arch=<arch> pkg:rpm/bash@5.1.8-6.el9?arch=x86_64
Python (wheel / sdist / requirements.txt / OCI) pkg:pypi/<name>@<ver> pkg:pypi/requests@2.28.2
Archive tar/zip générique pkg:generic/<name>@<ver> pkg:generic/apache-tomcat@10.1.44
Paquet Debian pkg:deb/<name>@<ver>?arch=<arch> pkg:deb/libssl3@3.0.1?arch=amd64
Java Maven (.jar autonome, pom.xml, ou OCI via skopeo) pkg:maven/<groupId>/<artifactId>@<ver> pkg:maven/org.yaml/snakeyaml@2.0
Go (go.sum / go.mod) pkg:golang/<module>@<ver> pkg:golang/github.com/gorilla/mux@1.8.1
npm / yarn / pnpm (package-lock.json, yarn.lock, pnpm-lock.yaml, ou OCI via skopeo) pkg:npm/<name>@<ver> pkg:npm/semver@7.5.4
Dart/Flutter (pubspec.lock / pubspec.yaml) pkg:pub/<name>@<ver> pkg:pub/provider@6.1.2
Rust (Cargo.lock) pkg:cargo/<name>@<ver> pkg:cargo/serde@1.0.200
PHP (composer.lock) pkg:composer/<vendor>/<name>@<ver> pkg:composer/monolog/monolog@3.5.0
Ruby (Gemfile.lock) pkg:gem/<name>@<ver> pkg:gem/racc@1.7.1
.NET (packages.lock.json, *.deps.json, *.csproj) pkg:nuget/<Name>@<ver> (casse conservée) pkg:nuget/Newtonsoft.Json@13.0.1
Swift (Package.resolved) pkg:swift/<hôte>/<org>/<dépôt>@<ver> pkg:swift/github.com/apple/swift-nio@2.62.0
Image de conteneur (--image) PURL fourni par l'outil d'analyse (syft/trivy/cdxgen), ou reconstruit selon l'écosystème détecté pkg:apk/alpine/musl@1.2.4-r2

Licence

Distribué sous licence GNU LGPL v3 (LGPL-3.0-only) — voir LICENSE ; la LGPL v3 complète la GPL v3, dont le texte est dans COPYING. La GUI (gui/) est soumise à la même licence.

Sécurité

Pour signaler une vulnérabilité, voir SECURITY.md.

About

Generates an SBOM (Software Bill of Materials) from a mixed list of packages (RPM, Python, Debian, Java, Go, npm/yarn, Maven, Dart/Flutter), generic archives, or a **container image** (OCI). The tool can also scan SBOMs, container images, etc. for CVEs, using Grype, Trivy and OSV-Scanner.

Topics

Resources

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages