Check P5 · execution surface family · severity CRITICAL · automatic · one of the 12 npm and PyPI package checks
Maps to OWASP LLM03 ATLAS AML.T0011 ATLAS AML.T0050 Code the package manager runs at install, before the package is ever imported: execution the person installing it did not ask for.
The question this check asks: does installing this package run something before anyone imports it? npm runs a package's preinstall, install and postinstall scripts during npm install; pip builds a release that ships no wheel from source, which runs its build code. This check reads what the registry's record declares.
Install-time code is the shortest path from a package to a machine: it runs during the install, with the installing user's rights, before any test or review of the code. That is why the testmetest campaign of October 2026 published npm packages that run code at install (the testmetest page has the record). It is also how a great deal of honest software works — a native module built with node-gyp, a binary fetched for the platform by a postinstall — so the check grades what the script is, not that there is one.
On npm the latest version's manifest is read for the three install scripts, and for gypfile, which makes npm run node-gyp rebuild even with no script written. Each script's command line is read with the same patterns the repository roster uses for a download piped into a shell, a fetched program run inline, and a decoded payload run.
A script that downloads code and runs it, in one line, is pkg-install-fetch-exec:<hook>: CRITICAL and 30 points on any package, new or old — an install script is native to npm, one that pipes a download into a shell is not. Any other install script is pkg-install-script:<hook>: a note at 0 points on an established package (first published 90 days ago or more) and HIGH and 12 points on a new one. The finding prints the command.
On PyPI, a latest release that ships no wheel is pkg-sdist-only: a note on an established package and MEDIUM and 4 points on a new one, because pip will build it from source and run its build code.
A new package with an honest native build. A first release that compiles a binding is HIGH for its first ninety days and a note after; the finding prints the command so the reader can see it is node-gyp rebuild and not a download. The download-and-run line is critical on a famous package too: a postinstall that fetches a script and pipes it to a shell is exactly the line a hijacked release would add, and the check does not exempt a name for being well known.
What an install script calls. The command line in the manifest is read; a script that runs node install.js is a script, and what install.js does is in the package's code, which this tier does not read. A PyPI release's build code. That a release has no wheel is read; its setup.py is not. Scripts of the package's own dependencies are not looked up. Code that runs on import rather than at install is not read either.
Scope, the same on every page of this roster: RepoGates does not see git clone, package managers or curl — outside Claude Code with the RepoGates plugin, whose hook refuses a clone or install that names a blocked repository on the command line, before it runs. The hook sees Bash tool calls in that one client and nothing else; a package named on the line (npm install express, pip install flask, npx, npm create) is judged by its own verdict under this roster, its declared repository riding along. Direct dependencies named on the command line only; transitive dependencies and lockfiles are not resolved. A package added by editing package.json or requirements.txt and installed later is not a line the hook judges. Edit and Write are read for one thing only — an API key about to be saved — checked on the machine and never sent; that guard asks, it never denies. This roster reads what npmjs.org and pypi.org publish about a package — its record, its release history, its declared repository — and never the package's code or what it installs after it. A registry other than those two (a --registry or --index-url on the line) is asked about, not assessed. A package the registry no longer serves is not assessed, never a pass. A PASS means nothing matched on what was read.
npm view NAME scripts gypfile
curl -s https://pypi.org/pypi/NAME/json | jq -r '.urls[].packagetype'
On npm, read every preinstall, install and postinstall line, and open the file it runs. On PyPI, no bdist_wheel in the list means pip builds from source. npm install --ignore-scripts installs without running any of them.
Why is an install script only a note on an established package? Because install scripts are how npm builds native modules and fetches platform binaries, and many of the most-used packages have one. A note on those keeps the warning for where it means something: a new package that runs code at install is a high finding, and a download piped into a shell is critical on any package.
Does RepoGates read the file an install script runs? No. The check reads the command line the registry's record declares. If the script runs a file in the package, what that file does is in the package's code, which this tier does not read.
What is an sdist-only release? A PyPI release that ships only a source distribution and no wheel. pip cannot install it as it is, so it builds it, and building runs the package's own build code on the installing machine. On a new package that is a medium finding; on an established one it is a note.
P1 Known malicious · C16 Install scripts · S10 Scripts and executables.
Add RepoGates to Chrome Pricing
Sources for this page: the rules and weights from the product's own roster and scoring tables; the testmetest record on its intel page.