Aller au contenu
CI/CD & Automatisation medium

Plumber : sécuriser vos pipelines GitLab CI et GitHub Actions

45 min de lecture

Logo Plumber

Si votre pipeline GitLab CI/CD utilise des images Docker non épinglées, des includes sur main ou un build basé sur Docker-in-Docker, ou si vos workflows GitHub Actions référencent encore des actions tierces par tag mutable, vous exposez votre chaîne à une attaque de supply chain. Plumber est un scanner de sécurité de pipelines : vous créez une configuration minimale au schéma v2.0, vous lancez plumber analyze, vous lisez trois ou quatre problèmes concrets, puis vous intégrez le contrôle dans votre CI avec le composant officiel. Ce guide s'adresse aux débutants et intermédiaires ; on commence volontairement par la CLI, ensuite par le composant GitLab, puis on termine par le PBOM, le score A-E et la Platform.

  • Créer une configuration minimale au schéma v2.0 avec plumber config init, puis la valider avant le premier scan
  • Migrer une configuration v1 existante vers le schéma v2.0 avec plumber config migrate
  • Lancer une analyse de sécurité locale sur un dépôt GitLab ou un dépôt GitHub et comprendre l'auto-détection
  • Corriger les failles fréquentes : image non épinglée, action tierce non pinnée par SHA, source d'action non autorisée, branche non protégée
  • Ajouter le composant GitLab CI/CD officiel sans créer de doubles pipelines ni de job .pre inutilisable
  • Exporter un PBOM et l'exploiter avec Trivy ou Grype

Plumber est un scanner de sécurité pour GitLab CI/CD et GitHub Actions. Côté GitLab, il interroge l'API du projet et lit la configuration mergée ; côté GitHub, il analyse en local les fichiers de .github/workflows via le moteur Rego. Dans les deux cas, il signale les écarts les plus dangereux : images non fiables, tags mutables, actions tierces non épinglées par SHA, sources d'actions non autorisées, branches non protégées, includes obsolètes, scripts téléchargés sans vérification ou usage de Docker-in-Docker. Chaque problème porte un code d'issue (ISSUE-XXX) que vous pouvez détailler avec plumber explain.

Plumber s'utilise de trois façons, et le choix change surtout l'ordre dans lequel vous apprenez l'outil. La CLI montre la sortie brute et permet d'itérer sur la configuration en quelques secondes ; le composant GitLab CI/CD évite d'écrire un job complet mais masque une partie du fonctionnement ; la Platform répond à un besoin de gouvernance, pas de découverte. Repérez votre situation dans la colonne de gauche, la troisième colonne explique le raisonnement.

Votre situationPorte d'entrée recommandéePourquoi
Vous découvrez PlumberCLI localeVous comprenez d'abord ce que l'outil détecte et comment lire la sortie
Vous voulez l'ajouter rapidement à un pipeline existantComposant GitLab CI/CDVous évitez d'écrire un job manuel complet
Vous devez centraliser plusieurs projetsPlatformVous passez de l'analyse projet par projet à la gouvernance

Avant de lancer votre premier scan, vous avez besoin de :

  • un dépôt Git lié à un projet GitLab ou GitHub ;
  • côté GitLab : un token avec les scopes read_api et read_repository (rôle Maintainer sur le projet, ou un Project Access Token doté de ce rôle) ;
  • côté GitHub : la CLI gh authentifiée (gh auth login). Plumber lit le token depuis ~/.config/gh automatiquement ; aucune variable d'environnement n'est nécessaire ;
  • un terminal interactif si vous voulez utiliser plumber config init.

Plumber est distribué comme binaire unique, sans dépendance à installer. Les trois premières méthodes posent l'exécutable sur votre poste ; la variante Docker évite toute installation et convient bien aux runners de CI. Quel que soit le canal, épinglez la version : :latest sur l'image Docker ferait varier vos résultats d'analyse d'une exécution à l'autre.

Fenêtre de terminal
mise use -g github:getplumber/plumber

Vérification :

Fenêtre de terminal
plumber version

La sortie doit afficher plumber version 0.4.22 (ou la version plus récente que vous avez installée) avec le hash de commit et la date de build.

