← all case studies Case study · npm · worm

How CyberXYZ caught
Mini Shai-Hulud on day zero.

One stolen npm token poisoned 323 packages across the AntV namespace in 27 minutes, with a credential harvester wired to run before any user script. No CVE existed. Here is the timeline, and the moment the firewall blocks it.

Live attack · case study

The Mini Shai-Hulud worm,
and where the firewall stops it.

Between 01:39 and 02:06 UTC on May 19, 2026, a single stolen npm publish token pushed malicious versions of dozens of packages across the AntV namespace and several widely used test libraries. Each one ran a credential harvester before any user script. No CVE existed. Here is the timeline, and the moment CyberXYZ blocks it.

323packages poisoned
639malicious versions
27 minpublish window
6credential stores targeted
  1. 01:39 UTC

    A stolen publish token goes live

    A single compromised npm publish token starts shipping. The first trigger package, jest-canvas-mock@2.5.3, looks ordinary, but it carries an optionalDependencies entry pointing at a raw GitHub commit hash.

  2. 01:39 UTC · commit-level signal fires

    A phantom commit drops the payload

    The referenced commit (github:antvis/G2#1916faa…) exists on no branch. npm resolves it, downloads it, and runs its prepare script on install. CyberXYZ reads the reference at the commit level and flags a dropper that exists only to deliver code.

  3. 01:44 UTC

    A second trigger lands

    size-sensor@1.0.4 ships with the same phantom-commit technique. Two unrelated, widely used packages now both carry the dropper.

  4. 01:56 UTC · script-scan signal fires

    The namespace is flooded

    A burst of 14 packages publishes at once, including @antv/g6, @antv/scale, timeago.js and echarts-for-react. Each embeds a roughly 500 KB obfuscated index.js wired to "preinstall":"bun run index.js", so it runs before any user script can inspect it.

  5. the verdict

    BLOCK, at install time

    Across every surface, installing any affected version through the proxy returns 403, CI gates fail, the editor flags it. The harvester never runs. No advisory needed, the lifecycle hook and the phantom commit are signal enough.

  6. on execution (had it run)

    What it would have stolen

    The payload enumerates the environment, hits the AWS metadata endpoint at 169.254.169.254, and harvests GitHub, AWS, GCP, Kubernetes, Vault and npm credentials, then exfiltrates them to t.m-kosche.com disguised as OpenTelemetry traces on port 443.

  7. the novel part

    Sigstore provenance forgery

    On a CI runner the payload mints a fresh GitHub Actions OIDC token and signs through public Sigstore, producing a package that appears to carry a valid provenance badge with no genuine link to the project. A signature alone is no longer proof.

  8. 02:22 UTC · 43 minutes in

    The first public disclosure lands

    Endor Labs publishes the initial write-up; Socket Threat Research later counts 639 malicious versions across 323 packages in this wave alone. By then, every install had already been blocked at the door.

Caught at install

Not a takedown after the fact.
The receipt.

The moment the first poisoned version hit the proxy, every runner and laptop got the same verdict, on the lifecycle hook, before the harvester could touch a single credential.

app.cyberxyz.io/proxy/findings
Search package, IP, OS…All typesAll risk levels live
May 19, 01:39 UTCnpmjest-canvas-mock@2.5.3BLOCKEDgha/runner CI/CD LinuxinternalCRITICAL
May 19, 01:44 UTCnpmsize-sensor@1.0.4BLOCKEDgitlab/runner CI/CD LinuxinternalCRITICAL
May 19, 01:56 UTCnpm@antv/g6· flagged buildBLOCKEDgha/runner CI/CD LinuxinternalCRITICAL
May 19, 01:56 UTCnpmtimeago.js· flagged buildBLOCKEDdev laptop DEV macOSinternalCRITICAL
May 19, 01:56 UTCnpmecharts-for-react· flagged buildBLOCKEDgha/runner CI/CD LinuxinternalCRITICAL

fig. 01 · proxy findings · every poisoned version blocked the moment it appeared

app.cyberxyz.io/notifications/15208
notification #15208May 19 · 01:39 UTC

Blocked install: jest-canvas-mock@2.5.3 on gha/runner · GitHub Actions

0/10
RISK SCORE critical · verdict block
SIGNALS
BLAST RADIUS0 packages · 0 malicious versions
jest-canvas-mock@2.5.3size-sensor@1.0.4@antv/* · 40 packages
STATEMENTEcosystem npm · decision block (confidence 1.0) · stolen publish token · no safe version, rotate credentials
SIGNALS · 4 of 4 fired
01Script & Tarball ScantriggeredCRITICAL

A ~500 KB obfuscated index.js is wired to a preinstall hook (bun run index.js) so it executes before any user script.

02Commit-Level AnalysistriggeredCRITICAL

optionalDependencies resolves to a raw GitHub commit that exists on no branch and only delivers the payload via the prepare hook.

03Credential & Exfil BehaviortriggeredCRITICAL

Reads the AWS metadata endpoint 169.254.169.254 and harvests GitHub, AWS, GCP, Vault and npm secrets, exfiltrating to t.m-kosche.com.

04Live Threat IntelmatchedCRITICAL

Matches the Mini Shai-Hulud campaign: 639 malicious versions across 323 packages in this wave.

PACKAGE CONTEXT
published May 19, 01:39 UTC · trigger package · phantom-commit dropper · preinstall: bun run index.js · exfil to t.m-kosche.com
PACKAGE jest-canvas-mockVERSION 2.5.3ECOSYSTEM npmDECISION BLOCK

fig. 02 · install verdict · the XYZ score and the four signals that fired

Sources: CyberXYZ threat intelligence, with Endor Labs and Socket Threat Research disclosures (May 19, 2026). Full technical write-up on the CyberXYZ blog.