Add .npmrc to harden npm install against supply-chain risk#2479
Merged
Conversation
Sets `ignore-scripts = true` to block lifecycle scripts from dependencies, and `save-exact = true` so new installs pin exact versions. Also includes a commented-out `min-release-age = 7` to be enabled once the project's minimum npm version is raised to 11+. Because `ignore-scripts` also skips this repo's own `prepare` script, contributors must now run `npm run prepare` once after install to wire up husky. This is documented in the Performance Lab handbook. Additionally aligns with the wordpress-develop and Gutenberg `.npmrc` defaults: `engine-strict`, `legacy-peer-deps`, `lockfile-version = 3`, and `prefer-dedupe`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
`engine-strict = true` correctly surfaced a latent inconsistency: the recent Dependabot bump to `lint-staged@17.0.2` requires Node >= 22.22.1, but `engines.node` is `>=20.19.0` and `.nvmrc` pins Node 20.19. CI consequently fails to `npm ci`. Comment it out for now with the same treatment as `min-release-age`: re-enable once the project's minimum Node version is raised to match current dep requirements (or the offending dep is downgraded). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
thelovekesh
approved these changes
May 14, 2026
Co-authored-by: thelovekesh <lovekesh.kumar@rtcamp.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an
.npmrcto the Performance repo to hardennpm installagainst supply-chain attacks and to align with the rest of the WordPress ecosystem.Sister PRs in adjacent WordPress projects:
min-release-age = 1to Gutenberg's.npmrc..npmrcconfig file ai#535 — adds an.npmrcto the WordPress AI repo, bumps Node to 24 / npm to 11, and setsmin-release-agefor the same reason.Relevant technical choices
Supply-chain hardening (new to the WordPress ecosystem in this repo)
ignore-scripts = true— blockspreinstall/install/postinstalllifecycle scripts in dependencies. This is the main entry point exploited by recent npm supply-chain attacks (malicious package executes arbitrary code at install time). Neitherwordpress-developnorgutenbergset this today; this repo goes one step further.preparescript (which sets up husky). Contributors must now runnpm run prepareonce afternpm installto wire up the git hooks. This is documented in the Performance Lab section of the WordPress Performance Team Handbook.min-release-age = 7— commented out for now. Refuses to install packages released within the last 7 days, mitigating fast-pulled malicious releases (similar to the Gutenberg change in Add a 1-day minimum release age to npm installs gutenberg#78191, but at 7 days vs. Gutenberg's 1 day). Requires npm ≥ 11; left commented until the project's minimum npm version is raised (the WordPress/ai sister PR raised Node to 24 / npm to 11 for this same reason — we can follow when ready).Alignment with
wordpress-developandgutenbergAdds the standard set already present in both
wordpress-develop/.npmrcandgutenberg/.npmrc:Commented out per 3484824.engine-strict = true— enforces the existingenginesfield inpackage.json(Node ≥ 20.19.0, npm ≥ 10.2.3). Without this, npm only emits a warning, which contributors routinely miss. Also a prerequisite for cleanly enablingmin-release-agelater. (Originally introduced in Gutenberg in Add "engines" to the package.json and "engine-strict = true" to the .… gutenberg#23600.)lockfile-version = 3— pins the lockfile format so contributors on differing npm versions don't accidentally regenerate it in an older format. Likely a no-op for current contributors on npm 10+, but worth locking in. (Introduced in Gutenberg in Update npm lockfile to version 3 gutenberg#65923.)prefer-dedupe = true— tellsnpm installto deduplicate rather than nest when adding new deps. Smallernode_modules, marginally more reproducible installs. (Introduced in Gutenberg in Setprefer-dedupeas the default. gutenberg#61630.)legacy-peer-deps = true— restores npm 6-style peer-dep behavior. The@wordpress/*peer-dep graph clashes with npm 7+ strict resolution; bothwordpress-developandgutenberguse this setting. Adding it preemptively for consistency.save-exact = true— pins exact versions when contributors runnpm install <pkg>. Existing^/~ranges inpackage.jsonaren't rewritten — they'll converge over time as deps are added/updated by contributors. (Dependabot preserves whatever prefix is already on each line, so this doesn't retroactively pin existing entries; thepackage-lock.jsonis what actually enforces reproducibility at install time.)Why the existing
package-lock.jsonisn't enoughThe lockfile already pins the full transitive tree at install time, so most of these settings (
save-exact,prefer-dedupe,lockfile-version) are defense-in-depth / hygiene rather than primary defenses. The two settings that meaningfully change the security posture areignore-scripts(blocks the install-script attack vector) and the futuremin-release-age(blocks freshly-published malicious releases before they're pulled). The rest aligns this repo with the rest of WordPress.Use of AI Tools
Wrote the
.npmrcand this PR description in collaboration with Claude Code (Opus 4.7). All technical choices were reviewed and decided by me; the diff is small and I verified each setting against npm docs and the upstream.npmrcfiles inwordpress-developandgutenberg.