Skip to content

Back to the catalog

Dependency Confusion: How Attackers Poison Your Build | Blog | Endor Labs
endorlabs.comarticle

Dependency Confusion: How Attackers Poison Your Build | Blog | Endor Labs

Endor Labs explainer breaking down three "tree poisoning" techniques in the open-source dependency tree: dependency confusion (public package impersonating a private one, exploiting "highest version wins" resolver logic, as in Alex Birsan's 2021 research against 35+ companies and PyTorch's 2022 torchtriton nightly-build compromise), typosquatting/combosquatting (human-error-driven lookalike package names, extended by AI-invented "slopsquatting"), and package poisoning/hijacking (maintainer account takeover or handoff abuse, as with event-stream in 2018, the XZ Utils backdoor in 2024, and the Shai-Hulud npm worm). It closes with concrete defenses: reserving package namespaces, pinning private registries, lockfiles/checksum verification, scrubbing internal names from public sources, and package firewalls with behavioral malware detection.

TL;DR Vendor explainer distinguishing dependency confusion, typo/combosquatting, and package poisoning/hijacking using real incidents (Birsan 2021, torchtriton, event-stream, XZ Utils, Shai-Hulud) and listing concrete mitigations.
Document

What to do

The page's payload is a set of per-ecosystem configuration specifics the summary flattens into "pinned private registries" and "lockfiles." Reproduced as stated:

  • Reserve names/namespaces. Claim internal package names on public registries before an attacker does, and use scoped packages like @company/package so a public name cannot shadow a private one.
  • Pin private sources, per package manager:
  • npm — scope a registry in .npmrc.
  • pip — use index-url alone and drop extra-index-url (the extra index is what lets resolution fall through to the public registry).
  • NuGet — use packageSourceMapping.
  • Lockfiles + version pinning. Commit package-lock.json, yarn.lock, or a hashed requirements.txt; verify checksums; avoid floating latest.
  • Remove public mentions of internal names. Reconnaissance is step one of dependency confusion — keep internal names out of public repos, configs, and CI logs.

Why the mechanism, not a bug

Dependency confusion is a design flaw, not a vulnerability: the resolver checks public and private registries together and picks the highest version. An attacker who learns an internal name publishes a public package under it with a high version, and the build resolves to the attacker's copy. On install, preinstall/postinstall scripts run — reading env vars, hitting cloud metadata endpoints, exfiltrating secrets, establishing persistence. This framing tells you why the extra-index-url removal and version pinning are the fixes that matter: they attack the "both registries, highest wins" convergence directly.

flowchart TD
    A[Internal name leaked in public repo/CI log] --> B[Attacker publishes public pkg, high version]
    B --> C{Resolver checks<br/>public + private}
    C -->|highest version wins| D[Malicious pkg installed]
    D --> E[install scripts: steal secrets, persist]
    C -.->|index-url only,<br/>scoped @company/pkg| F[Private version resolves]
    A -.->|scrub internal names| C

The diagram shows the two places a defense cuts the chain: scrubbing names removes the reconnaissance input, and single-source resolution (index-url alone, scoped packages) removes the fork the resolver would otherwise take.

Worth knowing

Two data points the summary omits that size the problem: Endor Labs estimates 70–90% of application code is open-source, and 95% of vulnerability risk lives in transitive dependencies — the roots you never picked, which is exactly where poisoning propagates upward.

Note the page's defense list is truncated mid-item ("Use a p…"), so at least one recommended control is not captured here.

Organisations named here

  • Alex Birsan
  • event-stream
  • Shai-Hulud
  • slopsquatting
  • torchtriton
  • xz-utils

Technologies named here

  • Endor Labs package firewall
  • lodash
  • npm
  • PyPI
  • pip
  • PyTorch nightly build
  • NuGet
  • package-lock.json / yarn.lock / hashed requirements.txt
  • XZ Utils
  • Apple, Microsoft, PayPal
  • PyTorch
  • Endor Labs

Techniques named here

  • Dependency confusion
  • Typosquatting
  • Combosquatting
  • Slopsquatting
  • Package poisoning
  • Account takeover / maintainer hijacking
  • Reconnaissance for internal package names
  • Malicious preinstall/postinstall scripts
  • DNS callback proof-of-concept

Threat actors named here

  • torchtriton malicious package
  • event-stream malicious dependency
  • XZ Utils backdoor
  • Shai-Hulud worm
  • protestware
  • slopsquatting