Skip to content
second brain

One Liner

2026-06-05 10:20

Harden JavaScript package installs by blocking lifecycle scripts and adding a release-age cooldown.

  • block lifecycle scripts to reduce dependency-install attack surface
  • add a minimum release age so fresh malicious releases do not install immediately

Requirement: npm >= 11.10

  • npm install -g npm@latest

  • npm config set ignore-scripts true
  • npm config set min-release-age 7

  • npm config ls -l | grep -E "ignore-scripts|min-release-age"

  • ~/.npmrc

  • project lifecycle hooks are also disabled
  • run needed scripts explicitly
  • use npm rebuild <pkg> for one package if needed
  • urgent install escape hatch: npm install <pkg> --min-release-age=0

Requirement: pnpm >= 10.16

Already blocked by default in pnpm 10+.

  • pnpm config set --global minimumReleaseAge 10080

  • pnpm config get minimumReleaseAge

  • ~/Library/Preferences/pnpm/rc

  • minimumReleaseAgeExclude
  • supports globs like @myorg/*

Requirement: bun >= 1.3.8

Already blocked by default.

  • add to ~/.bunfig.toml
  • [install]
  • minimumReleaseAge = 604800

  • minimumReleaseAgeExcludes = ["<pkg>"]

Machine-wide supply-chain hardening for npm, pnpm, and Bun.
Apply two protections to all three package managers (user-level, not per-project):
- Disable/block dependency lifecycle scripts (postinstall attack vector — Shai-Hulud, qix/chalk compromises)
- Minimum release age cooldown of 7 days (malicious releases are typically yanked within hours/days)
Units differ per manager: npm = DAYS, pnpm = MINUTES, Bun = SECONDS.
1. npm (requires npm >= 11.10)
Upgrade if older:
npm install -g npm@latest
Apply:
npm config set ignore-scripts true
npm config set min-release-age 7
Verify:
npm config ls -l | grep -E "ignore-scripts|min-release-age"
# ignore-scripts = true
# min-release-age resolves to a `before` date (7 days ago from now)
Writes to ~/.npmrc.
Caveats
ignore-scripts also disables the project's own npm lifecycle hooks (pre/postbuild, prepare, etc.) — run those scripts explicitly where needed.
Native deps (sharp, esbuild, lightningcss) still work — they ship prebuilt binaries via optionalDependencies. If a package breaks: npm rebuild <pkg> runs only that package's scripts.
Urgent fresh install: npm install <pkg> --min-release-age=0
If npm was bundled with Homebrew node, brew upgrade node may downgrade npm — re-upgrade after.
2. pnpm (requires >= 10.16)
Lifecycle scripts
Already blocked by default since pnpm 10.0.0 — nothing to do.
Per-project allowlist when needed: pnpm.onlyBuiltDependencies in package.json (v10) or allowBuilds map (v11). Never set dangerouslyAllowAllBuilds.
Cooldown (7 days = 10080 minutes)
pnpm config set --global minimumReleaseAge 10080
Verify:
pnpm config get minimumReleaseAge
# 10080
Writes minimum-release-age=10080 to the global rc (macOS: ~/Library/Preferences/pnpm/rc, Linux: ~/.config/pnpm/rc).
Escape hatches
minimumReleaseAgeExclude list (supports globs like @myorg/*).
pnpm 11 enables 1440-min cooldown by default + adds strictDepBuilds, trustPolicy: no-downgrade, blockExoticSubdeps — worth upgrading when convenient.
3. Bun (requires >= 1.3.8 — older 1.3.x had a bug ignoring global bunfig)
Lifecycle scripts
Already blocked by default (built-in allowlist of ~vetted packages) — nothing to do.
Per-project trust when needed: bun pm trust <pkg> → adds to trustedDependencies in package.json.
Cooldown (7 days = 604800 seconds)
Create/append ~/.bunfig.toml:
[install]
minimumReleaseAge = 604800
Escape hatch
[install]
minimumReleaseAgeExcludes = ["<pkg>"]
Live verification
Pick any package version published <7 days ago and confirm install is blocked:
V=$(npm view electron-to-chromium version)
npm view electron-to-chromium time --json | grep "$V" # confirm publish date is recent
mkdir -p /tmp/cooldown-test && cd /tmp/cooldown-test
echo '{"name":"t","version":"0.0.0"}' > package.json
bun add "electron-to-chromium@$V" # expect: blocked by minimum-release-age
pnpm add "electron-to-chromium@$V" # expect: error suggesting minimumReleaseAgeExclude
cd / && rm -rf /tmp/cooldown-test
If the latest version happens to be >7 days old, pick another fast-moving package (e.g. caniuse-lite).
Summary
Manager | Version req | Scripts setting | Cooldown setting | Config location
npm | >= 11.10 | ignore-scripts=true | min-release-age=7 (days) | ~/.npmrc
pnpm | >= 10.16 | default blocked (v10+) | minimumReleaseAge=10080 (minutes) | global rc
Bun | >= 1.3.8 | default blocked | minimumReleaseAge=604800 (seconds) | ~/.bunfig.toml

https://github.com/ilyas-muhammad/useful-prompt-collection/blob/main/supply-chain-hardening-npm-pnpm-bun.md