Postinstall scripts execute arbitrary shell code with your user's filesystem and network permissions the moment anyone runs npm install. This is the mechanism behind several real supply chain attacks — including the event-stream incident — where a legitimate package was transferred to a malicious maintainer who added a postinstall that harvested crypto wallets. OWASP A08 (Software and Data Integrity Failures) covers exactly this failure mode. CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) and CWE-114 (Process Control) both apply when a package executes code you have not reviewed. SSDF PW.4 requires that third-party components be vetted. A postinstall script making network requests to an unrecognized host should be treated as a confirmed incident, not a warning.
High because a malicious postinstall script executes with developer or CI system privileges at install time, before any code review occurs, and can exfiltrate secrets from the environment.
Before installing any unfamiliar package with a lifecycle script, inspect its source:
# View the package tarball contents without installing
npm pack package-name --dry-run
# Or inspect the published source at:
# https://www.npmjs.com/package/package-name?activeTab=code
For CI environments where postinstall scripts from known-safe packages are the only expected hooks, add ignore-scripts=true to .npmrc and explicitly enable only those you've reviewed. Never allow a postinstall script that fetches from an external URL without a documented, reviewed justification.
ID: dependency-supply-chain.security-vulns.postinstall-reviewed
Severity: high
What to look for: Examine package.json scripts for postinstall, prepare, preinstall, or install lifecycle scripts. Then check direct dependencies in package.json for packages known to use postinstall scripts: husky, electron, puppeteer, playwright, @swc/*, esbuild, sharp, node-gyp, bcrypt, sqlite3, and any package with install in its name. For each identified postinstall script, assess whether it performs a legitimate action (downloading a binary for the host platform, running build steps) vs. an unexpected one (network requests to unknown hosts, file system writes outside node_modules). Count every package with a postinstall, preinstall, or install lifecycle script. Enumerate each and classify as reviewed-safe or needs-investigation. Before evaluating, extract and quote the contents of any postinstall scripts found in node_modules/.scripts or package.json lifecycle hooks.
Pass criteria: No postinstall scripts detected, or all detected postinstall scripts belong to well-known legitimate packages (husky, puppeteer, playwright, esbuild, sharp, etc.) performing expected build-time operations. At least 1 implementation must be confirmed.
Fail criteria: An unfamiliar or suspicious package has a postinstall script that performs unexpected operations (calls to non-registry URLs, writes to directories outside node_modules, obfuscated code). Do NOT pass if any lifecycle script downloads or executes code from an external URL — this is a supply chain attack vector regardless of the package's reputation.
Skip (N/A) when: No package.json detected.
Detail on fail: "Package 'example-pkg@1.2.3' has a postinstall script that makes network requests to an unrecognized host — review before installing" or "Custom postinstall in package.json scripts runs an unreviewed shell script from ./scripts/setup.sh"
Remediation: Postinstall scripts run arbitrary code with your user's permissions at install time. Legitimate packages use them for things like downloading platform-specific binaries or running build steps. Suspicious ones can be vectors for supply chain attacks.
To inspect a package's postinstall script before installing:
# View the package scripts without installing
npm pack package-name --dry-run
# Or view the published package on npm
# https://www.npmjs.com/package/package-name?activeTab=code
For projects that don't need postinstall hooks from certain packages, you can disable them:
npm install --ignore-scripts
Note: this also disables legitimate build scripts, so use selectively.