npm Supply Chain Lockdown Framework
Harden any Node.js project against supply chain attacks in under 10 minutes using seven layered defences across config, tooling, and habits — without disrupting your normal workflow.
// TL;DR
The npm Supply Chain Lockdown Framework is a seven-layer defense system that hardens any Node.js project against supply chain attacks in under 10 minutes. It combines release age gating, install script blocking, exotic dependency restrictions, pre-installation firewall tools, lock file validation, clean CI installs, and deliberate upgrade habits. Use it when setting up a new JavaScript/TypeScript project, onboarding a team to a shared repo, auditing an existing project's dependency security, or responding to a publicised npm supply chain incident. It works with npm, pnpm, and bun without disrupting your normal development workflow.
// When should I use the npm Supply Chain Lockdown Framework?
Apply this skill whenever you are setting up a new JavaScript/TypeScript project, onboarding a team to a shared repo, or auditing an existing project's dependency security posture. Also invoke it immediately after a publicised npm supply chain attack to check whether your current setup would have protected you.
// What information do I need before applying the npm Supply Chain Lockdown Framework?
- Package manager in userequired
Which package manager(s) the project uses: npm, pnpm, and/or bun. - Project typerequired
Local dev only, CI/CD pipeline, production deployment, or monorepo — affects which tips are highest priority. - Existing config files
Current state of .npmrc, pnpm.config.yaml / pnpm-workspace.yaml, bunfig.toml, package.json, and lock files — so gaps can be identified. - CI environment
Whether the project runs automated installs in CI (GitHub Actions, CircleCI, etc.).
// What are the core principles behind the npm Supply Chain Lockdown Framework?
Release Age Gating
Most supply chain attacks are caught within hours of publication. Refusing to install packages newer than a set age window (e.g. 1 week) means you silently dodge the majority of attacks without any manual review.
Install Script Blocking
Nearly every supply chain attack uses post-install lifecycle scripts to execute malicious code the moment a package lands on your machine. Blocking all install scripts by default and maintaining an explicit allow list is the single highest-leverage config change available.
Exotic Dependency Blocking
Git-URL and tarball dependencies bypass the registry, can re-enable lifecycle scripts, and can ship their own config overrides. Blocking or restricting them to root-declared dependencies eliminates an entire class of attack vector.
Pre-Installation Auditing
Use a firewall tool (npq or Socket Firewall) aliased over your normal install commands so every package is scanned against known malicious signatures, typosquatting patterns, provenance records, and AI-detected threats before it ever touches your machine.
Lock File Integrity
Lock files contain the actual download URL for every package and are almost never reviewed in PRs. An attacker can silently swap a resolved URL and matching integrity hash, redirecting your next install to their server. Validate and commit lock files religiously.
Clean Install in CI
Use the clean install command (npm ci, or the --frozen-lockfile flag for pnpm/bun) in CI and production. It installs exactly what is in the lock file and throws an error rather than guessing if lock file and package.json disagree.
Deliberate Upgrade Habit
Blindly running a bulk update command is exactly the behaviour attackers count on. Every upgrade should be a conscious decision with a stated reason. Pin to exact versions so upgrades are never accidental. Use fewer packages overall — every dependency is an attack surface, and most attacks spread through transitive dependencies.
// How do you apply the npm Supply Chain Lockdown Framework step by step?
- 1
Enable Release Age Gating in your package manager config
npm: add `minimum-package-age=7` (days) to .npmrc. pnpm: add `minPackageAge: 10080` (minutes) to pnpm.config.yaml or pnpm-workspace.yaml — note pnpm 11+ defaults to 1 day even without this. bun: add `[install] packageAge = 604800` (seconds) to bunfig.toml. Warn your team that npx/bunx do NOT respect this setting — they always pull the latest version. If you must bypass age gating for a legitimate security patch, do so explicitly from the CLI, not via an LLM-generated command.
- 2
Block install scripts by default and build an explicit allow list
pnpm: already off by default. Run `pnpm approve-builds` after install to review and selectively allow scripts (e.g. esbuild). This writes to the `allowBuilds` config. bun: already off by default with a curated safe list. Add a `trustedDependencies` array to package.json listing only packages you explicitly approve. To override bun's built-in curated list, put at least one value in the array (empty array has a known bug that does not override defaults). Run `bun pm untrusted` to audit pending scripts. npm: set `ignore-scripts=true` in .npmrc, then use the `allow-scripts` npm package (Lava Moat) to replicate allow-list behaviour.
- 3
Block Git-based and exotic dependencies
npm: set `allow-git=none` in .npmrc to block all Git-URL deps, or `allow-git=root` to allow only those explicitly declared in your root package.json. pnpm: set `blockExoticSubdependencies=true` — this restricts Git repos and direct tarball URLs to direct dependencies only. bun: no native option yet; watch for the open PR that adds this. As a pnpm bonus: set `trustPolicy=no-downgrade` so pnpm fails if a package's trust level has decreased compared to its prior release, catching compromised publisher accounts.
- 4
Install and alias a pre-installation firewall tool
Choose one: (a) npq — alias your npm/pnpm/bun install commands to npq. It checks Snyk's vulnerability database, flags packages under 22 days old, catches typosquatting, verifies registry signatures and build provenance, warns on install scripts, and checks download count, readme, license, repo URL, and whether the maintainer's email domain is still registered. (b) Socket Firewall — alias your install commands to socket. Blocks human-confirmed malicious packages immediately; warns on AI-detected (unreviewed) threats. Also supports Python (pip, uv) and Rust (cargo). Attackers themselves have confirmed Socket detects malware before the package reaches your machine. After installing either tool, clear your package manager's cache so all previously cached packages are re-scanned on next install.
- 5
Validate your lock file with lockfile-lint (if not using pnpm)
pnpm users: skip this step — pnpm does not store swappable tarball sources in its lock file and will not install lock-file entries that are absent from package.json. npm/bun users: install `lockfile-lint` as a dev dependency and run it as a pre-install or CI check. It validates that every resolved URL points to a trusted host (the npm registry), that resolved URLs match the declared package name, and that integrity hashes are genuine. A tampered lock file fails the install. Ensure your lock file is committed to version control and is NOT in .gitignore.
- 6
Switch CI and production installs to clean install mode
npm: replace `npm install` with `npm ci`. pnpm: use `pnpm install --frozen-lockfile` or `pnpm ci` alias — pnpm auto-detects CI environments and applies this by default. bun: use `bun install --frozen-lockfile` or `bun ci`. Clean install installs exactly what is in the lock file, nothing more, and errors out if lock file and package.json are inconsistent rather than silently resolving the difference.
- 7
Adopt deliberate dependency hygiene habits
Three habits to enforce: (1) Never run a bulk update command (npm update, pnpm update, bun update) without reviewing each upgrade individually — ask 'why do I need this upgrade?'. (2) Reduce your dependency count — every package is an attack surface; attacks spread through transitive dependencies. Before adding a package, ask whether a small code snippet or a native API (e.g. fetch instead of axios, native array methods instead of lodash) achieves the same result. In agentic coding contexts, ask the AI to write the utility function instead of importing a package. (3) Pin all direct dependencies to exact versions in package.json so every upgrade is a deliberate choice, not an automatic range resolution. Remember: pinning only locks your direct deps; your dependencies' dependencies still use their own ranges, which is why Release Age Gating (Step 1) remains critical.
// What does the npm Supply Chain Lockdown Framework look like in real projects?
A solo developer starting a new pnpm monorepo for a SaaS product
Add `minPackageAge: 10080` to pnpm-workspace.yaml (Step 1). Confirm install scripts are already blocked by default and run `pnpm approve-builds` after first install to allow only necessary scripts like native compilers (Step 2). Set `blockExoticSubdependencies=true` and `trustPolicy=no-downgrade` in pnpm config (Step 3). Alias all install commands to Socket Firewall and clear the pnpm cache (Step 4). Skip lockfile-lint since pnpm's lock file is not vulnerable to URL-swapping (Step 5). Add `pnpm ci` to every CI pipeline job (Step 6). Pin all package.json entries to exact versions and audit any PR that modifies the lock file (Step 7).
A team using npm on an existing production app discovering a publicised supply chain incident
Immediately set `minimum-package-age=7` and `ignore-scripts=true` in .npmrc and install the `allow-scripts` (Lava Moat) package to restore only known-safe post-install scripts (Steps 1–2). Set `allow-git=none` in .npmrc (Step 3). Install npq, alias npm install to it, and clear npm's cache so all cached packages are re-checked (Step 4). Install lockfile-lint as a dev dependency, run it immediately to audit the existing lock file for tampered resolved URLs, and add it to the CI pipeline (Step 5). Replace all `npm install` calls in CI with `npm ci` (Step 6). Audit the package.json for any bloated or replaceable dependencies and pin remaining ones to exact versions (Step 7).
// What mistakes should I avoid when implementing the npm Supply Chain Lockdown Framework?
- npx and bunx do NOT respect Release Age Gating — they always pull the latest package version, bypassing your age window entirely.
- LLM-generated install commands may silently add flags that bypass age gating and install the latest version anyway — always review AI-suggested install commands.
- Setting bun's trustedDependencies to an empty array does NOT override bun's built-in curated allow list (known bug) — put at least one explicit entry in the array to force the override.
- Lockfile-lint is unnecessary if you are already on pnpm, but critical for npm and potentially bun, where lock file URL-swapping attacks are viable.
- Bulk update commands (npm update, pnpm update, bun update) are exactly the behaviour attackers rely on — never run them blindly.
- Pinning direct dependencies to exact versions does NOT protect transitive dependencies — your packages' packages still resolve ranges, which is why Release Age Gating must remain active.
- Not committing your lock file to version control makes Steps 5 and 6 entirely ineffective — confirm the lock file is tracked in git and not gitignored.
- Clearing your package manager cache after installing a firewall tool is mandatory — previously cached packages will not be re-scanned otherwise.
- The trust policy no-downgrade setting is pnpm-only and does not have an equivalent in npm or bun yet.
// What do the key terms in the npm Supply Chain Lockdown Framework mean?
- Release Age Gating
- A package manager configuration that prevents installation of packages newer than a specified age threshold (set in days for npm, minutes for pnpm, seconds for bun), causing the manager to install the most recent version that is older than the threshold instead.
- Install Scripts
- Lifecycle hooks (pre-install, post-install, etc.) that a package is permitted to execute immediately after being installed. Used legitimately for compiling native code or fetching binaries, but the execution vector used in nearly every supply chain attack.
- Exotic Dependencies
- Dependencies declared as Git URLs or direct tarball URLs rather than registry package names. They bypass the npm registry, can ship their own .npmrc overrides, and can re-enable install scripts — a known attack vector.
- Socket Firewall
- A pre-installation auditing tool that aliases over package manager install commands and blocks human-confirmed malicious packages while warning on AI-detected (unreviewed) threats, supporting npm, pnpm, bun, pip, uv, and cargo.
- npq
- A pre-installation auditing tool that aliases over npm/pnpm/bun install commands and checks packages against Snyk's vulnerability database, flags packages under 22 days old, detects typosquatting, verifies registry signatures and build provenance, and audits maintainer metadata.
- Typosquatting
- An attack where a malicious package is published under a name nearly identical to a popular package (e.g. 'expresss' instead of 'express'), hoping developers make a typo and install the attacker's package.
- lockfile-lint
- A dev-dependency tool that validates a project's lock file by checking all resolved URLs point to a trusted registry host, that resolved URLs match declared package names, and that integrity hashes are genuine — failing the install if tampering is detected.
- Clean Install
- A package manager command (npm ci, pnpm ci / --frozen-lockfile, bun ci / --frozen-lockfile) that installs exactly the dependency tree recorded in the lock file and throws an error rather than resolving discrepancies between lock file and package.json.
- Trust Policy (no-downgrade)
- A pnpm-specific setting that causes installation to fail if a package's trust level has decreased compared to its previous published release, catching attacks that compromise a previously trusted publisher account.
- allow-scripts (Lava Moat)
- An npm package that replaces npm's native install script behaviour with an explicit allow-list declared in package.json, giving npm users the same per-package script approval workflow that pnpm and bun provide natively.
// FREQUENTLY ASKED QUESTIONS
What is the npm Supply Chain Lockdown Framework?
It is a seven-layer defense system that hardens Node.js projects against supply chain attacks by combining release age gating, install script blocking, exotic dependency restrictions, pre-installation firewalls, lock file validation, clean CI installs, and deliberate upgrade habits. It works across npm, pnpm, and bun and can be fully implemented in under 10 minutes without disrupting your normal development workflow.
What is release age gating in npm?
Release age gating is a package manager setting that prevents installation of packages newer than a specified age threshold. Since most malicious packages are caught and removed within hours of publication, setting a window of 7 days (or similar) means your installs silently skip the dangerous period. Configure it in .npmrc for npm, pnpm-workspace.yaml for pnpm, or bunfig.toml for bun.
How do I block install scripts in npm, pnpm, and bun?
Set `ignore-scripts=true` in .npmrc for npm and use the `allow-scripts` (Lava Moat) package to create an allow list. pnpm blocks scripts by default — run `pnpm approve-builds` to selectively allow specific packages. bun also blocks by default with a curated safe list — add a `trustedDependencies` array to package.json with at least one entry to override defaults. Run `bun pm untrusted` to audit pending scripts.
How do I set up Socket Firewall or npq for npm supply chain protection?
Install either Socket Firewall or npq and alias your package manager's install command to it. Socket blocks confirmed malicious packages instantly and warns on AI-detected threats, supporting npm, pnpm, bun, pip, and cargo. npq checks Snyk's database, flags packages under 22 days old, and detects typosquatting. After installing either tool, clear your package manager cache so previously cached packages are re-scanned on the next install.
How does the npm Supply Chain Lockdown Framework compare to just running npm audit?
`npm audit` only checks already-installed packages against a vulnerability database after installation. The Lockdown Framework is proactive — it blocks malicious packages before they reach your machine using release age gating, install script blocking, and pre-installation firewall tools. It also addresses attack vectors npm audit ignores entirely, such as lock file tampering, exotic Git-URL dependencies, and typosquatting. The two are complementary but the framework provides far broader coverage.
When should I apply the npm Supply Chain Lockdown Framework?
Apply it whenever you set up a new JavaScript or TypeScript project, onboard a team to a shared repository, or audit an existing project's dependency security posture. Also invoke it immediately after a publicised npm supply chain attack to verify whether your current setup would have protected you. The framework takes under 10 minutes to implement and should be treated as a baseline security hygiene step for any Node.js project.
What results can I expect after implementing the npm Supply Chain Lockdown Framework?
You can expect your project to silently dodge the majority of supply chain attacks without manual review. Age gating skips malicious packages during their most dangerous window. Script blocking eliminates the primary execution vector attackers use. Firewall tools catch confirmed malware before installation. Lock file validation and clean installs prevent tampering. Combined, these layers make your project resilient against the attack patterns used in virtually all documented npm supply chain incidents.
Does npx respect release age gating settings?
No, npx and bunx do NOT respect release age gating — they always pull the latest package version, completely bypassing your configured age window. This is a critical pitfall. If you need to execute a package with npx, be aware you are outside the protection of your age gating config. Consider installing the package normally first (which respects age gating) and then running it, rather than relying on npx for packages you haven't vetted.
What is lockfile-lint and do I need it?
lockfile-lint is a dev-dependency tool that validates your project's lock file by checking that all resolved URLs point to a trusted registry, that URLs match declared package names, and that integrity hashes are genuine. You need it if you use npm or bun, because their lock files are vulnerable to URL-swapping attacks where an attacker silently replaces a download URL and matching hash. pnpm users can skip it — pnpm's lock file format is not vulnerable to this attack.
What is clean install and why should I use it in CI?
Clean install (npm ci, pnpm --frozen-lockfile, bun --frozen-lockfile) installs exactly the dependency tree recorded in your lock file and throws an error if the lock file and package.json disagree, rather than silently resolving the difference. Use it in CI and production to guarantee that builds are deterministic and that no unexpected package version resolution occurs. This prevents an attacker or an accidental range bump from injecting unreviewed code into your deployed application.
Is the npm Supply Chain Lockdown Framework only for npm?
No, it covers npm, pnpm, and bun with specific configuration steps for each. pnpm has the strongest built-in defaults — scripts are blocked by default, lock files resist URL swapping, and trust policies are native. npm requires the most manual setup. bun falls in between, with good defaults but some missing features like exotic dependency blocking (pending an open PR). The framework adapts its recommendations based on which package manager you use.