Le chemin le plus simple est toujours le même : configurer, valider, scanner, corriger. Plumber détecte automatiquement le provider en lisant votre remote origin : si l'URL pointe vers GitLab, il bascule sur le chemin API GitLab ; si elle pointe vers GitHub, il scanne en local le dossier .github/workflows via le moteur Rego.

  1. Créez une configuration minimale avec le wizard interactif

    Fenêtre de terminal
    plumber config init
    plumber config validate

    plumber config init pose des questions ciblées puis génère un fichier court au schéma v2.0, adapté à votre projet. plumber config validate vérifie ensuite que le YAML est correct et signale les fautes de frappe dans les noms de contrôles ou de clés.

  2. Authentifiez-vous auprès de votre provider

    Côté GitLab, créez un token via User Settings -> Access Tokens avec read_api et read_repository, puis exportez-le :

    Fenêtre de terminal
    export GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx

    Côté GitHub, authentifiez-vous une fois via la CLI gh, Plumber lit ensuite le token depuis ~/.config/gh sans variable d'environnement :

    Fenêtre de terminal
    gh auth login
    gh auth status
  3. Lancez l'analyse depuis le dépôt Git

    Fenêtre de terminal
    cd mon-projet
    plumber analyze

    Si votre dépôt possède un remote origin pointant vers GitLab ou GitHub, Plumber détecte automatiquement le provider, l'URL et le chemin du projet. Sur GitHub, l'analyse repose sur les fichiers locaux : aucun appel API n'est requis pour les contrôles workflow (l'API n'est appelée que pour la protection de branche).

  4. Lisez d'abord le résumé, pas toute la sortie

    Cherchez en priorité :

    • le pourcentage de conformité global ;
    • les contrôles en échec ;
    • les codes ISSUE-XXX ;
    • le nom précis du job ou du workflow concerné ;
    • le score A-E et les points sur 100.

Sur le dépôt de lab Bob74/pipeline-craft, la commande suivante analyse une branche de solution :

Fenêtre de terminal
export GITLAB_TOKEN=glpat-xxxx
plumber analyze --branch solution/lab-12 --score

Voici le résumé observé, au format 0.4 : la ligne de statut porte désormais le verdict de score (lettre, points, seuil requis) et non plus un pourcentage de conformité.

Auto-detected GitLab URL: https://gitlab.com
Auto-detected project: Bob74/pipeline-craft
Using configuration: .plumber.yaml
Summary
Status: FAILED ✗ (score D - 34.0/100 pts, required ≥ 100 pts)
Controls
- ISSUE-103 ×5 High : images non épinglées par digest
- ISSUE-412 ×1 High : Docker-in-Docker détecté
- ISSUE-401 ×5 Medium : jobs hardcodés
Plumber Score: D (34 / 100 pts)
Critical 0 High 6 Medium 6 Low 0

Le provider GitHub ne nécessite ni --gitlab-url ni GITLAB_TOKEN. Une fois gh auth login fait, lancez simplement :

Fenêtre de terminal
git clone <url-du-depot>
cd <depot>
plumber config generate --output .plumber.yaml --force
plumber analyze --score

Voici le résumé d'un scan réel mené sur nektos/act, avec la configuration par défaut de Plumber. La 0.4 restructure la sortie en sections Passed / Skipped / Failed, puis affiche un tableau récapitulatif des contrôles en échec et le score :

Sortie de plumber analyze --score sur un dépôt GitHub, version 0.4.22

Summary
Status: FAILED ✗ (score E - 8.8/100 pts, required ≥ 100 pts)
Controls
- Actions must come from authorized sources ISSUE-713 High 10
- Release workflows must not restore an untrusted cache ISSUE-705 High 2
- Branch must be protected ISSUE-505 High 1
- Workflows must declare permissions ISSUE-801 Medium 6
Plumber Score: E (9 / 100 pts)
Critical 0 High 13 Medium 6 Low 0

ISSUE-713 domine ici : dix actions tierces proviennent d'une source non autorisée (ni actions/*, ni github/*, ni allowlist), même quand elles sont pourtant épinglées par SHA. ISSUE-705 est un apport récent de la famille supply chain : il repère les workflows de release susceptibles de consommer un cache de build empoisonné. Ici aucun Critical, mais l'accumulation de High suffit à plafonner le score en E : le score gate exige 100 points par défaut, le job échoue donc.

Le cas le plus fréquent est simple : le dépôt n'a pas de remote origin, ou l'URL du remote ne pointe ni vers GitLab ni vers GitHub.

Passez alors explicitement les paramètres :

Fenêtre de terminal
# GitLab self-hosted
plumber analyze --gitlab-url https://gitlab.com --project mon-groupe/mon-projet
# GitHub Enterprise Server
plumber analyze --github-url https://github.example.com --project mon-org/mon-projet

Plumber documente 66 codes d'issues organisés en neuf familles (voir l'annexe en bas de guide). Mais sur un projet qui n'a jamais été audité, ce sont presque toujours les mêmes quatre erreurs qui dominent, quel que soit le provider. Commencez par celles-ci.

Dans le lab pipeline-craft testé, Plumber a signalé :

HIGH [ISSUE-103] Job 'pytest' uses image without digest pinning: docker.io/python:3.12-slim
HIGH [ISSUE-103] Job 'docker-build' uses image without digest pinning: docker.io/docker:27

Le problème n'est pas seulement l'usage de latest. Un tag de version comme python:3.12-slim reste mutable : si le registre republie l'image, votre pipeline peut exécuter autre chose demain.

Pour activer le mode strict, la clé se trouve dans le schéma v2.0 sous la section provider :

.plumber.yaml
version: "2.0"
gitlab:
controls:
containerImageMustNotUseForbiddenTags:
enabled: true
containerImagesMustBePinnedByDigest: true

2. Actions tierces non pinnées par SHA (provider GitHub)

Section intitulée « 2. Actions tierces non pinnées par SHA (provider GitHub) »

C'est la faille la plus visible détectée par le provider GitHub, désormais sous le code ISSUE-701 :

HIGH [ISSUE-701] job "…" references action "…@main" with a mutable
ref - pin by commit SHA instead

Un uses: owner/action@v4 ou uses: owner/action@main reste mutable côté maintainer. Si l'action est compromise, comme tj-actions/changed-files en mars 2025 (CVE-2025-30066), votre workflow exécute silencieusement le code modifié avec ses secrets.

Activez le contrôle dans la section GitHub :

.plumber.yaml
github:
controls:
actionsMustBePinnedByCommitSha:
enabled: true
trustGithubOfficialActions: true

L'option trustGithubOfficialActions exempte les actions GitHub de premier rang (actions/*, github/*) pour concentrer le signal initial sur la surface tierce, celle qui n'est pas maintenue par GitHub.

3. Includes sur main, master ou HEAD (provider GitLab)

Section intitulée « 3. Includes sur main, master ou HEAD (provider GitLab) »

Un include pointant sur une branche mutable rend votre pipeline difficile à reproduire et peut casser sans changement dans votre dépôt.

Avant
include:
- project: security/templates
file: /pipelines/sast.yml
ref: main
Après
include:
- project: security/templates
file: /pipelines/sast.yml
ref: v2.4.1

Le contrôle associé est includesMustNotUseForbiddenVersions avec ISSUE-404.

Si la branche par défaut n'est pas protégée, un push direct ou une modification non revue peut contourner vos pratiques de revue et de sécurité. Le contrôle existe sous les deux providers et renvoie ISSUE-501 (absence de protection) ou ISSUE-505 (protection incomplète).

Exemple minimal
gitlab:
controls:
branchMustBeProtected:
enabled: true
defaultMustBeProtected: true
namePatterns:
- main
allowForcePush: false

Une cinquième erreur très courante : Docker-in-Docker

Section intitulée « Une cinquième erreur très courante : Docker-in-Docker »

Le lab a également remonté :

HIGH [ISSUE-412] job "docker-build" uses Docker-in-Docker service "docker:27-dind"
↳ at .gitlab-ci.yml:55
↳ docs: https://getplumber.io/docs/cli/issues/ISSUE-412

Sur des runners partagés, DinD augmente le risque d'évasion de conteneur et de mouvement latéral. Si vous construisez des images, privilégiez Buildah. Kaniko reste utilisable via le fork de maintenance chainguard-forks/kaniko, le dépôt d'origine ayant été archivé en juin 2025 : c'est une voie de sortie, pas une cible. Le contrôle existe aussi côté GitHub avec ISSUE-413 (mode daemon non sécurisé).

