
Node-gyp Supply Chain Compromise: A Self-Propagating npm Worm That Hides in binding.gyp
Snyk documents an active npm supply-chain attack ("Node-gyp Supply Chain Compromise - June 2026") that abuses binding.gyp's GYP command-expansion syntax (<!(...)) to trigger arbitrary code execution via node-gyp during npm install, bypassing detection tools that only monitor preinstall/postinstall scripts. The worm, tracked as "Phantom Gyp"/"Miasma" (a Shai-Hulud descendant) by StepSecurity, has hit 57 packages including autotel-* and @vapi-ai/server-sdk, harvests npm/GitHub/AWS/GCP/Azure/Vault/Kubernetes credentials via a multi-stage obfuscated Bun-based loader, injects GitHub Actions workflows for persistence, and self-propagates by republishing from compromised maintainer accounts.
What it is
An active npm supply-chain worm ("Phantom Gyp" / "Miasma," a Shai-Hulud descendant), tracked by Snyk as Node-gyp Supply Chain Compromise – June 2026, spanning 57 packages and hundreds of malicious versions rated Critical / Embedded Malicious Code. It executes at install time via a weaponized binding.gyp rather than lifecycle scripts. Confirmed live victims include @vapi-ai/server-sdk and the autotel* family; the worm harvests npm/GitHub/AWS/GCP/Azure/Vault/Kubernetes credentials.
How it works
A package ships a binding.gyp but no prebuilt binary, so npm invokes node-gyp rebuild on its own — no preinstall/postinstall/install/prepare entry, and the package does not even set "gypfile": true. GYP's <!(...) command-expansion syntax runs a shell command during the configure phase. The 157-byte binding.gyp (byte-identical across @vapi-ai/server-sdk@1.2.2 and autotel@3.4.3) uses <!(node index.js ...) with "type": "none" so nothing compiles — running index.js is the whole point; output goes to /dev/null and an echo stub.c keeps gyp happy. The triggered index.js is a 4.5 MB obfuscated loader: a ROT-14 eval over a ~1.3M-entry char-code array, an AES-128-GCM self-decrypting layer (createDecipheriv, hardcoded key/IV/auth tag), and a 907-byte Bun runtime loader that detects OS/arch. After landing it injects GitHub Actions workflows for persistence and republishes from any reachable maintainer account.
Preconditions / Affected
Any npm install that resolves a malicious version — via lockfile, exact pin, or transitive dependency — is exposed; script-only scanners will not flag it. Most of the 57 packages cluster under the single npm account jagreehal, which owns all 25 autotel/autotel-* packages plus the @jagreehal/* scope and awaitly. Inflated versions (e.g. autotel-mcp up to 29.0.1; autotel publishing both 2.26.4 and 3.4.3 within one minute on June 4) are worm-generated republishes, not real releases.
What to do
- Audit lockfiles and transitive resolutions for the affected versions — do not rely on
npm install <pkg>pulling a clean release. - Treat any resolution of a malicious version as credential compromise: rotate npm, GitHub, AWS, GCP, Azure, Vault, and Kubernetes secrets, and check for injected GitHub Actions workflows.
- Consult the Snyk incident page for the full, continuously updated package/version list.
Caveats
A clean latest dist-tag is not evidence of safety: for at least some packages the maintainer re-pointed latest to a clean build (e.g. autotel latest → 3.4.2), but the malicious versions were not unpublished — autotel@3.4.3 and autotel@2.26.4 still return live tarballs. Snyk decoded the loader statically (no execution) and confirmed only the outer layers; the fetched text cuts off mid-description of the Bun stage, so downstream payload behavior is not fully detailed here.
Organisations named here
- autotel
- binding.gyp
- Shai-Hulud
Technologies named here
- npm
- node-gyp
- GitHub Actions
- Bun
- Node.js
- @vapi-ai/server-sdk
- StepSecurity
- Snyk
Techniques named here
- Phantom Gyp
Threat actors named here
- Miasma
Related in Supply Chain


