Safeguard
Incident Analysis

Dependency confusion attacks against major tech companies

A look at the dependency confusion attacks that hit Apple, Microsoft, PayPal, and PyTorch — and why the technique still works against top engineering orgs.

Safeguard Research Team
Research
7 min read

SAN FRANCISCO — In February 2021, security researcher Alex Birsan published findings that sent a chill through enterprise engineering organizations: using a technique he called "dependency confusion," he had successfully executed unauthorized code inside the internal build systems of Apple, Microsoft, PayPal, Netflix, Uber, Tesla, Yelp, and roughly 30 other companies — collecting over $130,000 in bug bounties along the way. Five years and dozens of copycat incidents later, dependency confusion remains one of the most consistently exploitable — and consistently underestimated — classes of software supply chain attack. Public registry telemetry, incident writeups, and repeated proof-of-concept disclosures since 2021 show the pattern has not gone away; it has migrated across ecosystems, from npm and PyPI to internal artifact repositories inside some of the world's most sophisticated engineering shops.

This report examines how the attack works, what happened at scale in 2021, why it re-emerged in high-profile incidents like the PyTorch compromise, and what security and platform teams can do to close the gap that keeps letting it happen.

The Mechanism: A Trust Assumption, Weaponized

Dependency confusion exploits a simple architectural gap. Most large engineering organizations maintain private, internally-named packages (e.g., internal-auth-utils, netflix-secure-http, paypal-config-loader) alongside dependencies pulled from public registries like npm, PyPI, or RubyGems. When a build tool is configured to check multiple registries — public and private — for a package name, and no explicit scoping or namespace reservation is enforced, most package managers default to installing whichever version has the higher semantic version number, regardless of source.

An attacker who can discover the name of an internal package — often trivially, by scraping leaked package.json files, internal GitHub repos, JavaScript source maps shipped to production, or even error messages — can publish a same-named package to the public registry with a deliberately inflated version number (e.g., 9.9.9). If the victim's build pipeline isn't hard-pinned to an internal source, it will pull and execute the attacker's package instead of the legitimate internal one — often with the elevated privileges of a CI/CD runner.

This is not a vulnerability in npm, PyPI, or any single tool. It is a systemic trust and configuration problem that persists across nearly every major package ecosystem, and it scales precisely with the size and complexity of the target's internal package graph — meaning the largest, most sophisticated engineering organizations are often the most exposed.

The 2021 Disclosures: A Who's-Who of Tech Giants

Birsan's research, published under the title "Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies," remains the reference case study because of both its scale and its method of discovery. Rather than targeting a single company, Birsan systematically mined public sources — leaked internal file listings, JavaScript bundles, and even employees' public GitHub activity — to compile lists of internal package names across dozens of organizations, then pre-emptively registered those names on npm and PyPI with callback payloads that beaconed back exfiltrated hostnames, usernames, and internal network paths.

The confirmed impact list included:

  • Apple — internal package execution confirmed on build infrastructure, part of a bounty exceeding $30,000 for combined findings.
  • Microsoft — one of the largest payouts in the disclosed set, reportedly the single highest bounty Birsan received.
  • PayPal — internal Node.js packages successfully hijacked, contributing to reports of a six-figure aggregate bounty pool across the research.
  • Netflix, Uber, Tesla, Yelp, Shopify — each confirmed exploitable via the same public-registry name-squatting technique, with call-back beacons executing on internal infrastructure before responsible disclosure.

What made the finding so alarming to defenders wasn't the exotic nature of the technique — squatting on a name is trivial — but how mechanically repeatable it was across organizations with entirely different tech stacks, build systems, and security postures. The common denominator wasn't a shared vulnerable library; it was a shared, largely invisible assumption in how package managers resolve names across mixed public/private sources.

The Pattern Repeats: PyTorch and Beyond

Dependency confusion did not stay a 2021 story. In December 2022, the PyTorch team disclosed that its torchtriton package had been compromised via essentially the same technique: a malicious package with an identical name was uploaded to the public Python Package Index (PyPI), and because PyPI's dependency resolution took priority over the private index PyTorch's nightly builds pulled from, anyone who installed the affected PyTorch nightly build between December 25–30, 2022 received a payload that exfiltrated system information, environment variables, and — critically — SSH keys, and could execute arbitrary code. PyTorch is maintained under the Linux Foundation and used across a substantial share of the machine learning engineering ecosystem, meaning the blast radius extended well beyond PyTorch's own infrastructure to every downstream team that had it in a CI pipeline during that window.

Security researchers and registry maintainers have continued to report opportunistic dependency confusion and name-squatting campaigns across npm and PyPI in the years since, frequently targeting patterns associated with large enterprises' internal tooling conventions (companies that prefix internal packages with predictable corporate names are disproportionately represented in these campaigns). Registry operators have added some mitigations — npm's scoped packages, PyPI's namespace reservations for certain prefixes — but these are opt-in controls, not default protections, and adoption inside large, decentralized engineering organizations remains inconsistent.

Why Tech Giants Keep Getting Hit

Three structural factors explain why organizations with mature security programs remain exposed to a technique this well-documented:

  1. Scale outpaces governance. Companies with thousands of internal packages across dozens of teams and repositories cannot manually audit every dependency resolution path. A single unscoped internal package name, buried in one team's build config, is enough.
  2. Build systems default to permissive resolution. Most package managers were designed to maximize convenience, not restrict provenance. Unless a team explicitly configures registry precedence, version pinning, or scope enforcement, the path of least resistance favors whichever source resolves first or highest.
  3. Internal package names leak constantly. Source maps, public job postings referencing internal tool names, conference talks, open-sourced internal tooling, and even error logs routinely expose exactly the information an attacker needs to register a squat package — meaning the attack surface regenerates itself even after an initial round of remediation.

What Effective Defense Actually Requires

Point fixes — reserving a few known package names, or manually pinning a handful of high-profile dependencies — do not scale to the problem's actual size. Durable defense requires continuous, automated visibility into every dependency an organization's build systems resolve, cross-referenced against what actually exists (and is actually reachable) in production code, plus registry-level controls (scoped namespaces, private registry precedence enforcement, and blocking public registry fallback for known-internal names) applied consistently across every repository and build pipeline — not just the ones a security team happens to remember to check.

How Safeguard Helps

Safeguard is built to close exactly this kind of gap before it becomes a headline. Our platform continuously generates and ingests SBOMs across an organization's full repository and build footprint, giving security teams a live, accurate inventory of every internal and third-party package name in use — the same visibility gap that let dependency confusion attacks succeed at scale in the first place. Griffin AI cross-references that inventory against public registries to flag unclaimed or unscoped internal package names before an attacker can squat them, and applies reachability analysis so teams aren't just told a name collision risk exists, but whether the exposed package path is actually reachable and exploitable in a live build or runtime path — cutting through alert noise to prioritize what matters. Where misconfigurations are found — missing registry scoping, unpinned versions, permissive resolution order — Safeguard can generate auto-fix pull requests that lock down registry precedence and reserve or pin the affected names directly in the offending build configuration. The result is a supply chain security posture that catches the Birsan-style attack pattern proactively, across an entire organization's dependency graph, rather than relying on a researcher's bug bounty submission to find out after the fact.

Never miss an update

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