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-3973 for @antv/g2, affecting 5.5.8 and 5.6.8.
  • MAL-2026-3982 for @antv/g6, affecting 5.2.1 and 5.3.1.
  • MAL-2026-3839 for @antv/x6, affecting 3.2.7 and 3.3.7.
  • MAL-2026-4026 for @antv/graphlib, affecting 2.1.4 and 2.2.4.
  • MAL-2026-4132 for echarts-for-react, affecting 3.0.7, 3.1.7, and 3.2.7.
  • MAL-2026-4156 for timeago.js, affecting 4.1.2 and 4.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.7
  • timeago.js: 4.1.2, 4.2.2
  • size-sensor: 1.0.4, 1.1.4, 1.2.4
  • jest-canvas-mock: 2.5.3, 2.6.3, 2.7.3
  • jest-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[.]com using 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 Again or reversed variants like niagA 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.json and .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:

  • preinstall or related lifecycle hooks running bun run index.js
  • Root-level index.js payloads added to package tarballs
  • @antv/setup optional dependency entries
  • GitHub dependency references to antvis/G2 commits such as 1916faa365f2788b6e193514872d51a242876569 or 7cb42f57561c321ecb09b4552802ae0ac55b3a7a

Network and GitHub markers:

  • t[.]m-kosche[.]com
  • https://t[.]m-kosche[.]com:443/api/public/otel/v1/traces
  • GitHub repositories with Dune-themed names such as harkonnen-melange-742 or sayyadina-stillsuit-852
  • Repository descriptions or search strings containing Shai-Hulud: Here We Go Again or niagA oG eW ereH :duluH-iahS
  • results/results-*.json files in suspicious public repositories

Persistence markers:

  • Unexpected .vscode/tasks.json entries with runOn: "folderOpen"
  • Unexpected .claude/settings.json, .claude/setup.mjs, or .claude/index.js
  • Suspicious systemd user services or macOS LaunchAgents named kitty-monitor or similar GitHub token monitor services
  • Unexpected GitHub Actions workflows named Run Copilot or files resembling .github/workflows/codeql.yml that 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