Skip to content

chore: harden npm supply chain (pnpm 11 + min-release-age + renovate)#306

Open
aromko wants to merge 7 commits into
mainfrom
chore/supply-chain-hardening
Open

chore: harden npm supply chain (pnpm 11 + min-release-age + renovate)#306
aromko wants to merge 7 commits into
mainfrom
chore/supply-chain-hardening

Conversation

@aromko
Copy link
Copy Markdown
Contributor

@aromko aromko commented May 15, 2026

Summary

Defense-in-depth against fresh malicious package versions (the ua-parser-js / event-stream / tj-actions/changed-files attack class):

  • pnpm 9.15.9 → 11.1.2 so we can use minimum-release-age.
  • .npmrc: minimum-release-age=10080 — pnpm refuses to install any package version published less than 7 days ago. (On pnpm 9 this would have been a silent no-op.)
  • package.json: pnpm.onlyBuiltDependencies allowlist (@swc/core, esbuild, lightningcss, sharp) — pnpm 10+ no longer runs untrusted postinstall scripts by default.
  • renovate.json hardening:
    • Fix nested packageRules that silently disabled automerge.
    • config:baseconfig:recommended (former is deprecated).
    • matchPackagePatternsmatchPackageNames regex (former deprecated).
    • Add vulnerabilityAlerts (priority 10, no schedule, bypasses release-age) + osvVulnerabilityAlerts.
    • Add lockFileMaintenance so transitive deps get refreshed weekly.
    • Add dependencyDashboard for visibility.
    • Pin GitHub Actions to commit SHAs (pinDigests).
    • Never automerge majors; require 14-day release age on them.
    • prConcurrentLimit: 5, prHourlyLimit: 2.

CI is unaffected — all workflows already read the pnpm version from package.json#packageManager via pnpm/action-setup@v4.

Inspired by core/main!33699, adapted for this repo.

Test plan

  • corepack pnpm install clean (no ignored-script warnings)
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (1/1)
  • pnpm-lock.yaml unchanged (pnpm 11 reads v9 lockfile format)
  • CI green on this PR
  • Renovate dashboard issue appears within ~1 schedule cycle
  • Next Renovate PR carries the pinDigests for pnpm/action-setup, etc.

Follow-ups (not in this PR)

Repo-side settings worth checking, since config files can't enforce them:

  • Branch protection on main: required review, required status checks.
  • CODEOWNERS for renovate.json, package.json, .npmrc so dep policy changes need approval.
  • The remote dependabot alerts surfaced on push (12 vulnerabilities) should be triaged — vulnerabilityAlerts will now create high-priority PRs for them once Renovate runs.

🤖 Generated with Claude Code

Defense-in-depth against fresh malicious package versions
(ua-parser-js / event-stream class of attack):

- Upgrade pnpm 9.15.9 -> 11.1.2 to unlock the `minimum-release-age` setting.
- .npmrc: refuse to install any package version published less than 7 days
  ago (`minimum-release-age=10080`). On pnpm 9 this would silently no-op.
- package.json: add `pnpm.onlyBuiltDependencies` allowlist (@swc/core,
  esbuild, lightningcss, sharp) — pnpm 10+ no longer runs untrusted
  postinstall scripts by default.
- renovate.json:
  * Fix nested `packageRules` that silently disabled automerge.
  * `config:base` -> `config:recommended` (former is deprecated).
  * `matchPackagePatterns` -> `matchPackageNames` regex (former deprecated).
  * Add `vulnerabilityAlerts` (priority 10, no schedule, bypasses
    release-age) and `osvVulnerabilityAlerts` for second-source advisories.
  * Add `lockFileMaintenance` so transitive deps get refreshed weekly.
  * Add `dependencyDashboard` for visibility.
  * Pin GitHub Actions to commit SHAs (`pinDigests`) — mitigates the
    tj-actions/changed-files compromise pattern.
  * Never automerge majors; require 14-day release age on them.
  * Add `prConcurrentLimit` / `prHourlyLimit` for noise control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
reference-app Ready Ready Preview, Comment May 15, 2026 0:43am

Request Review

sebald
sebald previously approved these changes May 15, 2026
@sebald
Copy link
Copy Markdown
Member

sebald commented May 15, 2026

@aromko need to update node I guess like in the other PR :D

pnpm 11 requires Node >= 22.13 (uses node:sqlite). CI failed with
ERR_UNKNOWN_BUILTIN_MODULE on Node 20.

dependency-track.yml already pins node-version: '22'; this aligns the
remaining workflows (which read .node-version).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI failed with ERR_PNPM_IGNORED_BUILDS for @swc/core and esbuild even
though they were listed in `pnpm.onlyBuiltDependencies` in package.json.

pnpm 11 reads the supply-chain controls from pnpm-workspace.yaml and
additionally requires an explicit `allowBuilds: { <name>: true }` map
(separate from the onlyBuiltDependencies allowlist) so that approvals
are deliberate and version-controlled. The package.json `pnpm` block is
no longer honored for this in pnpm 11.

Generated via `pnpm approve-builds --all` and tightened with comments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 15, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 1.88%. Comparing base (67a4182) to head (1c84748).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files

Impacted file tree graph

@@          Coverage Diff          @@
##            main    #306   +/-   ##
=====================================
  Coverage   1.88%   1.88%           
=====================================
  Files          9       9           
  Lines        212     212           
  Branches       9       9           
=====================================
  Hits           4       4           
  Misses       203     203           
  Partials       5       5           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Vercel ignores `packageManager` for projects created before its pnpm-10
default cutover and falls back to pnpm 9. pnpm 9 sees pnpm-workspace.yaml
and demands a `packages` field, failing the build with
`packages field missing or empty`.

Override the install command to install pnpm 11.1.2 globally before
running install. Avoids requiring the user to enable
ENABLE_EXPERIMENTAL_COREPACK in Vercel project settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lockfile format stays at v9.0 (pnpm 11 still writes v9 spec), but the
file is rewritten from scratch so `--frozen-lockfile` in vercel.json
won't trip on any pnpm 9 -> 11 resolution-detail drift.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two stacked Vercel issues from the previous attempt:

1. Vercel build was on Node 20 (project default; ignores .node-version),
   so pnpm 11 silently EBADENGINE-warned and the pre-installed pnpm 9
   stayed first on PATH.
2. The fallback pnpm 9 then choked on pnpm-workspace.yaml without a
   `packages` field.

Fix:
- package.json: declare `engines.node: ">=22.13"`. Vercel honors engines
  and will pick a Node 22 runtime.
- vercel.json: install via corepack (built into Node 22), pinned to the
  exact pnpm version from `packageManager`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On Vercel, the pre-installed pnpm 9 stays first on PATH even after
`corepack prepare pnpm@11.1.2 --activate`. pnpm 9 requires a `packages`
field whenever a pnpm-workspace.yaml exists, otherwise it bails with
"packages field missing or empty".

`packages: ['.']` declares the root as a single workspace member —
accepted by all pnpm versions; pnpm 10+ would also accept its absence.

Trade-off: Vercel build still runs under pnpm 9, which means the
`allowBuilds` install-script gate is bypassed there. The lockfile is
still enforced via --frozen-lockfile, and `minimum-release-age` was
applied at lock time, so no fresh versions can sneak in. To regain the
install-script gate on Vercel, set ENABLE_EXPERIMENTAL_COREPACK=1 in
Vercel Project Settings (Vercel UI; cannot be set from a config file).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants