Three npm Disasters That Were Predictable

We ran three real supply chain incidents through proof-of-commitment scoring. In every case, the structural signals were there before the attack. In two cases, they were screaming.

Supply chain attacks don't come from nowhere. They exploit structural conditions that existed long before the incident — single maintainers, token concentration, asymmetric blast radius. The conditions are visible. The question is whether anyone is looking.

We took three of the most consequential npm supply chain attacks in the last eight years and analyzed each one through proof-of-commitment — behavioral scoring that measures structural resilience, not CVE databases. For each incident, we ran the actual package through the API today, then reconstructed what the signals would have shown before the attack.

The results are honest. In some cases, the tool would have flagged the exact vulnerability exploited. In others, the signal was there but below the alarm threshold. We'll show both.


Case 1: event-stream (2018)

What happened

In November 2018, a developer named right9ctrl approached Dominic Tarr, the sole maintainer of event-stream, and offered to take over maintenance of the package. Tarr, who had moved on from the project, agreed. The new maintainer added a dependency called flatmap-stream, which contained an encrypted payload targeting Copay — a Bitcoin wallet — to steal cryptocurrency. The attack went undetected for two months. It targeted a specific downstream package while hiding in a generic utility with ~2 million weekly downloads at the time.

What proof-of-commitment sees today

Package Score Risk Maintainers Downloads/wk Age
event-stream 67 ⚠️ WARN 1 6.0M 14.7 yrs

Score: 67 — the lowest of the three packages we analyzed, sitting in the "GOOD" band but well below the "HEALTHY" threshold of 75. The WARN flag fires because of single-maintainer risk. It misses CRITICAL only because downloads are under 10 million per week.

What signals existed before the attack

In late 2018, the structural profile looked worse than it does today:

  • Single maintainer — Dominic Tarr was the only person with npm publish access. When he handed the keys over, there was no governance process, no second pair of eyes, no institutional memory.
  • Maintainer handoff pattern — A new, unknown contributor took ownership. This is the single strongest behavioral signal for social engineering attacks, and it's the one thing nobody was monitoring.
  • Sudden new dependency — The addition of flatmap-stream, a brand-new package with no download history, should have been a structural anomaly. An established package suddenly depending on an unknown package is a pattern, not just a diff.
  • Abandoned project, active downloads — Tarr had publicly stated he was no longer interested in maintaining the package. An abandoned but highly-downloaded package is the ideal target for social engineering.

Verdict: partially

Proof-of-commitment would have flagged the single-maintainer risk — that was the structural condition that made the social engineering attack possible. But it would not have specifically detected the maintainer handoff or the suspicious new dependency. Those signals require a different kind of analysis: behavioral diff over time, not a point-in-time score.

The score of 67 — notably lower than the 80+ scores of most popular packages — is itself a signal. It reflects the structural fragility that made this attack surface viable. But we'd be dishonest to claim it would have prevented the incident on its own.

What it does prove: the structural condition that enabled the attack was observable. One maintainer. No governance. High download count relative to maintainer investment. The social engineering succeeded because nobody was measuring the gap between usage and stewardship.


Case 2: ua-parser-js (2021)

What happened

In October 2021, the npm account of Faisal Salman — the sole maintainer of ua-parser-js — was compromised. The attacker published three malicious versions (0.7.29, 0.8.0, and 1.0.0) that contained a cryptominer and a credential-stealing trojan targeting Linux and Windows systems. The package had approximately 7 million weekly downloads at the time and was used by companies including Facebook, Microsoft, Amazon, and Google. The malicious versions were live for roughly four hours before removal.

What proof-of-commitment sees today

Package Score Risk Maintainers Downloads/wk Age
ua-parser-js 83 🔴 CRITICAL 1 25.4M 13.7 yrs

Today: score 83, single maintainer, 25.4 million weekly downloads. CRITICAL. The exact profile that was exploited.

