Un workflow peut passer actionlint, zizmor, poutine et un gate de posture à zéro finding, et rester dangereux. Les scanners détectent des motifs connus ; ils ignorent ce que vos secrets ouvrent, où vos runners sont branchés, et qui consomme vos artefacts. Cette page donne une méthode en sept questions pour établir ce qu'un workflow expose réellement, et décider quoi corriger en premier.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Situer ce qu'un scanner voit et ce qu'il ne peut pas voir
- Dérouler les sept questions sur un workflow quelconque
- Repérer la frontière de confiance, l'endroit où une donnée non fiable entre
- Évaluer le rayon d'explosion plutôt que la seule probabilité
- Choisir quels workflows modéliser en premier, et quand refaire l'exercice
Ce qu'un scanner ne peut pas savoir
Section intitulée « Ce qu'un scanner ne peut pas savoir »Les scanners raisonnent sur le fichier. Ils lisent un permissions:, une
référence d'action, une interpolation dangereuse. C'est précieux, et c'est
borné : trois catégories d'information leur échappent par construction.
| Ce que le scanner voit | Ce qu'il ignore |
|---|---|
secrets.DEPLOY_TOKEN est passé par env: | Que ce jeton ouvre la production de toute l'entreprise |
runs-on: [self-hosted, prod] | Que cette machine est sur le réseau d'administration |
| Une image est poussée sur un registre | Que quarante dépôts la consomment en base |
permissions: contents: write | Que ce dépôt est la source d'une action utilisée ailleurs |
Aucune de ces colonnes de droite n'est inscrite dans le YAML. Elles dépendent de votre contexte, et c'est exactement ce qu'une modélisation de menace formalise. Le scanner répond « ce workflow contient-il un motif dangereux connu ». La modélisation répond « que se passe-t-il si celui-ci est compromis ».
Les sept questions
Section intitulée « Les sept questions »L'enchaînement suit le trajet réel d'une attaque : entrer, exécuter, obtenir, sortir, propager. Chaque question se répond en une ligne, et c'est le passage d'une question à la suivante qui révèle les enchaînements dangereux.
1. Qui peut déclencher ce workflow ? | v2. Quel code s'exécute, et d'où vient-il ? | v3. Sur quelle machine, dans quel segment réseau ? | v4. Avec quel jeton, et quelles permissions ? | v5. Quels secrets sont présents dans l'environnement ? | v6. Vers où la machine peut-elle émettre du trafic ? | v7. Quel artefact est produit, et qui le consomme ensuite ?1. Qui peut déclencher ?
Section intitulée « 1. Qui peut déclencher ? »C'est la question qui détermine toutes les autres, parce qu'elle fixe la population d'attaquants possibles. Un workflow que seuls trois mainteneurs déclenchent et un workflow que n'importe quel inconnu déclenche par une pull request n'appellent pas le même niveau d'exigence.
| Déclencheur | Qui peut l'actionner |
|---|---|
push sur une branche protégée | Les personnes ayant le droit d'écriture, après revue |
pull_request | N'importe qui, sur un dépôt public |
pull_request_target | N'importe qui, et le job dispose des secrets |
issue_comment, issues | N'importe qui |
workflow_dispatch | Les personnes ayant accès au dépôt |
schedule | Personne, mais s'exécute sans surveillance |
Les deux lignes du milieu concentrent l'essentiel des incidents publics. La
ligne schedule mérite une mention particulière : un workflow planifié tourne
sans témoin, souvent la nuit, ce qui allonge le délai de détection.
pull_request_target demande d'être décomposé, parce qu'on le résume souvent
par « il donne les secrets à un inconnu ». C'est faux dit comme ça, et cela
fait manquer la seule étape sur laquelle vous pouvez agir. Trois choses
se distinguent :
- Qui déclenche : n'importe qui, par une pull request venue d'un fork.
- Quel code tourne par défaut : celui de la branche par défaut du dépôt de base, et non le contenu de la pull request. C'est donc votre workflow, celui que vous avez écrit et relu.
- Quand cela devient dangereux : au moment où ce workflow checkoute la référence de la pull request et en exécute quelque chose, build, tests ou simple installation de dépendances. C'est là, et seulement là, que le code d'un inconnu rencontre vos secrets et un jeton en écriture.
La documentation GitHub le dit sans détour : « Avoid using this event if you need to build or run code from the pull request. » Le déclencheur n'est pas le problème, l'exécution de code non fiable l'est.
2. Quel code s'exécute ?
Section intitulée « 2. Quel code s'exécute ? »Il ne suffit pas que le déclencheur soit sûr : encore faut-il savoir quel code tourne. Quatre sources se cumulent, et la troisième est la plus souvent oubliée.
- Le code du dépôt, à la référence checkoutée. Laquelle exactement ? Un
ref:pointant vers la HEAD d'une pull request change tout. - Les actions tierces, qui sont du code exécuté avec les mêmes droits que vos propres étapes.
- Les dépendances installées pendant le job. Un
npm ciexécute les scripts d'installation des paquets, y compris transitifs. - Les outils téléchargés par une action, dont la version n'est pas toujours figée.
3. Sur quelle machine ?
Section intitulée « 3. Sur quelle machine ? »Les runners hébergés par GitHub de la gamme standard sont des machines jetables, sur l'internet public, détruites après le job. Cette propriété tient au type de runner, pas au fait qu'il soit hébergé : vérifiez-la pour la gamme que vous utilisez plutôt que de la supposer acquise. Un runner auto-hébergé, lui, est votre infrastructure, avec vos accès réseau, et il persiste sauf configuration contraire.
La question à se poser n'est pas « ce runner est-il durci », mais « que joint cette machine » : bases internes, registres, API d'administration. Le cloisonnement par runner groups traite ce point en détail.
4. Avec quel jeton ?
Section intitulée « 4. Avec quel jeton ? »Le GITHUB_TOKEN du job, avec les permissions déclarées. La question utile est
de traduire chaque permission en capacité offensive : contents: write
permet de modifier le code, donc d'ajouter un workflow, donc de persister.
packages: write permet de publier une version piégée sous votre nom.
id-token: write permet d'obtenir un jeton cloud, si une trust policy l'accepte.
5. Quels secrets ?
Section intitulée « 5. Quels secrets ? »Listez ce qui est réellement présent dans l'environnement du job, pas ce que le workflow est censé utiliser. Un secret d'environnement n'est délivré qu'après validation des règles de protection ; un secret de dépôt est là dès le démarrage du job, qu'on s'en serve ou non.
Pour chaque secret, la question qui compte n'est pas « est-il masqué » mais « qu'ouvre-t-il, et combien de temps ». Un jeton statique de production vaut infiniment plus qu'un jeton OIDC de dix minutes restreint à un dépôt.
6. Vers où peut-il émettre ?
Section intitulée « 6. Vers où peut-il émettre ? »Toute exfiltration a besoin d'un canal de sortie. Par défaut, un runner joint n'importe quelle adresse. Restreindre les destinations autorisées ne prévient pas la compromission, et ne la rend pas inoffensive : elle en réduit l'exfiltration et le rayon d'explosion. Ce qui est déjà beaucoup, puisque le secret volé ne part plus vers une adresse arbitraire et que la tentative devient un échec de job, donc un signal.
Ce qu'un filtrage de sortie ne bloque pas mérite d'être su, sous peine de
s'en remettre à une protection qu'on croit totale : un code compromis peut
encore altérer un artefact publié, empoisonner un cache réutilisé par
les exécutions suivantes, modifier le workspace, ou se servir du
GITHUB_TOKEN vers les points d'accès GitHub restés autorisés. Le canal de
sortie ferme la porte de l'exfiltration, pas celle du sabotage.
7. Quel artefact, pour qui ?
Section intitulée « 7. Quel artefact, pour qui ? »C'est la question de la propagation, et celle qu'on oublie le plus. Un workflow compromis qui ne produit rien nuit à un seul dépôt. Un workflow compromis qui publie une image consommée par quarante services, ou une action réutilisée par toute l'organisation, transforme un incident local en incident de chaîne d'approvisionnement.
Repérer la frontière de confiance
Section intitulée « Repérer la frontière de confiance »La notion centrale tient en une phrase : la frontière de confiance est l'endroit où une donnée que vous ne contrôlez pas entre dans un contexte que vous contrôlez. Un workflow dangereux est presque toujours un workflow où cette frontière est franchie sans qu'on s'en aperçoive.
Les entrées non fiables sont peu nombreuses et se reconnaissent :
- le contenu d'une pull request de fork, code et fichiers de configuration ;
- les champs libres remplis par un tiers : titre et corps d'issue ou de pull request, commentaire, nom de branche, message de commit ;
- les entrées de
workflow_dispatch, saisies par un humain ; - les artefacts d'une autre exécution, dans un workflow déclenché par
workflow_run; - les réponses d'API externes consommées pendant le job.
Le test se formule ainsi : cette valeur peut-elle être choisie par quelqu'un qui n'a pas le droit d'écrire sur le dépôt ? Si oui, elle est non fiable, et tout ce qu'elle atteint devient suspect.
Le rayon d'explosion, plus utile que la probabilité
Section intitulée « Le rayon d'explosion, plus utile que la probabilité »Classer les risques par probabilité conduit à repousser indéfiniment les scénarios rares. Or en sécurité de chaîne d'approvisionnement, les incidents marquants sont précisément des événements qu'on jugeait improbables. Le classement par rayon d'explosion est plus opérant : il demande ce que coûte l'incident, pas sa fréquence.
| Rayon | Ce qui est atteint | Exemple |
|---|---|---|
| Le job | Une exécution, rien ne persiste | Un runner hébergé sans secret |
| Le dépôt | Code, releases, paquets de ce dépôt | contents: write détourné |
| L'organisation | Plusieurs dépôts, secrets partagés | Secret d'organisation exfiltré |
| Les consommateurs | Les utilisateurs de vos artefacts | Image ou action publiée piégée |
| L'infrastructure | Réseau interne, production | Runner auto-hébergé compromis |
Les deux dernières lignes justifient à elles seules l'exercice : ce sont celles où la remédiation dépasse largement votre dépôt, et où il faut prévenir des tiers.
Un exemple déroulé
Section intitulée « Un exemple déroulé »Prenons un workflow de publication réaliste : déclenché à la publication d'une release, il construit une image, la pousse sur un registre, l'atteste et la signe.
| Question | Réponse | Verdict |
|---|---|---|
| Qui déclenche | Publication d'une release, donc les mainteneurs | Population restreinte |
| Quel code | Le dépôt au tag, plus sept actions tierces épinglées par SHA | Surface tierce réelle |
| Quelle machine | Runner hébergé, jetable | Faible |
| Quel jeton | packages: write, id-token: write, attestations: write, contents: write | Élevé, mais justifié |
| Quels secrets | Le GITHUB_TOKEN du job, aucun secret statique | Bon |
| Sortie réseau | Non restreinte | Point faible |
| Artefact | Image publique consommée par des tiers | Rayon maximal |
La lecture croisée donne la priorité immédiatement, et elle n'est pas celle qu'on attendait. Le risque n'est ni le déclencheur, ni le jeton : c'est la combinaison de la dernière ligne et de l'avant-dernière. Une action tierce compromise parmi les sept dispose d'un canal de sortie libre et d'un artefact à large diffusion.
Les deux corrections prioritaires en découlent : restreindre le trafic sortant du job de publication, et maintenir les épinglages pour réduire la fenêtre pendant laquelle une action compromise reste en place. Le durcissement des permissions, qui aurait été le réflexe, arrive après.
Garder une trace : le gabarit à versionner
Section intitulée « Garder une trace : le gabarit à versionner »Une analyse faite de tête se perd, et surtout elle ne se compare pas. Six mois plus tard, personne ne sait si le point faible relevé a été traité, ni quelles réponses ont changé depuis. Le remède est un fichier, rangé dans le dépôt à côté des workflows qu'il décrit, qui passe en revue de code comme le reste.
workflow: .github/workflows/release.ymlrevu_le: 2026-09-22revu_par: equipe-plateforme
reponses: qui_declenche: "Publication d'une release, donc les mainteneurs" quel_code: "Le dépôt au tag, plus 7 actions tierces épinglées par SHA" quelle_machine: "Runner hébergé standard, jetable" quel_jeton: "packages:write, id-token:write, attestations:write, contents:write" quels_secrets: "GITHUB_TOKEN du job uniquement, aucun secret statique" sortie_reseau: "Non restreinte" quel_artefact: "Image publique consommée par des tiers"
frontiere_de_confiance: >- Le code tiers entre par les 7 actions épinglées. Tout le reste du job est sous contrôle des mainteneurs.rayon_d_explosion: consommateurs
actions: - quoi: "Restreindre le trafic sortant du job de publication" etat: a_faire echeance: 2026-10-15 - quoi: "Rejouer l'épinglage des actions (npm run actions:update)" etat: fait le: 2026-09-22Trois champs font tout le travail et sont ceux qu'on a envie de sauter. La date de revue dit si l'analyse est encore valable. La frontière de confiance nomme par où entre le code que vous ne contrôlez pas, ce qui est la question à laquelle un attaquant, lui, a déjà répondu. Le rayon d'explosion fixe la priorité entre plusieurs workflows, sans discussion d'opinion.
La liste d'actions transforme enfin le constat en travail suivi : un point faible sans échéance ni état n'est pas une décision, c'est une observation.
Par où commencer, et quand recommencer
Section intitulée « Par où commencer, et quand recommencer »Modéliser tous les workflows d'une organisation n'a pas de sens. Trois critères désignent ceux qui le méritent, et un workflow qui en cumule deux passe en tête.
-
Il est déclenchable par un tiers :
pull_requestsur dépôt public,pull_request_target,issue_comment. -
Il dispose de secrets ou de permissions d'écriture : publication, déploiement, signature.
-
Il produit quelque chose que d'autres consomment : image, paquet, action réutilisable, workflow réutilisable.
L'exercice se refait sur événement, pas sur calendrier. Quatre changements invalident une modélisation existante : l'ajout d'un déclencheur, l'ajout d'un secret ou d'une permission, le passage à un runner auto-hébergé, et le fait qu'un artefact gagne des consommateurs. Les trois premiers se voient dans le diff, le quatrième non, et c'est celui qui transforme silencieusement un risque local en risque de chaîne.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide 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
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
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Un workflow peut passer tous les scanners et rester dangereux : ils voient des motifs connus, pas ce que vos secrets ouvrent ni où vos runners sont branchés.
- La méthode tient en sept questions qui suivent le trajet d'une attaque : déclencheur, code, machine, jeton, secrets, sortie réseau, artefact.
- La frontière de confiance est l'endroit où une donnée choisie par quelqu'un sans droit d'écriture entre dans un contexte privilégié.
- Classez par rayon d'explosion plutôt que par probabilité : les incidents marquants étaient tous jugés improbables.
- Un workflow sans canal de sortie réduit l'exfiltration et le rayon d'explosion, sans empêcher le sabotage d'un artefact ou d'un cache.
- La question la plus oubliée est la septième : qui consomme l'artefact produit, et donc jusqu'où l'incident se propage.
- Modélisez en priorité les workflows déclenchables par un tiers, porteurs de secrets ou producteurs d'artefacts diffusés, et refaites l'exercice sur événement, pas à date fixe.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Sécuriser pull_request_target : Le déclencheur qui franchit la frontière de confiance par construction, et les gardes qui la rétablissent.
- OIDC : authentification sans secrets : Réduire la valeur de la cinquième question en remplaçant les jetons statiques par des jetons de quelques minutes.
- Checklist sécurité : Le passage systématique qui transforme les conclusions de cette modélisation en points de contrôle avant fusion.