Aller au contenu principal
Site en cours de refonte — quelques pages peuvent bouger ou évoluer.
23 avril 2026

Composer en CI : la CVE Perforce te rappelle que ton build exécute déjà des commandes

Composer n’est pas « juste un install de dépendances ». En CI, c’est déjà un moteur d’exécution (VCS, plugins, scripts) : ces CVE côté Perforce sont le bon moment pour blinder.

10 min de lecture
42 vues
réactions
Partager :
Composer en CI : la CVE Perforce te rappelle que ton build exécute déjà des commandes

Si tu utilises Composer en CI, ton pipeline exécute déjà des commandes. Pas “peut-être”. Déjà. Entre les drivers VCS (Git, Mercurial, SVN… et Perforce), les plugins, et les scripts définis dans composer.json, ton build est une surface d’exécution à part entière.

La release Composer 2.9.6 corrige justement des vulnérabilités de command injection liées au driver Perforce (deux CVE). Même si tu n’as jamais touché à Perforce de ta vie, c’est un rappel brutal d’un truc simple : “faire un composer install” en CI, ce n’est pas un acte neutre. Et si ton CI construit des entrées non maîtrisées (PR externes, branches de feature venant de partout, monorepo avec jobs dynamiques), tu veux être au clair sur ton blast radius.

Composer + VCS en CI : pourquoi une injection est plausible

Composer ne se contente pas de télécharger des archives depuis Packagist. Il sait aussi récupérer des dépendances directement depuis des repositories VCS. Quand tu déclares un dépôt de type VCS dans composer.json ou quand tu dépends d’un package qui n’est pas livré sous forme d’archive, Composer peut appeler des binaires système pour cloner, exporter, lister des tags, etc.

Perforce, dans ce modèle, n’est pas “juste un backend”. C’est un binaire (p4) que Composer peut invoquer. Et dès que tu as une chaîne « donnée non maîtrisée → commande shell », tu as un endroit où une injection peut exister. Les détails exacts de l’exploitation importent, mais l’hygiène à retenir est toujours la même : ton pipeline ne doit pas donner trop de pouvoir à des inputs qui peuvent être contrôlés par quelqu’un d’autre.

Le piège classique, c’est de croire que ça n’arrive que si tu “fais n’importe quoi”. En pratique, il suffit que ton CI fasse un build automatique sur des branches/PR, et que la config (ou un fichier lu par le build) permette d’influencer une URL de repository, un chemin, une ref, ou un paramètre qui finit dans un driver VCS. Ce n’est pas forcément ton repo principal, ça peut être un dépôt interne, un “template repo”, ou un job partagé que tout le monde réutilise.

La CVE « Perforce » : qui est vraiment concerné

Si tu n’utilises pas Perforce du tout, tu es probablement moins exposé sur cette vulnérabilité précise. Mais “moins exposé” ne veut pas dire “zéro sujet”. Déjà parce que Composer vit dans tes images Docker, tes runners, tes cache layers, et qu’il est souvent mis à jour de façon invisible. Ensuite parce que l’écosystème CI est rarement propre : une équipe a un repo historique, une autre a un package privé, et soudain tu as un job qui tire une dépendance depuis un VCS que personne ne surveille vraiment.

Tu es clairement dans la zone rouge si tu coches au moins une de ces réalités (oui, c’est volontairement concret) : tu as des repositories Composer en Perforce, tu as des dépôts privés récupérés via des drivers VCS, tu fais des builds sur des contributions externes (forks, PR) avec une CI qui a des secrets disponibles, ou tu as des runners partagés qui construisent des projets qui n’ont pas le même niveau de confiance.

Le scénario “qui pique” n’est pas forcément “un attaquant exécute une commande sur le runner”. Souvent, le vrai impact, c’est : exécution sur une machine qui a accès au réseau interne, récupération de tokens dans l’environnement, lecture de caches, compromission d’artefacts, ou déplacement latéral via des credentials trop larges. En CI, une exécution, c’est rarement isolé.

Audit express : comment savoir si ton repo/CI touche Perforce (ou un VCS non maîtrisé)

Je commence toujours par un audit bête et méchant, parce que c’est là que tu découvres les surprises. Dans le code, cherche dans tes composer.json des blocs repositories et repère les types qui ne sont pas “packagist”. Un type à perforce est évidemment un signal direct. Mais un type à vcs avec une URL interne, un schéma exotique, ou un dépôt “outil” un peu opaque mérite le même niveau de méfiance.

Côté CI, lis le pipeline comme un attaquant. Est-ce que des jobs s’exécutent sur des PR venant de forks ? Est-ce que composer install tourne avant d’avoir des garde-fous (review, permissions, sandbox) ? Est-ce que le job a des secrets injectés par défaut ? Est-ce que le runner est mutualisé entre projets ou entre équipes ? Dans beaucoup d’organisations, le problème n’est pas Composer, c’est la combinaison “build automatique” + “secrets partout” + “runners pas isolés”.

Je regarde aussi les caches. Composer cache des choses (dist, VCS checkouts selon les cas, metadata). Un cache partagé entre branches, ou pire entre repos, c’est un accélérateur d’ennuis. Même si la CVE du jour est patchée, un cache mal conçu peut transformer un incident local en problème qui se propage.

Correction immédiate : passer en Composer 2.9.6 (et arrêter les upgrades invisibles)

La réponse simple est la bonne : mets à jour Composer vers une version corrigée (2.9.6 ou plus récent). Mais ne t’arrête pas là. Le vrai fix durable, c’est de pinner la version de Composer utilisée en CI. Sinon tu vas te retrouver avec des runners qui se mettent à jour tout seuls, des différences entre environnements, et des “ça marchait hier” qui coûtent du temps.

