In Frank Herbert's Dune, a Shai-Hulud is a sandworm: a massive, blind, consuming creature that moves beneath the surface, swallowing everything in its path. It is an apt name for what security researchers discovered on May 11, 2026, buried inside some of the most widely used JavaScript packages in the world.
Mini Shai-Hulud is a supply chain worm attributed to a threat group called TeamPCP. It compromised over 160 npm packages, collectively downloaded hundreds of millions of times per week, and it did something that no npm-based attack had managed before: it produced infected packages with valid cryptographic proof of authenticity. To the tools designed to tell you whether a package is trustworthy, these packages looked perfectly legitimate. They were not.
This article walks through the technology that makes this attack possible, why it matters, and how to detect it on the systems you actually control.
What Is npm and Why Does It Matter?
Before understanding how this worm spreads, you need to understand the environment it lives in.
npm stands for Node Package Manager. It is the world's largest software registry, hosting over two million packages of reusable code that developers can drop into their own projects. When a developer needs to add routing to a web application, handle authentication, process data, or talk to an API, they almost never write that functionality from scratch. They pull it from npm.
The TanStack packages at the center of this attack are a good example. @tanstack/react-router, one of the compromised packages, handles how pages navigate in React-based web applications. It has over twelve million downloads per week. That number is not just one company using it; it represents thousands of organizations whose applications depend on that package. And that dependency does not stop at one level. Your application depends on TanStack, TanStack depends on other packages, and those packages depend on others still. This is called the dependency tree, and in a modern JavaScript project it can contain hundreds or thousands of packages from different authors.
This creates a fundamental trust problem. When you run npm install, you are executing code written by hundreds of strangers. Most of that code has never been personally reviewed by you or anyone at your organization. The ecosystem runs on the assumption that package authors are trustworthy and that the package registry is serving what it claims to serve.
Mini Shai-Hulud attacked that assumption directly.
What Is a CI/CD Pipeline and Why Is It a Target?
To understand how this worm spreads itself, you need to understand CI/CD: Continuous Integration and Continuous Delivery. This is the automated system that turns a developer's code commit into a tested, built, and published software package without requiring humans to manually run each step.
Think of it as a factory triggered by a doorbell. Every time a developer pushes code to GitHub, the bell rings and the factory starts running. For a package like @tanstack/react-router, the factory does roughly the following:
- A fresh Linux virtual machine (called a "runner") spins up in the cloud
- The runner pulls in all dependencies by running
npm install - The runner executes the test suite to verify nothing is broken
- If tests pass, the runner builds the package and publishes the new version to npm
- The runner is destroyed, wiped clean, ready to be used again from scratch
This ephemeral, throw-away nature of runners is important. Each job starts from a completely clean slate. Nothing persists between runs. It is designed to be reproducible and tamper-resistant.
But that runner, while it is alive, holds real credentials. It needs them to do its job. It needs an npm token to publish packages to the public registry. It may need GitHub tokens to read and write repositories. If the project deploys to cloud infrastructure, it may hold AWS, Azure, or Google Cloud credentials. These secrets are injected into the runner's environment when the job starts. They exist in memory. They are there because they have to be.
That is what makes CI/CD pipelines a high-value target. They are credential-dense environments that run automatically in response to code changes, and the code they execute during the build process is exactly what an attacker needs to control in order to reach those credentials.
The Modern Lock: How OIDC Replaced Static Secrets
Major CI/CD platforms recently moved away from storing static passwords and tokens as pipeline secrets. Instead, they use a system called OIDC, which stands for OpenID Connect.
The idea is elegant. Rather than TanStack storing a permanent npm token inside GitHub's secret vault, GitHub and npm establish a cryptographic trust relationship. When the TanStack release pipeline runs, GitHub issues a short-lived, signed token that essentially says: "I vouch that this runner is legitimately executing the TanStack/router Release workflow right now." npm trusts GitHub's signature and issues a temporary publish credential valid only for that session.
There is no static password to steal. The credential expires quickly. It only works for the specific pipeline it was issued to. This was considered a significant security improvement.
The flaw that Mini Shai-Hulud exposed is that the OIDC token, however short-lived, still exists in the runner's memory while the job is running. Any code that executes during that job can reach into memory and take it. And once the worm had the OIDC token, it could publish to npm with exactly the same authority as TanStack's legitimate release process.
How the Attacker Got Into TanStack's Pipeline
The attacker did not break into TanStack's systems. There was no stolen password, no compromised developer account, no brute force attack. The entry point was a misconfiguration in how TanStack's GitHub Actions workflows were set up, exploited through a technique called a Pwn Request.
GitHub Actions supports two types of workflows for handling pull requests from outside contributors. The standard type runs with minimal permissions, treating external code as untrusted. A second type, called pull_request_target, runs with elevated permissions. It exists for legitimate reasons: a project may want to automatically add labels to incoming pull requests, or post test results back to contributors who don't have write access to the repository. Doing those things requires the elevated permission level.
TanStack used pull_request_target so their CI pipeline could interact with pull requests from outside contributors. The attacker, operating through a GitHub account called voicproducoes created in March 2026, submitted a pull request from a fork of the TanStack repository. The pull request looked like an ordinary external contribution. But the attacker's code, once it ran inside TanStack's privileged pipeline environment, poisoned the GitHub Actions cache.
The GitHub Actions cache is shared storage that speeds up builds by saving and reusing files between runs. By poisoning the cache, the attacker's code was picked up by TanStack's next legitimate release run. When the Release workflow ran, it pulled in the compromised cache entry, which injected the malicious code into the build environment. From that point, the worm had a foothold inside TanStack's own CI pipeline, with access to the OIDC token that would let it publish to npm.
The entire attack chain required no stolen passwords and no broken cryptography. It was built on abusing legitimate features that were correctly implemented but incorrectly combined.
The Worm Payload: What It Actually Does
The malicious code injected into the TanStack packages is a 2.3 megabyte JavaScript file called router_init.js. A single line. Obfuscated across three separate layers to resist analysis. At the core of that obfuscation, once unwrapped, is a sophisticated credential harvester with persistence, self-replication, and data exfiltration capabilities.
Stealing Secrets from Memory
The most technically significant capability is the GitHub Actions runner memory scrape. The worm drops a Python script and pipes it to sudo python3 during execution. The script walks through the Linux /proc filesystem to find the GitHub Actions Runner.Worker process, then reads that process's memory directly, scanning for the internal format GitHub uses to track masked secrets:
{"value":"...","isSecret":true}
This extracts every secret configured in the workflow, including values that are masked in log output and never written to disk. The entire list of credentials the pipeline was entrusted with, from npm tokens to cloud API keys, is captured in plain text.
This technique matters beyond this specific campaign. It demonstrates that masking secrets in CI logs is not a security control. The values still exist in memory. Any code running in the same environment can reach them.
Spreading to New Victims
What makes Mini Shai-Hulud a true worm rather than a one-time attack is what happens next. After collecting npm tokens from the compromised environment, the worm queries the npm registry for every package published by that same maintainer account. It then downloads each clean package, injects itself into the tarball, and publishes the infected version using the stolen credentials.
This is how the compromise spread from TanStack to UiPath, Mistral AI, OpenSearch, DraftLab, and dozens of others. The worm did not need the attacker to take any action after the initial Pwn Request. Each infected CI pipeline became the mechanism for infecting the next ring of downstream package maintainers. The CI/CD infrastructure that organizations rely on for secure, automated delivery became the delivery mechanism for the attack itself.
Harvesting Files from Developer Machines
When the worm runs on a developer workstation rather than a CI runner, it reads over 100 specific file paths covering nearly every credential store a developer might have. Cloud provider credentials for AWS, Azure, and Google Cloud. Kubernetes configuration files. SSH private keys. npm and package manager auth tokens. Docker registry credentials. Git credentials. AI tool configurations including Claude Code and Kiro settings files. And notably: cryptocurrency wallet files for Bitcoin, Ethereum, Monero, Zcash, and several software wallets.
The breadth of that list is intentional. The worm is not looking for one type of credential to accomplish one specific goal. It is collecting everything to sort out value later.
Persistence After the Infection
On developer workstations, the worm installs persistence mechanisms designed to survive reboots and continue collecting credentials indefinitely. On Linux systems, it registers a systemd user service called gh-token-monitor that runs on every login. On macOS, it creates a LaunchAgent under the same name. Both services store stolen tokens locally and re-exfiltrate any new credentials they find.
Additionally, the worm poisons two developer tools specifically. It writes a modified configuration to Claude Code's settings.json that registers a hook firing on every session start. It writes a VS Code task configuration that fires every time a workspace folder is opened. Both hooks re-execute the worm payload, ensuring that credential collection resumes every time the developer opens their tools, regardless of whether they have rotated their credentials since the initial infection.
Where the Data Goes
Stolen credentials are encrypted and sent through two parallel channels. The first is the Session Protocol's file CDN, a service belonging to a legitimate privacy-focused messaging application. The domain is unlikely to appear on corporate blocklists because it belongs to a real, benign service. The data is encrypted before sending, so even if the traffic is intercepted, the content is not immediately readable.
The second channel uses GitHub's own infrastructure. The worm commits encrypted data to attacker-controlled repositories using the stolen GitHub token. Commits are disguised to blend in: authored as claude@users.noreply.github.com, with the message "chore: update dependencies," and branch names drawn from Dune universe terminology formatted to look like automated Dependabot updates.
The worm's code also contains one more detail worth noting. When it creates a new npm token on the victim's account using the stolen credentials, it sets the token's description to: IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner. This is a ransom threat made visible to anyone reviewing their npm token list. The worm includes a destructive routine triggered by token revocation. Security teams responding to this incident should not revoke npm tokens before isolating and imaging affected machines.
The SLSA Problem: When Trust Signals Lie
One of the most consequential aspects of this attack is what it revealed about software supply chain trust frameworks.
SLSA, pronounced "salsa," stands for Supply Chain Levels for Software Artifacts. It is an industry framework designed to give software consumers a verifiable way to know how a package was built. A package with SLSA Build Level 3 provenance carries a cryptographically signed attestation from a trusted build platform confirming which pipeline produced it. Many security teams treat SLSA attestations as a meaningful signal that a package is trustworthy.
Every compromised package in this attack carried valid SLSA Build Level 3 provenance. The attestations were not forged. They were legitimately issued by npm's Sigstore-based signing infrastructure, tied to the real TanStack/router Release workflow. They were accurate in the narrow sense that they correctly identified which pipeline produced the artifacts.
But those attestations said nothing about whether the pipeline was behaving as intended. A compromised build step can produce a validly-attested malicious package. The attestation proves provenance, not integrity. This distinction matters significantly for organizations that have adopted SLSA as a supply chain security control. It is a useful signal. It is not a guarantee.
Who Gets Hit and Where Detection Is Possible
Understanding this attack's detection surface requires being clear about which systems are actually visible to defenders.
GitHub's managed CI runners, the default infrastructure for GitHub Actions, are hosted machines that GitHub controls. No organization can deploy Sysmon, endpoint detection agents, or any logging tooling on those machines. The initial infection of TanStack's pipeline happened entirely on infrastructure that no external defender could instrument. The pipeline ran, the worm executed, and the compromised packages were published, all without any opportunity for endpoint-level detection by a third party.
Detection becomes possible at the point where a compromised package reaches infrastructure that an organization does own and can instrument. That means three environments:
Developer workstations are persistent Linux machines where Sysmon for Linux can be deployed. When a developer runs npm install on a project that depends on a compromised TanStack package, the worm executes on that machine. Process creation events, file modification events, and network connections are all visible here.
Self-hosted CI runners are GitHub Actions runner agents hosted on an organization's own servers or virtual machines. These are persistent, organization-owned infrastructure that can be fully instrumented. They are the higher-priority instrumentation target because they hold npm publish tokens and cloud credentials. Compromise of a self-hosted runner does not just expose that organization's secrets; it enables the worm to re-publish infected packages under that organization's namespace, propagating to the next ring of victims.
Corporate web proxies provide the one network-layer detection opportunity. If an organization routes its outbound developer workstation and self-hosted runner traffic through a corporate proxy, connections to the worm's C2 and exfiltration infrastructure become visible regardless of what is installed on the endpoint.
What the Attacker Does with the Data
Mini Shai-Hulud is not a traditional intrusion tool in the sense of providing an interactive shell back to an operator or staging a lateral movement framework. There is no Cobalt Strike beacon checking in for tasking. The payload's design is closer to a high-volume credential harvester that also happens to replicate itself.
The downstream use of the collected credentials is where the serious damage occurs, and it likely takes several forms.
The most probable use is sale on criminal markets. The credential breadth maps precisely to what initial access broker operations sell: npm tokens with publish rights, GitHub PATs, AWS IAM credentials, cloud service keys. There are active buyers for all of it. The dual-channel exfil architecture and the encrypted payload format suggest the operators want clean, organized credential bundles they can inventory and resell.
The explicit targeting of cryptocurrency wallet files suggests direct monetization as well. Crypto wallets on developer machines represent liquid assets that can be transferred immediately, without a buyer, and without reversal. If any of the affected developers or build servers had wallet files accessible, that represents a direct theft opportunity alongside the credential brokering.
The cloud credentials and SSH keys collected would give a buyer or the operators themselves the ability to move laterally into internal infrastructure and potentially stage a ransomware deployment or destructive wiper attack. The worm itself does not do this, but the access it provides creates that option for whoever acquires the data.
The technical sophistication of the campaign, specifically the three-layer obfuscation, the exploitation of OIDC trust chains, the use of Bun to evade Node.js monitoring hooks, and the SLSA provenance forgery technique, is consistent with a well-resourced threat group. Developers at defense contractors, financial institutions, healthcare organizations, and government agencies all use the packages that were compromised. That is a high-value intelligence target in addition to being a financially motivated credential theft operation.
The Detection Opportunity: Where Your Telemetry Fires
For organizations that have Sysmon for Linux deployed on developer workstations and self-hosted CI runners, several specific behaviors in this attack are detectable in ways that carry near-zero false positive rates.
The clearest signal is the grep process spawned during the memory scrape. The worm pipes a Python script through grep with a pattern targeting the GitHub Actions internal secret format: the string isSecret. No legitimate npm install pipeline creates a grep process searching for isSecret. If that string appears in a grep CommandLine on any instrumented system, treat it as a confirmed active attack.
The second signal is the Bun JavaScript runtime being spawned from within an npm install process tree. Bun being installed silently and executed against specific payload filenames during a package install context is not a pattern that occurs in legitimate dependency management. It catches the exact moment the worm payload begins executing, before any credential theft has occurred.
On developer workstations specifically, the installation of a systemd user service or macOS LaunchAgent named gh-token-monitor is campaign-specific and has no legitimate equivalent. Write-accesses to .claude/settings.json or .vscode/tasks.json by node or bun processes during an npm install context are similarly anomalous.
At the network layer, connections to git-tanstack.com and api.masscan.cloud have no legitimate business justification in any enterprise environment and should be treated as immediate indicators of active exfiltration.
The Honest Gap: What You Cannot Detect
The most important thing to understand about the defensive posture for this class of attack is where the blind spots are.
The initial compromise of package maintainers like TanStack happens entirely on GitHub-managed infrastructure that no external defender can instrument. By the time Mini Shai-Hulud reaches your environment, the upstream package is already compromised and sitting on npm waiting to be installed. Your detection window opens only at the moment npm install runs on a machine you own.
The GitHub GraphQL dead-drop channel is also not detectable at the network layer because github.com itself cannot be blocked in any practical engineering environment. That channel requires SCM-layer audit: searching your repositories for commits authored by claude@users.noreply.github.com that were not initiated through the legitimate Claude Code GitHub App, and reviewing branches that match the pattern dependabot/github_actions/format/ followed by Dune terminology.
Conclusion
Mini Shai-Hulud is not an incremental improvement on previous supply chain attacks. It is a demonstration of what happens when an attacker fully understands the modern software delivery ecosystem and weaponizes its trust mechanisms systematically.
The npm dependency model, the automation and credential density of CI/CD pipelines, the OIDC trust chain between GitHub and npm, the GitHub Actions cache sharing model, the SLSA provenance framework, the Bun runtime's lack of Node.js security hooks, the Session Protocol's legitimate CDN as an exfiltration channel: every element the worm used was chosen specifically because it is part of how modern software development is supposed to work. None of these features are broken. They were combined in a sequence that produced an outcome their designers did not anticipate.
The name is well chosen. Like the sandworm beneath the desert surface, this worm moved through the infrastructure developers rely on every day, consuming credentials silently, replicating through the pipelines that exist to deliver trusted software. Most of its victims had no way to know it had passed through until after it was gone.
For security teams responsible for developer workstations and self-hosted build infrastructure: deploy Sysmon for Linux, watch for the grep isSecret pattern, and watch for Bun spawning from npm install contexts. The detection window is narrow but it is real.
