Running npm install can trigger preinstall, install, postinstall and prepare scripts. Packages you depend on can ship the first three, and git dependencies also run prepare. That is arbitrary code execution at install time, which is why attackers target it and why you should know which of your packages use it.
The order
The npm scripts documentation lists the lifecycle for npm install in this order: preinstall, install, postinstall, prepublish (deprecated), preprepare, prepare and postprepare. The page says they also run for a global install of a named package.
If a package has a binding.gyp file and no custom install or preinstall script, npm defaults install to the node-gyp rebuild command. That is how native addons compile on your machine.
When prepare runs
Prepare runs before a package is packed during publish and pack. It also runs on a local npm install with no arguments, which means your own project's prepare script executes whenever someone installs it. For git installed packages, dependencies and devDependencies are installed and prepare runs before packaging. Since npm 7 it runs in the background, and the foreground scripts option shows its output.
Which ones are a supply chain concern
Install-time scripts from third-party packages are the main risk, because they run with the installing user's permissions, often on a developer laptop or CI runner holding tokens. Malicious packages commonly use postinstall for that reason.
The package-lock.json documentation describes a hasInstallScript flag that marks packages with a preinstall, install or postinstall script. Searching your lockfile for it gives a short list to review. The ignore-scripts setting tells npm not to run package scripts, though commands that target a script directly, like npm test, still run it.
A review routine
- Count entries in your lockfile with hasInstallScript set to true.
- Separate the ones you expect, such as native addon builds, from the ones you do not.
- For each unexpected one, check the package source and publisher.
- In CI, install with scripts disabled and then run only the build steps you trust.
- Look at your own scripts too, since prepare runs for anyone who installs the project.
| Script | Runs for dependencies? | Common legitimate use |
|---|---|---|
| preinstall | Yes | Environment checks |
| install | Yes | Compile native code |
| postinstall | Yes | Download binaries, setup |
| prepare | Git dependencies and local installs | Build before use |
For deeper handling, see auditing npm postinstall scripts safely and the ignore-scripts decision guide.
Quick check
Does npm run a dependency's postinstall script?
Yes, by default. Scripts defined by installed packages run during install unless you disable scripts.
What does hasInstallScript mean in the lockfile?
It marks a package that has a preinstall, install or postinstall script.
Safeguard scans your dependencies into an SBOM and can run policy checks on dependency changes before merge. The free scan is at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.