Skip to content
← Blog

Technical explainer

First-party vs third-party risk in npm

2 min read

When developers think about supply chain risk in npm, they usually think about packages they explicitly installed — the ones in package.json. This is the wrong place to focus.

The packages that are most likely to compromise your machine are not the ones you chose. They're the packages that came along with the ones you chose — the transitive dependencies.

The scale of transitive dependencies

A single top-level npm install express installs approximately 50 packages. npm install webpack installs approximately 300. A typical React application with a full development stack has a total dependency tree of 800–1,200 packages.

Of those packages, you directly chose perhaps 20–30. The other 780–1,170 were chosen by the packages you chose, and by the packages those packages chose.

You have reviewed 20–30 packages. You have implicitly trusted 780–1,170.

Why transitive dependencies are a better attack target

Less scrutiny. Your direct dependencies are named in your package.json. You probably know what they are, roughly what they do, and who maintains them. Your transitive dependencies are invisible until you run npm list --all. Most developers couldn't name their top 10 transitive dependencies.

More of them. With 1,000+ packages in a typical dependency tree, the probability that at least one is maintained by a low-security account is high.

Longer chains reduce accountability. When webpack has a dependency acorn, and acorn has a dependency acorn-walk, and someone compromises acorn-walk — who is responsible? The acorn-walk maintainer? The acorn maintainer who chose it as a dependency? The webpack maintainer? In practice, no one in this chain is actively monitoring acorn-walk security.

The "one degree of separation" attack pattern. The event-stream attack (Post 3) specifically exploited this: rather than adding malicious code to event-stream directly (which might be noticed), the attacker added a dependency (flatmap-stream) that contained the malicious code. The malicious code was one package removed from the target.

How to reduce transitive dependency risk

Prefer packages with small dependency trees. When choosing between two packages that solve the same problem, the one with fewer transitive dependencies is safer, all else being equal. Tools like npmgraph.js.org or bundlephobia.com show dependency trees visually.

Use npm why to understand why a transitive dependency exists:

# Why is minimist installed?
npm why minimist
# Output: minimist is included because acorn-walk requires it
#         acorn-walk is required by acorn
#         acorn is required by webpack

If a transitive dependency has known security issues, npm why tells you which of your direct dependencies is responsible for it — and whether you can switch to an alternative that doesn't bring it in.

Lock transitive dependencies. Your package-lock.json records all transitive dependencies. Committing it and using npm ci means your transitive dependencies don't change without a deliberate lockfile update.

Use Veln for transitive dependency verification. When npm ci installs all 1,000+ packages in your dependency tree, Veln runs the analysis pipeline on every package — not just the ones in your package.json. A compromised acorn-walk@3.1.1 gets the same analysis as a compromised express@5.0.0. The depth of the dependency tree doesn't affect Veln's coverage.


You review your direct dependencies. Veln reviews all 1,000+ packages in your dependency tree.