Le pattern que j’aime bien, c’est de choisir explicitement une version dans ton pipeline, et de la faire évoluer comme une dépendance normale. Tu review, tu testes, tu déploies. Pas de magie.

# Exemple GitHub Actions : version explicite de Composer
- name: Install Composer 2.9.6
  run: |
    composer --version || true
    composer self-update 2.9.6
    composer --version

- name: Install deps
  run: composer install --no-interaction --no-progress --prefer-dist

Autre approche, souvent plus propre : utiliser une image Docker qui embarque Composer avec un tag de version, plutôt que “whatever est installé sur le runner aujourd’hui”. Là aussi, l’idée est la même : rendre la version visible et reproductible.

# Exemple Dockerfile : pin Composer dans une image de build
FROM composer:2.9.6 AS composer

FROM php:8.3-cli
COPY --from=composer /usr/bin/composer /usr/bin/composer
# ... le reste de ton image de build

Une mise à jour de Composer, c’est rarement l’endroit où tu veux “gagner du temps” en laissant flotter les versions. Le coût est faible, et le jour où tu as un incident supply chain, tu seras content de pouvoir prouver exactement ce qui a tourné.

Durcir ton usage de Composer en CI : les protections qui paient vite

Le premier levier, c’est de réduire ce que Composer a le droit de faire. Si ton job est un build/test qui n’a pas besoin d’exécuter des scripts, coupe-les. Beaucoup de projets ont des post-install-cmd historiques qui ne servent plus qu’à “faire comme avant”. En CI, ces scripts sont une surface d’exécution cadeau. Si tu dois les garder, fais-le consciemment, et sache exactement ce qu’ils font.

Ensuite, traite les plugins Composer comme du code exécutable, pas comme de la config. Le mécanisme allow-plugins existe justement pour éviter que n’importe quoi s’exécute “par défaut”. Si ton projet autorise trop largement, tu t’achètes des emmerdes. C’est particulièrement vrai sur les repos qui agrègent des packages internes, des outils de build, ou des bundles maison.

Enfin, durcis la CI elle-même. Les règles simples qui évitent des nuits blanches : runners isolés (ou à minima bien segmentés), secrets non disponibles sur les builds non approuvés, permissions minimales sur les tokens (VCS, registry, cloud), et egress réseau limité quand c’est possible. Une command injection sur un runner sans secrets et sans accès réseau interne, c’est une autre histoire.

Le cas Perforce en particulier : attention aux runners “qui ont p4”

Un truc qu’on oublie : pour que Composer utilise Perforce, il faut que le binaire p4 soit utilisable dans l’environnement. Donc, si tu as des runners “génériques” qui embarquent plein d’outils, tu élargis mécaniquement la surface. Le même raisonnement vaut pour d’autres outils VCS. Plus ton runner ressemble à une boîte à outils universelle, plus il est difficile à défendre.

Si tu as une stack qui utilise Perforce, je ferais l’inverse : des runners dédiés, un accès réseau strictement nécessaire, des credentials Perforce à durée de vie courte, et une observation minimale des commandes lancées (au moins des logs exploitables, pas juste un “ça a échoué”). Le but n’est pas de devenir parano. Le but est d’éviter qu’un bug ou une vulnérabilité transforme ton CI en point d’entrée.

Si tu suspectes un incident : un runbook réaliste (pas un roman)

Quand tu suspectes qu’un pipeline a exécuté quelque chose qu’il ne devait pas, il faut agir vite, mais surtout agir utile. La première priorité, c’est de considérer que des secrets ont pu fuiter. Rotation des tokens CI, des tokens VCS, des credentials de registry, des clés cloud si elles étaient présentes. Pas demain, pas “quand on a le temps”. Tout de suite.

Ensuite, tu nettoies ce qui peut persister. Purge des caches Composer (ceux du runner et ceux stockés à part), invalidation des caches de dépendances s’ils sont partagés, et rebuild des images de build si tu as un doute sur ce qu’elles contiennent. Sur des runners long-lived, je préfère souvent repartir d’une base propre plutôt que de “réparer” à la main un environnement dont je ne suis plus sûr.

Dernier point, et c’est celui que tout le monde bâcle : la preuve. Va chercher les logs du job, le diff exact construit, la version exacte de Composer, et essaye de reconstituer les commandes qui ont tourné. Si tu utilises Perforce, regarde explicitement ce qui a été exécuté autour de p4. Même si tu ne trouves rien, tu veux être capable d’expliquer pourquoi tu penses que c’est contenu. En incident, l’incertitude est ce qui coûte le plus cher.

Mon avis : la vraie leçon, ce n’est pas « update Composer », c’est « traite ton CI comme de la prod »

Tu peux patcher Composer, et il faut le faire. Mais le sujet de fond, c’est le modèle de confiance. Si ton CI exécute composer install sur du code que tu ne contrôles pas vraiment, sur des runners qui ont des secrets et un réseau large, tu as déjà accepté un risque. Cette CVE Perforce est juste un moment où le risque devient visible.

Le bon move, c’est de profiter de l’alerte pour remettre un peu de discipline : versions pinées, runners segmentés, secrets minimisés, et une vraie décision sur ce que tu laisses exécuter. Ça ne rend pas ton système invulnérable. Ça rend l’échec beaucoup moins catastrophique.

Sources

Cet article vous a plu ?

Commentaires

Laisser un commentaire

Entre 10 et 2000 caractères

Les commentaires sont modérés avant publication.

Aucun commentaire pour le moment.

Soyez le premier à donner votre avis !