npm's Supply Chain Problem Isn't a Mystery. It's a Choice.

4 min read 1 source clear_take
├── "npm's supply chain security failures are structural and self-inflicted, not inevitable"
│  └── Kevin Patel (kevinpatel.xyz) → read

Patel's satirical framing argues that npm treats each supply chain attack as a novel event rather than a symptom of architectural decisions baked in since 2010. He catalogs a timeline from event-stream (2018) through everything (2024) to show the attacks are metronomic and predictable, yet npm's response never changes: patch the specific hole, issue a blog post, change nothing structural.

├── "Other ecosystems prove the problem is solvable — post-install scripts and weak integrity checks are the root cause"
│  └── top10.dev editorial (top10.dev) → read below

The editorial highlights Go modules as a counterexample: deterministic dependency resolution, a tamper-evident transparency log, and crucially no post-install script execution. That single architectural decision — no arbitrary code at install time — eliminates npm's most common attack vector, proving the problem is a design choice, not an inherent cost of package management.

└── "The pattern of treating each incident as isolated is the real failure"
  └── Kevin Patel (kevinpatel.xyz) → read

Beyond cataloging individual attacks, Patel's core argument is about the response pattern: npm follows the same script every time — patch, blog post, move on. The satirical Onion headline format is chosen deliberately to emphasize that the repetition itself is the indictment, not any single vulnerability.

What happened

A blog post titled "'No way to prevent this,' says only package manager where this regularly happens" went viral on Hacker News this week, borrowing The Onion's iconic recurring headline about gun violence and applying it to npm's supply chain security problem. The satirical framing struck a nerve: 304 upvotes and climbing, with a comment thread full of developers sharing war stories.

The post's thesis is blunt. npm is the only major package registry where supply chain attacks happen with metronomic regularity, and npm's response each time follows the same script: patch the specific hole, issue a blog post, change nothing structural. The author catalogs a timeline that most JavaScript developers could recite from memory: `event-stream` (2018, 1.5M weekly downloads, cryptocurrency wallet theft), `ua-parser-js` (2021, 8M weekly downloads, cryptominer injection), `colors` and `faker` (2022, maintainer sabotage), `everything` (2024, registry-wide dependency spam), and a rolling parade of typosquats and namespace confusion attacks that never individually make headlines but collectively affect thousands of projects per year.

The satirical framing works because it highlights the pattern: each incident is treated as a novel event rather than a symptom of architectural decisions baked into npm since 2010.

Why it matters

The uncomfortable comparison isn't just rhetorical. Other ecosystems have made structural choices that meaningfully reduce their attack surface.

Go modules solved the problem by design. Dependency resolution is deterministic, the module proxy (`proxy.golang.org`) caches and checksums every module version in a tamper-evident transparency log (`sum.golang.org`), and `go.sum` files are committed to source control. There is no post-install script execution. There is no way for a dependency to run arbitrary code at install time. That single architectural decision eliminates the most common npm attack vector.

crates.io requires every published crate to be built and audited against Rust's type system — which doesn't prevent all supply chain attacks but makes payload injection substantially harder. The `cargo-vet` tool lets teams attest to dependency audits, creating a chain of trust. PyPI, long the second-worst offender, has adopted PEP 740 (digital attestations via Sigstore), mandatory 2FA for critical projects, and Trusted Publishers — tying package uploads to verified CI/CD pipelines rather than individual API tokens.

npm's mitigations, by contrast, remain opt-in, voluntary, and incomplete. npm added mandatory 2FA for top-100 packages in 2022. GitHub's npm team added package provenance via Sigstore in 2023. But install scripts still run by default, namespace squatting remains trivial, and there is no transparency log for package contents — meaning a maintainer (or attacker with a stolen token) can publish a new version with entirely different code, and the registry won't flag it.

