Audit installs since 2026-05-19 01:39 UTC for any version published in the attack window. Rotate GitHub PATs, npm tokens, AWS keys, GCP service account keys, and Vault tokens for any CI runner or developer workstation that touched an affected version.
What happened
Between 01:39 and 02:06 UTC on May 19, 2026, a single stolen npm publish token was used to push malicious versions of 42 packages spanning the AntV organization and several unrelated but widely used npm libraries. A same day update expanded the confirmed list to 633 affected versions across more than 100 package names. The attack reuses the technique cluster the security community first labelled Mini Shai Hulud in earlier 2026 incidents. The payload is purpose built for credential theft on developer and CI environments.
Timeline
-
01:39 UTC
jest-canvas-mock@2.5.3 published. First trigger package. Uses an
optionalDependenciesentry pointing at a raw GitHub commit hash, fetched and executed via thepreparelifecycle hook on install. - 01:44 UTC size-sensor@1.0.4 published using the same phantom commit dropper technique.
-
01:56 UTC
First propagated batch of 14 packages published in a single burst, including @antv/l7-core, @antv/scale, @antv/g6, @antv/g-lite, @antv/path-util, timeago.js, and echarts-for-react. Each carries a roughly 500 KB obfuscated
index.jsat the package root, executed via apreinstallhook. - 02:05 to 02:06 UTC Second batch of 26 packages published, completing the AntV namespace coverage and adding jest-date-mock and other test ecosystem dependencies.
- 02:22 UTC Endor Labs publishes the initial disclosure. Same day update documents 591 additional malicious versions across the broader campaign.
How the attack works
Two execution paths run in parallel. The first set, the trigger packages, declares an optionalDependencies
entry that resolves to a raw GitHub commit reference such as github:antvis/G2#1916faa365f2788b6e193514872d51a242876569.
npm resolves the reference, downloads the commit, and runs its prepare script during install. The commit
is not part of any official branch and exists only to deliver the payload.
The second set, the propagated packages, embeds the payload directly. Each affected version ships an obfuscated
index.js of roughly 500 KB at the package root, wired with "preinstall":"bun run index.js"
so the payload runs under the bun runtime before any user script gets a chance to inspect the install. The
obfuscation routine resolves through globalThis.fc2edea72. The payload includes a
__DAEMONIZED reentrancy guard so it does not respawn itself on repeat installs in the same environment.
Socket Threat Research counted 639 malicious versions across 323 unique packages in the May 19 wave alone,
with the full campaign spanning 1,055 versions across 502 packages when combined with prior Mini Shai Hulud
incidents. The package list extends beyond the AntV namespace into @lint-md/*, @openclaw-cn/*,
@starmind/*, and several unrelated test ecosystem libraries.
What it steals
The collector targets credentials commonly present on developer workstations and CI runners. It enumerates the
process environment, attempts the AWS IMDSv2 metadata endpoint at 169.254.169.254 and the ECS task
metadata endpoint at 169.254.170.2, and parses local config files. The full target list, cross
referenced from Endor Labs and Socket disclosures, includes the following.
- GitHub.
GITHUB_TOKEN,ACTIONS_ID_TOKEN_REQUEST_URL,ACTIONS_ID_TOKEN_REQUEST_TOKEN. The OIDC request token is the key that enables Sigstore badge forgery on CI runners. - AWS.
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_SHARED_CREDENTIALS_FILE,AWS_CONFIG_FILE,AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,AWS_CONTAINER_CREDENTIALS_FULL_URI,AWS_WEB_IDENTITY_TOKEN_FILE,AWS_ROLE_ARN,AWS_ROLE_SESSION_NAME. - GCP. Service account JSON files and gcloud application default credentials.
- Kubernetes.
KUBECONFIGandKUBERNETES_SERVICE_HOSTfor in cluster service account token harvesting. - HashiCorp Vault.
VAULT_ADDR,VAULT_TOKEN,VAULT_AUTH_TOKEN,VAULT_API_TOKEN. - npm. Publish tokens and authentication cookies from
.npmrc.
Collected credentials are AES 256 GCM encrypted with an RSA OAEP wrapped session key, gzipped, and sent to two
parallel exfiltration channels. The primary endpoint is t.m-kosche.com at
https://t.m-kosche.com:443/api/public/otel/v1/traces, masquerading as OpenTelemetry trace data on port 443.
A secondary channel posts to filev2.getsession.org/file/, a Session P2P endpoint over TCP/443.
Stolen credentials are also published into attacker controlled GitHub repositories under a
results/results-<timestamp>-<counter>.json path, with repository names drawn from Dune themed
patterns like sayyadina-stillsuit-852 and atreides-ornithopter-112, and a reversed
marker string niagA oG eW ereH :duluH-iahS embedded in the repo description.
Sigstore provenance forgery
The most novel element is the abuse of the GitHub Actions OIDC token. With ACTIONS_ID_TOKEN_REQUEST_TOKEN
in hand on a CI runner, the payload mints a fresh OIDC identity token and uses it to sign payloads through the
public Sigstore Fulcio instance. The result is a package that appears to carry a valid Sigstore badge while having
no genuine provenance link to the project repository. This breaks an assumption that some downstream verifiers
rely on, namely that the presence of a Sigstore signature implies a verified build path.
Persistence on developer machines
Beyond exfiltration, the payload writes hooks into two configuration files commonly tolerated on developer machines.
~/.claude/settings.jsonis patched to add a custom hook that re executes the payload on every Claude Code session start.~/.vscode/tasks.jsonis patched with a task that triggers on workspace open, ensuring the collector runs every time VS Code is launched against a repository.
Indicators of compromise
| Type | Indicator | Context |
|---|---|---|
| npm package | jest-canvas-mock@2.5.3 |
First trigger package, phantom commit dropper |
| npm package | size-sensor@1.0.4 |
Second trigger package, phantom commit dropper |
| npm package | @antv/l7-core@2.26.10 |
Embedded payload, preinstall hook |
| npm package | @antv/setup |
Dropper alias injected via optionalDependencies |
| npm packages | @antv/g2, @antv/g, @antv/x6, @antv/l7, @antv/s2, @antv/f2, @antv/g2plot, @antv/graphin, @antv/data-set |
Additional AntV namespace packages confirmed affected |
| Primary C2 | t.m-kosche.com |
Exfiltration endpoint masquerading as OpenTelemetry |
| Exfil URL | https://t.m-kosche.com:443/api/public/otel/v1/traces |
Primary credential post target, port 443 |
| Secondary C2 | filev2.getsession.org/file/ |
Session P2P network over TCP/443 |
| git author email | claude@users.noreply.github.com |
Spoofed commit author on phantom drops |
| git commit sha | 1916faa365f2788b6e193514872d51a242876569 |
Phantom commit on antvis/G2 |
| git commit sha | 7cb42f57561c321ecb09b4552802ae0ac55b3a7a |
Phantom commit on antvis/G2 |
| SHA 256 | a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1c |
index.js in @antv/l7-core malicious release |
| preinstall string | "preinstall":"bun run index.js" |
Exact lifecycle hook used by propagated packages |
| obfuscation marker | globalThis.fc2edea72 |
Symbol used to resolve the obfuscated payload entry |
| repo path pattern | results/results-<timestamp>-<counter>.json |
Stolen credential write path inside attacker repos |
| repo description marker | niagA oG eW ereH :duluH-iahS |
Reversed campaign signature (decodes to "Shai-Hulud: Here We Go Again") |
| repo name pattern | sayyadina-*, atreides-*, harkonnen-* |
Dune themed attacker controlled GitHub repos |
Audit your lockfiles and CI logs for any install of the listed packages with publish times after 2026-05-19 01:39 UTC.
Inspect ~/.claude/settings.json and ~/.vscode/tasks.json on developer machines for unexpected
hooks. On any CI runner that ran npm install during the attack window, treat the runner as compromised
and rotate every secret it had access to.
MITRE ATT&CK mapping
- T1195.002 Supply Chain Compromise: Compromise Software Supply Chain
- T1078 Valid Accounts (stolen npm publish token)
- T1546 Event Triggered Execution (preinstall and prepare hooks, VS Code tasks)
- T1552.001 Unsecured Credentials: Credentials In Files
- T1552.005 Unsecured Credentials: Cloud Instance Metadata API
- T1041 Exfiltration Over C2 Channel
- T1556 Modify Authentication Process (Sigstore provenance forgery)
What CyberXYZ shipped in response
Within hours of the Endor Labs disclosure we shipped four changes across the detection engine.
- 28 named packages backfilled into the malicious package corpus with
source_tier=human_verified,attack_type=maintainer_token_compromise, andcampaign_name=mini_shai_hulud_antv_2026_05_19. For the three packages with article confirmed exact versions we setmalicious_versionsprecisely. For the other 25 we setall_versions_malicious=TRUEbecause token compromise means any version could be poisoned until forensics narrow the range. - Six indicators added to the network IOC corpus: the exfiltration domain, the spoofed git author marker, two phantom commit SHAs, and the payload file hash. Commit watchers and tarball scanners now match these on every new publish.
- 2,233 popular npm packages promoted to Tier 2 watchlist coverage. Any npm package with more than one million monthly downloads is now polled every 15 minutes by the watcher, in addition to the universal firehose path. This closes the gap that left widely used packages like jest canvas mock on lower tiers.
- Pub/Sub firehose path restored. The npm publish event stream had accumulated a 42 million message backlog after a relay restart. We seeked the subscription forward, redeployed the Fly poller, and scaled the subscriber drain rate to 500 messages per second. New publishes now reach the scanner within minutes of hitting the npm registry.
Customers running the CyberXYZ proxy are now protected against future install attempts of any affected version. Any matching request is blocked at the edge before the install hook gets to execute.
Recommended actions this week
Immediate. Block the listed packages at your registry proxy or private mirror. If you do not have a proxy in front of npm in CI, add one. Audit lockfiles for any install since 01:39 UTC on May 19. Treat any CI runner that touched an affected version as compromised and rotate every secret it had access to.
Short term. Disable preinstall, postinstall, and prepare hooks on CI runners using npm install --ignore-scripts. For packages that legitimately require install hooks, allow list them explicitly. Inspect ~/.claude/settings.json and ~/.vscode/tasks.json across the fleet for unauthorized hooks.
Long term. Add npm publish token rotation to your maintainer offboarding and incident playbooks. Require Sigstore provenance attestation to include a verified Git commit reference, not only a signature. Treat the presence of a Sigstore badge as a useful signal, not a sufficient one.
Maintainer token compromise is the fastest and most aggressive supply chain attack pattern of 2026. The publish takes minutes. The downstream cleanup takes weeks. Pre publish controls and registry side blocking are the only layers that meet this on its actual timeline. Everything that runs after install is already a response, not a prevention.
The CyberXYZ Supply Chain Firewall enforces malicious package blocks at the proxy layer, so a confirmed indicator becomes an organization wide protection the moment it lands in our corpus. If you want to see how your fleet would have responded to this attack, the team is happy to walk through the detection chain with you.
References
- Endor Labs, Mini Shai Hulud Returns: 42 Malicious npm Packages and Fake Sigstore Badges in AntV Ecosystem Attack
- Socket Threat Research, AntV Packages Compromised: Mini Shai Hulud Returns at Scale
- MITRE ATT&CK, T1195.002 Compromise Software Supply Chain
- MITRE ATT&CK, T1556 Modify Authentication Process
- Sigstore, OIDC signing with Sigstore
- npm documentation, npm scripts and lifecycle hooks