Shai-Hulud Strikes Again

A digital illustration of an npm package box being disrupted by a Mini Shai-Hulud, with data fragments and warning icons like locks and alerts emerging, symbolizing security vulnerabilities. A banner reads Research Insights.

Someone just published a malicious version of a package your developers installed this morning. They used the legitimate CI/CD pipeline. The artifact has valid provenance.

Between May 11 and 12, 2026, a self-propagating worm swept through the npm ecosystem, compromising over 200 packages maintained by TanStack, Mistral AI, UiPath, and Guardrails AI. The campaign calls itself Mini Shai-Hulud, and unlike most supply chain attacks you have read about, this one does not wait for your developers to run the compromised code. It executes at install.

For MSPs managing development environments, CI/CD pipelines, or any tenant running JavaScript tooling, the question is not whether this affected your clients, the question is whether you have the visibility to know.

The campaign calls itself Mini Shai-Hulud. The threat actor is TeamPCP.


What Actually Happened

The first wave hit in late April. Six packages, including SAP CAP components and intercom clients, were compromised via stolen npm tokens. Relatively contained. The second wave, which is still unfolding as of this writing, is a different class of problem.

Wave two started with TanStack Router. The initial access vector was a GitHub Actions abuse pattern involving pull_request_target workflows, cache poisoning, and OIDC token extraction from runner memory. Once the attackers had a valid GitHub token and npm publishing rights, the worm did the rest.

The compromised package publishes a new malicious version. That version’s package.json adds an optionalDependencies entry pointing to a malicious orphan commit at github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c, attributed to a GitHub user voicproducoes. The prepare script runs bun run tanstack_runner.js && exit 1.

The Bun runtime executes a heavily obfuscated 2.xMB JavaScript payload (router_init.js) that harvests credentials from 100+ file paths, environment variables, and process memory. Those credentials include GitHub tokens, npm tokens, AWS/GCP/Azure credentials, Kubernetes configs, HashiCorp Vault tokens, SSH keys, and crypto wallet material.

Using the stolen tokens, the malware enumerates all other npm packages owned by the compromised maintainer and publishes infected versions of them as well. Self-propagating. Each new infection hands the worm a fresh set of maintainer credentials. The blast radius scales with the size of the dependency graph.

By May 12, the count was somewhere between 160 and 200+ packages. Lists are still being updated.


Still have questions before choosing a plan?
Talk to a real human. No forms. No waiting. No Slack account needed.

No Slack account needed.

Why Security Controls Cannot Catch This

SLSA provenance is supposed to help. The attestations on these malicious versions were valid because they were built by the legitimate pipelines. The provenance verified that the build ran on GitHub Actions. It said nothing about what was injected into the build environment before the artifact was produced. Valid provenance on a compromised build is not a security control.

npm’s package signing tells you a package was signed. It does not tell you that the signing key wasn’t on a machine running router_init.js at the time.

SCA tools check packages against databases of known vulnerable versions. A package published yesterday that is not yet in any advisory database will pass clean. The malicious TanStack versions had a window of hours before they were detected. For packages with millions of weekly downloads, hours are enough.

The payload in router_init.js (SHA-256: ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c) is 2.xMB of heavily obfuscated JavaScript. Most SAST tools that run in CI/CD do not perform behavioral analysis on optional dependencies at install time. They are doing pattern matching on the source.

The Payload Behavior

The malware runs at package install, not at runtime. That distinction matters because it means it fires on developer workstations, in CI/CD runners, in Docker build stages, in any environment where someone runs npm install.

Credential harvesting targets are broad. The malware scans 100+ file paths covering common credential storage locations on macOS, Linux, and Windows. It reads environment variables wholesale. It hits process memory looking for GitHub Actions runner secrets. It makes HTTP requests to 169.254.169.254 (AWS IMDS), 169.254.170.2 (ECS task metadata), and 127.0.0.1:8200 (Vault API).

Exfiltration goes three ways. First, via the Session Protocol using filev2.getsession.org. Second, via git-tanstack.com for PyPI-related material. Third, via GitHub GraphQL dead-drops. The dead-drop commits come from [email protected], with commit messages like chore: update dependencies, on branches named after Dune terminology: dependabot/github_actions/format/fremen, /sardaukar, and similar. The repositories carrying stolen data have descriptions that read, “Mini Shai-Hulud has appeared.”

