Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English
Trivy a été compromis en mars 2026 (avis GHSA-69fq-xp46-6x23) : binaire 0.69.4, images Docker Hub 0.69.5 et 0.69.6, trivy-action avant 0.35.0, setup-trivy avant 0.2.6. Utilisez la version 0.72.0 ou plus récente, épinglée par digest. Mon analyse

Trivy : scanner de vulnérabilités, secrets et IaC

Par mis à jour le

Améliorer cette page

Trivy

Trivy est le scanner de sécurité open source d'Aqua Security, publié en 2019 et suivi par plus de 38 000 étoiles sur GitHub. En une commande, il cherche les vulnérabilités connues des dépendances et des images, les secrets oubliés dans le code et les mauvaises configurations de l'infrastructure as code. Cette leçon s'adresse à un DevOps qui veut un seul outil pour ces trois contrôles, et le faire bloquer en CI.

Trivy est aussi devenu, en 2026, un cas d'école de ce qu'il est censé détecter : en mars, des identifiants volés ont permis de publier une version piégée et de déplacer 76 des 77 tags de son action GitHub vers un voleur de secrets. On l'installe donc comme n'importe quelle dépendance, épinglé. Tout le reste est mesuré sur Trivy 0.74.0 : 4 vulnérabilités, un jeton GitHub et 10 erreurs de configuration sur un projet de test, 1411 vulnérabilités sur l'image node:14, et un trivy.yaml de deux lignes qui vide le rapport sans un mot.

  • Installer Trivy en version épinglée, après ses deux incidents de 2026
  • Scanner dépendances, secrets, images et infrastructure as code
  • Faire échouer un job CI sur un seuil décidé, et non sur le défaut
  • Neutraliser le piège du trivy.yaml chargé automatiquement
  • Produire un SBOM et le rescanner sans le code source
  • Situer ce que Trivy ne voit pas, et quand croiser avec Grype

Trivy combine une cible, ce qu'on scanne, et des scanners, ce qu'on y cherche. La même commande sert ainsi à vérifier un dépôt avant le merge, une image avant la publication ou un dossier Terraform avant le déploiement. Il s'appuie sur une base de vulnérabilités qu'il télécharge et met en cache, et sur des règles intégrées pour les erreurs de configuration.

CibleCommandeUsage typique
Dossier ou dépôttrivy fs .Dépendances, secrets, IaC d'un projet
Image de conteneurtrivy image <image>Paquets de l'OS et des langages dans l'image
Fichiers de configurationtrivy config .Dockerfile, Terraform, manifestes Kubernetes
SBOM existanttrivy sbom sbom.jsonRescanner un inventaire sans le code
Système complettrivy rootfs /Paquets installés sur une machine
Clustertrivy k8sWorkloads et configuration d'un cluster

Les scanners s'activent avec --scanners : vuln (vulnérabilités connues), secret (jetons et mots de passe), misconfig (erreurs de configuration) et license (licences). Mesuré : sur un dossier, trivy fs lance vuln et secret sans qu'on le demande, et n'analyse les configurations qu'avec misconfig explicitement demandé.

Après les incidents de 2026, la règle est simple : on n'installe pas « la dernière », on installe une version précise, au moins la 0.72.0, vérifiée. L'image officielle épinglée par digest est la forme la plus sûre, en CI comme sur le poste.

Fenêtre de terminal
TRIVY="aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969"
# Le cache de la base de vulnérabilités vit dans un volume, réutilisé d'un scan à l'autre
docker run --rm -v "$PWD:/src" -w /src -v trivy-cache:/root/.cache "$TRIVY" fs .

Sur le poste, un gestionnaire de versions comme asdf fixe la version dans le dépôt : asdf install trivy 0.74.0 puis asdf set trivy 0.74.0, qui l'écrit dans .tool-versions. Évitez latest, qui installerait sans prévenir la prochaine release, saine ou non.

Le banc de cette leçon est un petit projet volontairement fautif : un requirements.txt qui épingle requests==2.25.1, un fichier config.py qui contient un faux jeton GitHub, un Dockerfile et un Terraform mal configurés. Lancé sans option, Trivy trouve les deux premiers problèmes.

