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"