binding.gyp - an npm supply chain worm that abuses node-gyp instead of postinstall — and harvests CI/CD credentials
4 June 2026. StepSecurity reports an active, self-replicating npm worm that does not run via the usual preinstall/postinstall hooks but injects a binding.gyp file: npm then invokes node-gyp, and the attacker uses its shell expansion to execute code silently during npm install. The malware harvests credentials from npm, GitHub, AWS, GCP, Azure, HashiCorp Vault, Kubernetes and RubyGems, injects itself into GitHub Actions workflows and publishes poisoned versions of the victim’s other packages. Developing incident — the list of affected packages is growing.
TL;DR — 90 seconds
- Affected?
npm packages across several maintainer accounts; so far dozens of poisoned versions in families like
autotel-*,awaitly-*,executable-stories-*andnode-env-resolver-*(plus single packages such as@vapi-ai/server-sdk,ai-sdk-ollama). All known malicious versions were published between 3 and 4 June 2026. The list is growing — the authoritative, continuously updated listing is at StepSecurity.- Risk?
Silent code execution during
npm installviabinding.gyp/node-gyp(without apostinstallhook), credential theft (npm/GitHub/AWS/GCP/Azure/Vault/Kubernetes/RubyGems), CI/CD worm propagation and workflow injection.- Immediate action?
Check builds/CI against the known package versions, search dependencies for suspicious
binding.gyp, rotate affected credentials, check CI workflows for injectedsetup-bunsteps.- Recommendation?
Mid-market and enterprise: freeze lockfiles, enforce
--ignore-scriptsin CI, minimise and short-live token scopes, audit recently updated npm dependencies.- Criticality?
high (references the hero badge — actively spreading worm, immediate audit within the 48h window).
What is the problem?
According to StepSecurity (developing incident, as of 3 June 2026), this worm deliberately does not run via the preinstall/postinstall lifecycle hooks in package.json — exactly the place security tools, reviewers and npm’s own audit systems look at. Instead, the attacker places a small binding.gyp file in the package. As soon as npm sees a binding.gyp, it automatically invokes node-gyp to compile a supposed native addon. The attacker abuses the shell expansion in the sources array:
{
"targets": [
{
"target_name": "Setup",
"type": "none",
"sources": ["<!(node index.js > /dev/null 2>&1 && echo stub.c)"]
}
]
}
This silently runs node index.js during install. The scripts block of package.json contains only legitimate build commands; there is no postinstall hook to raise suspicion. According to StepSecurity the binding.gyp is only around 100 bytes — the index.js it triggers, however, is 4.5 to 4.9 MB of obfuscated code.
The malware runs in three stages according to StepSecurity. Stage 1 (obfuscated loader): the root index.js decodes an inner script via a ROT-N Caesar cipher, which then decrypts two AES-128-GCM-encrypted payloads using hardcoded keys. Stage 2 (runtime download): the first payload silently downloads the Bun JavaScript runtime (v1.3.13) from GitHub into a temporary directory — giving the attacker a fast, standalone runtime that leaves few traces in the Node.js process tree. Stage 3 (CI/CD worm): the second payload (~720 KB) is the actual worm and runs via the downloaded Bun runtime.
Who is affected?
| Affected | Not affected | Conditions / aggravating |
|---|---|---|
Projects/CI pulling a poisoned version of a listed package (families autotel-*, awaitly-*, executable-stories-*/eslint-plugin-executable-stories-*, node-env-resolver-* and single packages such as @vapi-ai/server-sdk, ai-sdk-ollama, mountly, wrangler-deploy) | Projects without one of the affected versions in the dependency tree (direct or transitive) | npm install runs without --ignore-scripts; node-gyp available and triggered by the injected binding.gyp |
| Developer machines and CI pipelines with reachable cloud/registry credentials | Pure runtime environments without an npm install step and without reachable secrets | Credentials of npm, GitHub (incl. PATs), AWS (incl. IMDSv2/ECS task role), GCP, Azure (incl. Key Vault), HashiCorp Vault, Kubernetes, RubyGems in reach |
| Maintainer accounts whose tokens are stolen — their own packages get poisoned onward | — | Stolen npm/RubyGems tokens allow publish; GitHub token allows workflow push |
StepSecurity explicitly calls the incident „developing“ — the list of compromised packages and versions is continuously extended. The authoritative, current listing is in the source; we deliberately refrain from presenting a snapshot as complete.
Impact
The core is that code execution happens where the usual detections do not look: not in postinstall, but in the node-gyp build that npm itself triggers when a binding.gyp is present. With that, the attacker bypasses conventional tools according to StepSecurity.
The consequences StepSecurity documents are those of a full CI/CD worm. First, credential harvesting: the malware scans the environment for npm tokens, GitHub tokens and PATs, AWS access keys (including the IMDSv2 and ECS task role endpoints), GCP service account credentials, Azure client secrets and Key Vault contents, HashiCorp Vault tokens (across multiple file paths and the local Vault API), Kubernetes service account tokens, RubyGems API keys and passwords from 1Password CLI, gopass and pass; it also extracts masked secrets from the GitHub Actions runner process memory. Second, workflow injection: using stolen GitHub tokens, the worm modifies CI/CD workflow files in repos the victim can push to and inserts a setup-bun step plus a payload execution step — so it runs on every future CI job. Third, package poisoning: using stolen npm/RubyGems tokens it queries all packages the victim maintains, downloads them, injects the payload and publishes new poisoned versions — this is how the worm jumps from one account to dozens of packages. Fourth, exfiltration: the harvested credentials are encrypted with a hardcoded RSA public key and pushed as „dangling commits“ (commits not reachable from any branch) into attacker-controlled GitHub repos — hard to find through normal repo browsing.
There is no CVE number for this campaign; it is an ongoing supply chain incident, not a single product flaw. That is precisely why the operational severity is high: anyone pulling an affected version potentially loses the keys to their entire cloud and registry landscape.
Mitigation / immediate measures
Note: the following steps are our operational recommendation based on the documented technique — not a vendor-certified guide.
Operational Decision Block
- Act immediately if … one of the listed packages (or one of its families) is in your
package-lock.json/ CI cache, directly or transitively. - Check with priority if … your CI has cloud/registry secrets in reach (npm publish token, GitHub PAT, AWS/GCP/Azure keys, Vault, Kubernetes).
- Pure awareness if … you do not pull any affected version and already enforce
--ignore-scriptsin CI.
Step 1 — harden installs
# Disable node-gyp/script execution on install globally (CI and local)
npm config set ignore-scripts true
# or per run:
npm ci --ignore-scripts
# Note: --ignore-scripts disables the automatic node-gyp execution that this
# binding.gyp technique exploits. Native modules then have to be built
# deliberately and in a controlled way.
Step 2 — audit the dependency tree
# Check whether an affected package family is in the tree (adjust/extend examples
# from the continuously updated StepSecurity list)
npm ls autotel autotel-mcp awaitly node-env-resolver executable-stories-vitest 2>/dev/null
# Search the lockfile for package names
grep -nE "autotel|awaitly|executable-stories|node-env-resolver|@vapi-ai/server-sdk|ai-sdk-ollama" package-lock.json
Step 3 — rotate credentials
If an affected version was installed (locally or in CI), treat as compromised:
- revoke and reissue npm, GitHub (incl. PATs) and RubyGems tokens
- rotate AWS/GCP/Azure keys, review Azure Key Vault
- rotate HashiCorp Vault and Kubernetes service account tokens
- assume password-manager CLI sessions (1Password/gopass/pass) are exposed
Step 4 — check CI workflows
# Check for injected setup-bun steps in workflows
grep -RInE "setup-bun|oven-sh/setup-bun" .github/workflows/
# Review unexpected workflow changes in git history
git log --oneline -- .github/workflows/Detection / verification
IOCs derived directly from the technique documented by StepSecurity.
Find suspicious binding.gyp in dependencies
# List binding.gyp files in the node_modules tree
find node_modules -name binding.gyp
# Check for the characteristic shell expansion in the sources array
grep -RIn "<!(" node_modules --include=binding.gyp
# Suspicious: a very small binding.gyp (~100 bytes) next to a very large index.js (several MB)
find node_modules -name binding.gyp -size -200c
Bun runtime download and traces
According to StepSecurity, stage 2 downloads the Bun runtime (v1.3.13) from GitHub
into a temporary directory. Check points:
- unexpected Bun binaries in temp paths on dev machines / CI runners
- outbound connections to GitHub release assets during npm install
- in workflows: a setup-bun step that does not originate from your repo
Exfiltration pattern
Exfiltration happens, according to StepSecurity, as "dangling commits" into
attacker-controlled GitHub repos (not reachable from any branch).
- check for unusual, unreferenced commits in your own repos
- review the GitHub audit log for unexpected workflow/content pushesOperator guidance
SMEs / Mittelstand
First contain, then rotate. Check lockfiles and CI caches against the affected package names; if a version is in there, treat all tokens reachable from the pipeline as compromised and rotate them. Enforce --ignore-scripts in CI — it disables the automatic node-gyp execution this technique exploits. Build native modules deliberately and in a controlled way afterwards.
Enterprise
Additionally: short-lived, tightly scoped CI credentials (OIDC instead of long-lived keys), egress control on build runners, and an audit that correlates recently updated npm dependencies with workflow changes. The workflow injection is the most dangerous part here — a setup-bun step injected once persists across future CI runs.
Kubernetes / containers
Rebuild build images once an affected version was in the tree; old layers carry the poisoned version onward. Rotate service account tokens, keep IMDS access on build pods restrictive (the malware explicitly targets IMDSv2/ECS task role).
Declarative stacks (NixOS / Talos / Flatcar)
Pin to known-good versions, reproducible rebuild, and the advantage of the declarative track: which npm version flowed into which build and when is provable — exactly what speeds up containing a supply chain incident.
What we did concretely
We treat supply chain worms as a pipeline-discipline question, not a single package. For the stacks we operate this means: --ignore-scripts is the default in CI, native builds run in a controlled way and not incidentally during install; CI credentials are short-lived and tightly scoped; egress on build runners is restricted. As a response to this incident we checked lockfiles and caches against the package families listed by StepSecurity and ran a sweep for binding.gyp files with the characteristic <!( shell expansion.
The lesson from this incident is not „yet another npm worm“ but the shift of the execution point: anyone who pins detection and policy only to preinstall/postinstall does not see a binding.gyp-triggered node-gyp execution. This fits the pattern we described with Shai-Hulud (@antv) and the Miasma/@redhat-cloud-services wave — only that the trigger mechanism is new here. That is why for us: install scripts (including implicit node-gyp builds) are off by default in CI, and token reach is kept as small as possible so that a single compromised install does not open the whole cloud landscape.
Frequently asked questions about the binding.gyp npm worm
Does npm install --ignore-scripts protect against the binding.gyp technique?+
Yes, in practice: the attack uses the automatic node-gyp execution that npm triggers when a binding.gyp is present. --ignore-scripts disables this install execution. Native modules then have to be built deliberately and in a controlled way — that is the price, and it is worth it.
Why do classic postinstall scanners not catch this?+
Because there is no postinstall hook. The scripts block of package.json contains only legitimate build commands; code execution runs via the 100-byte binding.gyp and node-gyp’s shell expansion in the sources array. Tools that only check lifecycle hooks do not see it, according to StepSecurity.
Which packages are affected?+
So far dozens of versions in families like autotel-*, awaitly-*, executable-stories-* and node-env-resolver-* as well as single packages such as @vapi-ai/server-sdk and ai-sdk-ollama, all published between 3 and 4 June 2026. It is a developing incident — the authoritative, current list is at StepSecurity, not here.
Which credentials are at risk if a build was affected?+
According to StepSecurity: npm, GitHub (incl. PATs), RubyGems tokens, AWS keys (incl. IMDSv2/ECS task role), GCP, Azure (incl. Key Vault) credentials, HashiCorp Vault and Kubernetes tokens, and passwords from 1Password CLI/gopass/pass. Additionally, masked secrets are extracted from the GitHub Actions runner memory. In the worst case: rotate everything the pipeline could reach.
How do I tell whether the worm has nested in our CI?+
Check your workflow files for a setup-bun step that does not originate from you (grep -RIn "setup-bun" .github/workflows/) and the git history of the workflows for unexpected changes. According to StepSecurity, the worm injects exactly this step so it runs on every future CI job.
Is there a CVE number or a patch?+
No. This is an ongoing supply chain campaign, not a single product flaw — accordingly there is no CVE and no vendor patch. The „fix“ is operational: avoid affected versions, harden installs, rotate exposed credentials, clean up CI workflows.
Conclusion
This worm is technically no more spectacular than the npm waves before it, but it hits a blind spot: code execution moves from postinstall into the node-gyp build that npm itself triggers on a binding.gyp. Anyone who pins policy and detection only to lifecycle hooks does not see it. The effective answer is unspectacular and well known: install scripts off by default in CI (--ignore-scripts), native builds deliberate and controlled, credentials short-lived and tightly scoped, workflow changes monitored. Because the incident is ongoing, the source governs the package list, not a snapshot. Do not dramatise, but check today — the reach of a single compromised install here extends into the entire cloud and registry landscape.
Sources
We check, mitigate and validate your npm/CI supply chain against worms like the binding.gyp incident.
Dependency and lockfile audit against the affected package families, --ignore-scripts enforcement in CI, token scoping and rotation, workflow integrity checks and egress control on build runners.
Platform operations instead of advice-on-paper: we check, mitigate and validate production pipelines — from the SBOM inventory through the stopgap measure to validation.

![[Translate to English:] Foto von Kai Ole Hartwig.](/fileadmin/_processed_/e/9/csm_ole-neu_73323ad80d.jpeg)

