
The npm registry spent the first week of June under sustained attack. Between June 1 and June 5, a self-propagating worm family called Miasma hit in three waves, compromising 57 packages, pushing over 286 malicious versions, and forging supply chain attestations that passed Sigstore signature verification. The attack introduced a technique called “Phantom Gyp” that sidesteps one of the most widely recommended developer defenses. If your CI pipeline installed @vapi-ai/server-sdk or ai-sdk-ollama after June 3, treat the environment as compromised and rotate credentials now.
Three Waves in Five Days
The Miasma campaign — a descendant of the Shai-Hulud worm family active since late 2025 — unfolded in rapid succession. On June 1, a compromised Red Hat employee’s GitHub account was used to push commits triggering GitHub Actions OIDC tokens, infecting 32 @redhat-cloud-services packages in 72 seconds. Two days later came the Phantom Gyp wave: 57 packages across 286+ malicious versions in under two hours, starting with @vapi-ai/server-sdk (408,000 monthly downloads) at 23:30 UTC. By June 5, a concurrent campaign called IronWorm — a Rust-built variant carrying an eBPF rootkit — hit 37 more packages, and a new Miasma variant pushed malicious commits into Microsoft’s Azure GitHub organizations, forcing Microsoft to disable 73 repositories.
How Phantom Gyp Works (And Why ––ignore-scripts Doesn’t Stop It)
The security community’s standard advice for supply chain hygiene has long included running npm install --ignore-scripts. Phantom Gyp makes that advice incomplete.
When npm encounters a binding.gyp file in a package, it automatically invokes node-gyp rebuild — interpreting the file as a signal that the package contains native C/C++ add-ons. This happens entirely outside the lifecycle script system that --ignore-scripts blocks. The attacker shipped a 157-byte malicious binding.gyp that uses gyp’s own command substitution syntax to embed a shell command, executing attacker-controlled code while returning a fake source filename so the build shows no errors.
The payload runs in four obfuscated stages, including a ROT cipher and AES-128-GCM encryption layer, before downloading the Bun JavaScript runtime to a temporary /tmp/b-* directory in under a second. Executing the final stage under Bun rather than Node.js specifically evades security tooling that monitors Node.js process activity.
A Worm That Signs Its Own Infections
What separates Miasma from a conventional credential stealer is what it does after stealing your tokens. Using harvested npm tokens, the worm enumerates every package the compromised maintainer owns and republishes backdoored versions with the Phantom Gyp payload injected. Then it uses stolen GitHub Actions OIDC tokens — issued by legitimate workflows before the compromise — to generate SLSA provenance attestations through Sigstore. The result: infected packages that pass npm audit signatures and cosign verify.
This is the uncomfortable lesson from June 2026: signed attestations are not sufficient to prevent pipeline hijacking. SLSA provenance verifies that a package was built by a GitHub Actions workflow in a named repository. It does not verify that the person who triggered that workflow was authorized to do so, or that the repository wasn’t already compromised. The trust model the ecosystem is building toward has real limits.
What It’s After
The credential harvest is purpose-built for CI/CD environments. Miasma targets npm tokens, GitHub personal access tokens, AWS credentials, GCP service account files, Azure tokens, HashiCorp Vault tokens, Kubernetes service account configurations, and SSH keys. IronWorm extends the target list to AI API keys — OpenAI, Anthropic, Google Gemini, and Cursor credentials. Exfiltration routes through 236 GitHub repositories controlled by the attacker, with credentials uploaded as encrypted JSON files.
Are You Affected?
If you installed any of the following after June 3, assume the worst: @vapi-ai/server-sdk, ai-sdk-ollama, or any package from the autotel, awaitly, executable-stories, node-env-resolver, or wrangler-deploy families. Clean version pins are @vapi-ai/server-sdk@0.11.0 and ai-sdk-ollama@0.13.0 or earlier.
Check your CI logs for these indicators of compromise:
- Unexpected
node-gyp rebuildinvocations - A
/tmp/b-*directory (the Bun runtime download) - Anomalously large
index.jsfiles (several megabytes) - curl requests fetching a Bun binary during install
What Actually Defends Against This
The unglamorous answer is your lockfile. According to Snyk’s analysis, when Miasma republished packages under existing version numbers with injected content, the SHA-512 integrity hashes in committed package-lock.json files would not match — and npm ci fails before executing any code. Teams running npm install without a lockfile, or ignoring lockfile integrity, had no automated protection.
Beyond the lockfile, StepSecurity recommends auditing why any package in your dependency tree has a binding.gyp — most pure-JavaScript packages have no legitimate reason for one. Egress filtering in CI catches the Bun runtime download. Restricting OIDC token permissions (removing id-token: write from workflows that don’t need it) limits the SLSA forgery vector. Scoped npm automation tokens with minimal publish rights contain the blast radius if a maintainer account is compromised.
The Miasma campaign will not be the last to exploit the gap between provenance verification and actual security. The ecosystem’s instinct to layer more attestation on top of the package registry is correct — but only as one component of a defense strategy that still starts with something as basic as committing your lockfile and running npm ci.













