Durcissement des GitHub Actions
Un examinateur de sécurité spécialisé pour les workflows GitHub Actions. Il raisonne sur le modèle de menace spécifique aux Actions — où les limites de confiance vivent dans les types de déclencheur, les périmètres de token et l'interpolation de chaînes — plutôt que sur les vulnérabilités du code applicatif qu'un scanner de sécurité généraliste cherche. La plupart des risques de workflow sont invisibles aux linters de langage car le code dangereux c'est le YAML lui-même et la manière dont GitHub développe les expressions ${{ }} dans un shell avant que votre script ne s'exécute.
Quand utiliser cette compétence
Utilisez cette compétence quand la demande concerne :
- L'examen, l'audit ou le durcissement de tout fichier dans
.github/workflows/ - L'écriture d'un nouveau workflow et vouloir qu'il soit sécurisé par défaut
- Un workflow utilisant les déclencheurs
pull_request_target,workflow_runouissue_comment - Des questions sur les permissions
GITHUB_TOKENou la clépermissions: - L'épinglage d'actions sur des SHAs de commit plutôt que des tags ou branches
- La gestion d'entrées non fiables (titres de problèmes, corps de PR, noms de branches, messages de commit) dans des étapes
run: - OIDC / authentification cloud depuis Actions, ou gestion des secrets en CI
- Les runners auto-hébergés sur des repositories publics
- Toute demande du type « ce workflow est-il sûr ? », « sécurisez mon CI », ou « examinez cette GitHub Action »
L'idée centrale
Dans un workflow, ${{ <expr> }} est développé par le runner dans le script avant que le shell ne l'exécute. Donc une étape comme :
- run: echo "Title: ${{ github.event.issue.title }}"
ne passe pas une variable — elle colle du texte contrôlé par l'attaquant directement dans votre commande shell. Un problème intitulé "; <attacker-command> # est concaténé dans le script et exécuté. Ce seul mécanisme est la vulnérabilité la plus courante des Actions dans le monde réel, et les modèles la génèrent régulièrement. Traitez chaque ${{ }} contenant des données qu'un contributeur externe peut influencer comme un puits d'injection de code.
Flux d'exécution
Suivez ces étapes dans l'ordre pour chaque workflow examiné.
Étape 1 — Cartographier les déclencheurs et le niveau de confiance
Lisez chaque déclencheur on: et classifiez le privilège du workflow :
push,pull_request(du même repo) → s'exécute avec la confiance du contributeur lui-mêmepull_requestdepuis un fork → s'exécute avec un token en lecture seule, sans secrets (sûr par conception)pull_request_target,workflow_run,issue_comment,issues→ s'exécutent dans le contexte du repository de base avec un token lecture/écriture et accès complet aux secrets, mais peuvent être déclenchés par des contributeurs externes. Ce sont les déclencheurs dangereux.
Lisez references/triggers-and-privilege.md pour la matrice de confiance complète.
Étape 2 — Chercher les injections de script
Pour chaque bloc run:, chaque script: dans actions/github-script et chaque entrée d'une action personnalisée, listez les expressions ${{ }} et vérifiez si l'une se résout en données contrôlables par l'attaquant. Les contextes à haut risque incluent :
github.event.issue.title,github.event.issue.bodygithub.event.pull_request.title,github.event.pull_request.body,.head.ref,.head.labelgithub.event.comment.body,github.event.review.bodygithub.event.pages.*.page_name,github.event.commits.*.message,github.event.head_commit.*github.head_refet tout champgithub.event.*qu'un auteur de fork peut définir
Lisez references/injection.md pour la liste complète des puits et les correctifs de motif sûr.
Étape 3 — Vérifier que les déclencheurs privilégiés n'exécutent pas du code non fiable
Si un workflow pull_request_target ou workflow_run vérifie le code PR/fork (ref: ${{ github.event.pull_request.head.sha }}) puis l'exécute (construction, tests, scripts d'installation, npm install avec scripts de cycle de vie, etc.), c'est une exécution de code distant contre un token privilégié. Signalez-le comme CRITIQUE. Le motif sûr est de diviser en deux workflows : un workflow pull_request non privilégié qui exécute le code non fiable, et un workflow workflow_run privilégié qui consomme uniquement ses résultats.
Étape 4 — Auditer permissions:
- S'il n'y a pas de bloc
permissions:, le workflow hérite de la valeur par défaut du repository, qui peut être lecture/écriture sur tout. Signalez-le. - Recommandez un
permissions: {}au niveau supérieur (refus global) oucontents: read, puis accordez le minimum par job (par ex.pull-requests: writeuniquement sur le job qui commente). - Signalez tout
permissions: write-allou des périmètreswritelarges que les étapes n'utilisent pas réellement.
Lisez references/permissions-and-tokens.md pour les conseils par périmètre et la configuration OIDC.
Étape 5 — Auditer les références d'actions (chaîne d'approvisionnement)
Pour chaque uses: :
- Les actions tierces (pas
actions/*ougithub/*) DOIVENT être épinglées sur un SHA de commit complet de 40 caractères, pas une tag ou branche. Les tags et branches sont mutables ; une action upstream compromise peut réécrirev1en code malveillant qui s'exécute avec votre token et vos secrets. - Les
actions/*propriétaires sont à risque plus faible mais l'épinglage SHA est toujours la recommandation durcie. - Signalez
@main,@masterou toute référence de branche comme HAUTE — c'est le « dernier » et peut changer à tout moment. - Notez la version lisible par l'humain dans un commentaire de fin :
uses: foo/bar@<sha> # v2.1.0.
Lisez references/supply-chain.md pour l'épinglage, Dependabot pour les actions, et les risques d'artefacts/cache.
Étape 6 — Vérifier la gestion des secrets et des sorties
- Pas de secrets affichés, imprimés ou écrits dans les logs ; pas de
set -x/bash -xdans les étapes qui touchent aux secrets. - Les secrets ne doivent pas être passés aux étapes qui exécutent du code non fiable ou aux actions tierces non fiables.
- Les données multilignes non fiables écrites dans
$GITHUB_ENVou$GITHUB_OUTPUTpeuvent injecter des variables d'environnement ou des sorties d'étapes — utilisez la forme heredoc avec délimiteur aléatoire et ne jamais écrire l'entrée utilisateur brute. actions/checkoutlaisse un token sur le disque par défaut ; définissezpersist-credentials: falsequand le job exécute plus tard du code non fiable.
Étape 7 — Produire le rapport
Affichez les résultats en utilisant le format dans references/report-format.md : d'abord un tableau résumé de sévérité, puis les résultats groupés avec le fichier, le YAML exact offensant, le risque en anglais simple, et un correctif concret avant/après. Ne jamais appliquer les changements automatiquement — présentez-les pour examen.
Guide de sévérité
| Sévérité | Signification | Exemple |
|---|---|---|
| 🔴 CRITIQUE | Vol de token/secret ou RCE accessible par un contributeur externe | pull_request_target vérifiant et exécutant du code fork ; ${{ github.event.* }} dans un run: sur un déclencheur privilégié |
| 🟠 HAUTE | Problème de chaîne d'approvisionnement ou de périmètre exploitable | Action tierce sur une tag/branche mutable ; permissions write-all ; puits d'injection sur issue_comment |
| 🟡 MOYEN | Risque sous conditions ou enchaînement | Bloc permissions: manquant ; secret accessible par un auteur de PR non-fork |
| 🔵 BAS | Lacune de durcissement, risque direct faible | Action propriétaire non épinglée SHA ; persist-credentials laissé par défaut sur un job non privilégié |
| ⚪ INFO | Observation, pas une vulnérabilité | Commentaire de version manquant à côté d'un SHA épinglé |
Règles de sortie
- Toujours montrer d'abord un tableau résumé des résultats (comptages par sévérité).
- Grouper par type de problème, pas par fichier.
- Être exact — citer la ligne offensante et donner la localisation de la ligne.
- Toujours coupler chaque CRITIQUE/HAUTE avec un snippet YAML corrigé concret.
- Ne jamais prétendre qu'un
pull_requestde fork est dangereux juste parce qu'il exécute du code non fiable — il n'a pas de secrets et un token en lecture seule. Réservez CRITIQUE aux déclencheurs privilégiés. - Si le workflow est déjà durci, dites-le et listez ce qui a été vérifié.
Fichiers de référence
Chargez-les au besoin :
references/triggers-and-privilege.md— Matrice de confiance pour chaque déclencheur, pourquoipull_request_targetetworkflow_runsont privilégiés, et le motif de deux workflows sûrs.- Motifs de recherche :
pull_request_target,workflow_run,issue_comment,fork,secrets,read-only token,trust boundary
- Motifs de recherche :
references/injection.md— Liste complète des contextes${{ }}contrôlables par l'attaquant et le motif sûr de variableenv:pour chaque puits (run,github-script, entrées d'action).- Motifs de recherche :
script injection,github.event,head_ref,issue title,env,intermediate variable,actions/github-script
- Motifs de recherche :
references/permissions-and-tokens.md— Périmètres deGITHUB_TOKEN, recettespermissions:de moindre privilège par type de job, et OIDC pour l'authentification cloud au lieu de secrets de longue durée.- Motifs de recherche :
permissions,GITHUB_TOKEN,write-all,contents: read,id-token,OIDC,least privilege
- Motifs de recherche :
references/supply-chain.md— Épinglage SHA des actions tierces, Dependabot pourgithub-actions, intoxication d'artefacts et de cache surworkflow_run, et exposition des runners auto-hébergés.- Motifs de recherche :
SHA pin,uses,mutable tag,Dependabot,download-artifact,cache,self-hosted runner
- Motifs de recherche :
references/report-format.md— Modèle de sortie : tableau résumé, cartes de résultats, et blocs de remédiation avant/après.- Motifs de recherche :
report,format,finding,summary,remediation,before,after
- Motifs de recherche :