Fenêtre de terminal
trivy fs .
requirements.txt (pip)
Total: 4 (UNKNOWN: 0, LOW: 0, MEDIUM: 4, HIGH: 0, CRITICAL: 0)
config.py (secrets)
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1)
CRITICAL: GitHub (github-pat)
GitHub Personal Access Token
1 [ GITHUB_TOKEN = "****************************************"

Deux points se lisent dans cette sortie. Le secret est classé CRITICAL et sa valeur est masquée dans le rapport, ce qui évite de le recopier dans un journal de CI. Et le Dockerfile comme le Terraform n'apparaissent pas : sans --scanners misconfig, Trivy ne les analyse pas. Un « 0 problème » sur un dossier d'infrastructure peut donc simplement signifier que le scanner n'a pas regardé.

Le scanner misconfig compare les fichiers de configuration à des règles intégrées : un conteneur qui tourne en root, un bucket ouvert au public, un chiffrement absent. Il couvre Dockerfile, Terraform, Kubernetes, Helm et CloudFormation.

Fenêtre de terminal
trivy fs --scanners vuln,secret,misconfig .
# ou, pour les seules configurations :
trivy config .

Mesuré sur les deux fichiers du banc, 3 constats pour le Dockerfile et 7 pour le Terraform, chacun avec son identifiant de règle :

Dockerfile
FROM python:3.12
RUN apt-get update && apt-get install -y curl
COPY . /app
CMD ["python", "/app/main.py"]
RègleSévéritéCe qu'elle signale
DS-0002HIGHAucune instruction USER non-root
DS-0029HIGHapt-get install sans --no-install-recommends
DS-0026LOWPas d'instruction HEALTHCHECK
AWS-0086, AWS-0087, AWS-0091, AWS-0093HIGHLe blocage d'accès public du bucket laisse passer ACL et politiques publiques
AWS-0132HIGHPas de chiffrement par une clé gérée par le client
AWS-0090MEDIUMVersionnement désactivé
AWS-0089LOWJournalisation désactivée
infra/main.tf
resource "aws_s3_bucket" "donnees" {
bucket = "mon-bucket-de-test"
}
resource "aws_s3_bucket_public_access_block" "donnees" {
bucket = aws_s3_bucket.donnees.id
block_public_acls = false
block_public_policy = false
}

Une règle n'est pas une vérité absolue : un bucket de site statique est public par conception. La bonne réponse est une exception documentée, pas la désactivation du scanner. La leçon suivante, sur Checkov, approfondit l'analyse de l'IaC avec un second outil.

trivy image inventorie les paquets du système d'exploitation de l'image et ceux des langages qu'elle embarque, puis les confronte à sa base. Le résultat dépend surtout de l'âge de l'image de base. Mesuré le 1er octobre 2026 :

Image scannéeVulnérabilitésDont CRITICALAvec --ignore-unfixed
nginx:1.31.3-alpine (Alpine 3.24.1)54054
node:14 (Debian 10, fin de vie)1411211086
Fenêtre de terminal
trivy image --scanners vuln \
nginx:1.31.3-alpine@sha256:4a73073bd557c65b759505da037898b61f1be6cbcc3c2c3aeac22d2a470c1752

L'option --ignore-unfixed masque les vulnérabilités sans correctif publié. Elle ne change rien sur l'image Alpine récente, où tout a déjà un correctif, et retire 325 constats sur node:14. Elle réduit le bruit de ce que l'équipe ne peut pas corriger, mais ne doit pas faire oublier qu'une image en fin de vie ne recevra plus de correctifs : la seule issue est de changer d'image de base. Les chiffres bougent chaque jour avec la base de vulnérabilités ; lisez-les comme un ordre de grandeur.

Mesuré : Trivy sort en code 0 quels que soient ses constats. Sans option, il informe et laisse passer. Le blocage se décide avec deux options : la sévérité retenue et le code de sortie voulu.

Fenêtre de terminal
trivy image --scanners vuln --severity CRITICAL --exit-code 1 "$IMAGE"

Sur le requirements.txt du banc, qui ne contient que 4 vulnérabilités MEDIUM, --severity HIGH,CRITICAL --exit-code 1 sort en 0, ce qui est voulu, et --severity MEDIUM --exit-code 1 sort en 1. Testez toujours le seuil dans les deux sens. Le choix des sévérités qui bloquent, et la place de ce contrôle parmi les autres, sont l'objet de la leçon sur le pipeline sécurisé.

Trivy charge tout seul un fichier trivy.yaml présent dans le dossier courant. C'est pratique sur un poste, et dangereux en CI : une pull request qui ajoute ce fichier modifie le comportement du scanner qui doit la juger.

trivy.yaml, ajouté par une pull request
severity:
- CRITICAL

Mesuré sur le banc : le journal affiche Loaded file_path="trivy.yaml", et les 4 vulnérabilités MEDIUM disparaissent du rapport, sans avertissement. En ajoutant scan: { scanners: [vuln] }, c'est le jeton GitHub CRITICAL qui disparaît à son tour. Le schéma suit ce chemin, de la pull request au job vert, et montre la parade, décidée dans le job lui-même et non dans le dépôt que la PR peut modifier.

Piège du trivy.yaml : une pull request ajoute un fichier trivy.yaml, le job CI extrait le dépôt avec ce fichier, trivy fs le charge automatiquement (Loaded file_path=trivy.yaml) et le rapport est vidé sans avertissement, le job reste vert ; la parade, décidée dans le job, passe les options en ligne de commande, impose --config ou supprime le fichier, et le rapport reste complet avec son seuil.Pull requestajoute trivy.yamlJob CI : dépôt extraitavec le trivy.yaml de la PRtrivy fs .Loaded file_path=trivy.yamlRapport vidé sans avertissementjob vertParade : options en ligne de commande,--config imposé ou fichier suppriméRapport completseuil appliqué
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Trois parades, à combiner :

  • passer les options en ligne de commande (--severity, --scanners, --exit-code), qui l'emportent sur le fichier ;
  • imposer un fichier de configuration contrôlé, hors du code de la PR, par --config ou la variable TRIVY_CONFIG ;
  • supprimer trivy.yaml, trivy.yml et .trivy.yaml avant le scan quand le dépôt est extrait, ou scanner l'image sans extraire le dépôt.

Un constat accepté se déclare dans un fichier .trivyignore, avec une date d'expiration : passé cette date, l'exception tombe et le constat réapparaît. La leçon sur la SCA l'a mesuré, ainsi que le piège de la variante .trivyignore.yaml, qui n'est lue qu'avec --ignorefile.

.trivyignore
# Composant non exposé, corrigé dans la release 2.0 (ticket SEC-142)
CVE-2021-23337 exp:2027-01-01

Un SBOM (Software Bill of Materials) est l'inventaire des composants d'un logiciel. Trivy le produit au format CycloneDX ou SPDX, puis sait rescanner cet inventaire plus tard, sans le code ni l'image : c'est ce qui permet de savoir en quelques minutes si une nouvelle CVE touche une version déjà livrée.

Fenêtre de terminal
# Produire le SBOM, sans recherche de vulnérabilités
trivy fs --format cyclonedx --output sbom.json .
# Le rescanner, aujourd'hui ou dans six mois
trivy sbom sbom.json

Mesuré sur le banc : le SBOM est au format CycloneDX 1.7 et contient 2 composants, requests et sa déclaration ; trivy sbom y retrouve les 4 vulnérabilités MEDIUM du scan direct. Produire un SBOM ne lance aucune recherche de vulnérabilités : ce sont deux étapes distinctes. Le module sur la supply chain consacre une leçon au SBOM.

Trivy sait évaluer une image contre le CIS Docker Benchmark, pour la partie qui concerne les images (section 4). Les contrôles manuels, comme « utiliser des images de base de confiance », restent sans verdict.

Fenêtre de terminal
trivy image --compliance docker-cis-1.6.0 \
nginx:1.31.3-alpine@sha256:4a73073bd557c65b759505da037898b61f1be6cbcc3c2c3aeac22d2a470c1752

Mesuré sur nginx:1.31.3-alpine : 4.1 (créer un utilisateur pour le conteneur) et 4.6 (ajouter un HEALTHCHECK) sont en FAIL ; 4.4, 4.7, 4.9 et 4.10, dont « pas de secrets dans les Dockerfiles », passent. Le premier échec rejoint ce qu'a montré la leçon sur la sécurité de Docker : l'image nginx officielle démarre en root.

En CI, le plus simple et le plus sûr est de lancer l'image officielle épinglée par digest, comme sur le poste : c'est l'action aquasecurity/trivy-action qui a été compromise en mars 2026, et un appel direct à l'image retire cet intermédiaire. Le workflow ci-dessous construit l'image, la scanne avec un seuil explicite et publie le rapport SARIF dans l'onglet Security.

.github/workflows/trivy.yml
name: Trivy
on:
pull_request:
push:
branches: [main]
permissions: {}
jobs:
scan:
runs-on: ubuntu-24.04
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Construire l'image
run: docker build -t "mon-app:$GITHUB_SHA" .
- name: Neutraliser une configuration Trivy venue de la PR
run: rm -f trivy.yaml trivy.yml .trivy.yaml
- name: Scanner, bloquant sur CRITICAL
env:
TRIVY: aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969
run: |
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:/out" "$TRIVY" \
image --scanners vuln --format sarif --output /out/trivy.sarif "mon-app:$GITHUB_SHA"
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "$TRIVY" \
image --scanners vuln --severity CRITICAL --exit-code 1 "mon-app:$GITHUB_SHA"
- name: Publier le rapport
if: always()
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
sarif_file: trivy.sarif

Le socket Docker n'est monté ici que dans le conteneur Trivy, le temps du scan, pour lui permettre de lire l'image construite sur le runner ; aucun conteneur applicatif n'y a accès. Sur GitLab, la même logique s'écrit avec l'image épinglée comme image: du job et GIT_STRATEGY: none quand on scanne une image du registre : sans dépôt extrait, aucun trivy.yaml ne peut s'inviter.

.gitlab-ci.yml
container_scanning:
image:
name: aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969
entrypoint: [""]
variables:
GIT_STRATEGY: none
TRIVY_USERNAME: "$CI_REGISTRY_USER"
TRIVY_PASSWORD: "$CI_REGISTRY_PASSWORD"
TRIVY_AUTH_URL: "$CI_REGISTRY"
TRIVY_CACHE_DIR: ".trivycache/"
FULL_IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
script:
- trivy image --exit-code 0 --format template --template "@/contrib/gitlab.tpl"
--output "$CI_PROJECT_DIR/gl-container-scanning-report.json" "$FULL_IMAGE_NAME"
- trivy image --exit-code 1 --severity CRITICAL "$FULL_IMAGE_NAME"
cache:
paths:
- .trivycache/
artifacts:
when: always
reports:
container_scanning: gl-container-scanning-report.json

La première commande produit le rapport affiché dans la merge request, sans bloquer ; la seconde porte le seuil. Le cache de la base de vulnérabilités (TRIVY_CACHE_DIR) évite de la retélécharger à chaque job.

Pour un scan continu des workloads d'un cluster Kubernetes, Trivy Operator lance Trivy dans le cluster et range ses résultats dans des ressources Kubernetes. Le projet est maintenu : le chart 0.36.0 (opérateur 0.34.0) date du 24 août 2026, après trois versions en deux mois. Son values.yaml lance Trivy 0.74.0, mais par tag seulement : le chart ne prévoit aucun champ de digest, et le paquet de règles aquasec/trivy-checks y est référencé par le tag flottant 1. Sur un cluster sensible, fixez ces références vous-même dans les values. L'opérateur n'a pas été déployé dans cette leçon.

Fenêtre de terminal
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm install trivy-operator aqua/trivy-operator \
--namespace trivy-system --create-namespace --version 0.36.0
kubectl get vulnerabilityreports -A
kubectl get configauditreports -A
kubectl get exposedsecretreports -A

Trivy compare ce qu'il trouve à des bases et à des règles : il ne voit que ce qui y figure. Une vulnérabilité pas encore publiée (0-day), une faille propre à votre code, un binaire copié à la main sans métadonnées, ou une erreur de logique métier lui échappent. Il complète le SAST et le DAST, il ne les remplace pas.

Deux scanners ne voient pas non plus la même chose. Mesuré dans la leçon sur les CVE : sur la même image node:14, Grype a trouvé 2032 correspondances quand Trivy en rapportait 1455, parce qu'ils ne lisent ni les mêmes bases ni les composants de la même façon. Croiser deux outils réduit les angles morts sans les supprimer.

  • Trivy scanne dépendances, secrets, images et IaC ; sur un dossier, misconfig n'est actif que si on le demande.
  • Après les incidents de 2026, on l'installe en version épinglée par digest, jamais latest ni la v0.69.4.
  • Il sort en code 0 par défaut : le blocage se décide avec --severity et --exit-code.
  • Un trivy.yaml posé dans le dépôt est chargé automatiquement et peut vider le rapport : options en ligne de commande, configuration imposée, fichier supprimé.
  • Un SBOM CycloneDX se rescanne plus tard avec trivy sbom, sans le code.
  • --ignore-unfixed réduit le bruit, mais une image en fin de vie se remplace.

Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Les questions ci-dessous reprennent les doutes les plus courants sur Trivy : installation, cibles, intégration en CI et limites, avec les mêmes versions épinglées que la leçon.

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn