Skip to content

Latest commit

 

History

History
152 lines (99 loc) · 5.18 KB

File metadata and controls

152 lines (99 loc) · 5.18 KB

Categories

The category answers one question: if I deny this, what breaks?

That is the question npm approve-scripts leaves entirely to the reader, and it is the reason allowlists get rubber-stamped. Faced with 60 packages and no information, approving all of them is the only move that reliably keeps the build working.

Rules are ordered, and the order encodes consequence rather than confidence. A package that both builds a native addon and prints a funding notice is a native build; calling it a notice and recommending denial would break the install. So the categories whose denial costs the most are tested first, and the harmless ones only match when nothing above them did.


native-build

Deny it and: the native addon is never built. require throws at runtime, with a message about a missing .node file that points nowhere near the cause.

Matched by: a binding.gyp with no declared install script, or a native-toolchain signal — node-gyp, node-pre-gyp, prebuild-install, prebuildify, cmake-js, node-gyp-build.

Recommended: allow.

The implicit case matters more than it sounds. npm supplies node-gyp rebuild for any package shipping a binding.gyp without its own install hook, and npm v12 blocks that synthesised script exactly as if it had been written out. There is nothing in package.json to grep for, so every search-based approach misses it — and it is the category whose denial is most expensive.


binary-download

Deny it and: the downloaded binary is missing. Commands that call it fail.

Matched by: a download piped into a shell, or network access together with evidence that an artefact is being fetched — a URL naming an archive or executable, a write of the response to disk, or the script saying outright that it is downloading a release.

Recommended: review. Base risk 25.

Two separate problems, both real. The security one: the bytes that execute are not the bytes your lockfile hashed, so lockfile integrity does not cover them. The operational one: it fails in an air-gapped or offline build, and the error looks nothing like its cause.

The distinction from an analytics beacon is load-bearing. Both open a socket; only one leaves you with a missing binary when denied, and telling a reader that denying an exfiltration script "will leave a missing binary" is worse than saying nothing.


codegen

Deny it and: generated files are absent. The package may fail to import.

Matched by: a build or generation toolchain — tsc, babel, rollup, webpack, esbuild, swc, patch-package, graphql-codegen, protoc.

Recommended: allow.


dev-tooling

Deny it and: git hooks are not installed. Nothing else changes, and CI is entirely unaffected.

Matched by: husky, simple-git-hooks, lefthook, core.hooksPath.

Recommended: deny.

Checked ahead of the network rules: a git-hook installer that fetches its own binary is still a git-hook installer, and denying it is still free.


telemetry

Deny it and: no install analytics are sent. The package works normally.

Matched by: scarf, segment.io, mixpanel, amplitude, or the words telemetry or analytics in the script text.

Recommended: deny.


notice

Deny it and: a console message is not printed.

Matched by: funding and thank-you messages, opencollective, a script whose whole body is echo or console.log — and only when no build toolchain and no network access are also present.

Recommended: deny.


no-op

Deny it and: nothing changes.

Matched by: exit 0, true, :, a bare echo, or a package that declares only hooks which are not install-time — a prepare on a registry dependency, for instance.

Recommended: deny.


unknown

Deny it and: unknown. Read the script before deciding.

Matched by: anything the rules above did not claim — and, deliberately, by any script that reads a file which normally holds credentials, whatever else it appears to do. Base risk 10, and never below medium severity when the network is involved.

Recommended: review, always.

This is the honest category, and it exists so the tool can decline to answer. scriptgate allowlist leaves these out of the generated block rather than guessing, and reports how many it withheld. An allowlist that silently approves everything the analysis did not understand is worse than no allowlist, because it looks like review happened.

There is no legitimate install-time reason to open ~/.npmrc or ~/.ssh, so a script that does is never given a category that would imply a purpose — no matter how much it also looks like a downloader.


Notes on prepare

prepare runs at install time only for dependencies resolved from a git URL, which is the one case where a prepare script belongs to code that did not come through the registry's publishing path. For a registry dependency it never runs on install, and reporting it would be noise.

scriptgate makes this distinction from the lockfile's resolved field, so it holds only for npm projects with a lockfile. Elsewhere prepare is treated as not install-time, which is correct for the overwhelming majority of packages.