critical
CVE
Not assigned
CWE
CWE-506
Affected Surface
@antv/g2 5.5.8 and 5.6.8, @antv/g6 5.2.1 and 5.3.1, @antv/x6 3.2.7 and 3.3.7, @antv/graphlib 2.1.4 and 2.2.4, echarts-for-react 3.0.7, 3.1.7, and 3.2.7, timeago.js 4.1.2 and 4.2.2, size-sensor 1.0.4, 1.1.4, and 1.2.4, jest-canvas-mock 2.5.3, 2.6.3, and 2.7.3, Hundreds of additional npm packages under @antv, @lint-md, @openclaw-cn, @starmind, and unscoped packages maintained by atool or prop
On 19 May 2026, the Mini Shai-Hulud / TeamPCP supply-chain campaign expanded into the AntV ecosystem with a rapid npm publish wave against the atool maintainer account and a smaller set of packages associated with the prop account. Public reporting varies slightly as registry and advisory data continues to settle, but independent sources agree on the core shape: hundreds of malicious npm versions were published in tightly clustered bursts, affecting major data visualization, charting, graph, testing, and utility packages.
The affected package set includes high-usage unscoped packages such as echarts-for-react, timeago.js, size-sensor, jest-canvas-mock, and jest-date-mock, plus core AntV packages including @antv/g2, @antv/g6, @antv/x6, @antv/l7, @antv/s2, @antv/f2, @antv/g, @antv/g2plot, @antv/graphin, @antv/data-set, and @antv/graphlib.
This is not a typosquat. The malicious releases were published through accounts with legitimate publish access, so projects using broad semver ranges could resolve to poisoned versions during a clean install even if maintainers did not explicitly choose those versions.
Advisory coverage
Package-level malware advisory data has started mapping representative affected versions for this wave:
MAL-2026-3973for@antv/g2, affecting5.5.8and5.6.8.MAL-2026-3982for@antv/g6, affecting5.2.1and5.3.1.MAL-2026-3839for@antv/x6, affecting3.2.7and3.3.7.MAL-2026-4026for@antv/graphlib, affecting2.1.4and2.2.4.MAL-2026-4132forecharts-for-react, affecting3.0.7,3.1.7, and3.2.7.MAL-2026-4156fortimeago.js, affecting4.1.2and4.2.2.
The advisory records classify the issue as CWE-506: Embedded Malicious Code and describe the campaign as a Mini Shai-Hulud worm that compromised atool, injected preinstall hooks, stole credentials, and attempted persistence. The details also note GitHub API dead-drop exfiltration into Dune-themed repositories and list stolen data categories such as AWS keys, GitHub tokens, npm tokens, GCP service accounts, Azure credentials, Kubernetes tokens, SSH keys, Docker auth, database connection strings, Stripe keys, and Slack tokens.
Affected packages and projects
The exact package count is still changing across reports because some sources count only newly observed packages while others include the broader campaign. Socket reported 639 compromised package versions across 323 unique packages in the May 19 wave and 1,055 versions across 502 unique packages across the full Mini Shai-Hulud campaign. SafeDep reported 637 malicious versions across 317 packages from atool, while OpenSource Malware reported 324 packages and 645 package-version artifacts when including the second prop maintainer account.
For triage, prioritize packages that combine high download volume with confirmed malicious versions:
echarts-for-react:3.0.7,3.1.7,3.2.7timeago.js:4.1.2,4.2.2size-sensor:1.0.4,1.1.4,1.2.4jest-canvas-mock:2.5.3,2.6.3,2.7.3jest-date-mock:1.0.11,1.1.11,1.2.11@antv/g2:5.5.8,5.6.8@antv/g6:5.2.1,5.3.1@antv/x6:3.2.7,3.3.7@antv/graphlib:2.1.4,2.2.4@antv/scale:0.6.2,0.7.2@antv/util:3.4.11,3.5.11@antv/l7:2.26.10,2.27.10@antv/s2:2.8.1,2.9.1@antv/data-set:0.12.8,0.13.8
Also search lockfiles and package caches for @lint-md/*, @openclaw-cn/*, @starmind/collector-cli, canvas-nest.js, filesize.js, onfire.js, relationship.js, slice.js, word-width, lint-md, mcp-echarts, and mcp-mermaid.
Full affected package-name inventory
The broadest public appendix for the May 19 wave lists 324 package names across the compromised atool and prop npm accounts. Socket reported 323 unique packages, while SafeDep reported the 317-package atool subset. The one-package difference appears to be a scope/counting difference, not a materially different attack. Use the following package-name inventory for lockfile, cache, artifact, and internal-registry searches:
@antv/a8
@antv/adjust
@antv/algorithm
@antv/async-hook
@antv/attr
@antv/ava
@antv/ava-react
@antv/awards
@antv/calendar-heatmap
@antv/chart-linter
@antv/chart-node-g6
@antv/chart-visualization-skills
@antv/ckb
@antv/color-schema
@antv/color-util
@antv/component
@antv/coord
@antv/d3-color
@antv/d3-interpolate
@antv/data-samples
@antv/data-set
@antv/data-wizard
@antv/dipper-component
@antv/dipper-hooks
@antv/dipper-map
@antv/dom-util
@antv/dumi-theme-antv
@antv/dw-analyzer
@antv/dw-random
@antv/dw-transform
@antv/dw-util
@antv/event-emitter
@antv/expr
@antv/f-charts
@antv/f-engine
@antv/f-lottie
@antv/f-my
@antv/f-react
@antv/f-test-utils
@antv/f-vue
@antv/f-wx
@antv/f2
@antv/f2-algorithm
@antv/f2-canvas
@antv/f2-context
@antv/f2-graphic
@antv/f2-my
@antv/f2-react
@antv/f2-site
@antv/f2-vue
@antv/f2-wordcloud
@antv/f2-wx
@antv/f6
@antv/f6-alipay
@antv/f6-core
@antv/f6-element
@antv/f6-hammerjs
@antv/f6-plugin
@antv/f6-ui
@antv/f6-wx
@antv/g
@antv/g-base
@antv/g-camera-api
@antv/g-canvas
@antv/g-canvaskit
@antv/g-compat
@antv/g-components
@antv/g-css-layout-api
@antv/g-css-typed-om-api
@antv/g-device-api
@antv/g-dom-mutation-observer-api
@antv/g-gesture
@antv/g-image-exporter
@antv/g-layout-blocklike
@antv/g-lite
@antv/g-lottie-player
@antv/g-math
@antv/g-mobile
@antv/g-mobile-canvas
@antv/g-mobile-canvas-element
@antv/g-mobile-svg
@antv/g-mobile-webgl
@antv/g-pattern
@antv/g-perf
@antv/g-plugin-3d
@antv/g-plugin-a11y
@antv/g-plugin-annotation
@antv/g-plugin-box2d
@antv/g-plugin-canvas-path-generator
@antv/g-plugin-canvas-picker
@antv/g-plugin-canvas-renderer
@antv/g-plugin-canvaskit-renderer
@antv/g-plugin-control
@antv/g-plugin-css-select
@antv/g-plugin-device-renderer
@antv/g-plugin-dom-interaction
@antv/g-plugin-dragndrop
@antv/g-plugin-gesture
@antv/g-plugin-gpgpu
@antv/g-plugin-html-renderer
@antv/g-plugin-image-loader
@antv/g-plugin-matterjs
@antv/g-plugin-mobile-interaction
@antv/g-plugin-physx
@antv/g-plugin-rough-canvas-renderer
@antv/g-plugin-rough-svg-renderer
@antv/g-plugin-svg-picker
@antv/g-plugin-svg-renderer
@antv/g-plugin-webgl-device
@antv/g-plugin-webgl-renderer
@antv/g-plugin-webgpu-device
@antv/g-plugin-yoga
@antv/g-plugin-zdog-canvas-renderer
@antv/g-plugin-zdog-svg-renderer
@antv/g-shader-components
@antv/g-svg
@antv/g-web-animations-api
@antv/g-web-components
@antv/g-webgl
@antv/g-webgl-compute
@antv/g-webgpu
@antv/g-webgpu-compiler
@antv/g-webgpu-core
@antv/g-webgpu-engine
@antv/g-webgpu-raytracer
@antv/g-webgpu-unitchart
@antv/g2
@antv/g2-brush
@antv/g2-extension-3d
@antv/g2-extension-ava
@antv/g2-extension-plot
@antv/g2-plugin-slider
@antv/g2-ssr
@antv/g2plot
@antv/g2plot-schemas
@antv/g6
@antv/g6-alipay
@antv/g6-cli
@antv/g6-core
@antv/g6-editor
@antv/g6-element
@antv/g6-extension-3d
@antv/g6-extension-react
@antv/g6-lite
@antv/g6-mobile
@antv/g6-pc
@antv/g6-plugin
@antv/g6-plugin-map-view
@antv/g6-plugins
@antv/g6-react-node
@antv/g6-ssr
@antv/g6-wx
@antv/gatsby-theme
@antv/geo-coord
@antv/gi-assets-advance
@antv/gi-assets-algorithm
@antv/gi-assets-basic
@antv/gi-assets-galaxybase
@antv/gi-assets-graphscope
@antv/gi-assets-hugegraph
@antv/gi-assets-janusgraph
@antv/gi-assets-neo4j
@antv/gi-assets-scene
@antv/gi-assets-tugraph
@antv/gi-assets-tugraph-analytics
@antv/gi-assets-xlab
@antv/gi-cli
@antv/gi-common-components
@antv/gi-mock-data
@antv/gi-public-data
@antv/gi-sdk
@antv/gi-sdk-app
@antv/gi-theme-antd
@antv/github-config-cli
@antv/gl-matrix
@antv/gpt-vis
@antv/gpt-vis-ssr
@antv/graphin
@antv/graphin-components
@antv/graphin-graphscope
@antv/graphin-icons
@antv/graphlib
@antv/hierarchy
@antv/infographic
@antv/insight-component
@antv/interaction
@antv/istanbul
@antv/knowledge
@antv/l7
@antv/l7-component
@antv/l7-composite-layers
@antv/l7-core
@antv/l7-district
@antv/l7-draw
@antv/l7-editor
@antv/l7-extension-g-layer
@antv/l7-layers
@antv/l7-leaflet
@antv/l7-map
@antv/l7-mapkit
@antv/l7-maps
@antv/l7-mini
@antv/l7-pass
@antv/l7-react
@antv/l7-renderer
@antv/l7-scene
@antv/l7-source
@antv/l7-three
@antv/l7-utils
@antv/l7plot
@antv/l7plot-component
@antv/larkmap
@antv/layout-gpu
@antv/layout-wasm
@antv/li-aiearth-assets
@antv/li-analysis-assets
@antv/li-core-assets
@antv/li-editor
@antv/li-p2
@antv/li-sam-assets
@antv/li-sdk
@antv/lite-insight
@antv/matrix-util
@antv/mcp-server-antv
@antv/mcp-server-chart
@antv/my-f2
@antv/my-f2-pc
@antv/narrative-text-editor
@antv/narrative-text-schema
@antv/narrative-text-vis
@antv/path-util
@antv/react-g
@antv/s2
@antv/s2-react
@antv/s2-react-components
@antv/s2-ssr
@antv/s2-vue
@antv/sam
@antv/scale
@antv/semantic-release-pnpm
@antv/smart-color
@antv/stat
@antv/t8
@antv/thumbnails
@antv/thumbnails-component
@antv/torch
@antv/translator
@antv/util
@antv/vendor
@antv/vis-predict-engine
@antv/webgpu-graph
@antv/word-scale-chart
@antv/wx-f2
@antv/x6
@antv/x6-angular-shape
@antv/x6-common
@antv/x6-components
@antv/x6-geometry
@antv/x6-plugin-clipboard
@antv/x6-plugin-dnd
@antv/x6-plugin-export
@antv/x6-plugin-history
@antv/x6-plugin-keyboard
@antv/x6-plugin-minimap
@antv/x6-plugin-scroller
@antv/x6-plugin-selection
@antv/x6-plugin-snapline
@antv/x6-plugin-stencil
@antv/x6-plugin-transform
@antv/x6-react
@antv/x6-react-components
@antv/x6-react-shape
@antv/x6-vector
@antv/x6-vue-shape
@antv/x6-vue3-shape
@antv/xflow
@antv/xflow-core
@antv/xflow-diff
@antv/xflow-extension
@antv/xflow-hook
@lint-md/cli
@lint-md/core
@lint-md/parser
@openclaw-cn/cli
@openclaw-cn/feishu
@openclaw-cn/libsignal
@openclaw-cn/toutiao-ops
@starmind/collector-cli
ai-figure
amapcn
ast-plugin
babel-plugin-version
boring-avatars-vanilla
byte-parser
canvas-nest.js
echarts-for-react
filesize.js
fixed-round
gantt-for-react
jest-canvas-mock
jest-date-mock
jest-electron
jest-expect
jest-less-loader
jest-random-mock
jest-url-loader
limit-size
lint-md
lint-md-cli
mcp-echarts
mcp-mermaid
miz
onfire.js
openclaw-cn
react-adsense
relationship.js
ribbon.js
size-sensor
slice.js
timeago-react
timeago.js
uri-parse
word-width
xmorse
How the payload executes
The malicious tarballs use install-time execution. Multiple reports identify a root-level obfuscated index.js payload and a lifecycle hook similar to:
{
"scripts": {
"preinstall": "bun run index.js"
}
}
Many packages also include an optionalDependencies entry named @antv/setup that points to a github: dependency under antvis/G2, such as github:antvis/G2#1916faa365f2788b6e193514872d51a242876569 or github:antvis/G2#7cb42f57561c321ecb09b4552802ae0ac55b3a7a. Because npm executes lifecycle hooks for Git dependencies, this gives the attacker a second execution path during install.
This matters because package scanners that alert minutes after publication are still operating after the most dangerous moment. If a developer workstation or CI runner installed the package during the window, the payload already executed before the package could be flagged.
Payload capabilities
The payload is a heavily obfuscated Bun-based JavaScript stealer. Across Socket, Aikido, SafeDep, StepSecurity, and package-advisory reporting, the capabilities include:
- Reading GitHub Actions runner process memory to recover masked CI/CD secrets in plaintext.
- Collecting environment variables and local credential files for GitHub, npm, AWS, Azure, GCP, Kubernetes, Vault, Docker, SSH, databases, and cloud deployment tooling.
- Exfiltrating data through
t[.]m-kosche[.]comusing a fake OpenTelemetry-style path, and through GitHub API dead drops when valid GitHub tokens are available. - Creating Dune-themed public GitHub repositories with campaign markers such as
Shai-Hulud: Here We Go Againor reversed variants likeniagA oG eW ereH :duluH-iahS. - Validating stolen npm tokens, enumerating publishable packages, injecting the malware, bumping versions, and republishing to spread the worm.
- Planting persistence in developer tooling, including
.vscode/tasks.jsonand.claude/settings.json, so package removal alone may not remove the foothold.
SafeDep’s analysis also reports additional persistence and abuse paths in observed samples, including GitHub Actions workflow injection, Sigstore/Fulcio/Rekor interactions, AI agent hook persistence, and system daemon persistence named kitty-monitor. Treat hosts that installed affected versions as potentially compromised, not merely as package-inventory findings.
Indicators of compromise
Package and dependency markers:
preinstallor related lifecycle hooks runningbun run index.js- Root-level
index.jspayloads added to package tarballs @antv/setupoptional dependency entries- GitHub dependency references to
antvis/G2commits such as1916faa365f2788b6e193514872d51a242876569or7cb42f57561c321ecb09b4552802ae0ac55b3a7a
Network and GitHub markers:
t[.]m-kosche[.]comhttps://t[.]m-kosche[.]com:443/api/public/otel/v1/traces- GitHub repositories with Dune-themed names such as
harkonnen-melange-742orsayyadina-stillsuit-852 - Repository descriptions or search strings containing
Shai-Hulud: Here We Go AgainorniagA oG eW ereH :duluH-iahS results/results-*.jsonfiles in suspicious public repositories
Persistence markers:
- Unexpected
.vscode/tasks.jsonentries withrunOn: "folderOpen" - Unexpected
.claude/settings.json,.claude/setup.mjs, or.claude/index.js - Suspicious systemd user services or macOS LaunchAgents named
kitty-monitoror similar GitHub token monitor services - Unexpected GitHub Actions workflows named
Run Copilotor files resembling.github/workflows/codeql.ymlthat dump secrets to artifacts
Remediation
Search source repositories, lockfiles, package-manager caches, CI logs, and built artifacts for the affected package names and versions. Do not rely only on package.json; semver ranges may have resolved to malicious versions during clean installs.
If an affected version installed on a workstation or runner, rotate credentials from a known-clean system. Prioritize npm publish tokens, GitHub PATs and Actions secrets, cloud keys, Kubernetes service account tokens, Vault tokens, SSH keys, Docker registry credentials, database credentials, and deployment secrets available to that host.
Remove persistence before assuming containment. Check .vscode, .claude, .github/workflows, user systemd services, and macOS LaunchAgents for suspicious files. Then audit npm publishing history and GitHub Actions runs for unexpected publishes, OIDC token usage, new workflows, or GitHub repositories created with user or organization tokens.
For prevention, apply a dependency cooldown window for newly published npm versions, block install scripts in untrusted contexts where practical, require lockfile review before clean CI installs, and restrict GitHub Actions OIDC permissions to workflows that truly need publishing. This wave again shows that valid maintainer accounts and valid provenance are not enough to prove a package is safe.
References
- Socket: Active Supply Chain Attack Compromises @antv Packages on npm
- Aikido: Mini Shai-Hulud strikes again
- SafeDep: Mini Shai-Hulud Strikes Again
- StepSecurity: Mass npm Supply Chain Attack Hits the AntV Ecosystem
- OpenSource Malware: TeamPCP compromises npm maintainer with over 540 packages
- CWE-506 Embedded Malicious Code