BLOG
Mini Shai-Hulud Weaponizes Tasks.JSON Files
Mini Shai-Hulud is a malicious npm worm by TeamPCP. It weaponizes tasks.json files, a technique first seen in North Korean Lazarus Group campaigns.
By c0a15726-c5b1-4b0d-85e6-fe15553df9e2 ·
Mini Shai-Hulud is a malicious npm worm by TeamPCP that spread to more than 1,000 repositories in its first wave. Vendor writeups have been calling its core trick “novel.” It isn’t. It’s a direct lift from the PolinRider and TasksJacker playbook that Lazarus Group has been running on developer machines since late 2025. If you’ve already built detection for TasksJacker, you have most of what you need. If you haven’t, this is the wave that should change that.
If you’ve already done the work of detecting TasksJacker in your estate, you already have most of what you need to detect this. If you haven’t, this is the wave that should make you start.

The four affected packages are cross-referenced on OpenSourceMalware.com and tagged mini-shai-hulud so they cluster with related findings as more reports come in. Links are below.
The tasks.json folderOpen technique
The technique is .vscode/tasks.json with "runOn": "folderOpen".
VS Code’s tasks.json is a legitimate, well-documented feature for defining build, test, and lint tasks inside a workspace. The runOptions block lets a task auto-execute on certain triggers, and folderOpen is exactly what it sounds like: when you open the folder, the task runs. For trusted workspaces, that happens silently, before any code review, before any extension loads, before a developer has a chance to think about what they just opened.
Drop a tasks.json like that into a repository, get a developer to open the repository in VS Code, and you have remote code execution on their machine. That’s the entire trick.
PolinRider has been using this for months. The DPRK/Lazarus-aligned campaign — which merges the older TasksJacker and Contagious Interview clusters — uses tasks.json folderOpen execution as a fallback persistence layer behind its npm-config-file injection (the obfuscated payload appended to postcss.config.mjs, tailwind.config.js, next.config.mjs, etc., signed ("rmcej%otb%",2857687)). Independent research has documented PolinRider/TasksJacker active across more than 1,900 public repositories.
Mini Shai-Hulud’s execution.js walks across every accessible repository on the victim’s GitHub account and writes exactly that file into up to fifty branches per repo. The technique didn’t get reinvented; it got cloned, scaled, and bolted onto a different worm.
What Mini Shai-Hulud added to the PolinRider playbook
Two things, and both extend the same theme of abusing developer tooling that runs code without asking.
1. Claude Code SessionStart hook. Alongside tasks.json, the payload drops a .claude/settings.json configuring a SessionStart hook that fires when Claude Code attaches to the workspace, plus a .claude/execution.js for the hook to call. If a developer doesn’t open the repo in VS Code but does open it in Claude Code — or runs both, as many now do — the payload executes anyway. This is the same folder-open-as-execution-trigger idea, ported to the AI assistant that increasingly sits in the same workflow position VS Code used to hold alone.
2. Bun runtime, not Node. The preinstall loader (setup.mjs) downloads Bun v1.3.13 from the official oven-sh/bun GitHub release and runs an 11.6 MB obfuscated execution.js under the Bun process, deleting the binary afterward. Detection rules tuned to Node.js process trees and module loaders will not match. The Bun release is a legitimate signed artifact pulled from GitHub, so basic egress blocklists won’t help either.
PolinRider’s persistence reach was the developer’s machine. Mini Shai-Hulud took the same mechanism, paired it with cloud-credential harvesting and a self-propagating npm worm, and pointed it at enterprise CI plus AI assistants. Same primitive, larger blast radius.
Why the technique transfer matters
This is the part worth pausing on.
PolinRider is attributed to a DPRK-aligned actor. The Mini Shai-Hulud / SAP CAP wave is being attributed across vendors to TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce) — the same group claimed the @bitwarden/cli@2026.4.0 compromise earlier in April with payload strings reading “Shai-Hulud: The Third Coming.” Whether TeamPCP is the original Shai-Hulud crew, a successor working from leaked tooling, or a copycat, is still unsettled. What is clear is that the tasks.json folderOpen primitive — first used at scale by a DPRK-aligned actor — has now crossed into a financially-motivated / worm-driven campaign.
Cross-pollination between APT-aligned and crimeware ecosystems is not new. What’s notable here is the speed: PolinRider’s tradecraft was documented publicly by ThreatLocker in February 2026 and operationalized in a different worm family by April. That window is short enough that defensive controls written against PolinRider-the-campaign should be reframed as controls against tasks.json-as-a-class-of-attack, because the next group to use it is already in the queue.
The four affected packages
All four are core to the SAP CAP ecosystem. If any of these versions touched a build host or developer machine on or after April 29, treat every credential reachable from that host as compromised and rotate.
Package
Malicious version
OSM link
mbt
1.2.48
@cap-js/sqlite
2.2.2
opensourcemalware.com/npm/@cap-js/sqlite
@cap-js/postgres
2.2.2
opensourcemalware.com/npm/@cap-js/postgres
@cap-js/db-service
2.10.1
opensourcemalware.com/npm/@cap-js/db-service
The mbt package was published with a stolen npm token from the legitimate cloudmtabot SAP service account. The three @cap-js packages were published via OIDC trusted publishing — the project’s trust configuration accepted the entire cap-js/cds-dbs repository rather than being scoped to a specific workflow on a protected branch.
IOCs to hunt with
These are attached to the OSM threat reports above and are the fastest way to confirm exposure across a fleet.
File hashes (SHA-256):
4066781fa830224c8bbcc3aa005a396657f9c8f9016f9a64ad44a9d7f5f45e34—setup.mjsloader. Byte-for-byte identical across all four packages, the single best fingerprint of the worm in autonomous action.80a3d2877813968ef847ae73b5eeeb70b9435254e74d7f07d8cf4057f0a710ac—execution.jsshipped withmbt@1.2.48.6f933d00b7d05678eb43c90963a80b8947c4ae6830182f89df31da9f568fea95—execution.jsshipped with@cap-js/sqlite@2.2.2.
Repository signatures (the borrowed PolinRider primitive — hunt these across every repo, not just CAP-related ones):
.vscode/tasks.jsoncontaining"runOn": "folderOpen"you didn’t author. This signature is the high-value hunt because it covers Mini Shai-Hulud, TasksJacker, PolinRider, and any future actor that borrows the same trick..claude/settings.jsoncontaining aSessionStarthook.claude/execution.js(any presence).github/workflows/format-check.ymlyou don’t recognizeA branch named
dependabout/github_actions/format/setup-formatter(note the typosquat —dependabout, notdependabot)
PolinRider co-occurrence signatures. If you find Mini Shai-Hulud artifacts, also grep for the PolinRider primary signature ("rmcej%otb%",2857687) and temp_auto_push.bat / config.bat. The two campaigns share enough tradecraft that a host with one can plausibly have the other.
Exfil dead-drops on GitHub. The malware creates public repositories on the victim’s own GitHub account with two-word Dune-themed names plus a number:
https://github.com/search?q=%22A+Mini+Shai-Hulud+has+Appeared%22&type=repositoriesOr the regex against your org’s repo list:
(sardaukar|mentat|fremen|atreides|harkonnen|gesserit|prescient|fedaykin|tleilaxu|siridar|kanly|sayyadina|ghola|powindah|prana|kralizec)-(sandworm|ornithopter|heighliner|stillsuit|lasgun|sietch|melange|thumper|navigator|fedaykin|futar|slig|phibian|laza|cogitor|ghola)-\d{1,3}The worm creates exfil repositories on the victim’s own GitHub account using Dune-themed names. If you find one of these inside your org, the harvested credentials are already in a publicly indexed location.
What to do now
Hunt
tasks.jsonfolderOpenas a class, not as a campaign. The same query catches Mini Shai-Hulud, PolinRider, TasksJacker, and the next variant. Build it once and keep it.Pin and audit CAP dependencies. Lock to versions below the malicious ones and audit any host that ran
npm installtouching them on or after April 29.Treat AI-tool config as code. Commits to
.claude/,.vscode/,.cursor/,.continue/should require review.SessionStarthooks andfolderOpentasks should be on an allowlist, not whatever the repo ships with.Rotate everything reachable from a developer machine or runner that touched these packages — npm tokens, GitHub PATs, AWS/GCP/Azure keys, SSH keys,
.envsecrets. The worm took what it could see.Tighten OIDC trust policies. If you publish from GitHub Actions, scope trust to a specific workflow on a specific protected branch, not a whole repository.
Consider workspace trust off by default in VS Code. It’s a blunt control, but it disarms the entire
folderOpenprimitive across every variant that uses it.
Further reading
ThreatLocker — Malicious VS Code tasks.json abuse enables multi-stage infostealer deployment
Jamf — Threat actors expand abuse of Microsoft Visual Studio Code
Mend — Shai-Hulud SAP CAP Supply Chain Attack via Claude Code
Hackread — TeamPCP Hijacks Bitwarden CLI, Uses Dependabot to Deploy Shai-Hulud Malware
Palo Alto Unit 42 — Shai-Hulud Worm Compromises npm Ecosystem