Safeguard
Supply Chain•

How do Composer canonical, only and exclude stop dependency confusion?

Composer 2 repositories are canonical by default, and the only and exclude options narrow what each can serve. Setup steps to block name confusion.

Safeguard Security Team
3 min read

Dependency confusion happens when a public package with the same name as your private one gets installed instead. In Composer 2, repositories are canonical by default, so the first repository that has a package wins. The only and exclude options narrow that further.

How Composer picks a repository

The Composer docs explain that it looks a package up in the topmost repository and stops at the first one that contains it. Packagist.org is added implicitly as the last repository unless you disable it. In Composer 1.x all repositories were non-canonical, so Composer kept searching. In 2.x all are canonical by default.

The attack the docs sketch is simple. If your private repository were non-canonical and someone published a higher version of the same name on Packagist, Composer could prefer the public one. With canonical behaviour, a higher version from Packagist is not even considered when your private repository has that name.

The three controls

ControlWhat it doesUse it when
Order of repositoriesHigher entries are checked firstAlways, put private sources above Packagist
canonical set to falseKeep looking in lower repositoriesRarely, and with care
onlyA repository may serve just the listed namesYou want a strict allow list per source
excludeA repository must not serve the listed namesYou need to block specific names from a source

Both only and exclude accept arrays of names, including wildcards such as a whole vendor prefix.

Steps to harden a mixed setup

  1. List the vendor prefixes you own, for example your company name.
  2. Put your private repository above Packagist in composer.json.
  3. Add an only entry on the private repository for your prefixes.
  4. Add an exclude entry on Packagist for those same prefixes, so a public package with your name cannot be served.
  5. Reserve your vendor name on Packagist. The Packagist about page says vendor names are protected once a package with that name has been published.
  6. Test by trying to require a package with your prefix that does not exist privately. It should fail, not fall through.

Mistakes to avoid

Do not set canonical to false just to pick up a fork with higher versions without understanding the consequence. The docs say non-canonical use is mainly for a source preferred when it has higher versions, and that is the setup a public attacker package would exploit. Also avoid mixing in many extra repositories. Each is another place a name can be served from.

Try it yourself

Click through it at your own pace, or open it full screen. Check where each package in the inventory came from.

Where Safeguard fits

Safeguard's reachability analysis helps separate advisories your code can reach from those it cannot. You can try a free scan on your own lock file.

Quick check

Is dependency confusion possible with Composer 2 defaults?

It is much harder, because repositories are canonical by default. You can still weaken it by setting canonical to false.

Does protecting my vendor name on Packagist help?

Yes. Packagist protects vendor names once a package with that name exists, so others cannot publish under it.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.