Guide
A timeline of package supply-chain attacks.
Documented incidents on npm, PyPI, and the open-source build chain. Updated August 2026. Corrections welcome at hello@klinn.dev.
What the record shows
- In 13 of the 15 incidents below, the malicious release was detected within days of publication, and in 8 of them within hours.
- A one-week cooldown before adopting new versions would have avoided installing the malicious release in 13 of the 15 incidents.
- Account takeover, through phished credentials or stolen publish tokens, is the most common vector: 7 of 15 incidents, and 8 counting the token-driven 2025 worm.
- No test suite catches any of these. Installing the package is what executes the payload, so a green CI run is participation, not protection.
The incidents
July 2018 · npm · detected in hours
eslint-scopeaccount takeover
An attacker logged into a maintainer account with a password reused from another breach and published 3.7.2, whose install script exfiltrated the npm tokens of everyone who installed it. Caught the same day; npm revoked every token issued before the attack.
November 2018 · npm · detected in weeks
event-streammaintainer handoff
Ownership of a popular but unmaintained package was handed to a helpful volunteer, who added a dependency called flatmap-stream and later shipped an encrypted payload inside it that targeted the Copay bitcoin wallet's build and stole wallet keys. It went unnoticed for several weeks until a deprecation warning led a student to the payload.
October 2021 · npm · detected in about four hours
ua-parser-jsaccount takeover
The maintainer's npm account was hijacked and three versions shipped a cryptominer plus a password stealer into a package with millions of weekly downloads. The malicious versions were live for roughly four hours before being pulled.
November 2021 · npm · detected in hours
coa and rcaccount takeover
The same playbook two weeks later, against two packages with tens of millions of combined weekly downloads. The credential stealer broke CI builds all over the world, which is exactly how it was noticed within hours.
January 2022 · npm · detected in hours
colors.js and faker.jsmaintainer sabotage
The packages' own maintainer shipped an infinite loop into colors and blanked faker in protest. Not an outside attack, which is the point: the publish channel itself is the trust boundary, and a legitimate author can break you as effectively as a hijacked one.
March 2022 · npm · detected in days
node-ipcmaintainer sabotage
The maintainer added geo-targeted code that overwrote files on machines with Russian or Belarusian IP addresses, later toned down to dropping a manifesto file. Tracked as CVE-2022-23812.
December 2022 · PyPI · detected in about five days
torchtritondependency confusion
An attacker registered the name of a PyTorch-internal dependency on public PyPI, so nightly installs resolved the malicious copy, which exfiltrated SSH keys and environment data. Timed over the holidays; live from December 25th to 30th.
December 2023 · npm · detected in about five hours
@ledgerhq/connect-kitaccount takeover
A former employee's npm token, obtained by phishing, pushed a wallet drainer into a library loaded at runtime by many crypto front ends. Roughly five hours live, hundreds of thousands of dollars drained from users of downstream sites.
March 2024 · Linux distributions · detected in about five weeks
xz-utilslong-game social engineering
After years of patient contribution, a persona known as Jia Tan gained co-maintainership and shipped an obfuscated backdoor targeting OpenSSH through liblzma in 5.6.0 and 5.6.1. Caught before it reached stable distributions by an engineer investigating a half-second ssh slowdown. Tracked as CVE-2024-3094.
October 2024 · npm · detected in about a day
@lottiefiles/lottie-playeraccount takeover
A compromised publish token pushed three versions containing a wallet drainer that executed on production websites embedding the player. Caught within about a day after user reports.
December 2024 · npm · detected in about five hours
@solana/web3.jsaccount takeover
Phished publish access shipped two backdoored versions of the main Solana JavaScript SDK that exfiltrated private keys from applications handling them. Live for roughly five hours.
December 2024 · PyPI · detected in about a day
ultralyticsbuild pipeline compromise
GitHub Actions cache poisoning let attackers inject a cryptominer into releases of a popular vision library without touching the repository's visible source. The build pipeline, not the code, was the compromise; the attacker republished days after the first cleanup.
March 2025 · GitHub Actions · detected in days
tj-actions/changed-filesbuild pipeline compromise
The action's version tags were retroactively repointed to a commit that dumped CI secrets into public build logs, affecting tens of thousands of repositories that referenced it. Tracked as CVE-2025-30066.
September 2025 · npm · detected in about two hours
chalk, debug and 16 siblingsaccount takeover
A maintainer was phished through a fake npm support domain, and 18 packages totaling on the order of two billion weekly downloads shipped a browser crypto-clipper that rewrote wallet addresses in transactions. Flagged within about two hours by automated diffing of published tarballs.
September 2025 · npm · detected in hours to days
Shai-Hulud wormstolen tokens, self-propagating
The first self-propagating npm worm: trojaned versions harvested credentials from infected machines with a secrets scanner, then used any stolen npm tokens to publish trojaned versions of further packages. Hundreds of packages were hit within days before registries and vendors contained it.
What the pattern implies
Three conclusions fall out of the table. First, freshness is the risk window: attackers need their version installed before someone notices, and someone almost always notices fast, so the most dangerous version of a package is the one published this week. Waiting out a short cooldown is the cheapest defense that exists.
Second, the compromise almost never lives in the repository you can read. Hijacked accounts publish tarballs that do not match the source, and build-pipeline attacks inject after the code review. Judging an update means reading the published package diff, not the GitHub diff.
Third, continuous integration is on the wrong side of the trust boundary. CI installs the dependency, runs its install scripts, and hands it the repository's secrets, all before any human judgment. Every incident above sailed through somebody's green build.
Primary sources
The incidents above are documented in public advisories and postmortems, among them the GitHub Advisory Database, the OSV database, the original xz-utils disclosure, and the event-stream issue thread. Dates and detection windows are approximate where reports differ.
This timeline is maintained by the team behind Klinn, which applies these lessons mechanically: a cooldown before any version is eligible, an advisory screen, a review of the published package diff, and your test suite run in a clean sandbox before an update can merge. How to auto-merge Dependabot PRs safely covers the practical setup.