Persistence is where this gets uncomfortable. On Linux, the malware installs a systemd unit. On macOS, it installs a LaunchAgent. The service is named gh-token-monitor. If the stolen GitHub token is revoked, the service executes rm -rf ~. The npm token itself is published with the description IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner. This is not malware trying to stay quiet. It is malware posing a threat.

IDE hooks land in .vscode/tasks.json, .vscode/setup.mjs, .claude/settings.json, and .claude/router_runtime.js. Injected GitHub Actions workflows appear as .github/workflows/codeql_analysis.yml if they were. The presence of .claude/ targeting is notable. Claude Code is part of the targeted surface.

Flowchart showing a multi-stage attack: initial npm install trigger, payload delivery, execution, credential harvesting, exfiltration, self-propagation like an npm Worm in the CI/CD Pipeline, and persistence—each stage lists techniques and targets used.

IOC’s

We are actively scanning client environments and tracing any breadcrumbs this campaign may have left behind. If you manage development environments, CI/CD pipelines, or any tenant running JavaScript or Python tooling, the investigation is already running. The question is what it finds.

Below is a link to the IOC’s. This list is a partial list, as of May 12.

https://github.com/guardzcom/security-research-labs/blob/main/Threat-Intel/IOCs/Shai-Hulud/README.md

Insights

Check for gh-token-monitor before revoking anything. The destructive wipe triggers on token revocation, not on detection. Isolate the machine first, then pull credentials. Revoking on a live infected host is the trigger.

@tanstack/setup In any dependency tree, there is a hard stop. That package does not exist in the legitimate TanStack org. Its presence means the worm has already run. Grep every package.json and package-lock.json across client environments now.

Valid provenance is not a detection control. The malicious versions carried valid SLSA Build Level 3 attestations because the pipeline was compromised before the build ran. Provenance verifies where the artifact was built, not what ran inside that environment beforehand.

Hunt the dead drops, not just the domains. Blocking the C2 domains is necessary but not sufficient. The exfil also runs through legitimate GitHub infrastructure via GraphQL dead-drops on branches named after Dune terminology. Those will never trigger a domain blocklist.

This campaign did not steal credentials to publish malicious packages. It hijacked the pipeline itself and published through it. Every GitHub Actions workflow with pull_request_target, and every cached dependency, every OIDC token issued to a runner is a potential injection point.

Track Guardz

This campaign is consistent with the broader shift we documented in the 2026 MSP Threat Report. We are tracking Mini Shai-Hulud actively and will publish updates as the indicator list grows.

Follow @GuardzCyber on X for real-time threat updates, IOC drops, and campaign developments as they happen.

For more research artifacts related to this campaign, see the Guardz Security Research Labs repository. Star it, watch it, and pull updates as the indicator set evolves.

Categories:

Subscribe to
Our Newsletter.

Abstract image of two overlapping shield shapes, one dark blue and one green, with a soft glowing effect on a light background—perfect for enhancing your single post template with a modern, secure aesthetic.
Abstract image with a large dark blue, semi-circular shape overlapping a bright green, glowing circular shape on a light gray background. Perfect for enhancing your single post template, the green circle appears partially blurred and luminous.

Keep your clients secure.

A stylized, dark blue shield icon with a green gradient glow on the right side, set against a light gray background—ideal for enhancing your single post template design.

Continue Reading

MDR migration guide for MSPs

MDR Migration Guide for MSPs: How to Reduce Security Gaps & Operational Risk

A glowing shield with the Microsoft 365 logo is surrounded by app icons and a large phishing hook, highlighting cybersecurity risks for SMBs. Text reads Research Insights and Kali365. The background is dark with neon blue and red highlights.

The Rise of Kali365 and Why MSPs Should Be Concerned

best EDR for MSPs

7 Best EDR for MSPs to Protect SMB Clients in 2026

A person in a futuristic chair sits at a high-tech control panel, looking out at a starry space scene with planets and mountains. The dashboard glows with colorful buttons and screens, like the perfect single post template for exploring new worlds.

Guardz, Your Cybersecurity
Co-Pilot for MSPs

Demonstrate the value you bring to the table as an MSP and gain visibility into your clients’ external postures.

Holistic Protection.
Hassle-Free.
Cost-Effective.
Slack
Slack
Chat with us No Slack account needed.