DEV Community

Cover image for The Worm in the Registry
Vektor Memory
Vektor Memory

Posted on

The Worm in the Registry

Yesterday, between 19:20 and 19:26 UTC, six minutes of automated publishing destroyed the trust model of modern JavaScript development.

In that window, 84 malicious package versions were pushed across 42 packages in the @tanstack namespace. Not by an attacker who stole a password. By TanStack's own legitimate release pipeline, using its own trusted identity, after attacker-controlled code hijacked the CI runner mid-workflow. @tanstack/react-router alone has 12.7 million weekly downloads. Within hours the worm had spread to Mistral AI's official npm SDK, UiPath, Guardrails AI, OpenSearch, and at least 170 packages across both npm and PyPI.

Total cumulative downloads of affected packages: over 518 million.

The repositories the attacker created to receive stolen credentials all contained the same string: “Shai-Hulud: Here We Go Again.”

They named it after the Dune sandworm. The one that lives under the surface on planet Arrakis. And something about a liquid that turns your eyes blue that a stranger gave you at Burning Man until you have to go to work on Monday and it's not very cool in the office, with all the strange looks and questions.

Part 1: What Just Happened
The attack is Wave 4 of the Mini Shai-Hulud campaign, attributed to a financially motivated threat group called TeamPCP. The earlier waves hit in September and November 2025 and in April 2026. Each iteration builds on the last.

What made Wave 4 different was not the scale. Wave 2 was larger. What made it different was this: for the first time in documented history, a malicious npm package carried valid SLSA Build Level 3 provenance attestation.

SLSA provenance is a cryptographic certificate generated by Sigstore. It is meant to verify that a package was built from a trusted source using a trusted pipeline. It is the current gold standard for supply chain integrity. The certificate said: this package is legitimate. The package was not legitimate.

To understand how that happened, you need to understand the attack chain:

