Supply Chain Attacks

npm Protestware Patterns From 2020 to 2026

A senior engineer's view of six years of npm protestware, from colors.js to peacenotwar, and the supply chain lessons that still apply to modern JavaScript shops.

Shadab Khan
Security Engineer
7 min read

Protestware is the awkward cousin of supply chain attacks. It is not financially motivated, it is rarely sophisticated, and it almost always ships through the maintainer's own account. It is also the single most effective reminder that npm publishes whatever the author pushes, political grievance included. Over the last six years the pattern has migrated from loud sabotage to subtler opinion-driven payloads, and the blast radius keeps growing because nothing about the npm install model has fundamentally changed.

What counts as npm protestware in the first place?

Protestware is functional code intentionally altered by its maintainer to deliver a political, ideological, or personal message, often by degrading behavior, printing a statement, or conditionally executing based on geolocation or environment. The definition matters because it distinguishes protestware from malware (criminal intent), from abandonment (no intent), and from maintainer exhaustion (resignation without sabotage).

The community's early working definition came out of the 2022 node-ipc incident, when researchers started separating "the author is angry" from "a contributor stole credentials." Both look similar at the wire level. A malicious publish to event-stream in 2018 and a maintainer publish to colors.js in 2022 both used legitimate author credentials. The difference only becomes visible when you read the diff. That is the whole problem: nobody reads the diff before npm install resolves.

For a senior engineer, the practical definition is "a package version signed by the rightful owner whose behavior diverges from user expectation for non-technical reasons." Treat everything else as a separate category.

How did the colors.js and faker.js incident in 2022 reshape expectations?

The January 2022 self-sabotage of colors (v1.4.44-liberty-2) and faker (v6.6.6) turned protestware from an edge case into a board-level conversation. Marak Squires, the maintainer, added an infinite loop and an ASCII banner reading "LIBERTY LIBERTY LIBERTY." Both packages had tens of millions of weekly downloads and were baked into the AWS CDK, among thousands of other dependents. Builds broke globally within hours.

What changed after colors was not the maintainer behavior, it was the downstream response. npm began reverting published versions more aggressively, GitHub took temporary ownership actions, and several large organizations started pinning transitive dependencies to exact SHAs for the first time. It also put the phrase "single-maintainer package" into every post-incident report for the next two years. Colors was a one-person project with global reach. That asymmetry is the real vulnerability.

What did node-ipc and peacenotwar add to the threat model?

In March 2022, RIAEvangelist published node-ipc versions that imported a peer package called peacenotwar. Under specific geolocation conditions (Russian and Belarusian IPs), it overwrote files on disk with heart emojis. This was the first time protestware targeted users by location rather than degrading globally, and it was the first time a dependency wrote to arbitrary paths as a message delivery mechanism.

The node-ipc event is important because it broke a mental boundary. Before 2022, engineers imagined protestware as roughly equivalent to a noisy console.log. After node-ipc, it became clear that a politically motivated author could ship conditional, destructive code through a package that most developers had never heard of but that sat four layers deep in their package-lock.json. It also introduced the peacenotwar dependency as an auxiliary payload module, a structural pattern that later malicious campaigns have imitated.

How have protestware patterns shifted between 2023 and 2026?

The pattern shifted from destruction to disruption, and then from disruption to surveillance-adjacent telemetry. Between 2023 and 2025 we observed three notable moves. First, maintainers who wanted to make a statement started using postinstall banners rather than runtime loops, because banners do not break CI and therefore do not get reverted. Second, protest commits began arriving as "quiet" one-line changes hidden inside otherwise legitimate feature PRs, particularly in i18n and analytics packages. Third, several packages began adding opt-out environment variables, essentially turning protestware into a feature flag.

By 2026, the pattern that worries me most is conditional analytics: a package that, on specific dates or in specific locales, phones home to a URL under maintainer control. The network request is not destructive, but it is not part of the documented behavior. Because it resembles legitimate telemetry found in packages like nuxt or next during their telemetry debates, it slips past most review processes. The boundary between opinion and exfiltration gets thinner every year.

Why does npm's publish model keep enabling this?

npm remains a publish-on-trust registry. A maintainer with valid credentials can publish any version they want, and the registry does not validate semantic continuity between versions. You can publish a patch release that rewrites 90% of the codebase, and the only signal to dependents is the version bump. Lockfiles help, but only for direct dependencies you have already resolved. Any fresh install or any ^ range still pulls the new version.

Two-factor authentication, introduced more aggressively after the 2021 ua-parser-js compromise, reduces account takeover but does not reduce intentional maintainer action. Provenance, launched by GitHub and npm in 2023, proves that a package was built from a given source repo but does not prove the source repo is benign. Sigstore-based signing, now widespread, proves identity but not intent. Every layer added over the last four years addresses a different threat from the one protestware represents.

The structural answer is behavioral: track what a package does across versions, not just who signed it. Almost nobody does this at scale, which is why protestware keeps working.

What should a senior engineer actually lock down in 2026?

Four controls cover most of it. Pin transitive dependencies with a lockfile and verify integrity hashes on CI restore, not just locally. Set a dependency freshness policy that delays adoption of new versions by a cooling-off window of 48 to 72 hours for anything outside your direct path. Run behavioral diffing on upgrades, comparing network calls, filesystem writes, and child processes between the installed and target version. Finally, maintain an internal mirror or proxy so you can roll back even if npm reverts a package out from under you.

Organizations that had internal mirrors during the colors incident recovered in minutes. Organizations that relied on the public registry recovered when npm did, which in some cases was hours. That delta matters when CI is blocking production deploys.

Beyond tooling, the cultural control matters. Teams that treat every dependency upgrade as a review event, however brief, catch more protestware than teams that auto-merge Dependabot PRs. The auto-merge pattern is convenient, and it is exactly the convenience that made colors-style incidents propagate so quickly in early 2022. A one-paragraph review note on every non-trivial upgrade changes the outcome more than most scanning tools do.

What is the realistic outlook for 2026 and beyond?

The registry model is not changing. npm will continue to accept whatever maintainers publish, and the structural incentive to maintain thousands of single-author packages is not going away. What is changing is tooling density: behavioral diffing, provenance verification, and runtime attestation are all becoming more accessible, and serious organizations are adopting them. Expect protestware to persist as a low-frequency, high-visibility event and for the blast radius to be increasingly determined by who has invested in containment versus who has not. The colors incident remains the clearest lesson, and the lesson is less about colors than about concentration: any ecosystem that lets a single person reach millions of machines will eventually produce an incident like this, and the question is how fast you recover.

How Safeguard.sh Helps

Safeguard.sh addresses protestware through a combination of behavioral reachability analysis and AI-BOM inventory: we track every direct and transitive npm package and diff runtime behavior across versions so a suspicious postinstall, network call, or filesystem write surfaces before it reaches production. Our Griffin AI engine flags suspicious commits that diverge from a maintainer's historical pattern, and our 100-level depth analysis walks transitive chains deep enough to catch embedded payloads like the peacenotwar pattern. For builds that pull artifacts from npm, Lino compliance enforces your dependency freshness policy and provenance requirements automatically, while container self-healing lets you roll back a bad dependency even when the upstream registry has not yet reverted it. The outcome is that a protest commit four layers deep in your lockfile stops being a 3 a.m. incident.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.