Composer 2.11

Composer 2.11

We are pleased to announce the release of Composer 2.11.0. The highlight of this release is the new cooldown dependency policy, which withholds newly published package versions for a configurable period of time before Composer will install them. Composer 2.11 also ships several security fixes, removes the source fallback behavior deprecated in 2.10, lets custom policies block during composer install, and brings noticeable dependency resolution performance improvements.

Private Packagist already supports cooldown periods today. Private Packagist organizations can enforce a cooldown period across all their projects, independently of the Composer version their developers, AI agents, and CI systems use.

Cooldown periods: withholding new releases

Most malicious releases we have seen on Packagist.org in the past months were published by attackers who briefly gained control of a legitimate package, for example through a compromised GitHub account. The malware policy introduced in Composer 2.10 blocks such versions once they have been flagged, but there is always a window between publication and flagging.

A cooldown period narrows or closes that window, it's sometimes also called a minimum release age. Automated malware scanners can flag the first problems within minutes of a release, so even a cooldown period of a single hour gives automatic detection a chance to act before a new version becomes a candidate for your composer update. Reports that need manual review take longer, and a cooldown period of a few hours up to a day makes it more likely that these are caught as well. A cooldown period cannot guarantee that a malicious release is flagged in time, but every hour of delay increases the chance.

The cooldown policy is configured like the other dependency policies under config.policy and is not enabled by default. To withhold all versions published within the last day:

{
    "config": {
        "policy": {
            "cooldown": {
                "period": "1 day"
            }
        }
    }
}

The period takes a string like "6 hours" with the units second, minute, hour, day or week, or an integer number of seconds. You can also set it in your global Composer configuration, or through the COMPOSER_POLICY_COOLDOWN_PERIOD environment variable, for example in CI.

The cooldown policy applies whenever Composer resolves dependencies, so during update and require. composer install continues to install exactly what is in your composer.lock. require, init and create-project skip withheld versions when choosing a version, and outdated and show --latest report the newest version the policy allows while pointing out releases still in their cooldown period. Dev versions like dev-main representing moving targets and platform packages are never withheld.

In a partial update like composer update vendor/package, locked packages which are not being updated are left alone. A full composer update re-evaluates every package, so if your lock file already contains a version still in its cooldown period, Composer may replace it with an older version.

Security fixes and choosing a cooldown period

A cooldown period applies to every new version, including releases that fix security vulnerabilities. With a cooldown period of one day, Composer will not install a security fix during the first day after its release unless you tell it to.

Composer cannot safely let security fixes skip the cooldown policy automatically. An attacker who gains access to a package's repository has the same permissions as the maintainer. They can publish a security advisory for the package themselves, or release a malicious version after a legitimate advisory was published. Any rule that exempts releases based on advisory data would give attackers a way around the cooldown period.

Instead, you decide when a release should skip the cooldown policy. To install a fix right away, use the new --no-cooldown option on update, require, remove or create-project. It only skips the cooldown policy. Blocking of versions with security advisories, malware and your other policies stays active:

composer update --no-cooldown vendor/package

Packages you always want to receive immediately, such as your own, can be exempted permanently with the ignore option, which supports reasons, version constraints and wildcards like the other dependency policies:

"cooldown": {
    "period": "1 day",
    "ignore": {
        "mycompany/*": "We trust the code in our own packages"
    }
}

Manually updating and bypassing the cooldown period for security fixes only works if you notice the vulnerability. The longer your cooldown period, the longer every fix for a vulnerability you are not actively tracking is delayed. A period of a few hours to a day covers most of the time automatic and manual detection need, while keeping the delay for security fixes short. Tools like Private Packagist Security Monitoring can notify you by email, Slack, Microsoft Teams or webhook when a security advisory affects a dependency in one of your projects, so you can apply the fix right away.

Why weren't cooldown dependency policies shipped in Composer 2.10 already?

Composer 2.10 introduced dependency policies, but Composer metadata did not yet contain a timestamp a cooldown policy could rely on. The only date was the time field, which Packagist.org derives from the commit date of the tag or takes directly from the package's composer.json. Both are controlled by whoever publishes the release. An attacker can backdate a malicious release by a few weeks, and a cooldown period based on time would let it through immediately. Tools built on the time field to delay updates could not protect against a compromised release, because the attacker controls the date these tools check.