Une fois les quatre erreurs de base corrigées, Plumber offre une famille de contrôles 7XX dédiée aux actions tierces, la surface la plus attaquée d'un workflow GitHub. Ces contrôles vont plus loin que le simple pinning et méritent d'être activés dès que votre pipeline est stable.

Restreindre les sources d'actions autorisées (ISSUE-713)

Section intitulée « Restreindre les sources d'actions autorisées (ISSUE-713) »

Épingler une action par SHA garantit qu'elle ne change pas, mais pas qu'elle vienne d'un auteur de confiance. Le contrôle githubActionMustComeFromAuthorizedSources signale toute action dont le propriétaire n'est ni GitHub, ni votre organisation, ni sur votre allowlist. C'est la parade directe contre le squat de dépôt ou une action tierce inconnue qui hériterait de vos secrets.

.plumber.yaml
github:
controls:
githubActionMustComeFromAuthorizedSources:
enabled: true
trustGithubOfficialActions: true
trustSameOrgActions: true
# Seuil d'étoiles minimal (0 = désactivé, nécessite l'API GitHub)
minimumStars: 0
trustedGithubActions:
- docker/setup-buildx-action
- docker/login-action
- ossf/scorecard-action

Bloquer les actions portant un CVE connu (ISSUE-703)

Section intitulée « Bloquer les actions portant un CVE connu (ISSUE-703) »

Le contrôle actionsMustNotCarryKnownCVEs croise chaque action épinglée avec les avis de sécurité publiés. Une seule action vulnérable remonte en Critical et, par le malus critique, plafonne le score global tant qu'elle n'est pas corrigée. Le contrôle complémentaire actionsMustNotBeArchived (ISSUE-702) signale les actions dont le dépôt amont est archivé, donc qui ne recevra plus aucun correctif.

.plumber.yaml
github:
controls:
actionsMustNotCarryKnownCVEs:
enabled: true
actionsMustNotBeArchived:
enabled: true

Le contrôle pullRequestTargetMustNotCheckoutHead détecte un workflow déclenché par pull_request_target qui fait un checkout de la HEAD de la pull request. C'est exactement le motif exploité par la compromission tj-actions/changed-files : le code non fusionné d'un contributeur externe s'exécute avec les secrets du dépôt.

.plumber.yaml
github:
controls:
pullRequestTargetMustNotCheckoutHead:
enabled: true

Fichier de configuration : schéma v2.0 par provider

Section intitulée « Fichier de configuration : schéma v2.0 par provider »

Le schéma v2.0 introduit en 0.3 organise les contrôles par provider : tout ce qui concerne GitLab CI/CD vit sous gitlab.controls:, tout ce qui concerne GitHub Actions vit sous github.controls:. Le top-level controls: de la v1 disparaît, et le bloc engine: aussi (le moteur Rego est désormais le seul disponible et tourne par défaut).

Ce fichier active quatre contrôles côté GitLab et deux côté GitHub, ce qui correspond aux erreurs les plus fréquentes décrites plus haut. La clé version: "2.0" est obligatoire, c'est elle qui indique à Plumber d'attendre les sections par provider. Un projet qui n'utilise qu'une seule plateforme peut retirer la section correspondant à l'autre sans conséquence.

