v1.3.0 — the gyp lens
Read what is actually inside binding.gyp — and diff it between versions.
Every install-script allowlist/approval tool, this one included until now, only checked that binding.gyp exists and called the result "implicit node-gyp rebuild". But gyp evaluates that file at configure time and runs the shell commands in it (subprocess.run(contents, shell=use_shell) in gyp-next's pylib/gyp/input.py), which made the build file a place to hide install-time code where approval tooling was not looking. That is what the June 2026 npm campaign used — ReversingLabs, 2026-06-04: 286 malicious versions across 56 packages.
The gyp lens
New src/gyp.js reads binding.gyp and the .gypi/.gyp files it includes, with a tolerant reader for the GYP dialect (single-quoted strings, # comments, trailing commas — JSON.parse cannot read real ones). Every channel from gyp-next's early/late/latelate variable regexes is covered:
| Channel | What it does |
|---|---|
<!( <!@( |
command expansion — gyp runs it in a shell |
>!( >!@( ^!( ^!@( |
the same in gyp's late/latelate phases — one character apart, invisible to a naive scan |
<!pymod_do_main( (+ >/^) |
imports a Python module and calls its DoMain() |
<|( >|( ^|( |
listfile expansion |
actions[].action · rules[].action · postbuilds[].action |
build steps that run commands |
make_global_settings |
replaces CC/CXX/LINK — a compiler hijack |
conditions |
flagged on the Python-eval sandbox escape |
Plain <(var) interpolation is never flagged. A structural-parse failure falls back to a raw-text scan marked partial — never a silent pass.
Wired through audit (gyp: signals, scored HIGH), review (findings printed above the raw file), --sarif (new gyp-exec-channel rule), and policy denyCapabilities.
Fix: the diff false negative
diff compared binding.gyp by existence, so a version that rewrote an existing one printed UNCHANGED: implicit node-gyp rebuild and exited 0 — the exact shape the June 2026 wave-2 releases had. It now compares contents: MODIFIED with a line-level diff and gainedChannels, exit 1. --json gains { gyp: { changed, gainedChannels } }.
v12-optional-gap is now version-gated
npm/cli#9562 was fixed by PR #9597 (npm 11.18.0, and in 12.0.0), which skips inert nodes during the script-collection walk. On a fixed npm the detector drops optional deps whose os/cpu exclude your platform, so it no longer tells modern-npm users to allowlist fsevents on Linux. Older npm behavior is unchanged.
⚠️ Re-baseline your manifest
A manifest --check baseline containing native packages may now show a new gyp capability. That is a real capability the tool previously could not see, not drift — re-baseline once with npm-script-lens manifest --write.
174 → 201 tests. CI green on ubuntu + windows × node 20/22; both compatibility canaries (npm-compat, pm-compat) repaired and green.