Packagist.org now includes a published-time for every version, based on when Packagist.org itself first made the version available (composer/packagist#1765).

If another Composer repository does not report a published-time for a version, that Composer needs to consider, the command fails and names the affected package, so a missing timestamp never silently disables the cooldown. For repositories you trust, you can set require-published-time to false to fall back to the time field. VCS, path and artifact repositories only have the time field.

The cooldown policy was implemented in #12692, #13098, #13099, #13100, #13102 and #13153. See the cooldown configuration documentation for all details.

Cooldown periods in Private Packagist

Private Packagist already supports the cooldown policy and provides a publication time for every version it serves, including mirrored open-source packages and your private packages. Like Packagist.org, it sets the publication time itself when it first imports a version, and if the code of a version changes, for example through a force-pushed tag, the cooldown starts again.

Organization admins can also set a cooldown dependency policy for the entire organization under Settings > Security. Private Packagist then holds back recently published versions for every project using the organization's Composer repository, regardless of the project's composer.json and of the Composer version in use, so projects that have not updated to Composer 2.11 yet are protected as well. Packages that should be available immediately can be added to the Packages without cooldown list. Read the cooldown dependency policy documentation for details.

Security fixes

Composer 2.11 includes the following security fixes:

  • Package names in vendor/composer/installed.json can not escape the vendor directory during install and reinstall anymore. GHSA-c968-33g3-wh7p
  • HTTP headers configured inline in a repository config, such as API tokens, are no longer being written into composer.lock / installed.json. GHSA-fm2w-xgf2-f9fh
  • TLS connections presenting the distrusted, compromised eMudhra intermediate certificates are now rejected, even if the system CA store trusts their roots. #13146
  • Bin proxies no longer embed package-controlled bin paths and shebangs unescaped, and bin paths containing shell metacharacters are rejected. #13056
  • secure-http now also catches insecure URLs which are not strictly valid or use git's <transport>::<address> syntax. #13144
  • Repository options like custom HTTP headers are no longer sent to dist URLs on other hosts whose name merely starts with the same string as the repository host. #13142

Source fallback removed

Composer 2.10 deprecated the fallback to a source checkout when a dist download fails, and added a temporary source-fallback config option to re-enable the old behavior. As announced, Composer 2.11 removes this option, and dist installs no longer fall back to source. #13050

You can still install packages from source explicitly (including per-vendor) using the preferred-install config option or the --prefer-source command line option.

Custom policies can block during install

Custom dependency policies, such as an internally maintained list of disallowed package versions, can now use the block-scope option already known from the malware policy. Setting it to all blocks matching versions during composer install as well, so a version added to your list after a lock file was created will fail the next install. #12990

Lock file warning with policy config

Composer now marks the lock file as outdated when the policy config changes, fixing an oversight in Composer 2.10 that ignored it. If your project defines a policy config, you will see a lock file warning after upgrading to 2.11 until you update the lock file. #13149

Other notable improvements

  • Faster dependency resolution: Dependency resolution is faster and uses less memory. #13071, #13072, #13103, #13123, #13124, #13132, #13174
  • No more HTTP/3 delay: Composer no longer requests HTTP/3, which added a delay to every new connection with recent curl versions. #13096
  • Parallel outdated: composer outdated and composer show --latest load package metadata in parallel. #13113
  • Audit direct dependencies only: The new --direct (-D) option on composer audit only audits direct dependencies. #13121
  • Terminal progress reporting: Composer reports progress and status to supporting terminals via OSC 7501 and OSC 9 escape sequences, configurable with COMPOSER_TERMINAL_STATUS. #13117
  • Rate limit handling: Composer waits and retries when a repository responds with a 429 status and a Retry-After header. #13082
  • Bitbucket SSH hostname: Support for Bitbucket Cloud's new ssh.bitbucket.org SSH hostname, which is now also used for the SSH fallback. #13152
  • Restricting VCS branches: The new only-branches option on VCS repositories restricts which branches are loaded. #13086
  • strict-psr-autoloader config: The --strict-psr-autoloader flag is now also available as a config option. #12893
  • Clearer conflict errors: Dependency conflict errors explain versions blocked by security advisories more clearly. #13108, #13075
  • Lock file merge conflicts: composer install now fails when composer.lock has a content-hash merge conflict. #13105

Find out about other features and bugfixes in the complete changelog.

Upgrading

Run composer self-update to upgrade to Composer 2.11. If you run into any problems, please report them on the Composer issue tracker.