AntV Mini Shai-Hulud Technical Analysis: npm Publish Abuse and CI Secret Theft
Technical teardown of the May 19 AntV npm wave, covering publish-right abuse, optional GitHub dependency droppers, Bun lifecycle execution, secret theft, persistence, and exfiltration.
Affected ecosystems: npm
Executive Summary
On May 19, 2026, a series of malicious npm packages were published under the AntV namespace and related projects. The attack involved compromised publication rights, used to inject malicious lifecycle scripts and optional GitHub dependencies that executed credential-stealing payloads. The campaign targeted developer and CI environments, specifically seeking GitHub, npm, and cloud credentials.
Analyst Assessment
This incident is part of the Mini Shai-Hulud family. The use of optional dependencies pointing to GitHub commit refs and the use of the Bun runtime for payload execution are key technical signatures. The attack appears highly automated, leveraging stolen maintainer tokens to perform a wide sweep of the AntV ecosystem.
Impact
Affected environments face immediate risk of credential theft (GitHub, npm, AWS, Vault, Kubernetes). The payload also establishes persistence via developer tool configurations (.vscode, .claude), allowing for long-term access and potential downstream supply chain propagation.
Exploitation Status
Active exploitation was observed during a tight window on May 19, 2026. Registry cleanup has since occurred, but affected versions may still exist in local caches, lockfiles, or internal mirrors.
Defender Guidance
Immediately audit lockfiles for '@antv/setup' or GitHub dependency refs under 'antvis/G2'. Inspect projects for unexpected .claude or .vscode configuration changes. Rotate any secrets (npm, GitHub, Cloud) that were present in environments where affected packages were resolved or executed.
Technical Analysis
Attack Overview
The May 19, 2026, supply chain attack targeting the AntV ecosystem represents a sophisticated escalation in the "Mini Shai-Hulud" campaign family. This wave specifically targeted high-profile data visualization and utility packages within the npm registry, including echarts-for-react, size-sensor, and jest-canvas-mock. Unlike simpler "dependency confusion" attacks, this campaign leveraged compromised maintainer credentials—specifically the atool account—to inject malicious logic directly into established, trusted packages. The scope of the attack was broad, affecting over 300 package versions in a coordinated burst. The primary goal was the systematic exfiltration of developer and CI/CD secrets, including GitHub tokens, npm credentials, and cloud service metadata, effectively turning the build pipelines of thousands of downstream users into credential-harvesting engines.
Step-by-Step Execution
The attack followed a precise multi-stage progression designed to maximize reach and minimize immediate detection:
- Initial Access: Attackers gained control of npm publication rights, likely through a stolen automation token or a compromised session belonging to the
atoolmaintainer. - Ghost Dependency Injection: Affected packages were updated to include
@antv/setupas an optional dependency. Crucially, this dependency was not resolved from the npm registry but instead pointed directly to specific git commit hashes on GitHub (e.g.,github:antvis/G2#7cb42...). - Execution: When a user installed an affected package, the optional dependency was fetched. The malicious
@antv/setuppackage utilized Bun-based lifecycle hooks (prepareorpreinstall) to execute an obfuscatedindex.jspayload. - Secret Harvesting: The payload performed an exhaustive search of the environment. It targeted
.gitconfig,.npmrc, AWS metadata endpoints (169.254.169.254), and environment variables containing "GITHUB_", "NPM_", or "VAULT_". - Memory Scraping: In GitHub Actions environments, the malware utilized advanced techniques to scrape the runner's memory for ephemeral OIDC tokens and transient secrets that aren't stored on disk.
- Persistence: The malware attempted to inject code into local developer tool configurations, such as
.claude/settings.jsonand.vscode/tasks.json, ensuring that the payload would be re-triggered whenever the developer opened the project or started a new session. - Exfiltration: Data was bundled and sent to attacker-controlled infrastructure at
t.m-kosche.comvia HTTPS requests disguised as OpenTelemetry traces, or through the decentralized Session network to avoid IP-based blocking.
Code-Level Mechanics
The core of the "Ghost Dependency" trick is found in the package.json of the compromised packages. By using optionalDependencies, the attacker ensures that the installation won't fail if the malicious repo is taken down, making the attack more resilient.
"optionalDependencies": {
"@antv/setup": "github:antvis/G2#7cb42f57561c321ecb09b4552802ae0ac55b3a7a"
}
The payload execution is driven by the Bun runtime, which is increasingly common in modern frontend toolchains but often less scrutinized than standard Node.js scripts in security audits. The use of prepare: bun run index.js && exit 1 is a clever "fail-open" strategy; the nonzero exit code prevents the dependency from appearing as successfully "installed" in some logs, while the side effects (credential theft) have already occurred.
Payload Behavior
Once active, the index.js payload is highly aggressive. It doesn't just look for files; it scans the process environment and active network connections. It specifically looks for CI/CD identifiers to adjust its behavior, becoming more stealthy in developer machines while performing loud, broad sweeps in automated build runners. The use of python-requests/2.31.0 for some API interactions indicates a modular approach where different components are fetched or triggered based on the host environment's capabilities.
Detection Opportunities
Defenders should prioritize auditing their dependency trees for any references to GitHub commit hashes, particularly those within the @antvis organization. Standard npm audit tools may miss these if the root package isn't considered "vulnerable" in the public database yet.
- Log Analysis: Search for outbound traffic to
t.m-kosche.comorfilev2.getsession.org. - Configuration Monitoring: Check for unauthorized changes to
.vscode/tasks.jsonor.claude/settings.json. - Runtime Protection: Implement egress filtering in CI/CD environments to block all non-essential outbound connections, particularly to unknown domains.
Evidence Gaps
While the technical execution is well-understood, several gaps remain. The exact method used to compromise the atool account is still under investigation; it is unclear if this was a result of a password reuse, a phishing attack, or a breach of a third-party service with broad npm permissions. Additionally, the full list of secrets exfiltrated from specific organizations is unknown, though the targeting suggests a high level of interest in cloud infrastructure and private source code repositories.
Indicators of Compromise (IOCs)
| Type | Indicator | Example | Notes |
|---|---|---|---|
| Package | npm | @antv/g2 | Affected: 5.5.8, 5.6.8 |
| Package | npm | @antv/l7-core | Affected: 2.26.10, 2.27.10 |
| Package | npm | echarts-for-react | Affected: 3.0.7 |
| Package | npm | jest-canvas-mock | Affected: 2.5.3 |
| Package | npm | size-sensor | Affected: 1.0.4, 1.1.4, 1.2.4 |
| url | Network IOCs | filev2.getsession.org/file/ | Exfiltration route via Session network |
| url | Network IOCs | github:antvis/G2#1916faa365f2788b6e193514872d51a242876569 | Malicious optional dependency ref used in jest-canvas-mock |
| url | Network IOCs | github:antvis/G2#7cb42f57561c321ecb09b4552802ae0ac55b3a7a | Malicious optional dependency ref used in echarts-for-react and size-sensor |
| domain | Network IOCs | t.m-kosche.com | Exfiltration domain mimicking OpenTelemetry traffic |
| hash | File, Path, Config, and Hash Artifacts | a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1c | SHA-256 of obfuscated index.js payload reported in @antv/l7-core |
Detection Opportunities
AntV Setup Phantom Dependency
lockfile_auditAudit lockfiles for '@antv/setup' pointing to github:antvis/G2 commit refs. False positives: None known for this specific pattern in these packages.
Mini Shai-Hulud Bun Lifecycle Execution
yararule MiniShaiHulud_Bun_Lifecycle { strings: $s1 = "\"prepare\": \"bun run index.js && exit 1\"" $s2 = "\"preinstall\": \"bun run index.js\"" condition: any of them } False positives: Legitimate use of Bun in scripts is possible but rare in these specific AntV packages.
Timeline
incident_start
jest-canvas-mock 2.5.3 published to npm registry (01:39:31Z)
propagation
size-sensor 1.0.4 published (01:44:36Z)
propagation
echarts-for-react 3.0.7 published (01:47:12Z)
propagation
Broader AntV batches observed around 02:05Z/02:06Z
Intelligence Sources
AntV Supply Chain Attack: Technical Deep Dive
In-depth analysis of the payload mechanics, persistence via .claude and .vscode, and propagation via CI identity.
npm Registry Metadata for AntV Packages
Primary registry metadata confirming publication timestamps and package state for affected AntV versions.
Runtime Analysis of AntV npm Malware
Analysis of runtime behavior in GitHub Actions, including memory scraping and OpenTelemetry-themed exfiltration.