Mini Shai-Hulud Hits AntV: 300+ Malicious npm Packages Published via Compromised Maintainer Account
May 18, 2026
0 mins readA supply chain attack affecting the @antv data visualization ecosystem and related npm packages is actively spreading through the npm registry. The attack, attributed to a threat group called TeamPCP and branded as another wave of the Mini Shai-Hulud campaign, published more than 300 malicious package versions across 323 packages in a 22-minute automated burst on May 19, 2026. The packages collectively represent approximately 16 million weekly downloads.
The attack vector was a compromised npm maintainer account. The malware embedded in affected packages harvests developer secrets and cloud credentials, establishes persistent C2 access, and attempts to self-propagate to additional packages using stolen npm tokens.
TL;DR
Attack type | Supply chain, compromised maintainer account |
Threat actor | TeamPCP (aliases: DeadCatx3, PCPcat) |
Campaign | Mini Shai-Hulud (ongoing since Sep 2025) |
Incident date | May 19, 2026, 01:39–02:06 UTC |
Packages compromised | 637 malicious versions across 323 packages |
Estimated weekly downloads | ~16 million |
Malware behavior | Credential theft, cloud secret harvesting, persistence, worm propagation |
Snyk coverage | Advisories in Snyk Vulnerability Database; Zero Day Report available in-app |
Immediate action | Pin to pre-May 19 versions, run |
Affected packages
The compromised atool npm account maintained 547 packages. The malicious publish window hit over 300 of them in two waves:
First wave: 01:39–01:56 UTC (~317 versions)
Second wave: 02:05–02:06 UTC (~314 versions)
The highest-download packages affected include:
Package | Snyk Advisor | Monthly Downloads |
| 4.2M | |
| 3.8M | |
| 2.2M | |
| 1.15M | |
| 1.0M+ |
Core @antv packages compromised include @antv/g2, @antv/g6, @antv/x6, @antv/l7, @antv/s2, @antv/f2, @antv/g, @antv/g2plot, @antv/graphin, and @antv/data-set, along with non-scoped packages like echarts-for-react, timeago.js, size-sensor, and canvas-nest.js.
AntV is an Alibaba-originated data visualization suite widely used across enterprise dashboards, financial reporting tools, and graph analysis platforms. Its breadth of adoption makes the maintainer account's package portfolio an unusually high-value target.
How the attack works
Stage 1: Maintainer account compromise
The attack begins with the atool npm account being compromised. How the account was obtained is still under investigation. With control of the account, the attacker had publish access to all 547 packages it maintains.
Stage 2: Automated malicious publish
The attacker published malicious versions in two rapid waves, pushing most packages twice (a small number of early-testing packages received three versions). Each malicious package tarball contains two additions:
A root-level
index.js:a 498KB heavily obfuscated Bun JavaScript payloadA modification to
package.jsonadding:"preinstall": "bun run index.js"
The preinstall lifecycle hook triggers automatically when a developer runs npm install, before any other installation logic executes.
Stage 3: Orphan commit injection for Sigstore provenance
One of the more subtle techniques in this wave is the injection of an optional dependency pointing to an orphan commit in the legitimate antvis/G2 repository:
Important to note that the GitHub-sourced dependency is the same method of attack that was used in the previous TanStack Shai-Hulud supply chain attack.
The commit was authored by the attacker but forged to appear as huiyu.zjt <huiyu.zjt@ant.com> (a real maintainer). No write access to the target repository was needed. The attacker forked antvis/G2, created an orphan commit carrying the payload, then deleted the fork. GitHub's object storage retains commits from deleted forks until garbage collection runs, leaving the malicious commit accessible via its hash.
Fetching that commit provides a path to execute the payload with Git-sourced credentials while appearing to reference a trusted repository.
Using stolen GitHub Actions OIDC tokens, the malware can then request signing certificates from Fulcio (https://fulcio.sigstore.dev) and create in-toto provenance statements via Rekor (https://rekor.sigstore.dev), producing packages with cryptographically valid SLSA Build Level 3 attestations.
The key insight here is that the signatures are legitimate because the build pipeline itself was compromised. Sigstore provenance tells you which pipeline produced an artifact, not whether that pipeline was behaving as intended. And so, a common misconception about relying solely on attestation evidence for published packages is that it is a bulletproof signal of legitimacy.
Stage 4: Credential harvesting
When bun run index.js executes on a developer's machine or in a CI runner, the payload targets over 80 environment variables and 100+ file paths. Targeted credential types include:
AWS: Access keys (
AKIA[0-9A-Z]{16}), session tokens, EC2 IMDS (169.254.169.254), ECS metadata (169.254.170.2), Secrets ManagerGCP: Service account JSON, application default credentials
Azure: Service principal credentials
GitHub: PATs and OIDC tokens (
gh[op]_[A-Za-z0-9]{36,})npm: Publish tokens with
bypass_2fascopeInfrastructure: Kubernetes service tokens, HashiCorp Vault tokens
Databases: MongoDB, MySQL, PostgreSQL, Redis connection strings
Services: Stripe keys, Slack tokens, Docker auth configs
SSH keys:
~/.ssh/id_*
In GitHub Actions environments, the payload attempts to read secrets directly from the Runner.Worker process memory via /proc/{pid}/mem, bypassing secret masking entirely.
Stage 5: Data exfiltration
All harvested credentials are serialized as JSON, gzip-compressed, encrypted with AES-256-GCM, and then the encryption key is wrapped using RSA-OAEP against a hardcoded attacker public key. This means defenders who find the exfiltrated data cannot determine what was stolen.
Exfiltration routes through two channels:
Primary C2:
https://t[.]m-kosche[.]com:443/api/public/otel/v1/traces(disguised as OpenTelemetry trace data)Secondary GitHub dead-drop: If a GitHub token is found, the payload creates repositories on the victim account with Dune-themed names (combinations of words like
sardaukar,fremen,atreidescombined withsandworm,ornithopter,stillsuit, plus a number 0-999), containing README files with characteristic phrases from previous waves of the Shai-Hulud campaign, reversed (e.g. “niagA oG eW ereH :duluH-iahS”). Stolen data is committed asresults/results-<timestamp>-<counter>.json. HTTP requests use"python-requests/2.31.0"as the User-Agent.
Stage 6: Persistence
The payload installs several persistence mechanisms that survive a simple npm uninstall:
AI coding agent hijacking: Creates
.claude/settings.jsonwith aSessionStarthook executingnode .claude/setup.mjs. This re-executes the malware whenever a developer opens a new Claude Code session in an affected directory. We’ll note that this is also a similar pattern we’ve seen in prior supply chain attack campaigns such as TanStack.IDE hooks: Modifies
.vscode/tasks.jsonwith"runOn": "folderOpen"to trigger on project open.OS-level daemon: Installs
~/.local/share/kitty/cat.py, a background daemon that polls the GitHub commit search API hourly for commands containing the keywordfiredalazer. Commands are RSA-PSS signed with a hardcoded 4096-bit public key, allowing the attacker to push arbitrary Python code to run on compromised machines long after the initial infection. The daemon is registered as a systemd user service on Linux (~/.config/systemd/user/kitty-monitor.service) and a LaunchAgent on macOS (~/Library/LaunchAgents/com.user.kitty-monitor.plist).
Token monitor: ~/.local/bin/gh-token-monitor.sh polls stolen GitHub tokens at 60-second intervals, enabling the attacker to react quickly if a token is about to expire.
Stage 7: Worm propagation
The payload searches for npm tokens with the bypass_2fa scope and uses them to republish additional packages that the compromised account can publish to. For GitHub Actions environments, it exchanges the Actions OIDC token for per-package npm publish tokens via:
It also injects a GitHub Actions workflow on a branch named chore/add-codeql-static-analysis, with the workflow file named "Run Copilot" (.github/workflows/codeql.yml). The workflow dumps toJSON(secrets) to an artifact named format-results.txt, then self-cleans by deleting the workflow run.
Impact analysis
The direct blast radius is any developer or CI environment that ran npm install against an affected package version between 01:39 and approximately 02:18 UTC on May 19, 2026.
CI/CD environments are at elevated risk. In those contexts, the payload can read all secrets from the runner process, not just those explicitly passed as environment variables. Any secret the GitHub Actions runner has access to, including OIDC tokens, repository secrets, and organization secrets scoped to that repository, should be considered compromised if an affected version was installed.
Developer machines present a significant risk as well. The persistence mechanisms mean that removing the affected packages alone does not remove the threat. The .claude/settings.json hook, VS Code task, and system daemon all continue running until explicitly removed.
The self-propagating component means that any npm token captured from a developer machine or CI runner could be used to poison additional packages outside the initial atool account portfolio, widening the blast radius beyond AntV and related packages.
Detection
Check your lockfile. If your package-lock.json or yarn.lock references any package maintained by the atool account, check whether the resolved version was published between 01:39 and 02:18 UTC on May 19, 2026.
Use Snyk to scan your projects. Snyk has published advisories in the Snyk Vulnerability Database for affected package versions and has deployed an in-app notification and a Zero Day Report for customers to investigate exposure across their organizations.
For a quick scan of a specific package in your tree:
Check for persistence artifacts. If you installed any affected packages, check for:
Package-level indicators:
Presence of
preinstallscriptbun run index.jsinnode_modules/<package>/package.jsonPayload SHA256:
a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1cOptional dependency referencing
github:antvis/G2#1916faa365f2788b6e193514872d51a242876569(or commits7cb42f57561c / dc3d62a2181b)
Network indicators:
Outbound requests to
169.254.169.254or169.254.170.2(cloud metadata endpoints) from non-cloud contextsHTTP requests to
t.m-kosche.com:443with OpenTelemetry pathsGitHub API calls with
python-requests/2.31.0User-Agent not originating from actual Python processes
Remediation
If you are uncertain whether you were affected, treat it as a confirmed compromise. The RSA-encrypted exfiltration means you cannot recover what was taken.
Step 1: Remove persistence before revoking tokens.
The gh-token-monitor.sh daemon polls tokens every 60 seconds. If a GitHub token expires or is revoked while the daemon is running, it may trigger further malicious actions. Stop and remove the persistence mechanisms first:
Step 2: Clean npm and reinstall on clean versions.
Using --ignore-scripts prevents any preinstall, postinstall, or prepare lifecycle scripts from running during installation. This should be standard practice in CI environments regardless.
Step 3: Rotate all credentials.
Assume anything reachable from any machine or CI runner that installed an affected package is compromised:
npm publish tokens
GitHub personal access tokens and Actions secrets
AWS access keys (and any IAM roles accessible from affected runners)
GCP service account keys
Azure service principals
Kubernetes service account tokens
HashiCorp Vault tokens
SSH keys present on affected machines
Database connection strings
Any API keys or service tokens stored in environment variables or config files
Step 4: Audit GitHub for injected workflows and dead-drop repos.
Step 5: Prevent future exposure.
Enable npm two-factor authentication with publish protection on all organization packages.
Add
npm install --ignore-scriptsto CI configuration as a default.Pin dependencies to exact versions using lock files with integrity checks.
Consider a registry cooldown policy: flag and hold packages published within the last 7 days.
Use Snyk to continuously monitor your dependency tree for malicious packages as they are flagged.
We highly recommend that you consult and follow the npm security best practices repository for security controls and practices to avoid future malware incidents.
The bigger campaign: Shai-Hulud Waves
The AntV attack is the latest wave in a campaign that TeamPCP has been running since September 2025. The progression shows steady escalation in scope, persistence, sophistication, and abuse of trusted infrastructure:
Wave | Date | Primary Target | Scale | Signature Technique |
Wave 1 (Shai-Hulud) | Sep 2025 | npm (broad) | ~4 packages | First self-propagating npm worm |
Wave 2 (SHA1-Hulud) | Nov 2025 | Zapier, Posthog, Postman | 600+ packages | Container breakouts; destructive wiper |
Wave 3 (Mini, SAP) | Apr 2026 | SAP CAP-JS, MBT | 4 packages | Claude Code |
Wave 4 (TanStack) | May 11, 2026 | @tanstack/* | 84 versions/42 packages | First valid SLSA provenance via OIDC hijack |
Wave 5 (AntV) | May 19, 2026 | @antv/* + 310 more | 637 versions/323 packages | Compromised maintainer account; expanded C2 |
Snyk has covered prior waves in depth:
Each wave has reused and extended the Bun-runtime-based obfuscated payload, adding new persistence mechanisms (the Claude Code SessionStart hook first appeared in Wave 3), new exfiltration infrastructure, and new methods for forging trusted provenance. The SLSA provenance problem in particular deserves attention: a valid Sigstore attestation confirms which pipeline produced a package, not whether that pipeline was compromised. Relying on provenance as a trust signal without also auditing the underlying pipeline configuration leaves a gap that this campaign has now exploited in two consecutive waves.
For a video walkthrough of remediation using Snyk for the broader Shai-Hulud campaign:

Shai-Hulud NPM Attack: Remediation with Snyk — Walkthrough of identifying and remediating compromised packages using Snyk tooling.
Attack timeline (May 19, 2026, UTC)
Time | Event |
01:39 | First malicious package version published from |
01:56 | First publication wave ends (~317 versions) |
02:05 | Second wave begins |
02:06 | Second wave ends (~314 additional versions) |
~02:18 | Detection; security researchers begin filing reports |
Ongoing | Investigation continues; additional packages may be identified |
Snyk coverage
Snyk has published advisories for affected package versions in the Snyk Vulnerability Database. Snyk customers can use the Zero Day Report in-app to investigate which projects across their organization are affected. The Snyk Vulnerability Database entries for affected packages reflect the current health status.
For background on the attack class, Snyk Learn's lesson on Compromise of Legitimate Packages covers how maintainer account compromise fits into the broader supply chain threat landscape.
Secure your supply chain with Snyk
87% of our respondents were impacted by supply chain security issues. Keep yours secure with Snyk.
