← Back to blog
supply chainnpmsmall businessremediation

npm install Just Ran Malware on Your Machine. You Didn't Even Know.

A self-propagating worm is using a blind spot in npm's native build system to execute code the moment you install a package. No install scripts. No warnings. Just binding.gyp.

Darius J Davis · June 3, 2026

#You thought install scripts were the problem. They're not the only one.

If you've been paying attention to supply chain security at all, you know that preinstall and postinstall scripts in npm packages are dangerous. They run arbitrary code during installation. Security tools flag them. Developers know to be suspicious of them.

So attackers found another way in.

On June 4, 2026, researchers at StepSecurity and Snyk disclosed an active campaign targeting npm's native build system. Over 50 packages across 280+ malicious versions have been compromised, including @vapi-ai/server-sdk with roughly 400,000 monthly downloads. The attack is nicknamed "Phantom Gyp," and it's part of the Mini Shai-Hulud worm family that has been tearing through the JavaScript ecosystem since March.

The vector is a file called binding.gyp.

#What is binding.gyp and why should you care?

When you install an npm package that includes native C/C++ code (things like database drivers, image processors, cryptography libraries), npm uses a tool called node-gyp to compile that code on your machine. The build instructions live in a file called binding.gyp.

Here's the important part: node-gyp processes binding.gyp automatically during installation. No install script needed. No preinstall or postinstall hook in package.json. Just the presence of binding.gyp in the package is enough to trigger execution.

And most security scanners don't check it.

The tools your team relies on to catch malicious packages are inspecting package.json for suspicious scripts. They're looking at the JavaScript source for obfuscated code. They are not, in most cases, parsing the contents of binding.gyp to determine whether the "native compilation" it triggers is actually compiling anything native at all, or just running attacker code.

#How the worm works

The attack chain is straightforward and brutal:

  1. Initial compromise. The attacker gains access to a maintainer's npm account, either through stolen credentials from a previous wave of Mini Shai-Hulud, a password reset on an expired email domain, or credential stuffing.
  1. Payload injection. They publish a new version of the package with a malicious binding.gyp file. The JavaScript source code doesn't change. The package.json scripts don't change. Nothing that most review processes check is different.
  1. Execution on install. When anyone runs npm install and pulls in the poisoned version, node-gyp kicks in and processes binding.gyp. The attacker's code executes on the developer's machine or, worse, on the CI/CD server.
  1. Credential harvesting. The payload goes after everything: AWS keys, GCP service account credentials, Azure tokens, GitHub Actions secrets, HashiCorp Vault tokens, npm publish tokens, and password manager data.
  1. Self-propagation. Here's where it becomes a worm. Using the stolen npm tokens, the malware automatically publishes poisoned versions of every other package the compromised maintainer has access to. Each of those packages now carries the same binding.gyp payload. Each installation harvests more credentials. Each credential unlocks more packages.

One compromised developer turns into ten. Ten turns into fifty. Fifty packages with 280+ malicious versions, spreading across the dependency graph in a rolling wave.

#Why this is worse than a normal supply chain attack

Most supply chain attacks are static. Someone poisons a package, it gets caught, the malicious version gets pulled. Done.

This one feeds itself. Every victim becomes a vector. And the binding.gyp path means it bypasses the defenses the ecosystem built after the last round of attacks. The security community spent years getting developers to be suspicious of install scripts. Attackers just moved to a different execution path that nobody was watching.

The packages affected so far include autotel, awaitly, executable-stories, node-env-resolver, and mountly, among others. But because this is a worm, the list is growing. By the time you read this, there may be more.

#What this means for your business

Every time one of your developers runs npm install, they are trusting hundreds of packages. Not just the ones listed in package.json, but every transitive dependency those packages pull in. A typical React application has 800 to 1,500 packages in its dependency tree. Any one of them could carry a malicious binding.gyp.

If your company builds or maintains a web application, a mobile app, an internal tool, or anything else in the JavaScript ecosystem, this is your problem. It doesn't matter if your developers are careful. It doesn't matter if you review your direct dependencies. A poisoned package three levels deep in your dependency tree will execute on your CI/CD server with access to every secret in that environment.

And if those secrets include your cloud provider credentials, your database connection strings, or your deployment keys, the attacker doesn't need to hack you. Your own build pipeline hands them everything.

#What to do

Right now:

  1. Audit your dependency tree for binding.gyp files. Run find node_modules -name binding.gyp -type f in your projects. Every result should correspond to a legitimate native module you expect to be there (like node-sass, sharp, bcrypt, better-sqlite3). If you see a binding.gyp in a package that has no business compiling native code, investigate immediately.
  1. Check for the known affected packages. Search your package-lock.json files for autotel, awaitly, executable-stories, node-env-resolver, mountly, and @vapi-ai/server-sdk. If you find any of them, check the version against the known-clean list and rotate every credential that CI/CD environment could reach.
  1. Rotate CI/CD secrets. If there's any chance your build pipeline ran npm install against an affected package, rotate your AWS keys, cloud provider credentials, npm tokens, GitHub PATs, and deployment keys. Don't wait to confirm exposure. Rotate now, investigate after.

Going forward:

  1. Gate native compilation. Configure your CI/CD pipeline to flag or block packages that trigger node-gyp. The --ignore-scripts flag prevents install scripts but does not prevent binding.gyp processing. You need npm install --ignore-scripts combined with a separate, audited build step for legitimate native modules.
  1. Pin your dependency versions. If you're still using ^ or ~ ranges, stop. Pin exact versions. Commit your lockfile. Review updates before accepting them.
  1. Use supply chain monitoring tools. Socket.dev and Snyk both detect suspicious native build configurations. If you're not using a tool like this, you're relying on the community to catch attacks before they reach you, and this one was live for days before disclosure.
  1. Enable npm provenance and MFA. If you publish packages, enable publish-time MFA and npm provenance attestations. Don't let a single stolen token turn your packages into worm carriers.

#This is the third major npm supply chain attack in four weeks

node-ipc backdoored through an expired domain. Mini Shai-Hulud worming through 170+ packages. Now Phantom Gyp exploiting a build system blind spot.

The JavaScript supply chain is under sustained attack. Not one-off incidents. A continuous campaign by a well-resourced group that adapts its techniques every time the ecosystem patches a hole.

If you're a business owner and your team builds anything in JavaScript, you need a plan for this. Not next quarter. Now.

~/teampcp/shai-hulud · supply chain worm

#Further reading

Share this article
LinkedInX / TwitterEmail

Ready to secure your business?

Free 30-minute consultation. No sales script.

Call (773) 417-9994