Add an npm resolution cooldown for the local-viewer frontend#864
Open
timhaines wants to merge 1 commit into
Open
Add an npm resolution cooldown for the local-viewer frontend#864timhaines wants to merge 1 commit into
timhaines wants to merge 1 commit into
Conversation
timhaines
force-pushed
the
add-npm-cooldown
branch
from
July 23, 2026 08:25
f09af8c to
b997318
Compare
The npm analog of the uv exclude-newer in usestrix#861. min-release-age=6 in an .npmrc under the frontend gates tree resolution (npm install/update, lockfile regen) so a version has to age a few days before it resolves — one day under the Dependabot cooldown so bumps still land here. min-release-age arrived in npm 11.10.0 and is a no-op on older npm, which would fail the cooldown open. engine-strict + engines.npm >= 11.10.0 make an older npm error instead. Does not affect npm ci (lockfile verbatim). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
timhaines
force-pushed
the
add-npm-cooldown
branch
from
July 23, 2026 08:44
b997318 to
1384d3e
Compare
Contributor
Greptile SummaryAdds a six-day npm package-resolution cooldown for the local-viewer frontend.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (2): Last reviewed commit: "Add an npm resolution cooldown to match ..." | Re-trigger Greptile |
Contributor
Author
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.
A resolver-layer release-age cooldown for the tree's npm ecosystem: the React/Vite local-viewer SPA under
strix/viewer/frontend. npm won't resolve a version until it's ~6 days old.What
strix/viewer/frontend/.npmrc—min-release-age=6. This gatesnpm install <pkg>/npm update/ lockfile regeneration, which is the manual and local path nobody opens a PR for. It does not affectnpm ci, which installs the lockfile verbatim, somake viewerand any CI build behave exactly as they do today.engines.npm >= 11.10.0+engine-strict=true—min-release-agelanded in npm 11.10.0. Older npm silently ignores the unknown key, which would fail the cooldown open with no error and no gate. The engine guard turns that into a hard error instead.That fail-open case is the reason the second half exists. A cooldown that quietly does nothing on a contributor's older npm is worse than no cooldown, because it reads as covered.
Why bother — 2026 has been a brutal year for npm
The attacks a cooldown defends against are smash-and-grab: a malicious version is published, harvests credentials the moment it installs, and is spotted and pulled within minutes to hours. The blast radius is whoever auto-resolves the newest version inside that window.
@tanstack/*packages in a ~6-minute window (@tanstack/react-routeralone is 12.7M weekly downloads). Caught in ~25 minutes, but npm's "no unpublish while dependents exist" policy left the tarballs installable for ~3 hours. It was also the first documented case of malicious npm packages carrying valid SLSA provenance: signing and attestation verified fine, so a release-age cooldown is the layer that would actually have caught it. [Socket]preinstalland a weaponizedbinding.gyp(node-gyp runs it during install, before any script is reviewed), harvesting npm/GitHub/AWS/GCP/Azure/Vault/K8s credentials and republishing from every account it reaches. Follow-on waves hit 300+ package versions (~16M weekly downloads) on 19 May and the@redhat-cloud-servicesnamespace on 1 Jun. [Microsoft] [The Register]None of these are direct strix dependencies, but the viewer is React-ecosystem npm, which is exactly the target class, and its graph already contains
debug(3× in the lockfile), one of the packages hijacked in the 2025 qix-maintainer compromise. Every incident above was caught well inside 6 days.The honest limit: a cooldown does nothing against a compromise that stays hidden longer than the window. It filters the smash-and-grab, which is what 2026 has mostly been. [cooldowns.dev]
Verification
Checked rather than assumed, since a cooldown that strands a locked version would be a real objection:
npm ciis unaffected, confirmed against the committed lock.enginesintopackage-lock.json's root entry on the nextnpm install, so the lock will pick up a three-line delta whenever someone next regenerates it.npm cidoesn't enforce that field, so nothing breaks in the meantime and there's no need to churn the lock in this PR.Notes
min-release-age-exclude: there are no capped-back or security-critical deps in the frontend tree. One behavioural difference from the Dependabot cooldown to be aware of —min-release-ageblocks annpm audit fixthat falls inside the window (keeps the vulnerable version, warns, non-zero exit) rather than auto-exempting security updates. If a frontend CVE ever needs a same-week bump, add that package tomin-release-age-exclude.make viewer) will need npm >= 11.10.0.Relationship to #860 and #861
Stands alone. It only touches
strix/viewer/frontend, so it can merge in any order, or on its own if you don't take the others.The three are complementary rather than sequential. #860's Dependabot cooldown gates when a bump PR opens; this gates what any resolution produces, so it covers the local and manual path the updater never sees, and it keeps working if the Dependabot config is later changed or removed. #861 is the same idea for the Python tree — same reasoning, different ecosystem, no shared code.
The 6-day value is picked to sit one day under #860's 7-day cooldown so its bumps still resolve here if both land. If #860 doesn't land, 6 days is a sensible standalone window either way.
Like the uv cooldown it's a little opinionated, since it changes what
npm installresolves, so it's happily droppable if you'd rather keep just the Dependabot side.🤖 Generated with Claude Code