The file nobody reviews
In June 2026 a supply chain attack started spreading across the npm registry by abusing a file most security tools simply do not inspect: binding.gyp. Instead of hiding its payload in a preinstall or postinstall script, which are now the most watched hooks in the ecosystem, the malware ships a weaponized binding.gyp. When npm install runs, node-gyp reads that file and executes attacker-controlled code automatically, with no postinstall anywhere in package.json.
Worse, it is a self-propagating worm. An infected machine does not sit still. It uses whatever credentials it finds to publish new poisoned versions of other packages, which in turn infect anyone who installs them. Every successful npm install becomes a new launch point.
If you build SaaS with AI help and pull in new dependencies without reading what came along, this is exactly the scenario that catches you.
What binding.gyp actually does
binding.gyp is the build file for native addons. Packages with a C or C++ part (database drivers, crypto libs, system bindings) need to compile native code at install time. node-gyp is the tool that reads binding.gyp, generates the project files, and kicks off the compiler. This is legitimate and has run for years across thousands of packages.
The catch is that the .gyp format is not a harmless declarative manifest. It describes build steps, and build steps can invoke commands. When you run npm install on a package with a native addon, node-gyp acts as a mini build orchestrator that spawns external processes. A malicious binding.gyp only has to turn that compilation moment into an arbitrary command execution moment.
For defenders, the distinction is brutal. A malicious postinstall is a suspicious line in package.json, easy to log and block. A malicious binding.gyp looks like just another build file for a package that legitimately compiles native code.
Why "block postinstall" misses this
The defense that became standard over the last few years targets lifecycle scripts. npm config set ignore-scripts true, CI policies that reject preinstall/postinstall, tools that alert when a new package ships an install hook. All of it attacks package.json.
binding.gyp is not a lifecycle script. It does not show up in package.json as a hook. The execution does not travel the path those defenses monitor. It travels through node-gyp, a build tool considered trusted. So:
- A scanner that only looks at
scriptsinpackage.json: sees nothing. - A CI policy that blocks
postinstall: never fires. - A human reviewer who checks install hooks: looks in the wrong place.
The code runs anyway, during a compilation step that looks like routine.
Where it hits you: dev and CI
The trigger is the same npm install you run a hundred times a day. Two places concentrate the risk.
On the dev machine, install runs with your credentials: npm tokens, cloud keys in ~/.aws, git sessions, environment variables already loaded. A payload that executes in that context has access to everything you have. And that access is exactly what fuels propagation: the worm needs a publish token to poison the next package.
In CI it is worse at scale. Pipelines run npm install or npm ci automatically, often with deploy secrets, registry tokens, and cloud credentials in the environment. A poisoned build can exfiltrate those secrets and publish packages under your organization before any human notices. An ephemeral runner does not save you if the payload already ran and already leaked the token.
Practical defenses
There is no silver bullet, but you can shrink the surface a lot.
1. `--ignore-scripts` where you can
Running npm install --ignore-scripts (or setting ignore-scripts=true in .npmrc) blocks lifecycle hooks. This does not kill the binding.gyp vector on its own, since native compilation follows its own path, but it closes the most common door and forces any native build to be explicit. Where you do not depend on native addons, keep scripts off by default and turn them on only when you consciously need to.
2. Sandbox and container in CI
Treat npm install as untrusted code execution, because that is what it is. Run dependency installation in a container or sandbox with no access to production secrets. Separate the step that resolves dependencies from the step that uses sensitive credentials. If the build needs to compile native code, let it compile in an isolated environment with restricted network and no deploy tokens in the process.
3. Pin plus lockfile with integrity
Use a lockfile (package-lock.json) with integrity hashes and run npm ci in CI instead of npm install. That guarantees you install exactly the artifact you reviewed, not whatever the registry serves today. Version pinning does not stop a malicious version from being published, but it stops that version from entering your build unless you update the lockfile on purpose, which gives you a review point.
4. Review new dependencies, especially with native code
The presence of binding.gyp and C/C++ code in a new dependency is a signal to look closely, not a detail to wave through. Before adding a package that compiles a native addon, check who publishes it, the version history, and what the build does. A version bump of a package that had no native code before and suddenly has some is a red flag.
5. Watch the registry, not just your repo
The worm spreads by publishing new versions. Monitoring unexpected publishes on packages you maintain, and reacting fast to strange versions of your dependencies, cuts the chain. Telemetry from those who watch npm from the outside (like the teams that track these attacks) is a legitimate part of the defense.
The angle for those building with AI
AI-built SaaS speeds up creation, but it also speeds up installing dependencies with no human review. When the flow is "the assistant suggested a package, I added it, I ran it," nobody opened the binding.gyp. Nobody checked whether that lib truly needed to compile native code. npm install runs, node-gyp runs, and the payload runs along.
The lesson of the June 2026 worm is not about one specific file. It is about where execution happens during an install. preinstall and postinstall got locked down because everyone started watching them. The attacker just stepped one file to the side, to a build file that still passes as trusted. Any defense glued to a single field name will always be one step behind.
Treat
npm installas untrusted code execution, on dev and in CI, because in 2026 that is exactly what it is.