What signals existed before the attack

  • Single maintainer with sole npm token — Faisal Salman was the only person who could publish. One compromised credential = full access to 7 million weekly installs.
  • High download-to-maintainer ratio — Even at 7M weekly downloads (below today's 25M), the ratio of "people who depend on this" to "people who can publish" was approximately 7,000,000 : 1.
  • No 2FA enforcement at time — npm did not require two-factor authentication for publishing in 2021. A single password could, and did, compromise the entire package.
  • Behavioral score looks fine — This is the uncomfortable part. The package was well-maintained, consistently released, widely adopted. Every quality metric was positive. The vulnerability was invisible to quality analysis.

Verdict: yes

Proof-of-commitment would have flagged this as CRITICAL — today, clearly. In 2021, with ~7M weekly downloads, it would have been right at the boundary of the CRITICAL threshold (>10M). It would have shown WARN at minimum, and the single-maintainer flag would have been visible.

The specific attack vector — npm token compromise — is exactly what the CRITICAL flag is designed to surface. One token. One person. Millions of installs. The structural risk was there for years before the attack. npm had no mechanism to flag it. npm audit showed nothing. CVE databases showed nothing. The package was healthy by every metric except the one that mattered.

Three years after the attack, nothing has structurally changed. ua-parser-js today still has one maintainer. Downloads have grown from 7M to 25M per week. The blast radius is 3.6x larger than when it was compromised. The same attack would work again, with more damage.


Case 3: colors.js (January 2022)

What happened

In January 2022, Marak Squiress — the sole maintainer of colors.js and faker.js — intentionally published breaking versions of both packages. colors.js v1.4.44 and v1.4.45 introduced an infinite loop that caused any application importing the package to hang indefinitely. faker.js v6.6.6 printed "LIBERTY LIBERTY LIBERTY" followed by corrupted output. Both packages were widely deployed: colors.js had approximately 35 million weekly downloads. Hundreds of open source projects and enterprise CI/CD pipelines broke immediately.

Marak had been publicly protesting the fact that large corporations were building commercial products on his unpaid open source work. The sabotage was intentional and announced. The entire incident was enabled by a single structural condition: he was the only person who could publish these packages.

What proof-of-commitment sees today

Package Score Risk Maintainers Downloads/wk Age
colors 65 🔴 CRITICAL 1 19.8M 15.1 yrs

Today: score 65, single maintainer, 19.8 million weekly downloads — and still CRITICAL. The download count has declined from ~35M since the incident damaged ecosystem trust, but the structural profile is unchanged. One person. One token. Millions of installs.

What signals existed before the incident

  • CRITICAL flag was present — At ~35M weekly downloads and sole maintainer, colors.js would have been flagged CRITICAL. The structural condition enabling the sabotage — one person, one publish token — was visible and unchanging.
  • High score provided false comfort — colors.js was a well-maintained package with 15 years of history, consistent releases, and stable download trends. Every quality metric was positive. The risk wasn't about quality — it was about governance. One maintainer with sufficient grievance or instability is all it takes.
  • No governance structure — There was no second reviewer, no multi-maintainer quorum, no npm organizational account. The entire blast radius — hundreds of CI/CD pipelines, enterprise build systems, downstream package chains — rested on one person's continued good will.
  • The signal is broader than external attacks — This case proves that CRITICAL flagging isn't only about token compromise. It's about any failure mode of the sole maintainer: burnout, grievance, instability, or compromise. Single-maintainer packages fail in all these modes equally.

Verdict: yes, clearly

The CRITICAL flag is explicitly designed for this scenario. Not just external attacks — any failure of the single person at the helm. Proof-of-commitment can't predict whether a maintainer will feel aggrieved. It can flag that one person's decision is the only thing standing between stability and chaos for millions of weekly installs.

npm audit showed nothing before the incident — there were no known vulnerabilities. CVE databases showed nothing. Every toolchain in the ecosystem reported green until the packages broke. The structural flag would have shown red for months.


Summary

Incident Attack vector npm audit CVE PoC flag Would PoC have helped?
event-stream (2018) Social engineering ⚠️ WARN Partially — flagged single maintainer, but wouldn't detect handoff
ua-parser-js (2021) Token compromise ⚠️ WARN* Yes — exact vulnerability flagged. *WARN at 2021 volume, CRITICAL today
colors.js (2022) Intentional sabotage 🔴 CRITICAL Yes — sole-maintainer flag visible for entire 15-year package history

The pattern across all three: zero pre-incident warnings from existing tools. npm audit looks at known CVEs — it can't flag structural risk. CVE databases are reactive by design — they catalog what already happened. Neither tool measures the conditions that make attacks viable.


What We Can't Catch (Yet)

Honesty about limitations matters more than marketing claims. Proof-of-commitment currently does not detect:

  • Maintainer handoffs — The event-stream attack exploited a change in who controls the package. The current scoring is point-in-time; it doesn't track behavioral diffs over time. This is the most important gap.
  • New dependency injection — The addition of flatmap-stream was a structural anomaly. Detecting sudden new dependencies in established packages would catch this class of attack. Not implemented yet.
  • Code-level malice — The tool measures structural governance, not code content. If a trusted maintainer ships malicious code, the structural score won't reflect it. This is a deliberate scope boundary — structural governance and code analysis are different problems.

What it does catch: the structural conditions that make these attacks possible and high-impact. Single maintainer. Token concentration. Asymmetric blast radius. These conditions precede every attack in this analysis by months or years.


Try It

Run any package through proof-of-commitment in 10 seconds:

npx proof-of-commitment axios ua-parser-js event-stream
# Or your whole project:
npx proof-of-commitment --file package.json

Web interface: getcommit.dev/audit

GitHub Action (comments on PRs):

- uses: piiiico/proof-of-commitment@main
  with:
    fail-on-critical: false
    comment-on-pr: true

MCP server for Claude Desktop, Cursor, or Windsurf:

{
  "mcpServers": {
    "proof-of-commitment": {
      "type": "streamable-http",
      "url": "https://poc-backend.amdal-dev.workers.dev/mcp"
    }
  }
}

All data sourced from public npm registry and GitHub APIs. Open source: github.com/piiiico/proof-of-commitment.


We're building Commit — trust infrastructure for the autonomous economy. Behavioral commitment data, not declarations.

Related: State of npm Trust: April 2026 · Declarations Are Gameable · The Missing Layer

Stay in the loop

Early access, research updates, and the occasional strong opinion.