← Dependency Confusion Check

What is dependency confusion?

Dependency confusion is a supply-chain attack where a package manager installs a public package instead of an organization's internal one of the same name, because the resolver was told about both places the name might live and had no rule saying the internal one always wins.

A real incident

In 2021, researcher Alex Birsan demonstrated this against more than 35 companies — Apple, Microsoft, PayPal, Netflix, Uber, and Tesla among them — by publishing decoy packages under their internal package names on the public npm and PyPI registries. Internal build systems, configured to check the public registry alongside (or instead of) a private one, installed the decoys and executed Birsan's code with real credentials. Some paid out six-figure bug-bounty totals for the finding.

The pip mechanism

pip's own documentation names the risk directly: --extra-index-url tells pip to consider a second index in addition to the primary one, and "the additional repository can specify a version of a package which will be installed instead of the one from index-url." pip compares versions across every configured index and installs whichever reports the higher number for a given name — so a public package published under your internal package's name, at a higher version, wins.

pip install -r requirements.txt --extra-index-url https://pkgs.internal.example.com/simple/

If acme-internal-utils only exists on the internal index today, publishing acme-internal-utils version 999.0.0 on the public PyPI is enough to have pip install the attacker's version everywhere this command runs.

The npm mechanism

npm resolves an unscoped dependency name (no @org/ prefix) against whatever registry is configured for that project — typically set globally in .npmrc with a bare registry= line. That configuration is local to each machine and each CI runner. A developer's laptop with the internal .npmrc resolves an internal package correctly; a CI runner, a new hire's fresh laptop, or an automated dependency-update bot without that file falls back to the public npm registry for the exact same unscoped name — and if nobody has claimed it there, an attacker can.

The fix

Check your own configuration

Paste your requirements.txt, pip.conf, package.json, and .npmrc into Dependency Confusion Check to find every --extra-index-url use and unscoped npm name at once, or run the CLI in your own checkout:

python3 check.py --fail-on-risk

That exits with a nonzero status if either pattern is present — wire it into your own CI to block a newly-added one before it merges. Already running a supply-chain platform like Socket? See how this checker differs from Socket — different layers, and worth running both.