From 8b7d166c8fc3f2ce0ff2bf1728e57d2a8e4c8bbb Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Wed, 5 Aug 2026 20:01:00 -0700 Subject: [PATCH] Add pnpm supply-chain policy (recipe A) Adds pnpm-policy.yaml enforcing a 2-day minimum release age on third-party npm releases: a compromised release is normally reported and yanked within hours, so the short wait catches it without stalling upgrades. First-party packages (accounts we maintain, plus our owned scopes) skip the wait since we publish them ourselves. The exemption list is derived from what we actually publish and resolve in this lockfile (via `intersect: true`), not hand-maintained, so it can't silently drift out of date. This repo pins pnpm@8.15.0 (< 10.16), which predates the allowBuilds key, so policy is generated with --builds-key onlyBuiltDependencies. No packages currently require install-script approval on this pnpm version. pnpm-workspace.yaml had no prior onlyBuiltDependencies entry to carry over. No git/URL-sourced transitive dependencies were found in pnpm-lock.yaml, so blockExoticSubdeps stays true (default). Wires `pnpm run policy:check` into the existing build-parser job in .github/workflows/ci.yml, right after the root `pnpm install` step. Part of the org-wide rollout tracked in constructive-io/constructive-planning#1464. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CEswUi4ANuB58rva48aHge --- .github/workflows/ci.yml | 3 +++ package.json | 4 ++++ pnpm-lock.yaml | 42 ++++++++++++++++++++++++++++++++++++++++ pnpm-policy.yaml | 39 +++++++++++++++++++++++++++++++++++++ pnpm-workspace.yaml | 34 +++++++++++++++++++++++++++----- 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 pnpm-policy.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67b79d5..efa83cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,6 +159,9 @@ jobs: - name: Install Dependencies ๐Ÿงถ run: pnpm install + - name: Check supply-chain policy + run: pnpm run policy:check + - name: Install Parser Dependencies ๐Ÿ“ฆ run: pnpm install working-directory: parser diff --git a/package.json b/package.json index 253bf61..ec171aa 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "description": "PostgreSQL query parser monorepo", "packageManager": "pnpm@8.15.0", "scripts": { + "policy": "pnpm-policy generate --builds-key onlyBuiltDependencies", + "policy:check": "pnpm-policy check --builds-key onlyBuiltDependencies", "build": "pnpm --filter libpg-query build", "test": "pnpm --filter libpg-query test", "clean": "pnpm --filter libpg-query clean", @@ -32,10 +34,12 @@ "publish:parser": "pnpm --filter @pgsql/parser publish" }, "devDependencies": { + "@constructive-io/pnpm-policy": "0.2.1", "@types/node": "^20.0.0", "copyfiles": "^2.4.1", "glob": "11.0.3", "pg-proto-parser": "^1.28.2", + "pnpm-policy": "0.2.2", "rimraf": "^5.0.0", "ts-node": "^10.9.1", "typescript": "^5.3.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eef64ba..12742e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: devDependencies: + '@constructive-io/pnpm-policy': + specifier: 0.2.1 + version: 0.2.1 '@types/node': specifier: ^20.0.0 version: 20.19.1 @@ -20,6 +23,9 @@ importers: pg-proto-parser: specifier: ^1.28.2 version: 1.28.2 + pnpm-policy: + specifier: 0.2.2 + version: 0.2.2 rimraf: specifier: ^5.0.0 version: 5.0.10 @@ -272,6 +278,10 @@ packages: '@babel/helper-validator-identifier': 7.27.1 dev: true + /@constructive-io/pnpm-policy@0.2.1: + resolution: {integrity: sha512-tAgH3Zgwn2shQXGlfl5zxmtY/tyZw9jYX7QAOFMp5ALKSrxO5xsxZQmckwMqOeHyyV/MZJWzIZ4t/QYznJeVmw==} + dev: true + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -777,6 +787,12 @@ packages: hasBin: true dev: true + /mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + dev: true + /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true @@ -785,6 +801,10 @@ packages: resolution: {integrity: sha512-kB1WKTng+IePQhZVs1UXtFaHBx4QEM5a0XKGAzYfCKvdx5DhNjCytNDWMUGpNNpHLotln+tiwcA52kWCIgGq1Q==} dev: true + /nested-obj@0.2.3: + resolution: {integrity: sha512-Py9HdJ/qECkQHvryUaPcaIou6t+U/mkpT66jG8al7jh8Opt3wAuTSSXdPDyY8r1ljEH8a0EH6M4YR28b+RQ8zg==} + dev: true + /noms@0.0.0: resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} dependencies: @@ -848,6 +868,14 @@ packages: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} dev: true + /pnpm-policy@0.2.2: + resolution: {integrity: sha512-Fj5/ACIZG/AGynTvhT8/7fJTf8ya3jG/Mkq/OMNVZhfTTJ1oniRTsKBHlXGF4sSecvIFqkmeHpn8CsQFwGcENA==} + hasBin: true + dependencies: + yaml: 2.9.0 + yamlize: 0.12.1 + dev: true + /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true @@ -1060,6 +1088,20 @@ packages: engines: {node: '>=10'} dev: true + /yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + dev: true + + /yamlize@0.12.1: + resolution: {integrity: sha512-rU2BN+gmyr0A4yK5i/Ps9JO1MjQGIGUelkaFglc9i4QqEDKU5HfDMdHhNhGMoeNeVw7nJqWpmDiFnxGb6FR5zA==} + dependencies: + mkdirp: 3.0.1 + nested-obj: 0.2.3 + yaml: 2.9.0 + dev: true + /yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} diff --git a/pnpm-policy.yaml b/pnpm-policy.yaml new file mode 100644 index 0000000..9bf084f --- /dev/null +++ b/pnpm-policy.yaml @@ -0,0 +1,39 @@ +# Supply-chain policy for this workspace. The pnpm settings it produces live in +# pnpm-workspace.yaml under the `Managed by pnpm-policy` marker โ€” edit this file, +# then run `pnpm run policy`. `pnpm run policy:check` fails CI when they drift. + +# Third-party releases wait two days. A compromised release is normally reported +# and yanked within hours, so the short wait catches it without stalling upgrades. +minimumReleaseAge: 2d + +# Transitive dependencies must resolve from the registry, not from git or a URL. +blockExoticSubdeps: true + +# The npm accounts WE publish under. Everything they publish skips the wait, so +# this lists accounts we control โ€” nobody else's. +maintainers: + - pyramation + +# Scopes we own outright, emitted as `@scope/*` globs so they also cover packages +# published there tomorrow. +scopes: + - "@constructive-io" + - "@constructive-db" + - "@launchql" + - "@pgpm" + - "@pgpmjs" + - "@pgsql" + +# Resolved from the pinned data package rather than regenerated per repo. +inventory: "@constructive-io/pnpm-policy/inventory.json" + +# Only emit the first-party names this lockfile actually resolves, instead of all +# ~1100 we publish. +intersect: true + +# Dependencies allowed to run install scripts. The value is the reason. +allowBuilds: {} + +# Third-party escape hatches. A reason is required; `until` expires the waiver so +# `check` makes you re-justify it instead of letting it live forever. +exceptions: [] diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 953012e..c7b2cc0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,10 +1,10 @@ packages: - 'parser' - 'versions/18' - - 'versions/17' - - 'versions/16' - - 'versions/15' - - 'versions/14' + - 'versions/17' + - 'versions/16' + - 'versions/15' + - 'versions/14' - 'versions/13' - 'types/18' - 'types/17' @@ -17,4 +17,28 @@ packages: - 'enums/16' - 'enums/15' - 'enums/14' - - 'enums/13' \ No newline at end of file + - 'enums/13' + +# Managed by pnpm-policy โ€” run `pnpm-policy generate` after editing pnpm-policy.yaml. + +# A third-party release must be 2d old before it can be installed. +# Most malicious releases are found and yanked well inside that window. +minimumReleaseAge: 2880 + +# Exempt from the wait: 6 scope glob(s), 5 first-party package(s). +# First-party membership comes from what pyramation publishes on npm โ€” waiting on your own release protects nothing. +minimumReleaseAgeExclude: + - "@constructive-db/*" + - "@constructive-io/*" + - "@launchql/*" + - "@pgpm/*" + - "@pgpmjs/*" + - "@pgsql/*" + - nested-obj + - pg-proto-parser + - pnpm-policy + - strfy-js + - yamlize + +# Transitive dependencies must come from the registry, not from git or a URL. +blockExoticSubdeps: true