Most of a modern application is code someone else wrote. This proverbial dependency tree, the web of direct and transitive open-source packages a build pulls in, hides most of its growth underground: you chose the trunk, but its roots run down through layer after layer of packages you never picked and never reviewed. That buried growth is what makes the tree a soft target. Poison one root and it travels up through everything that feeds on it.
Endor Labs research estimates that anywhere from 70-90% of the code in most applications comes from open-source, and that 95% of vulnerability risk lives in transitive dependencies.
Attackers have three ways to work poison into this modern application tree:
- Trick the package resolver into pulling a malicious package (dependency confusion)
- Trick the developer into planting the wrong one (typosquatting and combosquatting)
- Corrupt a package teams trust and let it rot from within (package poisoning and hijacking).
Each abuses the trust and automation that make package managers useful. Here is how the three "tree poisoning" methods compare at a glance.
The sections below define each technique and show how it plays out. For the wider attack landscape, see software supply chain attacks explained.
What is dependency confusion?
Dependency confusion is a software supply chain attack. An attacker publishes a public package with the same name as a company’s private package. Because many package managers prefer the highest version number, they may choose the attacker's public package.
They may check public registries along with private ones.
So the build pulls the public version instead of the trusted internal one.
This is a design flaw, not a bug. The resolver works as intended. The default "highest version wins" behavior, combined with checking both registries, is the whole exploit. An attacker who learns an internal package name can publish a public package with that name and a high version number, and the build resolves to it.
Attackers find those internal names through reconnaissance: leaked package.json files, build configs exposed in public repositories, error messages in CI logs, and documentation leaks. Once the malicious package installs, its preinstall and postinstall scripts run. They read environment variables, reach for cloud metadata endpoints, exfiltrate secrets, and establish persistence.
The defining proof of concept came from Alex Birsan in 2021. He found internal package names in public JavaScript files, published matching public packages, and used DNS callbacks to prove his code executed. The campaign reached more than 35 major companies, including Apple, Microsoft, and PayPal, and earned over $130,000 in bug bounties.
In December 2022, the PyTorch nightly build pulled a malicious torchtriton package that sat on PyPI for five days, targeting nightly builds and stealing SSH keys from affected machines. PyTorch documented the incident in its compromised nightly dependency report.
What is typosquatting?
Typosquatting registers packages with names a single character off from popular libraries, such as lodahs in place of lodash. A developer who mistypes a name or copies a wrong reference installs the malicious version. Package registries hold thousands of these attempts at any given time.
What is combosquatting?
Combosquatting is when the attacker registers a name that adds words to a popular package, like a prefix or suffix such as -js, secure-, or -utils, so the result reads as an official companion package. A developer sees a familiar root name and assumes the rest is legitimate.
Both combosquatting and typosquatting prey on human error and assumption rather than resolver logic. AI coding agents widen the opening, because they suggest plausible-but-wrong package names that a developer accepts without a second look. The version of this attack built on names that AI invents outright is slopsquatting.
Package poisoning and hijacking
Package poisoning injects malicious code into an existing, trusted package rather than creating a new malicious one. Hijacking, or account takeover, is how attackers get the access to do it: taking over a maintainer account through social engineering, using stolen credentials, or inheriting a project a burned-out maintainer handed off.
Once an attacker controls the package, a single malicious update ships to everyone who depends on it, and it carries the package's existing reputation and trust. That is what makes this family so damaging. The malicious code arrives inside a name developers already approved.
The pattern has a long record. In 2018, a handed-off npm package called event-stream received a malicious dependency aimed at stealing cryptocurrency wallet credentials.
In 2024, the XZ Utils backdoor followed two years of trust-building contributions before the payload went in. In 2025, the Shai-Hulud worm spread across 500 or more npm package versions on its own. A related variant, protestware, sees a maintainer sabotage their own package to make a point.
How to defend against all three
The same handful of controls cuts across dependency confusion, squatting, and poisoning. These are practical steps a team can apply today.
- Reserve your package names and namespaces. Claim your internal package names on public registries before an attacker can, and use scoped packages like @company/package so a public name cannot shadow a private one.
- Pin private sources. Configure package managers to resolve from your private registry. Scope a registry in .npmrc for npm, use index-url alone and drop extra-index-url for pip, and use packageSourceMapping for NuGet.
- Use lockfiles and version pinning. Commit package-lock.json, yarn.lock, or a hashed requirements.txt, verify checksums, and avoid floating latest versions that let resolution drift.
- Remove public mentions of private package names. Reconnaissance is the first step of dependency confusion, so keep internal names out of public repos, configs, and logs.
- Use a package firewall with real-time malware detection. Relies on behavioral analysis rather than signatures alone, which catches typosquats, dependency confusion attempts, and poisoned updates. It continously scans every new package to detect malware at the source, and then blocks it from landing on developer laptops, in CI/CD systems, or in production.
These techniques are spokes of a larger problem. For the full program, start with software supply chain security.
FAQ
What is dependency confusion? Dependency confusion is a supply chain attack where an attacker publishes a public package with the same name as a company's private internal package. Because many package managers prefer the highest version and check public registries alongside private ones, the build resolves to the attacker's public version. Alex Birsan's 2021 research used this technique against more than 35 companies, including Apple, Microsoft, and PayPal.
What is the difference between dependency confusion and typosquatting? Dependency confusion exploits the package resolver, which pulls a public package over a private one with the same name. Typosquatting exploits the developer, who mistypes or miscopies a package name and lands on a malicious lookalike. One attacks automation; the other attacks human error.
What is combosquatting? Combosquatting registers a package name that adds words to a popular one, such as a prefix or suffix like secure- or -utils, so it reads as an official companion package. It exploits the assumption that a name built around a familiar root is legitimate.
What is package poisoning? Package poisoning injects malicious code into an existing, trusted package rather than publishing a new malicious one. Attackers gain access through maintainer account takeover, stolen credentials, or an abandoned project handoff, then ship a malicious update that inherits the package's reputation. event-stream and XZ Utils are well-known examples.
How do you prevent these supply chain attacks? Reserve your package names and use scoped namespaces, pin private registry sources in your package manager config, commit lockfiles with version pinning and checksum verification, keep internal package names out of public view, and run malicious package detection with reachability analysis to catch and prioritize real threats before production.
Dependency confusion, squatting, and poisoning are systemic problems that need preventive controls and continuous monitoring. See how Endor Labs detects and blocks malicious packages with continous real-time scanning and a package firewall, or book a demo.
What's next?
When you're ready to take the next step in securing your software supply chain, here are 3 ways Endor Labs can help:
