Composer 2.7: minimal dependency changes and a security boundary fix

Understand the February 2024 security fix, preview focused dependency updates, and account for Composer 2.7's abandoned-package audit policy.
Updating the tool and updating packages are separate jobs
Composer 2.7.0 was released on 8 February 2024 with a security fix for the Composer executable and a flag for smaller dependency updates. Replacing the executable does not update the packages already recorded in composer.lock.
This is a historical behavior review for PHP developers who already use Composer and understand a lock file. The examples were run with Composer 2.7.0 on PHP 8.3.35 on 12 October 2026. That pin makes the reproduction specific; it is not a recommendation to install this old release into a current application.
The security fix concerned the working directory
CVE-2024-24821 concerned code execution through tampered vendor/composer/InstalledVersions.php or installed.php files when Composer ran in that directory. The code ran as the invoking user. The affected ranges were >=2.0,<2.2.23 and >=2.3,<2.7.0; the advisory names 2.2.23 and 2.7.0 as patched releases.
The advisory includes every CLI command, including self-update. A diagnostic invocation in a compromised checkout could therefore cross the same trust boundary. I recommend that you repair the tool from a trusted context and review who can modify the working tree; this advisory alone does not establish a remotely exploitable application endpoint.
Minimal changes preserves a compatible transitive version
The reproduction starts with fixture/app 1.0.0 requiring fixture/helper ^1.0, with helper locked at 1.0.0. Its local repository then offers app 1.1.0 and helper 1.1.0. An ordinary partial update with --with-dependencies selects both newer versions. With --minimal-changes, app still moves to 1.1.0 while helper stays at 1.0.0.
The script below previews the minimal case without changing the lock file. The downloadable runner creates the synthetic manifests and locks, executes this exact script, checks that the lock bytes stayed unchanged, and then applies the same resolution without --dry-run. The package names belong to the fixture, so the script is not a command for an arbitrary application checkout.
The fixture uses --no-install because these metapackages have no application files to install, and --no-audit to keep its solver experiment separate from advisory reporting. Plugins and scripts are disabled. These flags describe this controlled test, not a complete application-update procedure.
#!/bin/sh
set -eu
composer update fixture/app \
--with-dependencies --minimal-changes --dry-run \
--no-install --no-audit --no-plugins --no-scripts
Necessary changes and root requirements still matter
When app 1.1.0 instead requires helper ^1.1, the minimal update selects helper 1.1.0 too. The flag cannot preserve a version that no longer satisfies the selected package. It also does not constrain app itself to a patch release.
A second test makes helper a root requirement as well, still constrained by ^1.0 and locked at 1.0.0. Explicitly requesting app 1.1.0 with -w fails with exit code 2 and preserves the lock file. Using -W permits helper to move to 1.1.0. The difference is whether a dependency that is also a root requirement may be updated.
I recommend that you inspect the conflicting requirement before changing -w to -W. A larger permitted update set may be justified, but it changes the work under review. A small successful lock-file diff is evidence about declared constraints, not evidence that changed APIs work in the application.
An abandoned package can fail an audit
The release changed audit.abandoned to fail by default. Abandonment records a maintenance status, not a vulnerability finding.
The exact audit command below was run against an invented abandoned metapackage. Composer 2.7.0 returned JSON containing an abandoned entry and exited 1. The shell retained that failure. Do not apply the exit-code meanings of a later Composer release to this historical result.
#!/bin/sh
set -eu
composer audit --locked --format=json
Reproduce the boundary, then test the application
Download the source and tests below. With Docker, Python 3 and network access for the initial image build, run python3 frontend/scripts/verify-composer.py from the extracted folder. The build verifies release PHAR checksums. Execution has no external network or host checkout mount; generated locks and package metadata live in the disposable container.
The fixture proves solver and audit behavior using invented packages. It does not exploit the historical CVE, install a real application, contact a live vulnerability feed or verify runtime compatibility. For real maintenance, I recommend that you retain the reviewed lock, obtain current advisory data and run the application checks appropriate to the changed dependencies.
Reproduce the examples
Download the synthetic package repositories, six shell examples, pinned runtime and assertions. Requires Docker and Python 3; no repository account is needed.
Download the source and tests (.zip)
