From 1d1cee5aad60c47e32336ff5a66fd89d3e6d7608 Mon Sep 17 00:00:00 2001 From: Jon Bogaty Date: Mon, 27 Jul 2026 01:34:57 -0500 Subject: [PATCH] chore(deps): move pnpm overrides to pnpm-workspace.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The transitive security overrides lived in a `pnpm` field in package.json. pnpm 10.33 reads that, so they work today — but pnpm 11 ignores the field outright and SILENTLY, with no warning or error. `engines.pnpm` is ">=10", so a pnpm 11 bump is permitted and would leave every override inert while the repo still looked patched. A security control that fails silently is worse than an absent one. Moved all ten overrides to pnpm-workspace.yaml, which both pnpm 10 and 11 honour. Verified behaviour-preserving: pnpm-lock.yaml is byte-identical after the move, and its `overrides:` block still records all ten. Adds a CI guard so this cannot regress: it fails if a `pnpm` field reappears in package.json, if the overrides block goes missing, or if any declared override is absent from the resolved lockfile — i.e. declared but not taking effect. Tested all three failure modes plus the passing case. Verified the overrides are load-bearing rather than decorative: removing them and re-resolving reintroduces sharp 0.34.5 (high, < 0.35.0) and an extra svgo 4.0.2. With them, every package resolves to a single version at or above its advisory floor. --- .github/workflows/validate-packages.yml | 28 +++++++++++++++++++++++++ package.json | 17 +-------------- pnpm-workspace.yaml | 28 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 pnpm-workspace.yaml diff --git a/.github/workflows/validate-packages.yml b/.github/workflows/validate-packages.yml index b5112b6..2665d3d 100644 --- a/.github/workflows/validate-packages.yml +++ b/.github/workflows/validate-packages.yml @@ -137,3 +137,31 @@ jobs: exit 1 fi echo "directory.json is in sync with the package manifests" + + # The security overrides only work if pnpm actually reads them. pnpm 11 + # ignores a `pnpm` field in package.json outright and SILENTLY, so an + # override block that drifts back there would leave the repo looking + # patched while shipping vulnerable transitive deps. Fail loudly instead. + - name: Check pnpm overrides are where pnpm reads them + shell: bash + run: | + set -euo pipefail + if node -e "process.exit(require('./package.json').pnpm ? 0 : 1)"; then + echo "::error::package.json has a 'pnpm' field. pnpm 11 ignores it silently — move these settings to pnpm-workspace.yaml." + exit 1 + fi + if ! grep -q '^overrides:' pnpm-workspace.yaml; then + echo "::error::pnpm-workspace.yaml has no overrides block — the transitive security patches are missing." + exit 1 + fi + # Every override must actually appear in the resolved lockfile. + missing=0 + while read -r pkg; do + [ -z "$pkg" ] && continue + if ! grep -qE "^ '?${pkg}'?:" pnpm-lock.yaml; then + echo "::error::override '${pkg}' is not recorded in pnpm-lock.yaml — it is not taking effect." + missing=1 + fi + done < <(sed -n '/^overrides:/,$p' pnpm-workspace.yaml | tail -n +2 | sed -n 's/^ "\{0,1\}\([^":]*\)"\{0,1\}:.*/\1/p') + [ "$missing" -eq 0 ] || exit 1 + echo "pnpm overrides live in pnpm-workspace.yaml and are all present in the lockfile" diff --git a/package.json b/package.json index 1d87cf6..d14128d 100644 --- a/package.json +++ b/package.json @@ -55,20 +55,5 @@ "node": ">=24", "pnpm": ">=10" }, - "packageManager": "pnpm@10.33.0", - "//pnpm-overrides": "Security patches for transitive dependencies. Each range is pinned to the SAME major as the version Astro's tree already resolves \u2014 a bare '>=' pulls in the next major (js-yaml 5, undici 8, fast-uri 4, svgo 4) and breaks the build. esbuild is deliberately NOT overridden: the only open advisory (GHSA, low) is an arbitrary file read in esbuild's dev server on Windows, which this repo never runs \u2014 it builds a static site in CI \u2014 and forcing >=0.28.1 breaks @astrojs/vue. Revisit all of these when Astro is upgraded; several may become unnecessary.", - "pnpm": { - "overrides": { - "@babel/core": "^7.29.6", - "fast-uri": "~3.1.4", - "js-yaml": "^4.3.0", - "postcss": "^8.5.18", - "sharp": "^0.35.3", - "svgo": "^3.3.4", - "tar": "^7.5.18", - "undici": "^7.28.0", - "vite": "^6.4.3", - "yaml": "^2.8.3" - } - } + "packageManager": "pnpm@10.33.0" } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..920b9ae --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,28 @@ +# pnpm reads its settings from THIS file, not from a `pnpm` field in +# package.json. Since pnpm 11 the package.json field is ignored outright — and +# silently, with no warning — so overrides defined there become inert while the +# repo still looks patched. These are security patches for transitive +# dependencies, so they live here where both pnpm 10 and 11 honour them. +# +# Each range is pinned to the SAME major the tree already resolves: a bare +# ">=" pulls the next major (js-yaml 5, undici 8, fast-uri 4, svgo 4) and +# breaks the build. esbuild is deliberately absent — its only advisory is a low +# severity dev-server file read on Windows, which this repo never runs, and +# forcing >=0.28.1 breaks @astrojs/vue. +# +# Revisit when Astro is upgraded; several may become unnecessary. + +packages: + - . + +overrides: + "@babel/core": "^7.29.6" + fast-uri: "~3.1.4" + js-yaml: "^4.3.0" + postcss: "^8.5.18" + sharp: "^0.35.3" + svgo: "^3.3.4" + tar: "^7.5.18" + undici: "^7.28.0" + vite: "^6.4.3" + yaml: "^2.8.3"