The HN discussion surfaced a telling data point: of the ~2.3 million packages on npm, fewer than 15% have provenance attestations enabled. Compare that to PyPI's Trusted Publishers adoption, which crossed 40% of new project uploads within 18 months of launch. The tooling exists. The adoption doesn't, because npm hasn't made the secure path the default path.

Several commenters pushed back on the framing, arguing that npm's openness and permissiveness is a feature, not a bug — it's what enabled the JavaScript ecosystem to grow to its current dominance. This is true and irrelevant. The question isn't whether npm should have been permissive in 2012. It's whether npm should still be permissive in 2026, when supply chain attacks are an industrialized operation and the median `node_modules` folder contains 800+ transitive dependencies.

What this means for your stack

If you run JavaScript in production — and statistically, you do — here's what you can do today without waiting for npm to change:

1. Disable install scripts by default. Add `ignore-scripts=true` to your project's `.npmrc`. Re-enable them selectively for packages that genuinely need them (node-gyp bindings, etc.) via an `allow-scripts` list. This single change blocks the most common attack vector: malicious `postinstall` hooks.

2. Pin and lock aggressively. Use `npm ci` in CI (not `npm install`), commit your `package-lock.json`, and audit it in code review. Consider `npm audit signatures` to verify provenance on packages that support it.

3. Vendor critical dependencies. For small, foundational packages (the `is-odd` tier), copy the source into your repo. You don't need a registry to deliver 12 lines of code. The dependency you vendor is the dependency that can't be hijacked.

4. Use Socket.dev or Snyk for behavioral analysis. Static vulnerability databases (npm audit) only catch known CVEs. Socket's approach — flagging packages that add network calls, filesystem access, or obfuscated code between versions — catches the supply chain attacks that npm audit misses entirely.

5. Evaluate your actual dependency tree. Run `npm ls --all | wc -l` and sit with that number. If you're north of 1,000 transitive dependencies, you have attack surface you haven't thought about. Tools like `depcheck` can identify unused dependencies; removing them is free security.

For organizations with the leverage, push your critical open-source dependencies toward provenance attestation. The npm provenance system works — it's just underadopted because there's no incentive structure rewarding it.

Looking ahead

The Onion comparison is funny because it's structurally precise. After every mass shooting, the article reruns with only the names and dates changed. After every npm supply chain attack, the ecosystem reruns the same cycle: shock, a blog post from GitHub/npm, incremental tooling, no defaults changed. The author's implicit argument — that this is a policy choice, not a technical limitation — is correct. Go proved that a package ecosystem can be secure by default without sacrificing usability. The question is whether npm's stewards at GitHub will make the uncomfortable breaking changes required, or whether we'll still be writing this article in 2028 with different package names in the lede. Based on the last eight years of evidence, plan accordingly.

Hacker News 359 pts 163 comments

'No way to prevent this,' says only package manager where this regularly happens

→ read on Hacker News
eranation · Hacker News

I know people have opinions about cooldowns, but they would have saved you from axios, tanstack, and many other recent npm supply chain attacks. If you have Artifactory / Nexus, you probably already have cooldowns, but it's easy to set up if you don't.Why cooldowns? Most npm (or pypi)

aselimov3 · Hacker News

What are the actual guarantees that go/Rust make that Python/npm don’t? It seems like it might just be that Python/npm are juicier targets? I’m starting to try and avoid all third party packages

joeblubaugh · Hacker News

There has been a lot of pain at my various jobs installing a safe global npm config on every developer machine, asking people not to disable it, checking it with mdm tools. A safer out-of-the-box configuration is long overdue.

827a · Hacker News

There is no legitimate reason why postinstall scripts need to exist. The npm team needs to grow up and declare "starting with npm version whatever, npm will only run postinstall scripts for versions of packages published before ${today}".

imrozim · Hacker News

Every node js project starts with npm install and suddenly you have a 500 packages you didnt ask for. Half of them haven't been touched in years.

// share this

// get daily digest

Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.