What Is Dependency Confusion? Hijacking Internal Package Names
Dependency confusion is an attack where someone publishes a public package with the same name as your private internal one, hoping your build grabs theirs.
Many organizations have private packages, internal libraries published only to a private registry. Dependency confusion exploits how package managers resolve names: if a public registry has a package with the same name and a higher version, the build may pull the public (attacker-controlled) one instead of yours. The attacker never needs to breach your network; they just guess a name.
The resolution flaw
When a package manager is configured to check both a private and a public registry, it often picks the highest version it can find across all of them. An attacker publishes a public package with your internal name and a huge version number, and the resolver prefers it.
How the attack plays out
- The attacker learns an internal package name (from a leaked manifest, error message, or guess).
- They publish a package of that name to the public registry with a high version.
- Your build resolves to the public version and installs it.
- The malicious package's install script runs with your build's privileges.
How names leak
Internal package names show up in committed lockfiles, stack traces, public repos, and Dockerfiles. Attackers do not need much; a single reference to an internal name in a public artifact is enough to start.
Defenses
Scope or namespace internal packages so their names cannot collide. Configure the package manager to fetch internal names only from the private registry. Pin versions in a lockfile. Some registries let you claim or reserve a namespace publicly to block imposters.
Why it matters for CI
The install happens in the pipeline, where the secrets are richest. A confusion attack that lands in CI gives the attacker the build environment. Isolated, ephemeral runners with scoped credentials limit what that compromised install can reach.
Key takeaways
- Dependency confusion publishes a public imposter of your private package name.
- Resolvers that prefer the highest version across registries pull the attacker's package.
- Namespace internal packages and lock resolution to the private registry to block it.