
Dependency-Track est une plateforme open source de l'OWASP qui centralise l'analyse des vulnérabilités de tous vos projets à partir de leurs SBOM. Vous lui envoyez l'inventaire de chaque application, il le corrèle en continu avec les bases de vulnérabilités (NVD, GitHub Advisories, OSV) et signale les failles activement exploitées grâce aux catalogues KEV. Une CVE publiée demain sur une dépendance présente dans 50 projets vous alerte sans qu'aucun build ne soit relancé.
Ce guide s'adresse aux équipes DevOps et sécurité qui savent lancer un
conteneur et appeler une API avec curl. Il installe Dependency-Track
5.2.0 avec Docker Compose ou Helm, importe des SBOM produits par Trivy et
Syft, explique pourquoi un SBOM Syft n'affiche aucun graphe de
dépendances et comment le corriger, puis branche alertes et politiques sur le
signal KEV. Toutes les commandes ont été rejouées en lab le 14 octobre 2026.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »Le parcours suit le trajet réel d'un SBOM : il est produit par un scanner comme Trivy ou Syft, transmis à Dependency-Track via l'API REST, puis corrélé en continu avec les bases de vulnérabilités et les catalogues KEV. Les deux dernières étapes font la différence avec un scanner ponctuel : les notifications, qui préviennent quand une CVE apparaît sur un composant déjà inventorié, et les politiques, qui transforment un constat en règle opposable.
- Installer Dependency-Track 5.2 avec Docker Compose ou le chart Helm 2.x
- Activer les sources de vulnérabilités (NVD, OSV, GitHub) et les catalogues KEV
- Importer des SBOM via l'API REST et automatiser l'envoi dans vos pipelines CI/CD
- Comprendre pourquoi un SBOM Syft n'a pas de graphe de dépendances, et le réparer
- Prioriser avec les alertes et les politiques filtrées sur les vulnérabilités exploitées
- Ancrage SOCLE : ce guide met en œuvre SOCLE-VLN-INV-1, la corrélation multi-sources reliée aux artefacts par SBOM, SOCLE-VLN-INV-2, l'enrichissement par KEV et EPSS, et SOCLE-VLN-INV-3, l'inventaire continu
Pourquoi centraliser la gestion des SBOM ?
Section intitulée « Pourquoi centraliser la gestion des SBOM ? »Un scanner comme Trivy ou Grype détecte les vulnérabilités à un instant T, au moment du build. Dependency-Track garde au contraire l'inventaire de tous vos projets et le réévalue à chaque mise à jour des bases. C'est la seule façon de répondre vite à trois questions qui reviennent toujours :
- Une nouvelle CVE est publiée pour Log4j : quels projets l'embarquent ?
- Un auditeur demande la preuve qu'une vulnérabilité a été corrigée partout.
- L'équipe sécurité veut un tableau de bord de l'exposition de l'entreprise.
Le tableau ci-dessous résume la différence de nature entre les deux approches, qui sont complémentaires : le scanner bloque un build, Dependency-Track surveille ce qui est déjà livré.
| Approche | Scanner ponctuel (Trivy, Grype) | Dependency-Track |
|---|---|---|
| Quand ? | À chaque build ou scan | En continu, à chaque synchronisation des bases |
| Périmètre | 1 projet à la fois | Tout le portefeuille |
| Nouvelles CVE | Détectées au prochain scan | Alerte sans nouveau build |
| Exploitation réelle | Selon l'outil | Drapeau KEV et score EPSS (probabilité d'exploitation) par vulnérabilité |
| Historique | Non | Oui, métriques en séries temporelles |
Les notions à connaître avant de commencer
Section intitulée « Les notions à connaître avant de commencer »Cinq notions conditionnent la qualité des résultats. Le SBOM décrit ce que contient votre application, le graphe de dépendances dit qui tire quoi, le PURL et le CPE identifient chaque composant, les sources de vulnérabilités apportent les CVE connues, et les catalogues KEV disent lesquelles sont exploitées. Un maillon faible se traduit toujours de la même manière : des composants réellement vulnérables absents du tableau de bord.
Qu'est-ce qu'un SBOM et son graphe de dépendances ?
Section intitulée « Qu'est-ce qu'un SBOM et son graphe de dépendances ? »Un SBOM (Software Bill of Materials) est la liste complète des composants
d'un logiciel, comme la liste d'ingrédients d'un produit alimentaire. Au format
CycloneDX, il porte deux blocs distincts : components, l'inventaire à
plat, et dependencies, les relations entre composants. C'est ce second
bloc qui permet à Dependency-Track de dessiner l'arbre et de dire qu'une
faille de qs arrive par express. Le point d'entrée de l'arbre est le
composant décrit dans metadata.component, le projet lui-même.
{ "bomFormat": "CycloneDX", "specVersion": "1.6", "metadata": { "component": { "bom-ref": "demo-app", "type": "application", "name": "demo-app" } }, "components": [ { "bom-ref": "pkg:npm/express@4.18.2", "name": "express", "version": "4.18.2", "purl": "pkg:npm/express@4.18.2" }, { "bom-ref": "pkg:npm/qs@6.11.0", "name": "qs", "version": "6.11.0", "purl": "pkg:npm/qs@6.11.0" } ], "dependencies": [ { "ref": "demo-app", "dependsOn": ["pkg:npm/express@4.18.2"] }, { "ref": "pkg:npm/express@4.18.2", "dependsOn": ["pkg:npm/qs@6.11.0"] } ]}Dependency-Track accepte CycloneDX (format OWASP, recommandé) pour les SBOM et les documents VEX (déclarations d'exploitabilité). Les générateurs courants sont Trivy, Syft et cdxgen. Pour aller plus loin sur le contenu d'un inventaire, voyez le guide SBOM.
PURL et CPE : comment un composant est reconnu
Section intitulée « PURL et CPE : comment un composant est reconnu »Le PURL (Package URL) identifie un paquet dans son écosystème, par exemple
pkg:npm/lodash@4.17.20 ou pkg:apk/alpine/zlib@1.3.1-r2. Le CPE (Common
Platform Enumeration) est l'identifiant historique de la NVD, par exemple
cpe:2.3:a:lodash:lodash:4.17.20:*:*:*:*:*:*:*. La distinction compte en v5 :
l'analyseur interne rapproche la NVD uniquement par CPE, et les sources
GitHub Advisories et OSV par PURL. Un SBOM sans CPE ne recevra donc
aucun résultat de la NVD.
pkg:npm/lodash@4.17.20pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1pkg:apk/alpine/openssl@3.5.4-r0cpe:2.3:a:lodash:lodash:4.17.20:*:*:*:*:*:*:*Les sources de vulnérabilités
Section intitulée « Les sources de vulnérabilités »Dependency-Track recopie (mirroring) les bases publiques dans sa propre base PostgreSQL, puis compare vos composants à cette copie locale, sans appel extérieur au moment de l'analyse. Sur une installation neuve, seule la NVD est active : nous l'avons mesuré sur la 5.2.0, GitHub et OSV sont désactivés par défaut. Pour des paquets open source identifiés par PURL, OSV est la source la plus utile.
| Source | Identifiant | Rapprochement | Par défaut | Prérequis |
|---|---|---|---|---|
| NVD (NIST) | CVE- | CPE | Activée | Aucun, téléchargement des flux annuels |
| GitHub Advisories | GHSA- | PURL | Désactivée | Jeton GitHub sans scope, ou GitHub App |
| OSV (Google) | Variable | PURL | Désactivée | Aucun, choix des écosystèmes |
À côté de ces miroirs, des analyseurs interrogent des services externes au moment de l'analyse : OSS Index (compte Sonatype obligatoire), Snyk, Trivy (serveur Trivy), VulnDB et Checkmarx. Tous sont désactivés par défaut. L'analyseur interne, actif par défaut, est celui qui exploite les miroirs.
Les catalogues KEV : la preuve d'une exploitation réelle
Section intitulée « Les catalogues KEV : la preuve d'une exploitation réelle »Un catalogue KEV (Known Exploited Vulnerabilities) liste les vulnérabilités
exploitées dans la nature. Là où le CVSS (note de gravité de 0 à 10) note une gravité théorique, une
entrée KEV dit que des attaquants s'en servent déjà, ce qui en fait le meilleur
critère de priorité. Dependency-Track 5.1 et plus recopient trois
catalogues : CISA KEV et ENISA EU KEV, actifs par défaut et sans
inscription, et VulnCheck KEV, désactivé par défaut et soumis à un jeton
gratuit. Le rapprochement se fait par identifiant CVE, y compris à travers
les alias : une alerte GitHub GHSA-jfh8-c2jp-5v3q est marquée KEV parce
qu'elle correspond à CVE-2021-44228 (Log4Shell).
Installer Dependency-Track 5.2 avec Docker Compose
Section intitulée « Installer Dependency-Track 5.2 avec Docker Compose »Dependency-Track se déploie en deux conteneurs et une base : l'apiserver, qui porte toute la logique, le frontend, une application web statique qui interroge l'API, et PostgreSQL. Le fichier ci-dessous reprend le démarrage rapide officiel avec les images épinglées par digest. Dans cette configuration, l'API répond sur le port 8080 et l'interface sur le 8081, l'inverse de nombreux exemples v4.
Prérequis
Section intitulée « Prérequis »La consommation mémoire vient surtout de la synchronisation des bases au premier démarrage. Pendant la recopie complète de la NVD, l'apiserver a consommé 683 Mio sur la limite de 2 Gio du fichier officiel, et PostgreSQL 335 Mio.
- Docker ou Podman avec Compose v2
- 4 Go de RAM libres pour un essai, davantage en production
- PostgreSQL 14 ou plus, ici fourni par le fichier Compose
- Un accès HTTPS sortant vers
nvd.nist.gov(NVD),storage.googleapis.com(OSV),www.cisa.gov(CISA KEV),raw.githubusercontent.com(ENISA EU KEV),epss.empiricalsecurity.com(EPSS) et, si vous activez GitHub Advisories,api.github.com
Démarrer la pile
Section intitulée « Démarrer la pile »Les mots de passe sont volontairement triviaux pour un essai local : les
ports sont publiés sur 127.0.0.1 uniquement. En production, la base contient
l'inventaire complet de vos composants, information précieuse pour un
attaquant qui cherche une cible vulnérable.
-
Créer le fichier
docker-compose.ymlname: dependency-trackservices:apiserver:image: ghcr.io/dependencytrack/apiserver:5.2.0@sha256:7411c390182e6e42156ccf2ab033aa9120a1f37b7ce59e8c2da0d77ec23c0a5fdepends_on:postgres:condition: service_healthydeploy:resources:limits:memory: 2genvironment:DT_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/dtrack"DT_DATASOURCE_USERNAME: "dtrack"DT_DATASOURCE_PASSWORD: "dtrack"ports:- "127.0.0.1:8080:8080"volumes:- "apiserver-data:/data"restart: unless-stoppedfrontend:image: ghcr.io/dependencytrack/frontend:5.2.0@sha256:7e68cc134284d6206e813212cf2a20c72f438d76f2094c0f05fd5d5e14602ddaenvironment:API_BASE_URL: "http://localhost:8080"ports:- "127.0.0.1:8081:8080"restart: unless-stoppedpostgres:image: postgres:18.6-alpine@sha256:77f585114c32fbca283dc835b0596f4e52b51b4c6662d7810b2f4084f60a1873environment:POSTGRES_DB: "dtrack"POSTGRES_USER: "dtrack"POSTGRES_PASSWORD: "dtrack"healthcheck:test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]interval: 5stimeout: 3sretries: 3volumes:- "postgres-data:/var/lib/postgresql"restart: unless-stoppedvolumes:apiserver-data: {}postgres-data: {} -
Démarrer les services
Fenêtre de terminal docker compose up -dCompose attend que PostgreSQL soit healthy avant de lancer l'apiserver, grâce à
condition: service_healthy. La migration du schéma s'exécute au premier démarrage. -
Vérifier que l'API répond
Fenêtre de terminal curl -s http://localhost:8080/api/version | jq .En lab, l'API a répondu 13 secondes après
docker compose up -d. La sortie doit afficher"version": "5.2.0":{"framework": {"uuid": "e56f644e-31f0-4bbf-be71-c6a5a019b805","timestamp": "2026-10-08T09:04:45Z","version": "5.2.0","name": "Alpine"},"uuid": "7cd762ce-27e3-407c-9d2c-b6067a09c313","application": "Dependency-Track","timestamp": "2026-10-08T09:05:46Z","version": "5.2.0"} -
Ouvrir l'interface
Ouvrez
http://localhost:8081et connectez-vous avecadmin/admin. Dependency-Track impose un nouveau mot de passe dès la première connexion.
Où sont la santé et la clé de chiffrement ?
Section intitulée « Où sont la santé et la clé de chiffrement ? »La v5 sépare l'API publique d'un serveur de gestion sur le port 9000,
qui expose la santé et les métriques Prometheus. Le fichier Compose ne publie
pas ce port : on l'interroge depuis le conteneur, avec wget puisque l'image
5.2 ne contient plus curl. L'ancien /api/v1/health de la v4 renvoie
désormais 404.
docker compose exec apiserver wget -q -Y off -T 3 -O - http://127.0.0.1:9000/healthLa réponse doit commencer par {"status":"UP" et lister les migrations à
l'état COMPLETED. Second point à connaître : les secrets saisis dans
l'interface (jetons GitHub, VulnCheck, webhooks) sont chiffrés en base par une
clé KEK (Key Encryption Key, la clé qui protège les secrets) créée au premier démarrage dans
/data/.dependency-track/keys/secret-management-kek.json, donc dans le volume
apiserver-data. Une sauvegarde de PostgreSQL sans ce volume restaure
des secrets indéchiffrables : sauvegardez les deux ensemble.
Installer avec Kubernetes (chart Helm 2.x)
Section intitulée « Installer avec Kubernetes (chart Helm 2.x) »Le chart communautaire dependency-track 2.x cible la v5 (la version 2.7.0
déploie la 5.2.0). Il ne crée pas de PostgreSQL et ne génère pas la clé
KEK : il faut fournir les deux. Le chart hyades, qui servait d'aperçu de la
v5, est déprécié. Cette séquence a été rejouée sur un cluster kind jetable,
avec un PostgreSQL 18.6 déployé à part dans le même namespace.
helm repo add dependencytrack https://dependencytrack.github.io/helm-chartshelm repo update
kubectl create namespace dependency-track
# Identifiants de la base (PostgreSQL 14+ déjà joignable sous le nom "postgresql")kubectl -n dependency-track create secret generic dtrack-db \ --from-literal=username=dtrack --from-literal=password='MotDePasseFort'
# Clé de chiffrement des secrets (KEK), à conserver hors du clusterkubectl -n dependency-track create secret generic dtrack-kek \ --from-literal=kek="$(openssl rand -base64 32)"
helm install dependency-track dependencytrack/dependency-track \ --version 2.7.0 \ --namespace dependency-track \ --set database.jdbcUrl=jdbc:postgresql://postgresql:5432/dtrack \ --set database.existingSecret.name=dtrack-db \ --set secretManagement.database.kek.existingSecret.name=dtrack-kek \ --set apiServer.image.tag=sha256:7411c390182e6e42156ccf2ab033aa9120a1f37b7ce59e8c2da0d77ec23c0a5f \ --set frontend.image.tag=sha256:7e68cc134284d6206e813212cf2a20c72f438d76f2094c0f05fd5d5e14602dda \ --set frontend.apiBaseUrl=https://dtrack-api.mondomaine.frLe chart accepte un digest dans image.tag et rend alors
docker.io/dependencytrack/apiserver@sha256:…, le même digest que sur
ghcr.io. Vérifiez le déploiement avec kubectl -n dependency-track get pods :
l'apiserver passe Ready une fois la migration terminée. La clé KEK générée
par openssl rand est au format config, simple mais sans rotation ; le
mode keyset (Tink) la permet.
Deux points restent à votre charge. La base PostgreSQL doit exister avant
l'installation : une instance managée de votre fournisseur ou un opérateur
Kubernetes comme CloudNativePG ; en lab, un simple Deployment PostgreSQL 18.6
et son Service nommé postgresql ont suffi. L'API doit être joignable à
l'URL passée dans frontend.apiBaseUrl, puisque c'est le navigateur qui
l'appelle : exposez le service dependency-track-api-server (port 8080) par
un Ingress ou une Gateway, au même titre que le frontend.
Migrer une instance Dependency-Track 4
Section intitulée « Migrer une instance Dependency-Track 4 »La migration v4 vers v5 est manuelle et ne se fait pas en changeant le tag
de l'image. L'instance v4 doit être en 4.14.2 au minimum et arrêtée
pendant l'opération ; l'outil v4-migrator
(ghcr.io/dependencytrack/v4-migrator) recopie les données dans une base v5
neuve. Les modèles de notification v4 sont à réécrire (schéma Protobuf,
valeurs préfixées comme GROUP_NEW_VULNERABILITY), et la recherche floue
Lucene disparaît. La procédure officielle, avec la répétition à blanc
recommandée, est dans le guide
Migrating from v4 to v5.
La v4 reste en maintenance jusqu'en décembre 2026.
Configurer l'instance après l'installation
Section intitulée « Configurer l'instance après l'installation »Trois réglages rendent l'installation exploitable : le compte admin, livré avec un mot de passe public, les sources de vulnérabilités, sans lesquelles l'outil inventorie sans alerter, et la clé d'API que consommeront vos pipelines. Tant que la première synchronisation NVD n'est pas terminée, un projet peut afficher zéro vulnérabilité tout en étant affecté.
Changer le mot de passe admin
Section intitulée « Changer le mot de passe admin »Tant que le mot de passe initial n'a pas été changé, l'API de connexion renvoie
FORCE_PASSWORD_CHANGE avec un code 401 au lieu d'un jeton. Le changement
se fait dans l'interface à la première connexion, ou par l'API :
# Première tentative : renvoie FORCE_PASSWORD_CHANGE (HTTP 401)curl -s -X POST "http://localhost:8080/api/v1/user/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'username=admin&password=admin'
# Saisir le nouveau mot de passe sans qu'il apparaisse dans l'historique du shellread -rs -p "Nouveau mot de passe admin : " NEWPASS; echo
# Changer le mot de passe (HTTP 200 attendu)curl -s -X POST "http://localhost:8080/api/v1/user/forceChangePassword" \ --data-urlencode 'username=admin' --data-urlencode 'password=admin' \ --data-urlencode "newPassword=${NEWPASS}" --data-urlencode "confirmPassword=${NEWPASS}"
# Obtenir un jeton de session pour la suiteTOKEN=$(curl -s -X POST "http://localhost:8080/api/v1/user/login" \ --data-urlencode 'username=admin' --data-urlencode "password=${NEWPASS}")--data-urlencode envoie le même formulaire que -d, mais encode les
caractères spéciaux d'un mot de passe fort (&, +, %), qui casseraient
sinon la requête.
Activer les sources de vulnérabilités
Section intitulée « Activer les sources de vulnérabilités »La page Administration > Vulnerability Sources regroupe la NVD, GitHub et OSV ; chaque panneau propose Mirror now pour lancer la première synchronisation sans attendre la planification. La v5 télécharge la NVD sous forme de flux annuels : il n'y a plus de clé d'API NVD à saisir.
Active par défaut. La recopie complète part de l'année en cours et remonte jusqu'à 2002 : en lab, elle a traité 420 230 enregistrements en 14 min 19 s. La progression se suit dans les journaux :
docker compose logs -f apiserver | grep -E 'NvdVulnDataSource|Completed mirror'Rappel : la NVD ne produit de résultat que pour les composants qui portent un CPE.
- Ouvrez Administration > Vulnerability Sources > OSV.
- Activez la source et sélectionnez seulement vos écosystèmes (npm, Maven, PyPI, Alpine, Debian...).
- Enregistrez puis cliquez sur Mirror now.
En lab, les écosystèmes npm et Alpine ont été recopiés en 1 min 18 s (235 077 avis), Maven en 4 secondes de traitement. Pour Debian, choisissez l'écosystème Debian global plutôt que les versions une par une.
- Créez un jeton personnel GitHub : il ne demande aucun scope, il sert seulement à s'authentifier. Une GitHub App est l'alternative pour un compte d'organisation.
- Ouvrez Administration > Vulnerability Sources > GitHub, activez la source et collez le jeton.
- Cliquez sur Mirror now.
OSV intègre déjà les avis GitHub : activer les deux est rarement nécessaire.
Vérifier les catalogues KEV
Section intitulée « Vérifier les catalogues KEV »Les catalogues CISA KEV et ENISA EU KEV se synchronisent seuls peu après le premier démarrage. En lab, le journal a affiché 1 741 assertions CISA et 39 assertions ENISA moins d'une minute après le lancement. Le réglage se fait dans Administration > KEV Sources, et la planification par défaut est quotidienne à 02:00 UTC.
docker compose logs apiserver | grep MirrorKevDataSourceActivitySi le miroir CISA échoue depuis certains hébergeurs, la documentation officielle
propose de remplacer l'URL du flux par le miroir GitHub de la CISA,
https://raw.githubusercontent.com/cisagov/kev-data/main/known_exploited_vulnerabilities.json.
VulnCheck KEV, plus large et souvent plus rapide, demande un jeton
communautaire gratuit.
Créer une équipe et une clé d'API pour la CI
Section intitulée « Créer une équipe et une clé d'API pour la CI »Un pipeline s'authentifie avec une clé d'API rattachée à une équipe qui ne
porte que les permissions nécessaires. Depuis la 5.2, les comptes de service
(préfixe svc:) sont l'alternative recommandée par le projet pour les nouvelles
intégrations ; les clés d'équipe restent prises en charge et c'est ce chemin,
rejoué en lab, que montre ce guide. Un compte de service se crée dans
Administration > Access Management ; sa procédure et la fédération
d'identité de charge (workload identity, qui supprime la clé longue durée)
sont décrites dans la documentation officielle,
Service accounts.
-
Créer l'équipe
Fenêtre de terminal TEAM_UUID=$(curl -s -X PUT "http://localhost:8080/api/v1/team" \-H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" \-d '{"name":"CI-CD-Integration"}' | jq -r .uuid)echo "$TEAM_UUID" -
Accorder les permissions
Fenêtre de terminal for PERM in BOM_UPLOAD PROJECT_CREATION_UPLOAD VIEW_PORTFOLIO VIEW_VULNERABILITY; docurl -s -o /dev/null -w "$PERM %{http_code}\n" -X POST \"http://localhost:8080/api/v1/permission/${PERM}/team/${TEAM_UUID}" \-H "Authorization: Bearer $TOKEN"doneChaque ligne doit afficher le code 200.
-
Générer la clé
Fenêtre de terminal curl -s -X PUT "http://localhost:8080/api/v1/team/${TEAM_UUID}/key" \-H "Authorization: Bearer $TOKEN" | jq -r .keyLa clé commence par
odt_et compte 46 caractères. La réponse contient aussi unmaskedKey(odt_aHdRHy6Z****…) : c'est la seule forme que l'interface affichera ensuite.
Quelles permissions donner à une clé de CI ?
Section intitulée « Quelles permissions donner à une clé de CI ? »Une équipe CI/CD n'a besoin que des quatre premières lignes du tableau :
VIEW_PORTFOLIO et VIEW_VULNERABILITY servent à relire les résultats
dans le pipeline. Les permissions suivantes autorisent la modification des
verdicts d'analyse et des politiques : accordées à un pipeline, elles
permettraient à une clé compromise de marquer une vulnérabilité comme non
applicable et de faire disparaître l'alerte au lieu de la corriger. La v5
ajoute aussi des variantes fines comme VULNERABILITY_ANALYSIS_READ.
| Permission | Rôle |
|---|---|
BOM_UPLOAD | Envoyer des SBOM |
PROJECT_CREATION_UPLOAD | Créer le projet automatiquement à l'envoi |
VIEW_PORTFOLIO | Lire les projets et composants |
VIEW_VULNERABILITY | Lire les vulnérabilités |
VULNERABILITY_ANALYSIS | Modifier l'analyse des vulnérabilités |
PORTFOLIO_MANAGEMENT | Gérer les projets et composants |
POLICY_MANAGEMENT | Gérer les politiques |
SYSTEM_CONFIGURATION | Configuration système |
Envoyer un SBOM à Dependency-Track
Section intitulée « Envoyer un SBOM à Dependency-Track »Dependency-Track ne scanne rien lui-même : il consomme des SBOM produits
ailleurs. La qualité de l'analyse dépend donc du générateur, de ses
identifiants (PURL, CPE) et de son bloc dependencies. Un composant
sans identifiant exploitable reste listé mais n'est jamais rapproché d'une CVE,
et un SBOM sans relation vers la racine donne un projet sans arbre.
Générer un SBOM avec Trivy ou Syft
Section intitulée « Générer un SBOM avec Trivy ou Syft »Les deux outils produisent du CycloneDX 1.7 dans les versions testées (Trivy 0.75.0, Syft 1.54.1). Les commandes sont équivalentes ; leur différence de contenu fait l'objet de la section « Syft ou Trivy ».
# Trivy : un projet (lit package-lock.json, pom.xml, go.sum...)trivy fs --format cyclonedx --output sbom.json .
# Trivy : une image de conteneurtrivy image --format cyclonedx --output nginx-sbom.json \ nginx:1.29.2-alpine@sha256:61e01287e546aac28a3f56839c136b31f590273f3b41187a36f46f6a03bbfe22
# Syft : un projetsyft dir:. -o cyclonedx-json=sbom-syft.json
# Syft : une image, sans inventaire de fichiers (voir plus bas)SYFT_FILE_METADATA_SELECTION=none syft \ nginx:1.29.2-alpine@sha256:61e01287e546aac28a3f56839c136b31f590273f3b41187a36f46f6a03bbfe22 \ -o cyclonedx-json=nginx-sbom-syft.jsonLes fichiers Syft portent un suffixe -syft parce qu'ils passent par une
correction du graphe avant l'envoi (voir la section « Syft ou Trivy »). Les
exemples d'envoi qui suivent utilisent le sbom.json de Trivy.
Un projet d'exemple : une application Java avec Log4j 2.14.1
Section intitulée « Un projet d'exemple : une application Java avec Log4j 2.14.1 »Pour reproduire les sorties de ce guide (vulnérabilités KEV, alerte,
violations de politique), créez un projet Maven minimal qui déclare
log4j-core 2.14.1, la version touchée par Log4Shell. Trivy lit le
pom.xml et en tire trois composants : l'application, log4j-core et
log4j-api.
mkdir legacy-app && cd legacy-appcat > pom.xml <<'EOF'<project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>fr.exemple</groupId> <artifactId>legacy-app</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.14.1</version> </dependency> </dependencies></project>EOFtrivy fs --format cyclonedx --output sbom.json .Les avis Maven arrivent par OSV (écosystème Maven) ou GitHub Advisories : activez l'un des deux avant l'envoi, sans quoi le projet reste à zéro vulnérabilité.
Envoyer le SBOM par l'API
Section intitulée « Envoyer le SBOM par l'API »Le formulaire multipart (POST /api/v1/bom) est la méthode la plus simple
et ne souffre d'aucune limite de longueur de ligne de commande. Le champ
autoCreate crée le projet s'il n'existe pas, à partir du couple
projectName et projectVersion. Corollaire : une faute de frappe sur le nom
crée un projet parallèle au lieu d'échouer.
read -rs -p "Clé d'API : " API_KEY; echo
RESP=$(curl -s -X POST "http://localhost:8080/api/v1/bom" \ -H "X-Api-Key: $API_KEY" \ -F "autoCreate=true" \ -F "projectName=legacy-app" \ -F "projectVersion=1.0.0" \ -F "bom=@sbom.json")echo "$RESP" | jq .BOM_TOKEN=$(echo "$RESP" | jq -r .token)PROJECT_UUID=$(echo "$RESP" | jq -r .projectUuid)read -rs -p "Clé d'API : " API_KEY; echojq -n --arg bom "$(base64 -w 0 sbom.json)" \ '{projectName: "legacy-app", projectVersion: "1.0.0", autoCreate: true, bom: $bom}' \ > payload.json
RESP=$(curl -s -X PUT "http://localhost:8080/api/v1/bom" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ --data @payload.json)echo "$RESP" | jq .BOM_TOKEN=$(echo "$RESP" | jq -r .token)PROJECT_UUID=$(echo "$RESP" | jq -r .projectUuid)Passer par un fichier évite l'erreur Argument list too long dès que le SBOM
dépasse quelques centaines de kilo-octets.
Les deux méthodes renvoient un jeton de traitement et, en v5, l'UUID du
projet. Les variables BOM_TOKEN et PROJECT_UUID capturées ici servent à
toutes les commandes suivantes du guide :
{ "token": "01a13926-efdb-75a1-a1d7-903fb5297923", "projectUuid": "f43f0501-daf5-4bb0-b5e9-a1324b80bf6c"}La taille maximale d'un SBOM est de 100 Mio par défaut
(DT_BOM_UPLOAD_MAX_SIZE_BYTES) ; au-delà, l'API répond 413.
Suivre le traitement
Section intitulée « Suivre le traitement »Le traitement est asynchrone : le jeton permet de savoir quand l'inventaire et la première analyse sont terminés.
curl -s "http://localhost:8080/api/v1/bom/token/${BOM_TOKEN}" \ -H "X-Api-Key: $API_KEY"La sortie passe de "processing":true à :
{"processing":false,"status":"COMPLETED"}Syft ou Trivy : pourquoi le graphe de dépendances reste vide
Section intitulée « Syft ou Trivy : pourquoi le graphe de dépendances reste vide »Avec Syft 1.54.1, Dependency-Track 5.2 importe bien les composants et leurs
relations, mais le projet n'a aucune dépendance directe : la vue
Dependency Graph n'a pas de point de départ et reste vide. Avec Trivy
0.75.0, le même projet a un arbre complet. La cause n'est pas le format des
bom-ref en PURL : Syft n'écrit aucune entrée dependencies pour le
composant racine décrit dans metadata.component, alors que Trivy y rattache
l'application ou le système d'exploitation.
Le schéma compare les deux arbres obtenus pour le même projet Node.js : à gauche, la racine Trivy est reliée au fichier de verrouillage, qui mène aux bibliothèques ; à droite, la racine Syft reste orpheline alors que les bibliothèques sont bien reliées entre elles.
Ce que nous avons mesuré
Section intitulée « Ce que nous avons mesuré »Le test a porté sur deux cibles analysées par les deux outils, puis envoyées
dans quatre projets d'une instance 5.2.0 : un projet Node.js (express
4.18.2, lodash 4.17.20, 70 paquets dans package-lock.json) et l'image
nginx:1.29.2-alpine. La colonne de droite est la valeur renvoyée par
GET /api/v1/dependencyGraph/project/{uuid}/directDependencies.
| Cible | Générateur | Composants | Relations dependsOn | Lien depuis la racine | Dépendances directes du projet |
|---|---|---|---|---|---|
| Projet Node.js | Trivy 0.75.0 | 70 | 128 | Oui | 1 (package-lock.json) |
| Projet Node.js | Syft 1.54.1 | 71 | 129 | Non | 0 |
| Image nginx | Trivy 0.75.0 | 71 | 179 | Oui | 1 (alpine) |
| Image nginx | Syft 1.54.1 | 1 306 | 165 | Non | 0 |
Les relations entre bibliothèques existent dans les deux cas : 28 composants du
projet Syft portent bien leurs propres enfants dans Dependency-Track. Il manque
seulement le premier maillon. Deuxième écart sur l'image : Syft inventorie
par défaut les fichiers possédés par les paquets (1 235 composants de type
file), qui gonflent le projet sans rien apporter à l'analyse.
SYFT_FILE_METADATA_SELECTION=none ramène l'inventaire à 71 composants.
Rattacher la racine avec jq
Section intitulée « Rattacher la racine avec jq »En attendant que Syft produise ce lien, une transformation jq ajoute l'entrée manquante : elle rattache au composant racine toutes les bibliothèques dont aucun autre composant ne dépend, c'est-à-dire les dépendances de premier niveau.
jq '(.metadata.component["bom-ref"]) as $root | ([.dependencies[]?.dependsOn[]?] | unique) as $children | ([.components[] | select(.type == "library") | .["bom-ref"]] - $children) as $top | .dependencies = ([.dependencies[]? | select(.ref != $root)] + [{ref: $root, dependsOn: $top}])' \ sbom-syft.json > sbom-syft-graphe.jsonEnvoyé dans Dependency-Track, le SBOM corrigé donne 1 dépendance directe
au projet Node.js (demo-app@1.0.0, que Syft déclare et qui dépend d'express et
de lodash) et 13 à l'image nginx (les paquets Alpine de premier niveau).
C'est une reconstruction : pour une image, ces 13 paquets sont ceux que rien
d'autre ne requiert, pas forcément ceux qu'a installés le Dockerfile.
Et les vulnérabilités trouvées ?
Section intitulée « Et les vulnérabilités trouvées ? »Le graphe n'est pas la seule différence. Syft ajoute des CPE devinés à chaque paquet, Trivy n'en met pas. Résultat mesuré après une recopie complète de NVD, OSV (npm, Alpine) et sans GitHub Advisories :
| Projet | Trivy 0.75.0 | Syft 1.54.1 |
|---|---|---|
| Node.js | 18 (avis GitHub via OSV) | 31 (les 18 mêmes + 13 entrées NVD) |
| Image nginx | 25 (OSV Alpine) | 39 (les 25 mêmes + 14 entrées NVD) |
Sur le projet Node.js, les 13 entrées NVD de Syft sont toutes des
doublons : les mêmes CVE déjà présentes sous leur identifiant GHSA-. Sur
l'image, les 14 entrées NVD portent sur libpng, zlib, musl et c-ares,
rapprochées par CPE sans tenir compte des correctifs rétroportés par Alpine :
elles demandent une vérification au cas par cas. Retenez surtout l'inverse : un
SBOM Trivy sur une instance où seule la NVD est active ne produit aucun
résultat, faute de CPE. Activez OSV ou GitHub dans ce cas.
Consulter les résultats
Section intitulée « Consulter les résultats »Les endpoints ci-dessous alimentent aussi l'interface et se prêtent à un export
vers un outil de suivi. Deux changements v5 à connaître : les listes sont
paginées à 100 éléments par défaut (pageSize et pageNumber), et les
métriques du portefeuille sont un instantané recalculé périodiquement, alors
que celles d'un projet sont à jour après chaque analyse.
Lister les vulnérabilités d'un projet
Section intitulée « Lister les vulnérabilités d'un projet »L'endpoint attend l'UUID du projet, renvoyé à l'envoi du SBOM. Le champ
source indique quelle base a produit le résultat, utile quand deux
sources divergent sur la sévérité. Les findings exposent aussi isKev et
epssScore pour la priorisation.
curl -s "http://localhost:8080/api/v1/finding/project/${PROJECT_UUID}?pageSize=500" \ -H "X-Api-Key: $API_KEY" \ | jq -c '.[] | {composant: .component.name, id: .vulnerability.vulnId, source: .vulnerability.source, severite: .vulnerability.severity, kev: .vulnerability.isKev, epss: .vulnerability.epssScore}'Extrait réel sur le projet legacy-app, qui embarque log4j-core 2.14.1 :
{"composant":"log4j-core","id":"GHSA-jfh8-c2jp-5v3q","source":"GITHUB","severite":"CRITICAL","kev":true,"epss":0.99999}{"composant":"log4j-core","id":"GHSA-7rjr-3q55-vv33","source":"GITHUB","severite":"CRITICAL","kev":true,"epss":0.99977}{"composant":"log4j-core","id":"GHSA-p6xc-xr62-6r2g","source":"GITHUB","severite":"HIGH","kev":false,"epss":0.99999}Deux des huit vulnérabilités du composant sont KEV : Log4Shell
(CVE-2021-44228) et CVE-2021-45046, reconnues par leurs alias alors que
l'avis est d'origine GitHub. Le troisième exemple montre pourquoi on croise
les signaux : un EPSS élevé sans entrée KEV reste à surveiller.
Lire les métriques d'un projet et du portefeuille
Section intitulée « Lire les métriques d'un projet et du portefeuille »Le bloc metrics d'un projet compte désormais aussi les vulnérabilités KEV
(champ kev). Pour le portefeuille, un appel à /refresh force le recalcul
quand on veut un chiffre immédiat après un envoi.
# Un projetcurl -s "http://localhost:8080/api/v1/project/${PROJECT_UUID}" -H "X-Api-Key: $API_KEY" \ | jq '{name, version, metrics: (.metrics | {critical, high, medium, low, kev, components, vulnerableComponents})}'
# Le portefeuille (instantané)curl -s "http://localhost:8080/api/v1/metrics/portfolio/current" -H "X-Api-Key: $API_KEY" \ | jq '{projects, vulnerableProjects, critical, high, medium, low}'L'indicateur le plus parlant pour un pilotage est le rapport entre projects
et vulnerableProjects, qui se suit dans le temps bien mieux qu'un total brut
de CVE.
Intégrer Dependency-Track dans un pipeline CI/CD
Section intitulée « Intégrer Dependency-Track dans un pipeline CI/CD »Les trois pipelines suivants appliquent le même schéma : générer le SBOM pendant le build, puis l'envoyer avec une clé d'API stockée en secret. Ils n'échouent pas sur la présence de vulnérabilités : l'envoi est asynchrone et le job se termine avant l'analyse. Bloquer une release suppose d'attendre le jeton puis d'interroger les violations de politique du projet.
GitLab CI
Section intitulée « GitLab CI »La projectVersion alimentée par CI_COMMIT_REF_NAME crée un projet par
branche, ce qui permet de comparer une branche à main mais multiplie les
entrées si les branches ne sont jamais nettoyées. DTRACK_URL et
DTRACK_API_KEY sont déclarées comme variables CI/CD masquées du projet.
stages: - build - security
generate_sbom: stage: build image: name: aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa entrypoint: [""] script: - trivy fs --format cyclonedx --output sbom.json . artifacts: paths: - sbom.json expire_in: 1 week
upload_sbom: stage: security image: curlimages/curl:8.22.0@sha256:58adaa4e8dca9c988bae2aba4ab3434a0bb2da16bbe3f92dec39ec7785166777 needs: - generate_sbom script: - > curl --fail -s -X POST "${DTRACK_URL}/api/v1/bom" -H "X-Api-Key: ${DTRACK_API_KEY}" -F "autoCreate=true" -F "projectName=${CI_PROJECT_NAME}" -F "projectVersion=${CI_COMMIT_REF_NAME}" -F "bom=@sbom.json"GitHub Actions
Section intitulée « GitHub Actions »Le workflow ne tourne que sur push vers main : les pull requests de
dépôts forkés n'ont pas accès aux secrets, et un workflow qui en dépend
échouerait de toute façon. Les secrets passent par env: plutôt que par une
expression ${{ }} dans le script, ce qui évite l'injection de modèle
signalée par zizmor. L'action anchore/sbom-action télécharge Syft au
démarrage (la v0.24.3 prend la 1.54.0 par défaut, syft-version l'aligne sur la
1.54.1 mesurée dans ce guide) : le
workflow applique donc la correction jq de la section « Syft ou Trivy »
avant l'envoi, faute de quoi le projet n'aurait aucun graphe. jq est
préinstallé sur les runners ubuntu-latest.
name: SBOM Upload
on: push: branches: [main]
permissions: {}
jobs: sbom: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Generate SBOM uses: anchore/sbom-action@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3 with: syft-version: v1.54.1 format: cyclonedx-json output-file: sbom-syft.json upload-artifact: false
- name: Rattacher la racine du graphe (SBOM Syft) run: | jq '(.metadata.component["bom-ref"]) as $root | ([.dependencies[]?.dependsOn[]?] | unique) as $children | ([.components[] | select(.type == "library") | .["bom-ref"]] - $children) as $top | .dependencies = ([.dependencies[]? | select(.ref != $root)] + [{ref: $root, dependsOn: $top}])' \ sbom-syft.json > sbom.json
- name: Upload to Dependency-Track env: DTRACK_URL: ${{ secrets.DTRACK_URL }} DTRACK_API_KEY: ${{ secrets.DTRACK_API_KEY }} PROJECT_NAME: ${{ github.repository }} PROJECT_VERSION: ${{ github.ref_name }} run: | curl --fail -s -X POST "${DTRACK_URL}/api/v1/bom" \ -H "X-Api-Key: ${DTRACK_API_KEY}" \ -F "autoCreate=true" \ -F "projectName=${PROJECT_NAME}" \ -F "projectVersion=${PROJECT_VERSION}" \ -F "bom=@sbom.json"credentials() injecte la clé dans l'environnement du job ; Jenkins masque sa
valeur dans les journaux, mais pas dans un fichier que le script écrirait
lui-même. Ici la version du projet suit le BUILD_NUMBER, ce qui crée une
entrée par build et rend l'historique verbeux : préférez le tag Git pour une
release.
// Jenkinsfilepipeline { agent any
environment { DTRACK_URL = 'https://dependency-track.mondomaine.fr' DTRACK_API_KEY = credentials('dtrack-api-key') }
stages { stage('Generate SBOM') { steps { sh ''' docker run --rm -v "$PWD:/app" \ aquasec/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa \ fs --format cyclonedx --output /app/sbom.json /app ''' } }
stage('Upload SBOM') { steps { sh ''' curl --fail -s -X POST "${DTRACK_URL}/api/v1/bom" \ -H "X-Api-Key: ${DTRACK_API_KEY}" \ -F "autoCreate=true" \ -F "projectName=${JOB_NAME}" \ -F "projectVersion=${BUILD_NUMBER}" \ -F "bom=@sbom.json" ''' } } }}Être alerté des nouvelles vulnérabilités
Section intitulée « Être alerté des nouvelles vulnérabilités »C'est la fonction qui justifie la centralisation : une CVE publiée demain sur un composant présent dans plusieurs projets déclenche une alerte sans nouveau build. Le piège est inverse de celui des scanners : mal filtrées, les notifications deviennent un bruit que plus personne ne lit. La v5 apporte la réponse avec les expressions de filtre en CEL (Common Expression Language), qui permettent par exemple de ne notifier que les vulnérabilités KEV.
Créer une alerte
Section intitulée « Créer une alerte »Les canaux sont des publishers déjà présents dans l'instance : Slack,
Microsoft Teams, Mattermost, Webex, Jira, email, Kafka, webhook et console. Une
alerte associe un publisher, une portée (PORTFOLIO ou SYSTEM), des
groupes d'événements et, optionnellement, un filtre.
-
Ouvrir Administration > Notifications > Alerts et créer une alerte de portée Portfolio.
-
Choisir le publisher (Slack par exemple) et renseigner sa destination : pour Slack, le seul paramètre est l'URL de webhook (
destinationUrl), qui est un secret à part entière. -
Cocher les groupes voulus, par exemple
NEW_VULNERABILITY. -
Saisir une expression de filtre pour réduire le bruit, par exemple
subject.vulnerability.is_kevpour ne recevoir que les vulnérabilités exploitées.
Le même réglage se fait par l'API, en deux temps (création puis mise à jour du filtre). Dans ce test, le publisher console écrit les notifications dans le journal de l'apiserver :
PUB=$(curl -s "http://localhost:8080/api/v1/notification/publisher" -H "Authorization: Bearer $TOKEN" \ | jq -r '.[] | select(.extensionName=="console") | .uuid')
RULE=$(curl -s -X PUT "http://localhost:8080/api/v1/notification/rule" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d "{\"name\":\"KEV uniquement\",\"scope\":\"PORTFOLIO\",\"level\":\"INFORMATIONAL\",\"publisher\":{\"uuid\":\"$PUB\"}}" \ | jq -r .uuid)
curl -s -X POST "http://localhost:8080/api/v1/notification/rule" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d "{\"uuid\":\"$RULE\",\"name\":\"KEV uniquement\",\"scope\":\"PORTFOLIO\",\"level\":\"INFORMATIONAL\",\"enabled\":true,\"notifyOn\":[\"NEW_VULNERABILITY\"],\"filterExpression\":\"subject.vulnerability.is_kev\"}"À l'envoi du SBOM contenant log4j-core 2.14.1, le journal a reçu 2
notifications GROUP_NEW_VULNERABILITY, une par vulnérabilité KEV, et aucune
pour les six autres.
Quels événements déclenchent une notification ?
Section intitulée « Quels événements déclenchent une notification ? »Deux groupes sont à distinguer soigneusement. NEW_VULNERABILITY signale une
CVE nouvellement rapprochée d'un composant, y compris sur un projet déjà
inventorié ; NEW_VULNERABLE_DEPENDENCY signale un composant vulnérable que
vous venez d'introduire. Le premier est un signal de veille, le second
un défaut de développement. Les groupes …_SUMMARY sont planifiés par une
expression cron et envoient un résumé.
| Groupe | Déclencheur |
|---|---|
NEW_VULNERABILITY | Nouvelle vulnérabilité sur un composant |
NEW_VULNERABLE_DEPENDENCY | Nouveau composant vulnérable ajouté |
POLICY_VIOLATION | Violation d'une politique |
BOM_PROCESSED / BOM_PROCESSING_FAILED | Fin ou échec de traitement d'un SBOM |
VULNERABILITY_RETRACTED | Vulnérabilité retirée par sa source |
PROJECT_AUDIT_CHANGE | Changement de statut d'analyse |
NEW_VULNERABILITIES_SUMMARY | Résumé planifié des nouvelles vulnérabilités |
NEW_POLICY_VIOLATIONS_SUMMARY | Résumé planifié des violations |
Les modèles de message utilisent Pebble et un schéma Protobuf : un
modèle écrit pour la v4 doit être réécrit (champs suffixés List, valeurs
préfixées comme LEVEL_INFORMATIONAL). Depuis la 5.2, les connexions sortantes
(notifications, sources de données, analyseurs) sont filtrées par
DT_OUTBOUND_ALLOWED_DESTINATIONS (par défaut external,private) : une
destination en boucle locale comme localhost doit y être ajoutée, et les
adresses link-local ou de métadonnées cloud restent refusées. Les charges utiles
des webhooks peuvent être signées en HMAC-SHA256.
Définir des politiques de conformité
Section intitulée « Définir des politiques de conformité »Les politiques évaluent chaque composant et lèvent une violation quand une condition est remplie. En v5, le moteur repose sur CEL : l'éditeur garde ses conditions structurées et ajoute une condition Expression pour les cas qu'elles ne couvrent pas, dont le signal KEV.
Quelles conditions sont disponibles ?
Section intitulée « Quelles conditions sont disponibles ? »La condition License est la seule qui traite de conformité juridique et non de sécurité : c'est le moyen d'attraper l'arrivée d'une dépendance sous licence incompatible. Age et Version distance servent d'indicateurs indirects d'abandon ou de retard, et Expression accepte une règle CEL libre.
| Condition | Ce qu'elle teste |
|---|---|
| Severity | Sévérité des vulnérabilités (CRITICAL, HIGH...) |
| EPSS | Score EPSS le plus élevé du composant |
| Vulnerability ID | Une vulnérabilité précise |
| CWE | Catégorie de faiblesse (CWE-79, CWE-89...) |
| License / License group | Licences ou groupes de licences |
| Coordinates / Package URL / CPE | Un composant précis |
| Age / Version distance | Ancienneté, retard sur la dernière version |
| Component hash | Empreinte d'un composant |
| Expression | Expression CEL libre |
Créer une politique
Section intitulée « Créer une politique »Le niveau Fail n'interrompt aucun build par lui-même : Dependency-Track
enregistre la violation et émet un événement POLICY_VIOLATION, à charge pour
votre pipeline d'interroger l'API et de décider. Warn et Info servent à
la priorisation. Une politique sans projet assigné s'applique à tout le
portefeuille.
-
Ouvrir Policy Management > Policies et cliquer sur Create Policy
-
Ajouter une condition, par exemple Severity IS CRITICAL
-
Choisir le niveau de violation : Fail, Warn ou Info
-
Restreindre la portée si besoin, à des projets ou à des tags
Exemple : signaler tout composant touché par une vulnérabilité KEV
Section intitulée « Exemple : signaler tout composant touché par une vulnérabilité KEV »Cette politique utilise la condition Expression, de type SECURITY. Elle
lève une violation pour tout composant qui porte au moins une vulnérabilité
présente dans un catalogue KEV actif.
POLICY=$(curl -s -X PUT "http://localhost:8080/api/v1/policy" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"name":"Vulnérabilités exploitées (KEV)","operator":"ANY","violationState":"FAIL"}' \ | jq -r .uuid)
curl -s -X PUT "http://localhost:8080/api/v1/policy/${POLICY}/condition" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"subject":"EXPRESSION","violationType":"SECURITY","value":"vulns.exists(vuln, vuln.is_kev)"}'Pour inclure aussi les vulnérabilités pas encore cataloguées mais à forte
probabilité d'exploitation, la documentation officielle propose
vulns.exists(vuln, vuln.is_kev || vuln.epss_score > 0.5).
Exemple : interdire les versions de Log4j antérieures à 2.17.1
Section intitulée « Exemple : interdire les versions de Log4j antérieures à 2.17.1 »La condition COORDINATES vise un composant par son groupe, son nom et un intervalle de version, indépendamment des bases de CVE. C'est le moyen d'interdire durablement une version que vous savez problématique, y compris avant publication d'un identifiant officiel.
POLICY=$(curl -s -X PUT "http://localhost:8080/api/v1/policy" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"name":"Bloquer Log4j < 2.17.1","operator":"ANY","violationState":"FAIL"}' \ | jq -r .uuid)
curl -s -X PUT "http://localhost:8080/api/v1/policy/${POLICY}/condition" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"subject":"COORDINATES","operator":"MATCHES","violationType":"OPERATIONAL","value":"{\"group\":\"org.apache.logging.log4j\",\"name\":\"log4j-core\",\"version\":\"<2.17.1\"}"}'Après un nouvel envoi du SBOM, les violations se lisent ainsi :
curl -s "http://localhost:8080/api/v1/violation/project/${PROJECT_UUID}" \ -H "Authorization: Bearer $TOKEN" \ | jq -c '.[] | {politique: .policyCondition.policy.name, type, composant: .component.name}'{"politique":"Bloquer Log4j < 2.17.1","type":"OPERATIONAL","composant":"log4j-core"}{"politique":"Vulnérabilités exploitées (KEV)","type":"SECURITY","composant":"log4j-core"}Regrouper des projets en collection
Section intitulée « Regrouper des projets en collection »Une application découpée en quinze microservices apparaît comme quinze projets sans lien, et personne ne sait répondre à la question de sa posture globale. Un projet collection agrège les métriques de ses enfants sans dupliquer leurs composants. Les enfants s'y rattachent par la relation parent, l'agrégation ne se déduit pas des noms.
curl -s -X PUT "http://localhost:8080/api/v1/project" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "Platform E-Commerce", "version": "1.0", "collectionLogic": "AGGREGATE_DIRECT_CHILDREN"}'La v5 supprime la valeur NONE (un projet ordinaire n'a simplement pas de
collectionLogic) et propose trois logiques :
| Logique | Agrège |
|---|---|
AGGREGATE_DIRECT_CHILDREN | Tous les enfants directs |
AGGREGATE_DIRECT_CHILDREN_WITH_TAG | Les enfants directs portant le tag de collection, obligatoire |
AGGREGATE_LATEST_VERSION_CHILDREN | Les enfants directs marqués comme dernière version |
Dépannage
Section intitulée « Dépannage »La panne la plus dangereuse ne se voit pas : un projet qui affiche zéro vulnérabilité. Ce résultat peut signifier que tout va bien, que les bases ne sont pas encore recopiées, ou que les composants n'ont pas l'identifiant attendu par les sources actives. Avant de conclure, vérifiez le nombre de composants, les sources activées et l'état du miroir.
Problèmes d'installation
Section intitulée « Problèmes d'installation »Les défaillances de démarrage viennent le plus souvent d'une configuration v4 recopiée telle quelle, ou d'une image tirée sans tag précis.
| Symptôme | Cause probable | Solution |
|---|---|---|
| L'API annonce une version 4.x | Image tirée avec latest, qui pointe sur la v4 | Épingler 5.2.0 et son digest |
| L'apiserver ne se connecte pas à la base | Variables v4 ALPINE_DATABASE_* | Utiliser DT_DATASOURCE_URL, _USERNAME, _PASSWORD |
| Refus de démarrer après mise à jour | Propriété allow-local-connections encore présente (5.2) | La remplacer par DT_OUTBOUND_ALLOWED_DESTINATIONS |
| Interface vide ou erreur réseau | API_BASE_URL du frontend incorrecte | Pointer vers l'URL de l'API vue par le navigateur |
/api/v1/health répond 404 | Endpoint v4 | Santé sur le port 9000 : /health |
| Secrets illisibles après restauration | Volume /data (clé KEK) non sauvegardé | Sauvegarder base et volume ensemble |
Problèmes d'envoi et de résultats
Section intitulée « Problèmes d'envoi et de résultats »Un 403 se distingue d'un 401 : la clé est valide mais l'équipe n'a pas la
permission demandée. C'est le cas typique d'une équipe créée sans
PROJECT_CREATION_UPLOAD, qui réussit vers un projet existant et échoue dès
qu'autoCreate doit intervenir.
| Symptôme | Cause probable | Solution |
|---|---|---|
| 401 Unauthorized | Clé invalide ou révoquée | Vérifier la clé, en générer une nouvelle |
| 403 Forbidden | Permission manquante | Ajouter BOM_UPLOAD et PROJECT_CREATION_UPLOAD |
| 413 Payload Too Large | SBOM au-delà de 100 Mio | Augmenter DT_BOM_UPLOAD_MAX_SIZE_BYTES |
| Graphe de dépendances vide | SBOM Syft sans lien depuis la racine | Rattacher la racine avec jq, ou générer avec Trivy |
| Zéro vulnérabilité avec un SBOM Trivy | Seule la NVD est active, et Trivy n'émet pas de CPE | Activer OSV ou GitHub Advisories |
| Liste tronquée à 100 éléments | Pagination v5 par défaut | Passer pageSize et pageNumber |
| Colonne KEV vide | Miroir KEV en échec | Vérifier les journaux MirrorKevDataSourceActivity |
Commandes de diagnostic
Section intitulée « Commandes de diagnostic »L'endpoint /api/version répond sans authentification et confirme que
l'apiserver tourne ; la santé détaillée, migrations comprises, est sur le port
de gestion.
docker compose psdocker compose logs -f apiservercurl -s http://localhost:8080/api/version | jq .versiondocker compose exec apiserver wget -q -Y off -T 3 -O - http://127.0.0.1:9000/healthÀ retenir
Section intitulée « À retenir »Un point domine tous les autres : Dependency-Track ne vous protège que des vulnérabilités portant sur des composants effectivement inventoriés et identifiés comme les sources actives l'attendent. Ce qui manque au SBOM n'existe pas pour l'outil, et un tableau de bord vert reflète la qualité de vos SBOM avant de refléter votre sécurité.
- Dependency-Track 5 exige PostgreSQL 14+, se configure par
DT_DATASOURCE_*et se migre à la main depuis la 4.14.2 ; le taglatestdésigne encore la v4. - Sur une installation neuve, seule la NVD est active et ne rapproche que par CPE : activez OSV pour les paquets open source.
- Les catalogues CISA KEV et ENISA EU KEV sont actifs par défaut depuis la 5.1 ; filtrez alertes et politiques sur
is_kevpour traiter d'abord ce qui est exploité. - Un SBOM Syft 1.54.1 n'a pas de lien depuis la racine : le graphe reste vide jusqu'à la correction jq ; Trivy 0.75.0 le fournit.
- La clé d'API n'est affichée qu'une fois ; préférez les comptes de service (5.2) pour les nouvelles intégrations.
- L'envoi est asynchrone : suivez le jeton avant d'interroger résultats et violations.
- Sauvegardez PostgreSQL et le volume
/dataensemble, sans quoi les secrets chiffrés sont perdus. - Preuve attendue par le SOCLE (SOCLE-VLN-INV-1 et SOCLE-VLN-INV-2) : une plateforme de corrélation multi-sources reliant les vulnérabilités aux artefacts via SBOM, avec des vulnérabilités enrichies par CISA KEV et EPSS au-delà du CVSS. Les projets alimentés par la CI et la colonne KEV de leurs findings constituent cette preuve.
Prochaines étapes
Section intitulée « Prochaines étapes »Deux directions prolongent utilement cette mise en place. En amont, améliorer la qualité des SBOM, puisque c'est elle qui borne tout ce que l'outil peut détecter. En aval, documenter l'exploitabilité réelle avec des documents VEX, que Dependency-Track importe, et réduire le flux d'alertes à la source avec Renovate.
Ressources
Section intitulée « Ressources »La documentation officielle a déménagé avec la v5 et fait référence sur deux
sujets que ce guide ne détaille pas : la liste complète des propriétés de
configuration (variables DT_*) et les notes de mise à jour, à lire avant
chaque montée de version mineure.
- Documentation v5 : https://dependencytrack.github.io/docs/
- Changements de la v5 : https://dependencytrack.github.io/docs/next/concepts/changes-in-v5/
- Notes de mise à jour : https://dependencytrack.github.io/docs/next/guides/upgrading/
- Dépôt GitHub : https://github.com/DependencyTrack/dependency-track
- Releases : https://github.com/DependencyTrack/dependency-track/releases
- Chart Helm : https://github.com/DependencyTrack/helm-charts