Attack chain: Wave 4, May 11 2026
─────────────────────────────────────────────────────────────────
May 10 Attacker forks TanStack/router as zblgg/configuration
(renamed to avoid fork-list searches)
Malicious commit authored as: claude claude@users.noreply.github.com
(impersonating the Anthropic Claude GitHub App)
Prefixed [skip ci] to suppress automated CI on push
May 11 PR submitted triggering pull_request_target workflow
Workflow runs attacker's fork code
Malicious pnpm store injected into GitHub Actions cache
Legitimate maintainer PR later merged to main
Release workflow restores the poisoned cache
Attacker code reads OIDC token from runner process memory
(/proc//mem — direct memory extraction)
19:20 Attacker uses OIDC token to publish 84 malicious artifacts
19:26 Publishing complete
Valid SLSA Build Level 3 attestation generated automatically
by the legitimate Sigstore stack
19:50 StepSecurity detects and reports to TanStack maintainers
21:30 GitHub security advisory published
Three separate vulnerabilities chained. None sufficient alone. The commit impersonated the Claude GitHub App. The cache poisoning was a known pattern documented in 2024 but not yet patched in this workflow. The OIDC memory extraction is the technical escalation: the attacker never needed npm credentials. They extracted the publishing token directly from the runner’s process memory at runtime.

The worm then did what Shai-Hulud does. It used stolen GitHub tokens to enumerate every package the compromised maintainer controlled and published infected versions of each. Self-propagating. One account becomes dozens.

The payload exfiltrated stolen credentials through three redundant channels simultaneously: a typosquat domain (git-tanstack.com), the Session decentralised messenger network, and GitHub API dead drops embedded in commit messages. The dead man's switch was back: a persistent daemon that polls GitHub every 60 seconds, and runs rm -rf ~/ if the token is revoked. A 1-in-6 chance of running rm -rf / on systems geolocated to Israel or Iran.

The malware checked for Russian-language system configuration and terminated without exfiltrating data if found.

Someone is making geopolitical decisions inside a JavaScript package manager.

Part 2: This Is Not New, This Is Accelerating
Wave 4 is the headline. The context is what matters.

Shai-Hulud campaign timeline
─────────────────────────────────────────────────────────────────
Sep 2025 Wave 1: chalk, debug, 16 packages. 2.6bn weekly downloads.
Attack vector: phishing against maintainer account.
Duration: 2 hours live.
Nov 2025 Wave 2: Shai-Hulud worm v2. Self-propagating.
Dead man's switch introduced.
GitLab, Red Hat issue coordinated advisories.
Apr 2026 Wave 3: SAP packages, Bitwarden CLI, Aqua Security Trivy,
Checkmarx. Security tooling itself compromised.
May 2026 Wave 4: TanStack, Mistral AI, UiPath, Guardrails AI.
First malicious packages with valid SLSA provenance.
170+ packages. 518M+ cumulative downloads.
And behind all of this, the baseline numbers:

npm ecosystem: malicious package growth
─────────────────────────────────────────────────────────────────
2018: 38 malicious packages reported
2024: 2,168 (arXiv, 2025)
2024: 3,000+ (Snyk, 2025)
Q4 2025: 120,612 malware attacks blocked
in a single quarter (Sonatype, 2026)
2025: 454,648 new malicious packages (Sonatype, 2026)
Average transitive dependencies per npm project: 79
Dependencies un-upgraded over a year: 80%
Weekly npm download requests: 9.8 trillion
The average npm project pulls in 79 packages the developer did not explicitly choose. Every one of those is a trust decision made by someone else, at some point, which you are inheriting every time you run npm install. Nobody is auditing 79 packages. The math does not work.

Part 3: The Long Game That Preceded All of This
Before Shai-Hulud, before TeamPCP, there was a GitHub account called Jia Tan.

XZ Utils is a compression library. It ships in essentially every Linux distribution. It is the kind of software nobody thinks about, which is precisely why it was chosen.

In October 2021, Jia Tan began contributing to XZ Utils. Small commits. Bug fixes. Nothing suspicious. Over two years, the contributions grew in frequency and quality. The account engaged in mailing list discussions, helped triage issues, and built a consistent record of reliable work. Meanwhile, the project’s sole maintainer, Lasse Collin, was receiving emails from other accounts pressuring him to hand over control. He was unpaid. He was dealing with mental health challenges by his own account. He was one person maintaining critical infrastructure used by millions of machines.

The pressure worked. In 2023, Jia Tan became co-maintainer.

In February 2024, version 5.6.0 shipped with a backdoor embedded not in the source code but in the build system, hidden inside test files. It activated only under specific conditions: Debian or Fedora, systemd linked against the library, x86–64 hardware. It hijacked SSH authentication. CVSS score: 10.0. Maximum possible.

XZ Utils backdoor: CVE-2024-3094
─────────────────────────────────────────────────────────────────
Oct 2021 Jia Tan account created
2021-2023 Legitimate contributions, trust accumulation
2022-2023 Coordinated pressure campaign on Lasse Collin
2023 Jia Tan granted co-maintainer access
Feb 2024 Backdoor shipped in XZ 5.6.0 (CVSS 10.0)
Mar 29 2024 Andres Freund notices SSH authentication is 500ms slow
Investigates. Finds the backdoor.
Debian, Red Hat, Arch roll back immediately.
Half a second. The entire Linux SSH infrastructure nearly compromised by half a second of latency noticed by one engineer who was annoyed enough to investigate.

The operation spanned two years and three months. State-level patience, state-level resources, a detailed map of the Linux dependency graph. The malicious code was not in the repository. It was in the compiled tarballs. Not the source anyone was reviewing.

Eric Raymond’s thesis in The Cathedral and the Bazaar (1999) is that given enough eyeballs, all bugs are shallow. The XZ attack is a direct falsification of that premise for a specific attack class: supply chain compromise via trusted insider. The eyeballs were on the source code. The malicious code was in the build artifacts.

Part 4: Who Is Watching
Here is the question without a comfortable answer.

npm has 2.1 million packages. GitHub has over 420 million repositories. The ecosystem runs on volunteer maintainers, most unpaid, many of them one-person devs. There is no regulatory framework. There is no mandatory quality control. There is no liability structure. The model is: publish what you like, and if someone finds a problem, patch it. Peace, love, code and combi vans, dude for sure.

Contrast this with pharmaceuticals. Aviation. Financial systems. Food. These industries have enforced audit requirements, liability frameworks, regulatory bodies with real teeth. A pharmaceutical company that ships a contaminated batch faces legal consequences. An npm maintainer whose account is compromised faces condolences on GitHub.

Bruce Schneier’s Liars and Outliers (2012) frames this precisely: societal trust systems break down when defection becomes individually rational. The open source trust model works when contributing good code is the dominant strategy. Jia Tan demonstrated that defection is possible at the reputation layer, not the code layer. The attack was social before it was technical.

Join The Writer's Circle event
What makes Wave 4 particularly troubling is that TeamPCP defeated the most sophisticated technical countermeasure currently deployed. SLSA provenance was supposed to be the answer to exactly this problem. The certificate said legitimate. The package was not legitimate. The tool designed to restore trust was used to launder it.

Adam Shostack’s Threat Modeling (2014) asks: who is the adversary, what do they want, and what is the weakest point in the chain? The answer in 2026 is: the weakest point is no longer the code. It is the pipeline that builds and signs the code. And now, increasingly, it is the certificate that verifies the pipeline.

Part 5: The Economics Nobody Wants to Talk About
There is a corner of the developer community that argues all software should be free. Open source, no exceptions. Charging for code is ideologically impure.

The argument is not wrong about principles. Linux is real. The open source track record is real.

But it papers over the economics.

Lasse Collin was maintaining a library present in every Linux distribution, unpaid, alone, while dealing with mental health challenges. That is not a security failure at the code level. It is a predictable outcome of a structural model that places critical infrastructure on individual volunteers with no institutional support. Jia Tan did not exploit bad code. They exploited exhaustion.

The developers building production software in 2026 are paying real money: API costs, server infrastructure, government registrations, legal compliance, documentation, and support. Not everyone has a VC-funded runway. The median indie developer is self-funded, building something they believe in, hoping the revenue arrives before the savings run out.

Peter Steinberger lost money on OpenClaw before pivoting to a commercial model. Most open source project founders know this story from the inside. The peanut gallery on Reddit demanding everything be free has generally not shipped production software at scale, paid for the servers, handled the compliance, or supported the users.

The question is not whether software should be free. The question is who bears the cost of maintaining it safely, and what happens when the answer is nobody in particular. The XZ attack answered that question empirically. The answer is: a state actor with two years of patience and a burned-out maintainer.

Part 6: Why Closed Source During Hardening Is Not a Betrayal
I keep VEKTOR Slipstream closed source during active development. I hear about this question regularly, from skepticism to outright disgust.

The practical reason has nothing to do with ideology. It is about sequencing.

Open source at the wrong stage means releasing code before you have found your own bugs. It means community pressure to stabilise public APIs before they are stable. It means anyone who clones the repository at the wrong moment gets the version with the FTS5 mismatch, the opts passthrough that silently drops metadata, the sovereign screener blocking legitimate writes because override is in the RISK_TOKENS list. Not malicious. Just unfinished.

The planned open source path for vex, the memory portability layer, follows the principle: release when the core is stable enough that community contributions help rather than destabilise. The .vmig.jsonl format is already public. The spec is at vektormemory.com. The approach is: earn trust by shipping something that works, then invite scrutiny.

Jia Tan spent two years earning trust through legitimate contributions before exploiting it. The lesson is not that trust is worthless. The lesson is that trust needs a substrate. Working software with a track record. That takes time. Time during which keeping the source closed is a responsible choice, not a political one.

Part 7: What Actually Needs to Change
The npm ecosystem is not ungovernable. It is ungoverned. Those are different problems.

Wave 4 broke SLSA provenance attestation as a trust anchor. That is a significant escalation. The response needs to match the escalation.

Hardening the pipeline, not just the code. The TanStack attack exploited pull_request_target, a known vulnerable pattern. GitHub published the attack pattern in 2024. TanStack was still running it in 2026. Security advisories that do not produce workflow changes are not security advisories. They are documentation of future incidents.

Funded maintainership for load-bearing packages. The OpenSSF has mechanisms. The Linux Foundation has mechanisms. The gap is that nobody has built a reliable dependency graph showing which packages are structurally critical to the global software supply chain. Sonatype’s data gets closest. This is a solvable problem that has not been solved because no institution with money has decided it is their problem yet.

Regulatory frameworks. The EU Cyber Resilience Act (2024) begins to establish liability for software products. It does not yet cover open source maintainers in any useful way. The US has nothing equivalent. Software supply chain regulation is where food safety regulation was in the early twentieth century: the consequences are visible, the framework does not exist, and someone will eventually decide the cost of inaction is higher than the cost of governance.

Isolation as default. The Register’s coverage of the Wave 4 attack ended with a line worth repeating: “running everyday commands like npm install is unsafe, and software development is now best done in isolated, ephemeral environments." That is not a fringe security opinion anymore. That is the current practical baseline.

The worm is still in the registry. As of this writing, StepSecurity has confirmed propagation continues: Intercom’s official Node.js SDK was compromised at 14:41 UTC today, 36 hours after the TanStack attack, via a hijacked OIDC publishing pipeline from yesterday’s victims.

One account. Then dozens. Then hundreds.

The ground keeps moving, get the thumper out.

References
Incident reports (Wave 4, May 2026)

StepSecurity. (2026, May 11). TeamPCP’s Mini Shai-Hulud Is Back. stepsecurity.io
Snyk. (2026, May 12). TanStack npm Packages Hit by Mini Shai-Hulud. CVE-2026–45321. snyk.io
Wiz. (2026, May 12). Mini Shai-Hulud Strikes Again. wiz.io
Cybernews. (2026, May 12). Hundreds of NPM packages compromised in a new supply chain attack. cybernews.com
The Register. (2026, May 12). Cache-poisoning caper turns TanStack npm packages toxic. theregister.com
XZ Utils

CVE-2024–3094. CVSS 10.0. Disclosed March 29, 2024.
Boehs, E. (2024). Everything I Know About the XZ Backdoor. — Definitive timeline of the Jia Tan operation.
Wikipedia. XZ Utils backdoor. en.wikipedia.org
Reports and data

Sonatype. (2026). 11th Annual State of the Software Supply Chain. 454,648 new malicious packages; 9.8 trillion npm downloads.
Sonatype. (2026). Open Source Malware Index Q4 2025. 120,612 attacks blocked in one quarter.
arXiv. (2025). Open Source, Open Threats? 31,267 vulnerabilities analysed, 2017–2025.
Palo Alto Networks Unit 42. (2026). The npm Threat Landscape. unit42.paloaltonetworks.com
Books

Raymond, E. S. (1999). The Cathedral and the Bazaar. O’Reilly. — Open source development models and Linus’s Law.
Schneier, B. (2012). Liars and Outliers. Wiley. — On trust systems, defection, and societal resilience.
Shostack, A. (2014). Threat Modeling: Designing for Security. Wiley.
Regulation

EU Cyber Resilience Act. (2024). Regulation on horizontal cybersecurity requirements for products with digital elements.
Published by Vektor Memory. The .vmig.jsonl memory portability spec: vektormemory.com/spec. VEKTOR Slipstream SDK: vektormemory.com/downloads

AI
Cybersecurity
Cyber Security Awareness
Github
NPM

Top comments (0)