On June 3, 2026, at 23:30 UTC, an attacker published a malicious version of @vapi-ai/server-sdk, the official Vapi.ai voice AI server SDK downloaded more than 408,000 times per month. Within one hour, 50+ additional packages had been compromised across multiple maintainer accounts. When StepSecurity's researchers traced the attack, they found a technique they had not seen used at this scale before: instead of the postinstall or preinstall scripts that security tools watch, the attacker used a 157-byte binding.gyp file to trigger code execution during npm install.
Most security scanners did not catch it.
TL;DR: The Miasma worm compromised 57 npm packages including @vapi-ai/server-sdk (408K+ monthly downloads) using Phantom Gyp, a binding.gyp file that executes during npm install without touching package.json scripts. Most security scanners miss it. Five controls reduce exposure: audit binding.gyp in your dependency tree, pin lockfile hashes, network-isolate installs, use tools that check native build files, and treat unexpected install output as a red flag.
What happened
StepSecurity named the malware family Miasma based on its self-spreading behavior. The June 3 campaign was its third wave. Two days earlier, Miasma had compromised 32 packages under the @redhat-cloud-services npm namespace. On June 3 it returned, first hitting @vapi-ai/server-sdk, then spreading to packages maintained by jagreehal, including ai-sdk-ollama (120,000+ monthly downloads), and then to packages across the autotel, awaitly, executable-stories, node-env-resolver, and wrangler-deploy ecosystems.
The attacker did not target a single maintainer. They compromised one account, stole credentials from that environment, and used those credentials to access and publish malicious versions of every package that maintainer touched. The worm's self-spreading behavior comes from this cycle: compromise a package, execute during install, steal credentials, use credentials to compromise more packages.
The exfiltration path was GitHub. The malware created repositories on the account liuende501 with names like nemean-hydra-34343, then uploaded stolen credentials as encrypted JSON files to a results/ directory. The repo descriptions were not subtle: 34 were labeled "Miasma - The Spreading Blight" and 195 carried the reversed string "niagA oG eW ereH :duluH-iahS", which reads "Shai-Hulud: Here We Go Again", a direct reference to StepSecurity's blog post about the previous RedHat Cloud Services compromise two days earlier.
The full list of compromised packages included @vapi-ai/server-sdk versions 0.11.1, 0.11.2, 1.2.1, 1.2.2, and ai-sdk-ollama versions 0.13.1, 1.1.1, 2.2.1, 3.8.5, among 57 packages total.
The Phantom Gyp technique
The reason this attack bypassed conventional tools is worth understanding, because the technique will be replicated.
When you run npm install, Node.js package tooling checks package.json for lifecycle scripts: preinstall, install, postinstall. Security tools that scan for malicious install scripts focus here, flagging packages that run arbitrary shell commands in those hooks.
binding.gyp is a different mechanism. It is a configuration file used by node-gyp, the tool that compiles native Node.js addons from C/C++ source. When binding.gyp is present in a package, the npm installer triggers a build process that executes the file's contents. Critically, this happens whether or not the package actually has native code to compile, and whether or not there are any package.json lifecycle scripts.
Miasma's payload was a 157-byte binding.gyp file with no legitimate native module purpose. It was there purely to execute code on install. Because it did not appear in package.json, most scanners did not flag it.
The technique requires node-gyp to be present in the environment, which it is by default in most Node.js development setups. The attack does not work in environments where native module compilation has been explicitly disabled.
Why AI SDK packages are high-value targets
The Miasma campaign specifically hit AI developer tools: voice AI SDKs, AI coding assistants, AI workflow frameworks. This is not a coincidence.
Teams using AI SDKs have the same npm supply chain exposure as any other Node.js project, but credential theft from an AI SDK environment yields more valuable results than credential theft from a typical web app. An AI development environment typically contains:
- Production API keys for paid AI services (Anthropic, OpenAI, Google, etc.)
- Cloud credentials for the infrastructure running the AI application
- Database credentials for data the AI processes
- CI/CD tokens with publish access to the team's own packages
Stolen AI API keys enable an attacker to run unlimited inference at the victim's cost, access data submitted to the AI service, and potentially use the API to generate harmful content attributed to the victim's account. The $500/month AI API budget becomes the attacker's resource. This is a higher-value target than a typical web application's credentials.
5 controls that limit exposure
None of these require changing your stack. They are policy and tooling decisions.
1. Audit binding.gyp in your dependency tree. Run this after every npm install:
find node_modules -name "binding.gyp" | while read f; do
pkg=$(echo "$f" | sed 's|node_modules/||;s|/binding.gyp||')
echo "$pkg: $(cat "$f" | head -3)"
done
Any binding.gyp file in a package that does not have legitimate native module code (C/C++ source files in the same package) is a red flag. Packages like canvas, sharp, bcrypt, and node-sass have legitimate binding.gyp files. An AI SDK with no .cc or .cpp files should not have one.
2. Pin exact versions in your lockfile and verify hashes. A package-lock.json with integrity hashes pins each package to a specific published artifact. When an attacker publishes a malicious version, installing with npm ci (not npm install) against a verified lockfile prevents installing the new version. Commit your lockfile. Never run npm install in production without reviewing what changed in the lockfile first.
3. Run npm install in a network-isolated environment for sensitive projects. If the install process cannot make outbound network requests, credential exfiltration fails even if malicious code executes. CI/CD environments with network egress restrictions contain the blast radius. For local development this is impractical, but production dependency installs should happen in restricted environments.
4. Use a supply chain security tool that checks native build files. Tools like Socket.dev and StepSecurity's OSS Package Security explicitly analyze binding.gyp and other non-standard execution vectors. A scanner that only checks package.json scripts would have missed this attack. Run a scanner on every npm install in your CI pipeline.
5. Treat unexpected npm install output as a red flag. npm install should not produce unexpected network requests, create files outside node_modules, or output strings that look like base64-encoded data. If your CI logs show unexpected activity during install, stop the pipeline and investigate before deploying.
What to do if you installed a compromised version
If you installed any of the compromised packages in the June 3 Miasma campaign, treat the environment as compromised.
Rotate all credentials immediately. Any API key, cloud credential, database password, or CI/CD token that existed in the environment where the install ran should be considered stolen. This includes AI API keys, which attackers will use to generate inference costs or access your data. Rotation is not optional.
Audit recent activity on rotated credentials. Check for API calls made in the hours after the install, particularly from unusual IP addresses or at unusual times. For AI API keys, check for inference calls you did not make.
Audit your own published packages. If you maintain npm packages and have credentials for those in the compromised environment, check whether malicious versions were published under your namespace. Review your npm publish history.
Identify the malicious packages and remove them. Run npm ls to see which versions of which packages are installed. Cross-check against the StepSecurity compromised package list. Remove and reinstall from a clean lockfile.
For a structured response process, the AI vendor security incident response guide covers the same loop applied to vendor-side incidents.
How fast it spread: the two-hour window
The speed of the June 3 campaign is worth understanding for incident response planning. The attacker worked faster than most security teams can respond.
| Time (UTC) | Event |
|---|---|
| Jun 3, 23:30 | First malicious @vapi-ai/server-sdk version published |
| Jun 3, ~00:00 | Worm uses stolen credentials to access jagreehal account |
| Jun 3, ~00:30 | 50+ packages across jagreehal namespace published with malicious versions |
| Jun 3, ~01:00 | autotel, awaitly, executable-stories, node-env-resolver, wrangler-deploy families hit |
| Jun 3, ~01:00 | 236 GitHub dead-drop repositories created on liuende501 account |
| Jun 4 | StepSecurity publishes incident report; maintainers notified |
The gap between first compromise and public disclosure was approximately two to four hours. Any team that ran npm install against an affected package during that window, without lockfile pinning, would have installed the malicious version and had credentials exfiltrated before any public warning existed.
This is the core challenge of supply chain attacks: the attacker controls the timeline. Defense depends on either having controls in place before the attack (lockfile pinning, network isolation, binding.gyp monitoring) or detecting the compromise through behavioral signals (unexpected network calls during install) rather than waiting for a public disclosure.
The broader pattern
Miasma is not the first worm to target the npm registry, and it will not be the last. The Phantom Gyp technique, now that it is documented and proven effective, will be copied. The next variant may use a different non-standard execution mechanism, gyp.js, .node-pre-gyp, or the install field in binding.gyp that triggers npm scripts indirectly.
The governance implication is that install-time security scanning needs to cover more than package.json. The full attack surface for a malicious npm package includes lifecycle scripts, native build files, postinstall binaries, and any mechanism that causes the installer to execute code before a human reviews the package's actual behavior.
For teams using AI coding tools, AI SDKs, and agent frameworks, the dependency tree is growing faster than the security review process can keep up. A team that installs an AI SDK without checking what that SDK's own dependencies do during install is accepting risk it has not measured.
Checklist
- Searched lockfile for all 57 compromised package names from the June 3 campaign
- Rotated all credentials if any compromised version was installed
- Added
binding.gypaudit to CI pipeline (check for unexpected native build files) - Confirmed
npm ci(notnpm install) is used in production builds against committed lockfile - Reviewed supply chain security tooling to ensure native build files are in scope
- Added npm install output monitoring to CI (unexpected network calls fail the build)
- For package maintainers: reviewed own publish history for unauthorized versions
