When a composer install becomes code execution: CVE-2026-40176 and CVE-2026-40261 in the Composer supply chain

Composer is the quiet backbone of every PHP pipeline. That is exactly what makes two vulnerabilities published on 10 April 2026 so unpleasant: a deliberately harmless composer install can lead to arbitrary command execution — even without Perforce installed on the target system.
TL;DR — the 90-second summary
- CVE-2026-40176 (CVSS 7.8)
Insufficient escaping in Composer repository metadata — a crafted
composer.jsonleads to code execution in the build user's context- CVE-2026-40261 (CVSS 8.8)
Same class triggerable via any Composer repository including Packagist, no Perforce installation required
- Immediate patch
Composer 2.9.6 or 2.2.27 LTS — plus Packagist has disabled publishing of Perforce source metadata
- The actual risk
The build user holds SSH keys, registry tokens, cloud credentials — RCE in the build is lateral movement
- Four guardrails
Lockfile builds, frozen Composer binary in the CI image, Renovate with cooling-off, read-only Composer mirror
- Who is on the hook
every Mittelstand company with a PHP stack — TYPO3, Sylius, Symfony, Laravel are equally exposed
What is the problem?
On 10 April 2026 the Composer maintainer team published two vulnerabilities and shipped patches at the same time: CVE-2026-40176 (CVSS 7.8) and CVE-2026-40261 (CVSS 8.8). Both flaws sit on the path where Composer processes repository metadata. A crafted composer.json or a manipulated source reference containing shell meta-characters is insufficiently escaped and passed to a subprocess. Result: arbitrary code execution in the build user's context.
The more severe variant can be triggered via any Composer repository — including Packagist — and does not require a Perforce installation on the target. As an immediate measure, Packagist.org disabled publishing of Perforce source metadata on 10 April.
We were not hit in our pipelines. That is not a feat — it is the result of the same discipline we apply to other tools in our build chain. In this post we frame the incident and describe the structural decisions that make the difference — regardless of whether the next Composer CVE or the next npm wave is coming.
Impact: why a build-RCE is not a build problem
Composer vulnerabilities don't hit the product, they hit the path to it. The actual code in your repository is clean, the app runs, the build would succeed — and in exactly that moment a subprocess on your CI runner executed a foreign command. Three effects make the risk profile uncomfortable.
Builds often run with higher privileges than the running app
SSH keys, container-registry tokens, cloud credentials, deployment tokens — it all sits in the build context. Code execution in the build is therefore almost never just a build problem; it is the step to lateral movement. The same pattern we describe in our CI/CD concentration post.
Composer in CI images is rarely updated
In the pipeline reviews of the past weeks Composer was, in many findings, in a frozen image tag with no automatic update path. A 2.9.4 or 2.8.x in CI stays invisible until an incident makes it visible. Application dependencies get Renovate PRs, the toolchain doesn't.
Composer packages are transitive
Even an organisation that keeps its own composer.json clean depends on the behaviour of tools pre-installed in the CI container and on plugins that load other packages. Exactly that class of plugins contributed in April 2026 — visible in the well-known Intercom Composer plugin incident — to the distribution of the Mini Shai-Hulud wave into the Packagist ecosystem.
Who is affected?
Even a comparatively lean PHP stack usually has a significant number of direct and transitive Composer dependencies. A TYPO3, Sylius or Symfony-based platform is no different here from a larger enterprise stack. Anyone without a clear control loop in CI for which packages are even accepted does not carry a "smaller" risk — just a less visible one.
Three constellations are particularly exposed:
- CI runners without mirror discipline — whatever comes via Packagist comes unfiltered.
- Production servers running
composer installdirectly — updates on the live server mean compromised server credentials and potentially persistent backdoors in the application on successful exploitation. - Stacks with a large plugin surface — TYPO3 extensions, Sylius plugins, Symfony bundles: every additionally loaded package extends the attack surface in the build context.
Mitigation and immediate actions — the four guardrails
Quick start in the order in which we currently push pipelines through — each line knocks out a specific class of incidents.
composer self-update 2.9.6 # or 2.2.27 (LTS line)
composer diagnose # check repository list
composer config --global --unset repositories.perforce-*
composer audit # known vulns against lockfile
1. Reproducible installs via composer install against a committed composer.lock
Our builds do not update packages, they install exactly what the lockfile pins. A changed source reference therefore only enters the build via an explicit lockfile commit, not spontaneously in the background. composer update has no place in CI.
2. A defined Composer path locally and in CI
We use a frozen Composer binary in a clearly versioned variant. Composer is not spontaneously updated on the runner via composer self-update; CI images are built, signed, released. The version jump to Composer 2.9.6 or 2.2.27 LTS is therefore a deliberate step, not background activity.
3. Updates via Renovate with cooling-off
New Composer versions are not updated from the shell, but proposed as a pull request and merged after a cooling-off period (typically 72–168 hours). The cooling-off gives the community time to spot a bug or security flaw. The exception remains a known, actively exploited CVE in the running version.
4. Read-only Composer mirror for production builds
Wherever possible, CI runs against an internal mirror that forwards only approved versions. That is effort, but it gives a reliable audit log of which packages ever entered the build context — and filters out packages that have been compromised for an hour of their lifetime on a public registry. Same pattern as the npm mirror we documented after the EVM/DeFi cluster of 6 May.
Detection and verification
Five core questions that bring clarity in half an hour — whether you could be hit today and where the biggest lever sits.
- Which Composer versions run today on your CI runners? A
composer --versionin every image that actively builds. - Which Composer versions run on production servers where
composer installis executed directly? If you can't answer that, that is the answer. - Are there entries with
"type": "perforce"or suspicious source references in yourcomposer.jsonor in transitive lockfiles? - Which CI builds between 5 and 10 April 2026 ran via Packagist against an unpatched Composer binary? These builds belong on a re-run list with a patched toolchain.
- Which secrets were available in the build context — SSH keys, container-registry tokens, AWS credentials? For all of these: after an unclear build incident rotate, don't hope.
Quick-check snippets we run in the first hour:
# Composer binaries across all reachable images
for img in $(docker images --format '{{.Repository}}:{{.Tag}}'); do
docker run --rm "$img" composer --version 2>/dev/null \
| grep -i composer
done
# Perforce repositories in the lockfiles
git grep -nE '"type":\s*"perforce"' -- 'composer.lock' '**/composer.lock'Operator recommendation
What should be operationally in place for which PHP stack right now — in bullet form, because the decisions are usually not between "right" and "wrong", but between "residual risk is accepted" and "residual risk is luck".
- If your CI installs Composer from the distro package or via
self-updateon the runner — then freeze it to 2.9.6 in a dedicated build-image layer as the next sprint step. No further build optimisations before that. - If you have no Composer mirror today — then set up a mini mirror via Satis or a Packagist proxy in your internal Artifactory as a read-only cache, populated from the lockfile. Not a big repo setup, a lunch break.
- If Renovate is not yet running — then it is as important for Composer versions as it is for application packages. Toolchain updates belong in the same PR discipline as library updates.
- If you run
composer installdirectly on production servers — then the next pipeline iteration is to move to pre-built artifacts (tarball, OCI image) that are only deployed.composer installin production is an RCE multiplier. - If you run a TYPO3, Sylius or Symfony platform with a large extension/plugin surface — then an SBOM inventory of direct and transitive Composer dependencies is a mandatory part of the audit log, ideally CycloneDX.
Cross-references: the npm EVM cluster post for mirror topologies, the CI/CD concentration post for the structural reasoning, and the AI security audits post for embedding into release discipline.
Conclusion
Most PHP pipelines we see in review have individual pieces. Rarely all of them. Either there is a lockfile, but Composer is pulled in CI via self-update to whatever is the latest binary at the time. Or Renovate dutifully proposes PRs, but without a cooling-off, so a just-compromised version is in the build the next day. Or production servers build differently than CI, and nobody can say with certainty which Composer version is running there after an incident.
The question is not whether Composer 2.9.6 sits in your images. The question is whether your pipeline would even notice the next incident — and whether after noticing you know in the same hour which builds in the days before ran against the compromised version.
A longer write-up with examples of our GitLab CI components, the Renovate configuration and our mirror topology is available (in German) at ole-hartwig.eu.
Frequently asked questions about the Composer CVEs
Why is Composer as a supply-chain vector particularly delicate under NIS-2?+
Because NIS-2 explicitly demands supply-chain security and security in the acquisition and development of systems within its ten risk-management domains. A build pipeline that uncontrollably pulls arbitrary packages from a public registry is exactly where these two domains overlap. Anyone without an audit path over installed packages cannot demonstrate in audit what they actually do.
How long does it take to retrofit these guardrails?+
Lockfile discipline and Composer pinning can be implemented within a few days. Renovate with a maturation window takes a little longer because it must align with your own release logic. A read-only mirror is the larger investment, typically two to three weeks for build-out, sync logic and CI integration. For a clean overall picture we plan with two to four weeks, depending on how grown your pipelines are.
What is the point of an internal Composer mirror when Packagist works fine?+
A read-only mirror inserts a controlled filter between your builds and the public registry. An hour of life for a compromised package on the public side is enough to hit nightly builds. In the mirror nothing happens, because the new package has not been approved. On top, you get an audit log of which packages ever ran in your builds — worth its weight after an incident.
Is upgrading Composer to 2.9.6 enough?+
It is the necessary first step but not sufficient on its own. You don't need to upgrade one Composer installation, you need to upgrade all of them: local dev containers, every CI image, every production server on which Composer runs. If a single worker continues with an old version, the incident has an open path. So check image builds, version pinning and any cron-driven Composer calls.
We do not use Perforce — are we still affected?+
Yes. CVE-2026-40261 (CVSS 8.8) can be triggered through any Composer repository, including Packagist, and does not require a Perforce installation. Composer executes the injected commands even when Perforce is not installed. The other flaw (CVE-2026-40176) is narrower, but the risk profile is set by the more severe one.
How disciplined is your PHP pipeline really?
If you run a PHP stack with Composer in CI and on production, the next pipeline review pays off. 30 minutes, no pitch. We walk through with you whether lockfile, Composer binary, Renovate and mirror discipline work in combination — and where the next two or three steps would have the largest lever before the next Composer or npm CVE arrives.
This is the operational routine from DevSecOps as a Service and the Outsourced IT Department — supply-chain hardening as mirror discipline, not as gut feeling.

