
Mini Shai-Hulud strikes again: npm worm compromises hundreds of @antv packages
Aikido's malware team documents a new wave of the Mini Shai-Hulud npm worm compromising hundreds of packages including Alibaba's @antv suite, echarts-for-react, and timeago.js. The payload runs via a preinstall hook (bun run index.js), harvests CI/CD, cloud, and SSH credentials, plants persistent backdoors in .vscode/tasks.json and .claude/settings.json, exfiltrates via a disguised telemetry endpoint (t.m-kosche.com), and self-propagates by stealing npm tokens to republish poisoned versions of other packages the victim can publish to — over 2,700 rogue GitHub repos have been created from stolen tokens so far. The article includes a full appendix of affected package/version pairs and mitigation guidance emphasizing package-age gating over post-publish detection.
What it is
Aikido's writeup of the "Mini Shai-Hulud" npm worm wave that began landing on May 19, hitting Alibaba's @antv data-visualization suite (@antv/g2, g6, x6, l7, s2, f2, g, g2plot, graphin, data-set) plus echarts-for-react (~1.1M weekly downloads), timeago.js, size-sensor, and canvas-nest.js. Also affected: packages under @lint-md, @openclaw-cn, @starmind, and unscoped names. Same campaign as the earlier SAP and TanStack waves.
How it works
A root-level index.js is injected into the tarball and run at install via a modified package.json:
"preinstall": "bun run index.js"The payload is heavily obfuscated (string-array lookup tables, runtime decoding, custom decryptor). It also adds a github:-prefixed optional dependency ("exotic dependency") pointing at a repo+commit, giving a second install-time execution path from outside the npm tarball:
{
"optionalDependencies": {
"@antv/setup": "github:antvis/G2#7cb42f57561c321ecb09b4552802ae0ac55b3a7a"
}
}Propagation: after stealing npm tokens, it validates them, enumerates the owner's publishable packages, downloads each tarball, injects the payload + preinstall hook, bumps the version, and republishes — so one broadly-scoped maintainer account seeds the next wave.
Preconditions / Affected
Any project with an affected package in its dependency tree. Because execution is at install time, the code runs on every machine (dev or CI) that installs a poisoned version before any scanner flags it. Note Bun is invoked in the preinstall — but npm also runs lifecycle scripts for the git dependency, so the exotic-dep path fires independently.
What to do
- Do not treat lockfile rollback as sufficient. The payload plants backdoors in
.vscode/tasks.jsonand.claude/settings.json; check and clean these files or the attacker keeps a foothold after the dependency is removed. - Rotate everything the payload targets: GitHub tokens, npm tokens, GitHub Actions OIDC tokens, AWS credentials, Kubernetes service-account files, HashiCorp Vault tokens, SSH/private keys, Docker auth files, and any database connection strings or secrets in env/local files.
- Audit your GitHub account for rogue repos created via stolen tokens (Dune-themed naming, reversed campaign markers — 2,700+ observed campaign-wide).
- Watch build logs for exfiltration disguised as a telemetry URL; note stolen data is encrypted before exfil, so network captures won't yield plaintext.
Caveats
The page text is truncated mid-sentence on C2 infrastructure ("The C2 infras…"), so specific C2 indicators and any full IOC list are not reproduced here. Version numbers of the poisoned releases are not given in the excerpt.
flowchart TD
A[Install poisoned pkg] --> B[preinstall: bun run index.js]
A --> C[github: optional dep executes]
B --> D[Harvest CI/cloud/SSH secrets]
C --> D
D --> E{npm token valid?}
E -->|yes| F[Republish to owned pkgs — worm spreads]
D --> G[Backdoor .vscode + .claude configs]
G -.survives lockfile rollback.-> H[Persistent foothold]Organisations named here
- t.m-kosche.com
Technologies named here
- Aikido Safe Chain
- @antv/g2
- Aikido Devices Protection
- @antv/g6
- @antv/x6
- Aikido Intel
- Aikido malware detection / central feed
- @antv/l7
- @antv/s2
- @antv/f2
- @antv/g
- @antv/g2plot
- @antv/graphin
- @antv/data-set
- echarts-for-react
- timeago.js
- size-sensor
- canvas-nest.js
- @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-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-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-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-brush
- @antv/g2-extension-3d
- @antv/g2-extension-ava
- @antv/g2-extension-plot
- @antv/g2-plugin-slider
- @antv/g2-ssr
- @antv/g2plot-schemas
- @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-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-components
- @antv/graphin-graphscope
- @antv/graphin-icons
- @antv/graphlib
- @antv/hierarchy
- @antv/infographic
- @antv/insight-component
- @antv/interaction
- @antv/istanbul
- @antv/knowledge
- @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-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-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
- @beproduct/nestjs-auth
- @cap-js/db-service
- @cap-js/postgres
- @cap-js/sqlite
- @dirigible-ai/sdk
- @draftauth/client
- @draftauth/core
- @draftlab/auth
- @draftlab/auth-router
- @draftlab/db
- @intercom/intercom-php
- @lint-md/cli
- @lint-md/core
- @lint-md/parser
- @mesadev/rest
- @mesadev/saguaro
- @mesadev/sdk
- @mistralai/mistralai
- @mistralai/mistralai-azure
- @mistralai/mistralai-gcp
- @ml-toolkit-ts/preprocessing
- @ml-toolkit-ts/xgboost
- @openclaw-cn/cli
- @openclaw-cn/feishu
- @openclaw-cn/libsignal
- @openclaw-cn/toutiao-ops
- @opensearch-project/opensearch
- @squawk/airport-data
- @squawk/airports
- @squawk/airspace
- @squawk/airspace-data
- @squawk/airway-data
- @squawk/airways
- @squawk/fix-data
- @squawk/fixes
- @squawk/flight-math
- @squawk/flightplan
- @squawk/geo
- @squawk/icao-registry
- @squawk/icao-registry-data
- @squawk/mcp
- @squawk/navaid-data
- @squawk/navaids
- @squawk/notams
- @squawk/procedure-data
- @squawk/procedures
- @squawk/types
- @squawk/units
- @squawk/weather
- @starmind/collector-cli
- @supersurkhet/cli
- @supersurkhet/sdk
- @tallyui/components
- @tallyui/connector-medusa
- @tallyui/connector-shopify
- @tallyui/connector-vendure
- @tallyui/connector-woocommerce
- @tallyui/core
- @tallyui/database
- @tallyui/pos
- @tallyui/storage-sqlite
- @tallyui/theme
- @tanstack/arktype-adapter
- @tanstack/eslint-plugin-router
- @tanstack/eslint-plugin-start
- @tanstack/history
- @tanstack/nitro-v2-vite-plugin
- @tanstack/react-router
- @tanstack/react-router-devtools
- @tanstack/react-router-ssr-query
- @tanstack/react-start
- @tanstack/react-start-client
- @tanstack/react-start-rsc
- @tanstack/react-start-server
- @tanstack/router-cli
- @tanstack/router-core
- @tanstack/router-devtools
- @tanstack/router-devtools-core
- @tanstack/router-generator
- @tanstack/router-plugin
- @tanstack/router-ssr-query-core
- @tanstack/router-utils
- @tanstack/router-vite-plugin
- @tanstack/solid-router
- @tanstack/solid-router-devtools
- @tanstack/solid-router-ssr-query
- @tanstack/solid-start
- @tanstack/solid-start-client
- @tanstack/solid-start-server
- @tanstack/start-client-core
- @tanstack/start-fn-stubs
- @tanstack/start-plugin-core
- @tanstack/start-server-core
- @tanstack/start-static-server-functions
- @tanstack/start-storage-context
- @tanstack/valibot-adapter
- @tanstack/virtual-file-routes
- @tanstack/vue-router
- @tanstack/vue-router-devtools
- @tanstack/vue-router-ssr-query
- @tanstack/vue-start
- @tanstack/vue-start-client
- @tanstack/vue-start-server
- @tanstack/zod-adapter
- @taskflow-corp/cli
- @tolka/cli
- @uipath/access-policy-sdk
- @uipath/access-policy-tool
- @uipath/admin-tool
- @uipath/agent-sdk
- @uipath/agent-tool
- @uipath/aops-policy-tool
- @uipath/ap-chat
- @uipath/api-workflow-tool
- @uipath/apollo-core
- @uipath/apollo-react
- @uipath/apollo-wind
- @uipath/auth
- @uipath/case-tool
- @uipath/cli
- @uipath/codedagent-tool
- @uipath/codedagents-tool
- @uipath/codedapp-tool
- @uipath/common
- @uipath/context-grounding-tool
- @uipath/data-fabric-tool
- @uipath/docsai-tool
- @uipath/filesystem
- @uipath/flow-tool
- @uipath/functions-tool
- @uipath/gov-tool
- @uipath/identity-tool
- @uipath/insights-sdk
- @uipath/insights-tool
- @uipath/integrationservice-sdk
- @uipath/integrationservice-tool
- @uipath/llmgw-tool
- @uipath/maestro-sdk
- @uipath/maestro-tool
- @uipath/orchestrator-tool
- @uipath/packager-tool-apiworkflow
- @uipath/packager-tool-bpmn
- @uipath/packager-tool-case
- @uipath/packager-tool-connector
- @uipath/packager-tool-flow
- @uipath/packager-tool-functions
- @uipath/packager-tool-webapp
- @uipath/packager-tool-workflowcompiler
- @uipath/packager-tool-workflowcompiler-browser
- @uipath/platform-tool
- @uipath/project-packager
- @uipath/resource-tool
- @uipath/resourcecatalog-tool
- @uipath/resources-tool
- @uipath/robot
- @uipath/rpa-legacy-tool
- @uipath/rpa-tool
- @uipath/solution-packager
- @uipath/solution-tool
- @uipath/solutionpackager-sdk
- @uipath/solutionpackager-tool-core
- @uipath/tasks-tool
- @uipath/telemetry
- @uipath/test-manager-tool
- @uipath/tool-workflowcompiler
- @uipath/traces-tool
- @uipath/ui-widgets-multi-file-upload
- @uipath/uipath-python-bridge
- @uipath/vertical-solutions-tool
- @uipath/vss
- @uipath/widget.sdk
- agentwork-cli
- ai-figure
- amapcn
- ast-plugin
- babel-plugin-version
- boring-avatars-vanilla
- byte-parser
- cmux-agent-mcp
- cross-stitch
- filesize.js
- fixed-round
- gantt-for-react
- git-branch-selector
- git-git-git
- guardrails-ai
- intercom-client
- jest-canvas-mock
- jest-date-mock
- jest-electron
- jest-expect
- jest-less-loader
- jest-random-mock
- jest-url-loader
- lightning
- limit-size
- lint-md
- mbt
- mcp-echarts
- mcp-mermaid
- Mistral AI
- miz
- ml-toolkit-ts
- nextmove-mcp
- onfire.js
- openclaw-cn
- react-adsense
- relationship.js
- ribbon.js
- safe-action
- slice.js
- timeago-react
- ts-dna
- uri-parse
- word-width
- wot-api
- xmorse
- npm
- Bun
- GitHub Actions
- VS Code
- Claude Code
- Aikido Security
- @antv/setup
- tanstack
Techniques named here
- Malicious preinstall lifecycle script
- Obfuscated payload
- Exotic/optional dependency execution
- Credential harvesting
- Encrypted exfiltration disguised as telemetry
- GitHub repo exfiltration fallback
- Persistence via IDE/AI tooling config
- npm worm propagation
Threat actors named here
- Mini Shai-Hulud
Related in Supply Chain