.plumber.yaml
version: "2.0"
gitlab:
controls:
containerImageMustNotUseForbiddenTags:
enabled: true
tags:
- latest
- dev
- main
containerImagesMustBePinnedByDigest: true
containerImageMustComeFromAuthorizedSources:
enabled: true
trustDockerHubOfficialImages: true
trustedUrls:
- $CI_REGISTRY_IMAGE:*
- registry.gitlab.com/security-products/*
branchMustBeProtected:
enabled: true
defaultMustBeProtected: true
namePatterns:
- main
allowForcePush: false
includesMustNotUseForbiddenVersions:
enabled: true
forbiddenVersions:
- latest
- main
- HEAD
github:
controls:
actionsMustBePinnedByCommitSha:
enabled: true
trustedOwners:
- actions
- github
branchMustBeProtected:
enabled: true
defaultMustBeProtected: true
namePatterns:
- main

Si vous arrivez d'une version antérieure et que votre .plumber.yaml utilise encore le top-level controls:, plumber config migrate ré-écrit le fichier au schéma v2.0 en préservant commentaires et ordre :

Fenêtre de terminal
# Crée .plumber.yaml.v2 à côté du fichier original
plumber config migrate
# Ou écrase en place (le fichier original part en .plumber.yaml.bak)
plumber config migrate --in-place

La migration encapsule le bloc controls: existant sous gitlab.controls:, upgrade version: "1.0" vers "2.0" et supprime le bloc engine: devenu obsolète. Les sections gitlab: ou github: déjà présentes sont laissées intactes.

Ces quatre commandes se lisent en deux groupes : validate et view inspectent ce que Plumber comprend réellement de votre fichier, diff et generate le confrontent au template officiel. Notez le --force de la dernière : sans lui, config generate refuse d'écraser un .plumber.yaml existant.

Fenêtre de terminal
# Vérifier le fichier
plumber config validate
# Voir la configuration effective sans commentaires
plumber config view
# Comparer la configuration locale au défaut (sortie colorisée)
plumber config diff
# Générer le template complet officiel (sections gitlab: et github:)
plumber config generate --output .plumber.yaml --force

plumber config diff est apparu en 0.3 et permet de voir d'un coup d'œil ce qui diffère du template officiel : vert pour ce que vous avez ajouté ou modifié, rouge pour ce que vous avez retiré. Utile sur un projet hérité avant de proposer un durcissement.

Ajouter Plumber à GitLab CI avec le composant officiel

Section intitulée « Ajouter Plumber à GitLab CI avec le composant officiel »

Une fois le premier scan local compris, le bon point d'entrée côté GitLab est le composant CI/CD officiel.

Ajoutez d'abord le token GITLAB_TOKEN dans Settings -> CI/CD -> Variables, puis utilisez ce squelette :

.gitlab-ci.yml
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
- if: $CI_COMMIT_TAG
include:
- component: gitlab.com/getplumber/plumber/plumber@0.4.22

Ce bloc workflow:rules évite de créer à la fois un pipeline de branche et un pipeline de merge request sur le même push.

Le bloc inputs: surcharge les valeurs par défaut du composant. Les deux réglages qui comptent le plus sont stage, qui sort le job du .pre évoqué juste au-dessus, et min_points (ou min_score), le seuil du Plumber Score en dessous duquel le job échoue. Les deux chemins de PBOM déclenchent l'export des inventaires.

.gitlab-ci.yml
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
- if: $CI_COMMIT_TAG
include:
- component: gitlab.com/getplumber/plumber/plumber@0.4.22
inputs:
stage: test
min_points: 80 # échoue sous 80 points sur 100 (ex-threshold)
config_file: .plumber.yaml
pbom_file: plumber-pbom.json
pbom_cyclonedx_file: plumber-cyclonedx-sbom.json

Le composant recherche la configuration dans cet ordre :

  1. l'input config_file s'il est défini ;
  2. le fichier .plumber.yaml à la racine du dépôt ;
  3. la configuration par défaut embarquée dans l'image.

Gardez cette variante pour les besoins spécifiques. Si vous voulez ajouter --mr-comment et --badge dans un job manuel, séparez-les en deux jobs : le commentaire de merge request n'a de sens qu'en pipeline de MR, et le badge ne se met à jour que sur la branche par défaut.

Sur un GitLab auto-hébergé, vous ne pouvez pas consommer directement le composant depuis gitlab.com. Le chemin correct est le suivant :

  1. Importez le dépôt https://gitlab.com/getplumber/plumber.git dans votre instance.

  2. Activez le CI/CD Catalog dans le projet importé.

  3. Publiez une release en lançant un pipeline sur un tag déjà importé, par exemple v0.4.22.

  4. Consommez ensuite le composant local dans vos pipelines.

.gitlab-ci.yml
include:
- component: gitlab.example.com/infrastructure/plumber/plumber@0.4.22

Une fois la lecture du scan comprise, vous pouvez passer au PBOM.

Le PBOM (Pipeline Bill of Materials) est l'inventaire des dépendances de votre pipeline : images Docker, composants GitLab, templates et includes.

Fenêtre de terminal
plumber analyze \
--pbom plumber-pbom.json \
--pbom-cyclonedx plumber-cyclonedx-sbom.json

Les deux fichiers décrivent le même pipeline, mais pas pour le même lecteur. Le PBOM natif conserve des métadonnées propres à Plumber qu'aucun autre outil ne sait interpréter ; le CycloneDX perd ce détail au profit d'un format que Trivy, Grype ou Dependency-Track consomment directement. Rien n'empêche de générer les deux dans la même exécution.

FormatQuand l'utiliserCe qu'il contient
PBOM natifVous voulez comprendre le pipeline en détailMétadonnées riches propres à Plumber
CycloneDXVous voulez brancher un autre outilFormat standard pour Trivy, Grype ou Dependency-Track

La sous-commande sbom prend le fichier en entrée sans retélécharger les images ; le filtre --severity limite la sortie aux niveaux qui justifient une action immédiate.

Fenêtre de terminal
trivy sbom plumber-cyclonedx-sbom.json --severity HIGH,CRITICAL

Le préfixe sbom: indique à Grype que l'argument est un inventaire et non une image, et --fail-on high fait sortir la commande en code non nul, ce qui casse le job de CI dès qu'une vulnérabilité de ce niveau apparaît.

Fenêtre de terminal
grype sbom:plumber-cyclonedx-sbom.json --fail-on high

Remonter les findings dans les tableaux de bord sécurité

Section intitulée « Remonter les findings dans les tableaux de bord sécurité »

Le PBOM inventorie le pipeline ; pour faire remonter les écarts de conformité dans les plateformes, Plumber produit deux rapports de findings standardisés. Vous les générez en ajoutant un drapeau à plumber analyze.

  • --sarif écrit un rapport au format SARIF 2.1.0, consommé par GitHub Code Scanning (onglet Security du dépôt) et par le Security Dashboard de GitLab.
  • --glsast écrit un rapport GitLab SAST (gl-sast-report.json) qui alimente le Security Dashboard et le widget de merge request GitLab.
Fenêtre de terminal
# Rapport SARIF - GitHub Code Scanning ou Security Dashboard GitLab
plumber analyze --sarif plumber.sarif
# Rapport SAST natif GitLab - widget de merge request
plumber analyze --glsast gl-sast-report.json

Dans un job GitLab manuel, déclarez le fichier produit comme artefact de rapport sast pour que GitLab l'affiche automatiquement dans la merge request :

.gitlab-ci.yml
plumber-scan:
# ... un script qui lance « plumber analyze --glsast gl-sast-report.json »
artifacts:
reports:
sast: gl-sast-report.json

Ces deux formats ne remplacent pas la lecture du résumé : ils servent à historiser les findings et à les rapprocher des autres scanners de sécurité déjà branchés sur vos tableaux de bord.

À ce stade, vous savez déjà lancer Plumber et lire les erreurs les plus utiles. Les fonctionnalités suivantes sont pratiques, mais elles ne doivent pas être votre point d'entrée.

C'est la commande à réflexe devant un ISSUE-XXX que vous ne connaissez pas : elle fonctionne hors ligne, sans configuration ni dépôt, et le préfixe ISSUE- peut être omis.

Fenêtre de terminal
plumber explain ISSUE-412
# raccourci accepté : plumber explain 412

Cette commande renvoie la description du problème, son impact, la remédiation et le lien vers la documentation officielle. Trois flags complètent l'usage :

Fenêtre de terminal
# Liste compacte des 66 codes disponibles
plumber explain --list
# Référence exhaustive (description + remédiation pour chaque code)
plumber explain --all
# Sortie JSON, exploitable dans un script
plumber explain ISSUE-701 --json

Depuis la 0.4, le Plumber Score est la seule porte de sortie : il pilote le code de retour du job via --min-points / --min-score, alimente le badge et les commentaires de merge request. Le flag --score affiche le détail à l'écran (lettre, points, barre, décompte par sévérité), --score-point ajoute le décompte complet des points.

Fenêtre de terminal
plumber analyze --score
plumber analyze --score-point

Sur le scan de nektos/act présenté plus haut, le résultat était :

Plumber Score: E (9 / 100 pts)
Critical 0 High 13 Medium 6 Low 0

Le score sert à prioriser : il ne remplace pas la lecture des contrôles en échec. Il applique des plafonds par code et des poids par sévérité, si bien qu'un même nombre d'issues ne produit pas mécaniquement la même note. Une issue Critical, ou une accumulation de High comme ici, suffit à plafonner les points tant qu'elle n'est pas corrigée.

Ces deux drapeaux attendent des noms de contrôles séparés par des virgules, pas des codes d'issues. Ils servent à isoler une correction en cours ; ne les laissez pas dans le job de CI, sinon vous cessez de mesurer les contrôles écartés.

Fenêtre de terminal
# Uniquement les images et la protection de branche
plumber analyze --controls containerImageMustNotUseForbiddenTags,branchMustBeProtected
# Tout sauf la protection de branche
plumber analyze --skip-controls branchMustBeProtected

Avec le composant GitLab, activez-les ainsi :

.gitlab-ci.yml
include:
- component: gitlab.com/getplumber/plumber/plumber@0.4.22
inputs:
mr_comment: true
badge: true

La Platform n'est pas le bon point de départ pour un débutant. Elle devient pertinente quand vous devez :

  • suivre plusieurs projets dans le temps ;
  • disposer d'un tableau de bord centralisé ;
  • gérer des politiques communes à plusieurs équipes ;
  • auditer des variables, des quotas ou des règles de merge request à plus grande échelle.

Plumber v0.4.22 documente 66 codes d'issues organisés en neuf familles, chacun avec une description et une remédiation accessibles via plumber explain ISSUE-XXX. La numérotation a évolué : les actions tierces ont désormais leur propre famille 7XX, la plus active. Pour obtenir la liste exacte depuis votre installation, utilisez :

Fenêtre de terminal
# Liste compacte (un code = une ligne)
plumber explain --list
# Référence complète (description + remédiation pour chaque code)
plumber explain --all
# Détail d'un code en JSON, exploitable dans un script
plumber explain ISSUE-701 --json

Le tableau ci-dessous donne la grille de lecture pour identifier rapidement la famille concernée par une issue remontée :

FamillePlageCouvreExemples
Images de conteneursISSUE-1XX (3 codes)Sources, tags, digestISSUE-101 source non autorisée, ISSUE-102 tag interdit, ISSUE-103 image non pinnée par digest
Exécution et expressionsISSUE-2XX (12 codes)Variables, injection, contexteISSUE-203 debug trace, ISSUE-207 injection template, ISSUE-209 $GITHUB_ENV écrit, ISSUE-213 toJson(github)
Secrets et jetonsISSUE-3XX (7 codes)Fuites, secrets: inherit, tokensISSUE-302 secrets: inherit, ISSUE-307 credentials persistés, ISSUE-309 toJson(secrets)
Jobs, templates et structureISSUE-4XX (17 codes)Jobs, includes, DinD, OIDCISSUE-401 job hardcodé, ISSUE-404 include mutable, ISSUE-412 DinD, ISSUE-421 publish sans OIDC
Protection de brancheISSUE-5XX (2 codes)branchMustBeProtectedISSUE-501 absence de protection, ISSUE-505 protection incomplète
Hygiène de workflowISSUE-6XX (1 code)NommageISSUE-601 workflow sans name:
Actions tierces (supply chain)ISSUE-7XX (15 codes)Pinning, sources, CVE, archivage, code distantISSUE-701 action non SHA, ISSUE-703 CVE connu, ISSUE-705 cache empoisonné, ISSUE-713 source non autorisée, ISSUE-714 code distant mutable
Permissions et déclencheursISSUE-8XX (4 codes)permissions:, triggersISSUE-801 pas de permissions:, ISSUE-802 trigger dangereux, ISSUE-804 pull_request_target piégé
Hygiène du dépôtISSUE-9XX (5 codes)Dependabot, scanners, SECURITY.mdISSUE-903 pas d'outil de mise à jour, ISSUE-904 pas de scanner statique, ISSUE-905 pas de SECURITY.md

La famille 7XX est la plus stratégique pour la supply chain : elle concentre les protections contre les actions tierces compromises, la surface la plus attaquée d'un workflow GitHub. Les familles 8XX et 9XX sont quasi-exclusivement GitHub : elles couvrent ce que l'écosystème Actions expose comme risques (permissions du GITHUB_TOKEN, Dependabot, scanners CI, SECURITY.md).

Avant de parcourir le tableau, gardez en tête deux points : la migration v1 → v2 est nécessaire dès que votre .plumber.yaml utilise encore le top-level controls:, et le provider GitHub s'appuie sur la CLI gh pour l'authentification (pas de variable d'environnement à exporter).

SymptômeCause probableSolution
Le scan tourne sans votre configAucun .plumber.yaml trouvé, défaut embarqué utilisé (0.4.13+)Normal ; générez votre fichier avec plumber config generate pour l'ajuster
unknown key 'controls' at rootConfiguration encore au schéma v1Lancez plumber config migrate pour passer au schéma v2.0
Avertissement --threshold is deprecatedGating encore basé sur le pourcentagePassez à --min-points ou --min-score (composant : min_points / min_score)
GITLAB_TOKEN environment variable is requiredVariable manquante côté provider GitLabExportez GITLAB_TOKEN avant plumber analyze
401 Unauthorized (GitLab)Token incomplet ou invalideVérifiez read_api + read_repository sur le token
Aucune authentification GitHub détectéePas de session gh activeLancez gh auth login ; Plumber lit ensuite ~/.config/gh automatiquement
403 Forbidden on MR commentScope insuffisantUtilisez api si vous activez mr_comment
403 Forbidden on badgeScope insuffisant ou rôle trop faibleUtilisez api et un rôle Maintainer
Le composant ne s'exécute pasJob en .pre sans autre stage normalForcez stage: test
Deux pipelines sont créés sur le même pushworkflow:rules absentAjoutez le bloc recommandé par GitLab
Le projet n'est pas auto-détectéRemote Git absent ou mal nomméVérifiez le remote origin ou passez --gitlab-url / --github-url et --project
  • Plumber scanne la sécurité de vos pipelines GitLab CI/CD et GitHub Actions depuis la même CLI (v0.4.22 ; le composant GitLab suit désormais le même numéro de version)
  • Le chemin débutant est simple : plumber analyze fonctionne sans configuration (défaut embarqué depuis 0.4.13), puis config init et config validate pour l'ajuster
  • Le schéma v2.0 sépare les contrôles par provider (gitlab.controls: / github.controls:) ; plumber config migrate fait la conversion depuis la v1
  • Le job échoue via le Plumber Score : --min-points ou --min-score (composant : min_points / min_score) ; --threshold est déprécié
  • Commencez par corriger les images non épinglées, les actions non SHA (ISSUE-701), les includes mutables et les branches non protégées
  • Activez ensuite les protections supply chain : sources autorisées (ISSUE-713), CVE connues (ISSUE-703), pull_request_target piégé (ISSUE-804)
  • Le PBOM sert à inventorier le pipeline ; le CycloneDX facilite l'intégration avec Trivy et Grype
  • Les rapports --sarif et --glsast font remonter les findings dans GitHub Code Scanning et le Security Dashboard GitLab
  • Le score est la seule porte de sortie (0.4) : une issue Critical ou une accumulation de High plafonne les points tant qu'elle n'est pas corrigée
  • 66 codes d'issues en neuf familles ; la liste exacte vient toujours de plumber explain --list sur votre version
  • Gardez la Platform pour les besoins de gouvernance multi-projets

Ces questions reviennent systématiquement avant l'adoption : ce que fait l'outil, ce qu'il coûte, ce qu'il apporte face aux scanners déjà en place, s'il touche à vos fichiers et comment il fait échouer un pipeline.

  • Sécuriser les pipelines CI/CD : Les pratiques de durcissement et de gouvernance qui prolongent les findings remontés par Plumber.
  • OWASP Top 10 CI/CD : Le référentiel qui nomme les risques derrière les familles d'issues détectées ici.
  • Trivy : L'outil qui prend le relais sur le CycloneDX exporté, pour passer de la posture du pipeline aux vulnérabilités des dépendances.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn