From 46f26927b357c7ecb530c7ac231867d040b47c80 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 24 Sep 2026 20:46:01 -0700 Subject: [PATCH 01/10] Move the production backup and restore check to PostgreSQL 18.6 Neon runs the production and preview databases on 18.6, and pg_dump refuses a server newer than itself, so the backup and restore-test image moves from postgres:17.11 to postgres:18.6. The audit prompt also learns that a stopped manual dispatch leaves a cancelled security-audit run on a pgstencil commit beside the real one, and that a cancelled run is not a verdict. Co-Authored-By: Claude Sonnet 5 --- .github/audit/hosted.md | 5 ++++- hosted/README.md | 4 ++-- hosted/scripts/production-backup.mjs | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/audit/hosted.md b/.github/audit/hosted.md index 219711275..a179c7ee3 100644 --- a/.github/audit/hosted.md +++ b/.github/audit/hosted.md @@ -30,7 +30,10 @@ gh api repos/diffplug/pgstencil/commits//check-runs \ --jq '.check_runs[] | select(.name=="security-audit") | .conclusion' ``` -The first must be `ahead` or `identical`, the second `success`. The packed code +The first must be `ahead` or `identical`, the second `success`. A commit can +carry several `security-audit` runs, and a `cancelled` one, from a manual +dispatch that was stopped, is not a verdict: ignore it, and require that a +completed run succeeded and none failed. The packed code itself is audited in `diffplug/pgstencil` by that repository's own `security-audit` workflow against its `SECURITY.md`; do not audit the tarballs' contents here — audit how `hosted/` configures the adapter. Distinguish tested diff --git a/hosted/README.md b/hosted/README.md index 3e3cc1f38..5fa25a7f0 100644 --- a/hosted/README.md +++ b/hosted/README.md @@ -142,8 +142,8 @@ Use dedicated Dormouse resources in the existing Cloudflare, Neon, and Postmark accounts. 1. Create a dedicated Dormouse production Postgres database on Neon, on - PostgreSQL 17; the backup/restore tooling pins PostgreSQL - 17.11. Keep development and previews separate. Enable backups and a + PostgreSQL 18; the backup/restore tooling pins PostgreSQL + 18.6. Keep development and previews separate. Enable backups and a suitable PITR window, and verify a restore into a separate database before accepting real accounts. 2. Create a Cloudflare Hyperdrive configuration for that database with **query diff --git a/hosted/scripts/production-backup.mjs b/hosted/scripts/production-backup.mjs index d7e611880..b5aca9615 100644 --- a/hosted/scripts/production-backup.mjs +++ b/hosted/scripts/production-backup.mjs @@ -10,7 +10,7 @@ import { required, hyperdriveOrigin } from "./preview.mjs"; // Only the encrypted archive leaves the runner, after decrypt-and-restore succeeds. const temporary = await mkdtemp(join(tmpdir(), "hosted-backup-")); const container = `hosted-restore-${randomUUID()}`; -const image = "postgres:17.11-alpine"; +const image = "postgres:18.6-alpine"; function run(step, command, args, env = process.env) { const result = spawnSync(command, args, { env, From 814f6c09bfe4004da643e5033d893be905a65c7d Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 24 Sep 2026 20:46:01 -0700 Subject: [PATCH 02/10] Vendor pgstencil e79cc4d (0.2.0, tested on PostgreSQL 18) pgstencil main now ships compose.yaml on postgres:18.6-alpine, so Hosted's integration tests run on the version production runs. The 0.2.0 release prep renames the archives; the pins, the production preflight, and the artifact test follow. Its security-audit run on e79cc4d succeeded. Co-Authored-By: Claude Sonnet 5 --- hosted/package.json | 4 ++-- hosted/scripts/production.mjs | 2 +- hosted/server/tests/artifacts.test.ts | 14 +++++------ pnpm-lock.yaml | 32 +++++++++++++------------- pnpm-workspace.yaml | 4 ++-- vendor/build.json | 10 ++++---- vendor/pgstencil-0.1.0.tgz | Bin 16093 -> 0 bytes vendor/pgstencil-0.2.0.tgz | Bin 0 -> 16412 bytes vendor/pgstencil-auth-0.1.0.tgz | Bin 36738 -> 0 bytes vendor/pgstencil-auth-0.2.0.tgz | Bin 0 -> 37112 bytes 10 files changed, 33 insertions(+), 33 deletions(-) delete mode 100644 vendor/pgstencil-0.1.0.tgz create mode 100644 vendor/pgstencil-0.2.0.tgz delete mode 100644 vendor/pgstencil-auth-0.1.0.tgz create mode 100644 vendor/pgstencil-auth-0.2.0.tgz diff --git a/hosted/package.json b/hosted/package.json index a979c5aaf..ac00faf5e 100644 --- a/hosted/package.json +++ b/hosted/package.json @@ -18,8 +18,8 @@ "preview:smoke": "node scripts/preview-smoke.mjs" }, "dependencies": { - "@pgstencil/auth": "file:../vendor/pgstencil-auth-0.1.0.tgz", - "pgstencil": "file:../vendor/pgstencil-0.1.0.tgz", + "@pgstencil/auth": "file:../vendor/pgstencil-auth-0.2.0.tgz", + "pgstencil": "file:../vendor/pgstencil-0.2.0.tgz", "kysely": "^0.29.5", "hono": "^4.13.8", "@hono/node-server": "^2.0.10", diff --git a/hosted/scripts/production.mjs b/hosted/scripts/production.mjs index 7de24de04..e47a9c3c5 100644 --- a/hosted/scripts/production.mjs +++ b/hosted/scripts/production.mjs @@ -42,7 +42,7 @@ export async function verifyPackages() { ); assert.deepEqual( manifest.files.map((entry) => entry.filename).sort(), - ["pgstencil-0.1.0.tgz", "pgstencil-auth-0.1.0.tgz"], + ["pgstencil-0.2.0.tgz", "pgstencil-auth-0.2.0.tgz"], "Expected both pinned pgstencil archives", ); for (const entry of manifest.files) { diff --git a/hosted/server/tests/artifacts.test.ts b/hosted/server/tests/artifacts.test.ts index cec619b39..e29caa278 100644 --- a/hosted/server/tests/artifacts.test.ts +++ b/hosted/server/tests/artifacts.test.ts @@ -14,8 +14,8 @@ test("consumed package bytes match the recorded source snapshot", () => { // The sync only packs committed revisions; production preflight requires this. expect(build.dirty).toBe(false); expect(build.files.map((file) => file.filename).sort()).toEqual([ - "pgstencil-0.1.0.tgz", - "pgstencil-auth-0.1.0.tgz", + "pgstencil-0.2.0.tgz", + "pgstencil-auth-0.2.0.tgz", ]); for (const file of build.files) { expect( @@ -37,8 +37,8 @@ test("consumed package bytes match the recorded source snapshot", () => { } // Same-version tarball refreshes must update installed code as well as metadata. for (const [archive, entry] of [ - ["pgstencil-0.1.0.tgz", "pgstencil"], - ["pgstencil-auth-0.1.0.tgz", "@pgstencil/auth/better-auth"], + ["pgstencil-0.2.0.tgz", "pgstencil"], + ["pgstencil-auth-0.2.0.tgz", "@pgstencil/auth/better-auth"], ]) { const file = fileURLToPath(import.meta.resolve(entry)); const archivePath = `package/dist/${file.split("/").at(-1)}`; @@ -59,8 +59,8 @@ test("both pinned specifiers name the recorded archives", () => { ) as { dependencies: Record }; const workspace = readFileSync("../pnpm-workspace.yaml", "utf8"); for (const [name, filename] of [ - ["pgstencil", "pgstencil-0.1.0.tgz"], - ["@pgstencil/auth", "pgstencil-auth-0.1.0.tgz"], + ["pgstencil", "pgstencil-0.2.0.tgz"], + ["@pgstencil/auth", "pgstencil-auth-0.2.0.tgz"], ]) { expect(dependencies[name]).toBe(`file:../vendor/${filename}`); const override = workspace.match( @@ -77,7 +77,7 @@ test("Hosted declares every peer of the pinned archives", () => { const { dependencies } = JSON.parse( readFileSync("package.json", "utf8"), ) as { dependencies: Record }; - for (const archive of ["pgstencil-0.1.0.tgz", "pgstencil-auth-0.1.0.tgz"]) { + for (const archive of ["pgstencil-0.2.0.tgz", "pgstencil-auth-0.2.0.tgz"]) { const manifest = execFileSync( "tar", ["-xOf", "../vendor/" + archive, "package/package.json"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e362ad666..fbad1ac75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,8 +5,8 @@ settings: excludeLinksFromLockfile: false overrides: - pgstencil: file:vendor/pgstencil-0.1.0.tgz - '@pgstencil/auth': file:vendor/pgstencil-auth-0.1.0.tgz + pgstencil: file:vendor/pgstencil-0.2.0.tgz + '@pgstencil/auth': file:vendor/pgstencil-auth-0.2.0.tgz importers: @@ -97,8 +97,8 @@ importers: specifier: ^2.0.10 version: 2.1.1(hono@4.13.8) '@pgstencil/auth': - specifier: file:../vendor/pgstencil-auth-0.1.0.tgz - version: file:vendor/pgstencil-auth-0.1.0.tgz(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@file:vendor/pgstencil-0.1.0.tgz(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11) + specifier: file:../vendor/pgstencil-auth-0.2.0.tgz + version: file:vendor/pgstencil-auth-0.2.0.tgz(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11) hono: specifier: ^4.13.8 version: 4.13.8 @@ -106,8 +106,8 @@ importers: specifier: ^0.29.5 version: 0.29.5 pgstencil: - specifier: file:../vendor/pgstencil-0.1.0.tgz - version: file:vendor/pgstencil-0.1.0.tgz(kysely@0.29.5)(supports-color@10.2.2) + specifier: file:../vendor/pgstencil-0.2.0.tgz + version: file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2) react: specifier: ^19.2.6 version: 19.3.0 @@ -2455,14 +2455,14 @@ packages: cpu: [x64] os: [win32] - '@pgstencil/auth@file:vendor/pgstencil-auth-0.1.0.tgz': - resolution: {integrity: sha512-uSTdJg6o55DELcvTfeDJTJClsupe/z1NJJw0RFVdAOZIu+DexsSb/Xx5kY5Bcpyq7V51DKhu5rQzZ2BC14yUGw==, tarball: file:vendor/pgstencil-auth-0.1.0.tgz} - version: 0.1.0 + '@pgstencil/auth@file:vendor/pgstencil-auth-0.2.0.tgz': + resolution: {integrity: sha512-hrYW0KoGSAOiLxrr4sashv5ImR7DmgzshbcXCol2gaNdA7kw243F+xe7vyuFXjI2l2NC5Th5Di4/gExm1pTqvQ==, tarball: file:vendor/pgstencil-auth-0.2.0.tgz} + version: 0.2.0 engines: {node: '>=24'} peerDependencies: hono: ^4.13.7 kysely: ^0.29.5 - pgstencil: ^0.1.0 + pgstencil: ^0.2.0 '@phosphor-icons/react@2.1.10': resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} @@ -5389,9 +5389,9 @@ packages: pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} - pgstencil@file:vendor/pgstencil-0.1.0.tgz: - resolution: {integrity: sha512-XKNjf7opuYcQU1+zK0YCFb3eXvMGsjIrHIXyUANpZoBVWd1XV8c4i4u6S0d3rqjOw9LHeTu/2kdA+Z9wSrCSPw==, tarball: file:vendor/pgstencil-0.1.0.tgz} - version: 0.1.0 + pgstencil@file:vendor/pgstencil-0.2.0.tgz: + resolution: {integrity: sha512-YizN4tswKpHODDQymqiW+TbN+Khe4snCJDy0C/PPbPXrP6/NUpEaSybaMnwP97z8bb2N/oa3TQA5hixqkk3lAQ==, tarball: file:vendor/pgstencil-0.2.0.tgz} + version: 0.2.0 engines: {node: '>=24'} peerDependencies: kysely: ^0.29.5 @@ -7952,14 +7952,14 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.21.2': optional: true - '@pgstencil/auth@file:vendor/pgstencil-auth-0.1.0.tgz(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@file:vendor/pgstencil-0.1.0.tgz(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11)': + '@pgstencil/auth@file:vendor/pgstencil-auth-0.2.0.tgz(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11)': dependencies: better-auth: 1.7.3(pg@8.23.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11) hono: 4.13.8 jose: 6.2.12 kysely: 0.29.5 openid-client: 6.8.8 - pgstencil: file:vendor/pgstencil-0.1.0.tgz(kysely@0.29.5)(supports-color@10.2.2) + pgstencil: file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2) transitivePeerDependencies: - '@cloudflare/workers-types' - '@lynx-js/react' @@ -10911,7 +10911,7 @@ snapshots: dependencies: split2: 4.2.0 - pgstencil@file:vendor/pgstencil-0.1.0.tgz(kysely@0.29.5)(supports-color@10.2.2): + pgstencil@file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2): dependencies: cheerio: 1.2.0 kysely: 0.29.5 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e70ea5fb0..798567bd9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -43,5 +43,5 @@ peerDependencyRules: "@hono/node-ws>@hono/node-server": ^2.0.0 minimumReleaseAge: 1440 overrides: - pgstencil: file:vendor/pgstencil-0.1.0.tgz - '@pgstencil/auth': file:vendor/pgstencil-auth-0.1.0.tgz + pgstencil: file:vendor/pgstencil-0.2.0.tgz + '@pgstencil/auth': file:vendor/pgstencil-auth-0.2.0.tgz diff --git a/vendor/build.json b/vendor/build.json index 217299d6b..52a85fb35 100644 --- a/vendor/build.json +++ b/vendor/build.json @@ -1,14 +1,14 @@ { - "commit": "eaba6d3f15b7a7ecb102e88d3e446e13a32b2f72", + "commit": "e79cc4d350c0e66066405b1c042f6f6df7526381", "dirty": false, "files": [ { - "filename": "pgstencil-0.1.0.tgz", - "sha256": "7e95e6f90b567eb6e6e757fe3f0c32ff2b0e65ff7791b51da43c442536947c7a" + "filename": "pgstencil-0.2.0.tgz", + "sha256": "014bce9e19c028acf8093a608538cb57bbc04a2da1b4b7e57435e3cf2c7f024f" }, { - "filename": "pgstencil-auth-0.1.0.tgz", - "sha256": "0f7539ad5171c2d88dd294eda5d1d96af08a4946945a01d4259f356c07c26fca" + "filename": "pgstencil-auth-0.2.0.tgz", + "sha256": "f1c07fa34ec45ecbe7f100b1f0ceccce50d882cbef8b22f00a0e1ca81bec88ea" } ] } diff --git a/vendor/pgstencil-0.1.0.tgz b/vendor/pgstencil-0.1.0.tgz deleted file mode 100644 index b5e62eb3650d6d88f6db8b737cb15ec98f792aed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16093 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 jtA*~i48d&N02~1T=c$E1 diff --git a/vendor/pgstencil-0.2.0.tgz b/vendor/pgstencil-0.2.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..85b20e5859393fa6441d542b9f21450440646c13 GIT binary patch literal 16412 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 literal 0 HcmV?d00001 diff --git a/vendor/pgstencil-auth-0.1.0.tgz b/vendor/pgstencil-auth-0.1.0.tgz deleted file mode 100644 index 5abcc6fa90c9a2fcdc1b0ddf187751455714e444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36738 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 diff --git a/vendor/pgstencil-auth-0.2.0.tgz b/vendor/pgstencil-auth-0.2.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3ce440a4ab93c1f4c4ad3dddd5bcb22f8b63eaa2 GIT binary patch literal 37112 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 E0Os@M1^@s6 literal 0 HcmV?d00001 From 2e4c591fb2b050ae9b39b1154801f01050978b37 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 24 Sep 2026 20:50:23 -0700 Subject: [PATCH 03/10] State the cancelled-run rule in the spec, not only the audit prompt The security-hosted FAIL IF still failed a commit with a cancelled duplicate run. The prompt now ignores cancelled and requires at least one success and no other conclusion, and the spec says the same. Co-Authored-By: Claude Sonnet 5 --- .github/audit/hosted.md | 8 ++++---- docs/specs/security-hosted.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/audit/hosted.md b/.github/audit/hosted.md index a179c7ee3..2febbb861 100644 --- a/.github/audit/hosted.md +++ b/.github/audit/hosted.md @@ -30,10 +30,10 @@ gh api repos/diffplug/pgstencil/commits//check-runs \ --jq '.check_runs[] | select(.name=="security-audit") | .conclusion' ``` -The first must be `ahead` or `identical`, the second `success`. A commit can -carry several `security-audit` runs, and a `cancelled` one, from a manual -dispatch that was stopped, is not a verdict: ignore it, and require that a -completed run succeeded and none failed. The packed code +The first must be `ahead` or `identical`. A commit can carry several +`security-audit` runs, and a `cancelled` one, from a manual dispatch that was +stopped, is not a verdict: ignore `cancelled`, then require at least one +`success` and no other conclusion. The packed code itself is audited in `diffplug/pgstencil` by that repository's own `security-audit` workflow against its `SECURITY.md`; do not audit the tarballs' contents here — audit how `hosted/` configures the adapter. Distinguish tested diff --git a/docs/specs/security-hosted.md b/docs/specs/security-hosted.md index 73803d056..d11f7e0df 100644 --- a/docs/specs/security-hosted.md +++ b/docs/specs/security-hosted.md @@ -27,7 +27,7 @@ Pinned by `hosted/server/tests/workers.test.ts` and `hosted/server/tests/policy. - **FAIL IF** a production Worker exposes the captured-email inbox or deterministic clock controls, or imports the testing injection module; inspect `hosted/server/worker.ts`, the build configuration, and `hosted/server/tests/worker-entry.ts`. - **FAIL IF** an archive's SHA-256 differs from `vendor/build.json`, `build.json` records `dirty`, either archive's `package/dist/provenance.json` is missing, records `dirty`, or names a commit other than `build.json`'s, the core/auth pnpm overrides cease resolving to those archives, or a runtime import depends on a sibling source checkout. -- **FAIL IF** `vendor/build.json`'s commit is not on pgstencil `main` (`gh api repos/diffplug/pgstencil/compare/...main`, status `ahead` or `identical`), or that commit's `security-audit` check run (`gh api repos/diffplug/pgstencil/commits//check-runs`) is missing or not `success`. pgstencil's own audit is the evidence for the packed code; Dormouse audits only how Hosted configures it. +- **FAIL IF** `vendor/build.json`'s commit is not on pgstencil `main` (`gh api repos/diffplug/pgstencil/compare/...main`, status `ahead` or `identical`), or that commit's `security-audit` check runs (`gh api repos/diffplug/pgstencil/commits//check-runs`) include no `success`, or any conclusion other than `success` and `cancelled`. pgstencil's own audit is the evidence for the packed code; Dormouse audits only how Hosted configures it. - **FAIL IF** the local email inbox accepts a foreign Host or Origin or cross-site Fetch Metadata; inspect `allowedDevRequest` in `hosted/server/dev-host-guard.ts`, including the upgrade guard in `hosted/server/dev.ts`. - **FAIL IF** the production deploy can proceed without `preflight` establishing an uncached Hyperdrive, a matching migration/runtime database, and distinct runtime and migration roles; inspect `preflight` in `hosted/scripts/production.mjs` and its ordering ahead of the deploy step in `.github/workflows/hosted-production.yml`. From 0b64caafb98e89321e4cf1e02fb802b972d03727 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 25 Sep 2026 00:17:58 -0700 Subject: [PATCH 04/10] Install audited pgstencil release from npm for Hosted --- .github/audit/hosted.md | 16 +-- .github/renovate.json | 10 ++ docs/specs/hosted.md | 8 +- docs/specs/security-audit.rationale.md | 2 +- docs/specs/security-hosted.md | 6 +- hosted/README.md | 35 ++---- hosted/package.json | 4 +- hosted/scripts/changed.mjs | 2 +- hosted/scripts/changed.test.mjs | 1 - hosted/scripts/production.mjs | 60 +++------ hosted/server/tests/artifacts.test.ts | 110 ++++------------- package.json | 1 - pnpm-lock.yaml | 38 ++---- pnpm-workspace.yaml | 6 +- scripts/spec-lint.mjs | 2 +- scripts/sync-pgstencil.mjs | 161 ------------------------- vendor/build.json | 14 --- vendor/pgstencil-0.2.0.tgz | Bin 16412 -> 0 bytes vendor/pgstencil-auth-0.2.0.tgz | Bin 37112 -> 0 bytes 19 files changed, 102 insertions(+), 374 deletions(-) delete mode 100644 scripts/sync-pgstencil.mjs delete mode 100644 vendor/build.json delete mode 100644 vendor/pgstencil-0.2.0.tgz delete mode 100644 vendor/pgstencil-auth-0.2.0.tgz diff --git a/.github/audit/hosted.md b/.github/audit/hosted.md index 2febbb861..d91575275 100644 --- a/.github/audit/hosted.md +++ b/.github/audit/hosted.md @@ -18,11 +18,11 @@ Read `docs/specs/hosted.md`, `hosted/server/`, `hosted/src/`, `hosted/scripts/`, Deployment boundary quantifies over the preview and production paths, which live in those scripts and workflows rather than in the Worker. -Verify the vendored packages by their provenance rather than by reading them: -hash each archive in `vendor/` against `vendor/build.json`; read each archive's -own claim with `tar -xOf vendor/.tgz package/dist/provenance.json` and -check that it names `build.json`'s commit and does not record `dirty`; then -check that commit against pgstencil `main` and its audit: +Verify the installed `pgstencil` and `@pgstencil/auth` packages by reading each +`dist/provenance.json`, without auditing package code. Require a 40-hex commit, +no `dirty: true`, and the same commit in both packages. Confirm `pnpm-lock.yaml` +resolves both through the npm registry with integrity hashes. Then check the +commit against pgstencil `main` and its audit: ```sh gh api repos/diffplug/pgstencil/compare/...main --jq .status @@ -33,9 +33,9 @@ gh api repos/diffplug/pgstencil/commits//check-runs \ The first must be `ahead` or `identical`. A commit can carry several `security-audit` runs, and a `cancelled` one, from a manual dispatch that was stopped, is not a verdict: ignore `cancelled`, then require at least one -`success` and no other conclusion. The packed code +`success` and no other conclusion. The released code itself is audited in `diffplug/pgstencil` by that repository's own -`security-audit` workflow against its `SECURITY.md`; do not audit the tarballs' +`security-audit` workflow against its `SECURITY.md`; do not audit the installed packages' contents here — audit how `hosted/` configures the adapter. Distinguish tested code from pending production configuration; do not treat local provider simulations as live OAuth acceptance, and treat a checked-in placeholder as no @@ -45,7 +45,7 @@ report its state as INFO under `### Qualitative findings`. ## Qualitative pass -You own `hosted/` and `vendor/`. You **read** `.github/workflows/hosted-preview.yml` +You own `hosted/` and the installed pgstencil boundary. You **read** `.github/workflows/hosted-preview.yml` and `.github/workflows/hosted-production.yml` for the Deployment boundary above, but you do not own them: `ci-and-secrets` owns those workflows' credentials, environments, reviewers, and token placement diff --git a/.github/renovate.json b/.github/renovate.json index 2d81c9159..87bae9803 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -148,6 +148,16 @@ "matchPackageNames": ["node"], "enabled": false }, + { + "description": "pgstencil releases are staged by a workflow and approved with 2FA; group the three packages and open the PR as soon as one is approved", + "matchManagers": ["npm"], + "matchPackageNames": ["pgstencil", "@pgstencil/**"], + "groupName": "pgstencil", + "groupSlug": "pgstencil", + "separateMajorMinor": false, + "minimumReleaseAge": null, + "schedule": ["at any time"] + }, { "description": "canopy pins the pristine @xterm/addon-webgl AND @xterm/xterm to the exact commit the SDF fork's sdf branch is based on (the addon's beta counter is offset from core's; canopy/README.md records the current correspondence). They are the UpstreamVsFork regression baseline and move in lockstep with the hand-cut @diffplug/xterm-addon-webgl-sdf tarball, which Renovate cannot see, so bumping either one is a manual edit made when the fork rebases — never a Renovate bump (see canopy/README.md and docs/specs/webgl-text.md). File-scoped: lib and standalone still track upstream betas via the xterm group above. Must stay last; later rules win", "matchManagers": ["npm"], diff --git a/docs/specs/hosted.md b/docs/specs/hosted.md index e06595d3a..a8aee932c 100644 --- a/docs/specs/hosted.md +++ b/docs/specs/hosted.md @@ -9,11 +9,11 @@ **Must run committed Better Auth migrations before deploying code that needs them, never during a Worker request.** Postgres is reached through an uncached Hyperdrive binding. The runtime creates and closes its database pool within each request. -**Must pin locally packed core/auth packages through root pnpm overrides and commit archives, provenance, and lockfile together.** `vendor/build.json` records the source commit, archive hashes, and `dirty: false`, which production preflight requires; each archive's `package/dist/provenance.json` names its source commit, and a pack pgstencil marked dirty cannot be vendored. No runtime import depends on a sibling checkout. The auth migrations remain owned by the package. +**Must install released core/auth packages from npm and commit their lockfile integrity hashes.** The installed packages' `dist/provenance.json` must name the same clean pgstencil commit; no runtime import depends on a sibling checkout. The auth migrations remain owned by the package. -**Must declare every peer dependency of the pinned archives in `hosted/package.json`**, so they share Hosted's copy and Renovate updates them. +**Must declare every peer dependency of the installed packages in `hosted/package.json`**, so they share Hosted's copy and Renovate updates them. -Source of truth: `auth` in `hosted/server/worker.ts`; `workerApp` in `hosted/server/worker-app.ts`; `migrations` in `hosted/server/migrations.ts`; `scripts/sync-pgstencil.mjs`. Pinned by `hosted/server/tests/artifacts.test.ts`. +Source of truth: `auth` in `hosted/server/worker.ts`; `workerApp` in `hosted/server/worker-app.ts`; `migrations` in `hosted/server/migrations.ts`; `verifyPackages` in `hosted/scripts/production.mjs`. Pinned by `hosted/server/tests/artifacts.test.ts`. ## Identity and login @@ -65,7 +65,7 @@ Source of truth: `touchesHosted` in `hosted/scripts/changed.mjs`; `.github/workf ## Production releases -**Must deploy only manually selected main revisions after Hosted tests/build and accepted clean package provenance.** `verifyPackages` checks the archive hashes and each archive's own packed provenance; preflight checks uncached Hyperdrive, matching migration/runtime database identity with distinct roles, and required Worker secret names. Back up, encrypt, decrypt, and restore-test before applying migrations; upload only the encrypted archive. Production has no public candidate URL. +**Must deploy only manually selected main revisions after Hosted tests/build and accepted clean package provenance.** `verifyPackages` checks both installed packages' clean, matching provenance; preflight checks uncached Hyperdrive, matching migration/runtime database identity with distinct roles, and required Worker secret names. Back up, encrypt, decrypt, and restore-test before applying migrations; upload only the encrypted archive. Production has no public candidate URL. **Must record an immutable annotated hosted/YYYY-MM-DD tag only after live verification.** Tags identify the deployed commit and verification run/attempt; retries are idempotent and redeployments get new tags. Dating and repeat-deployment suffixes: `recordDeployment`. Code rollback never reverses migrations. diff --git a/docs/specs/security-audit.rationale.md b/docs/specs/security-audit.rationale.md index 373b66820..489860f2e 100644 --- a/docs/specs/security-audit.rationale.md +++ b/docs/specs/security-audit.rationale.md @@ -8,7 +8,7 @@ The three release-gate pieces are named separately because they break independen One context holding every subject matter degrades application security — the domain with the most code behind it, and the easiest to crowd out with API responses. -Hosted accounts were split out of `application-security` on 2026-09-21. That domain already carried remote control (where the depth goes), the local boundaries, Hosted, and the catch-all sweep, and it had overrun the 32-minute deadline more than once, so a remote-control pass that ran out of time took the Hosted results down with it. Hosted is a disjoint tree — `hosted/`, `vendor/`, and the two `hosted-*.yml` workflows it reads for the Deployment boundary — with its own spec, so it splits cleanly and now writes its own fragment. It also gives the pgstencil provenance checks a prompt that is about them rather than a paragraph inside one about pairing code. It runs on Opus for the same reason `application-security` does: the Worker's origin gate and the deployment path are read, not enumerated. +Hosted accounts were split out of `application-security` on 2026-09-21. That domain already carried remote control (where the depth goes), the local boundaries, Hosted, and the catch-all sweep, and it had overrun the 32-minute deadline more than once, so a remote-control pass that ran out of time took the Hosted results down with it. Hosted is a disjoint tree — `hosted/`, its installed pgstencil dependencies, and the two `hosted-*.yml` workflows it reads for the Deployment boundary — with its own spec, so it splits cleanly and now writes its own fragment. It also gives the pgstencil provenance checks a prompt that is about them rather than a paragraph inside one about pairing code. It runs on Opus for the same reason `application-security` does: the Worker's origin gate and the deployment path are read, not enumerated. Folding the application-security scope back into a shared context is how that spec stops being audited without anyone deciding to stop auditing it. diff --git a/docs/specs/security-hosted.md b/docs/specs/security-hosted.md index d11f7e0df..11601ef1c 100644 --- a/docs/specs/security-hosted.md +++ b/docs/specs/security-hosted.md @@ -23,11 +23,11 @@ Pinned by `hosted/server/tests/workers.test.ts` and `hosted/server/tests/policy. ## Deployment boundary -**Must vendor pgstencil from a commit on its `main`.** A Dormouse branch may vendor a pgstencil branch while a cross-repo change is in flight; `main` must not merge it until pgstencil has. +**Must depend on a released pgstencil** whose installed `dist/provenance.json` names a commit on pgstencil `main` with a passing `security-audit`. The core and auth packages must name the same clean commit. - **FAIL IF** a production Worker exposes the captured-email inbox or deterministic clock controls, or imports the testing injection module; inspect `hosted/server/worker.ts`, the build configuration, and `hosted/server/tests/worker-entry.ts`. -- **FAIL IF** an archive's SHA-256 differs from `vendor/build.json`, `build.json` records `dirty`, either archive's `package/dist/provenance.json` is missing, records `dirty`, or names a commit other than `build.json`'s, the core/auth pnpm overrides cease resolving to those archives, or a runtime import depends on a sibling source checkout. -- **FAIL IF** `vendor/build.json`'s commit is not on pgstencil `main` (`gh api repos/diffplug/pgstencil/compare/...main`, status `ahead` or `identical`), or that commit's `security-audit` check runs (`gh api repos/diffplug/pgstencil/commits//check-runs`) include no `success`, or any conclusion other than `success` and `cancelled`. pgstencil's own audit is the evidence for the packed code; Dormouse audits only how Hosted configures it. +- **FAIL IF** either installed pgstencil package lacks `dist/provenance.json`, records `dirty`, or names a different commit; or `pnpm-lock.yaml` resolves either package from anything but the npm registry. Inspect `verifyPackages` in `hosted/scripts/production.mjs` and `hosted/server/tests/artifacts.test.ts`. +- **FAIL IF** that commit is not on pgstencil `main` (`gh api repos/diffplug/pgstencil/compare/...main`, status `ahead` or `identical`), or its `security-audit` check runs (`gh api repos/diffplug/pgstencil/commits//check-runs`) include no `success`, or any conclusion other than `success` and `cancelled`. pgstencil audits the released code; Dormouse audits only how Hosted configures it. - **FAIL IF** the local email inbox accepts a foreign Host or Origin or cross-site Fetch Metadata; inspect `allowedDevRequest` in `hosted/server/dev-host-guard.ts`, including the upgrade guard in `hosted/server/dev.ts`. - **FAIL IF** the production deploy can proceed without `preflight` establishing an uncached Hyperdrive, a matching migration/runtime database, and distinct runtime and migration roles; inspect `preflight` in `hosted/scripts/production.mjs` and its ordering ahead of the deploy step in `.github/workflows/hosted-production.yml`. diff --git a/hosted/README.md b/hosted/README.md index 5fa25a7f0..8cca6f2f7 100644 --- a/hosted/README.md +++ b/hosted/README.md @@ -5,7 +5,7 @@ The marketing website is a separate application. Hosted voice and the managed Relay are not implemented. See [the spec](../docs/specs/hosted.md). This file is the whole operator runbook, in the order an operator works: run -locally, refresh packages, set up GitHub, provision previews, provision +locally, update packages, set up GitHub, provision previews, provision production, release, accept, recover. Marketing, desktop releases, Hosted production, and PR previews have separate deployment credentials. A passing local test implies no cloud resource and no real-provider acceptance. @@ -35,29 +35,20 @@ Tests run the production composition in real workerd with disposable Postgres clones and a local OAuth simulator. The build includes a Wrangler dry-run; it does not deploy. -## Refresh private packages +## Update pgstencil -```sh -node scripts/sync-pgstencil.mjs /path/to/pgstencil [revision] -``` +`hosted/package.json` installs released `pgstencil` and `@pgstencil/auth` from +npm. They share a version and Hosted declares their peer dependencies. Approved +pgstencil releases are exempt from the pnpm and Renovate cooldowns because the +release workflow requires a passing security audit, stages the archives, and +requires a maintainer's 2FA approval before publishing. -This checks out the pgstencil revision (default `HEAD`) in a temporary clean -worktree, runs `pnpm packages:pack` there, vendors core/auth, records the commit, -`dirty: false` and SHA-256 hashes in `vendor/build.json`, and installs. Each -archive carries its own `package/dist/provenance.json`, and the sync refuses one -that is missing it, that reports a dirty pack, or that names a commit other than -the one packed here. To try an unfinished pgstencil change, commit it to a local -branch and sync that revision. -The sync also warns when the vendored commit is not on pgstencil's -`origin/main`: a Dormouse branch may vendor a pgstencil branch while a -cross-repo change is in flight, but Dormouse `main` must vendor a pgstencil -`main` commit, and the nightly audit fails until it does. The direct Node -command also works before the archives exist (pnpm may otherwise auto-install -first). See `docs/specs/hosted.md` -> "Application boundary" for what has to be -committed together. - -Re-run integration tests after every refresh. Vendor an accepted pgstencil -revision before a production release. +Run `pnpm install` and re-run Hosted's integration tests after an update. The +production preflight reads `dist/provenance.json` from the installed packages +and requires matching clean commits. `docs/specs/security-hosted.md` -> +"Deployment boundary" owns the upstream audit check. For an unreleased change, +pack pgstencil in a clean checkout and use a temporary pnpm override on a +branch. ## Resource inventory diff --git a/hosted/package.json b/hosted/package.json index ac00faf5e..39ef3d0fd 100644 --- a/hosted/package.json +++ b/hosted/package.json @@ -18,8 +18,8 @@ "preview:smoke": "node scripts/preview-smoke.mjs" }, "dependencies": { - "@pgstencil/auth": "file:../vendor/pgstencil-auth-0.2.0.tgz", - "pgstencil": "file:../vendor/pgstencil-0.2.0.tgz", + "@pgstencil/auth": "^0.2.1", + "pgstencil": "^0.2.1", "kysely": "^0.29.5", "hono": "^4.13.8", "@hono/node-server": "^2.0.10", diff --git a/hosted/scripts/changed.mjs b/hosted/scripts/changed.mjs index ace7d765f..ac1db63ae 100644 --- a/hosted/scripts/changed.mjs +++ b/hosted/scripts/changed.mjs @@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url"; export function touchesHosted(paths) { return paths.some( (path) => - /^(hosted\/|vendor\/|lib\/src\/(theme|lib\/(themes\/|(?:local-json-store|is-record|css-color)\.ts$))|scripts\/sync-pgstencil\.mjs$|\.github\/workflows\/hosted-[^/]+\.yml$)/.test( + /^(hosted\/|lib\/src\/(theme|lib\/(themes\/|(?:local-json-store|is-record|css-color)\.ts$))|\.github\/workflows\/hosted-[^/]+\.yml$)/.test( path, ) || ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"].includes(path), diff --git a/hosted/scripts/changed.test.mjs b/hosted/scripts/changed.test.mjs index 85cc5f9e6..ce6a5a43e 100644 --- a/hosted/scripts/changed.test.mjs +++ b/hosted/scripts/changed.test.mjs @@ -5,7 +5,6 @@ test("Hosted and shared inputs trigger previews; unrelated application changes d for (const path of [ "hosted/README.md", "hosted/server/worker.ts", - "vendor/build.json", "pnpm-lock.yaml", ".github/workflows/hosted-preview.yml", "lib/src/theme-colors.css", diff --git a/hosted/scripts/production.mjs b/hosted/scripts/production.mjs index e47a9c3c5..801cffe82 100644 --- a/hosted/scripts/production.mjs +++ b/hosted/scripts/production.mjs @@ -1,7 +1,5 @@ import assert from "node:assert/strict"; -import { createHash } from "node:crypto"; import { readFile, writeFile, mkdir, rm } from "node:fs/promises"; -import { spawnSync } from "node:child_process"; import { resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { required, cloudflare, hyperdriveOrigin } from "./preview.mjs"; @@ -31,54 +29,30 @@ export function productionConfig(base, env) { }; } export async function verifyPackages() { - const manifest = JSON.parse( - await readFile(new URL("../../vendor/build.json", import.meta.url), "utf8"), - ); - assert.match(manifest.commit, /^[a-f0-9]{40}$/); - assert.equal( - manifest.dirty, - false, - "Production requires a clean sync of a committed pgstencil revision; refresh the vendored packages first", - ); - assert.deepEqual( - manifest.files.map((entry) => entry.filename).sort(), - ["pgstencil-0.2.0.tgz", "pgstencil-auth-0.2.0.tgz"], - "Expected both pinned pgstencil archives", - ); - for (const entry of manifest.files) { - assert.match(entry.filename, /^pgstencil(?:-auth)?-[\w.-]+\.tgz$/); - const archive = new URL(`../../vendor/${entry.filename}`, import.meta.url); - assert.equal( - createHash("sha256") - .update(await readFile(archive)) - .digest("hex"), - entry.sha256, - "Vendored archive checksum mismatch", - ); - // The archive's own provenance is what pgstencil's audit is keyed to — - // docs/specs/security-hosted.md -> "Deployment boundary". - const packed = spawnSync( - "tar", - ["-xOf", fileURLToPath(archive), "package/dist/provenance.json"], - { encoding: "utf8" }, + const commits = []; + for (const name of ["pgstencil", "@pgstencil/auth/better-auth"]) { + // Resolve the installed entrypoint, then read the provenance beside it. + const entry = import.meta.resolve(name); + const provenance = JSON.parse( + await readFile(new URL("./provenance.json", entry), "utf8"), ); - assert.equal( - packed.status, - 0, - `${entry.filename} carries no package/dist/provenance.json; refresh the vendored packages first`, + assert.match( + provenance.commit, + /^[a-f0-9]{40}$/, + "Production requires accepted, clean pgstencil provenance", ); - const provenance = JSON.parse(packed.stdout); assert.notEqual( provenance.dirty, true, - "Production requires accepted, clean pgstencil provenance; refresh the vendored packages first", - ); - assert.equal( - provenance.commit, - manifest.commit, - `${entry.filename} was packed from a commit other than the one vendor/build.json records`, + "Production requires accepted, clean pgstencil provenance", ); + commits.push(provenance.commit); } + assert.equal( + commits[0], + commits[1], + "Production requires accepted, clean pgstencil provenance", + ); } export async function preflight(env, config, api = cloudflare(env)) { const origin = hyperdriveOrigin(required(env, "DATABASE_URL")); diff --git a/hosted/server/tests/artifacts.test.ts b/hosted/server/tests/artifacts.test.ts index e29caa278..21d5a6ac1 100644 --- a/hosted/server/tests/artifacts.test.ts +++ b/hosted/server/tests/artifacts.test.ts @@ -1,94 +1,36 @@ import { test, expect } from "vitest"; -import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; -import { execFileSync } from "node:child_process"; -import { fileURLToPath } from "node:url"; -test("consumed package bytes match the recorded source snapshot", () => { - const build = JSON.parse(readFileSync("../vendor/build.json", "utf8")) as { - commit: string; - dirty: boolean; - files: { filename: string; sha256: string }[]; - }; - expect(build.commit).toMatch(/^[a-f0-9]{40}$/); - // The sync only packs committed revisions; production preflight requires this. - expect(build.dirty).toBe(false); - expect(build.files.map((file) => file.filename).sort()).toEqual([ - "pgstencil-0.2.0.tgz", - "pgstencil-auth-0.2.0.tgz", - ]); - for (const file of build.files) { - expect( - createHash("sha256") - .update(readFileSync("../vendor/" + file.filename)) - .digest("hex"), - ).toBe(file.sha256); - // Each archive names the pgstencil commit it was packed from, so the bytes - // themselves — not just build.json — say what pgstencil's own audit covers. - const provenance = JSON.parse( - execFileSync("tar", [ - "-xOf", - "../vendor/" + file.filename, - "package/dist/provenance.json", - ]).toString(), - ) as { commit: string; dirty?: boolean }; - expect(provenance.commit).toBe(build.commit); - expect(provenance.dirty).not.toBe(true); - } - // Same-version tarball refreshes must update installed code as well as metadata. - for (const [archive, entry] of [ - ["pgstencil-0.2.0.tgz", "pgstencil"], - ["pgstencil-auth-0.2.0.tgz", "@pgstencil/auth/better-auth"], - ]) { - const file = fileURLToPath(import.meta.resolve(entry)); - const archivePath = `package/dist/${file.split("/").at(-1)}`; - const packed = execFileSync("tar", [ - "-xOf", - "../vendor/" + archive, - archivePath, - ]); - expect(readFileSync(file)).toEqual(packed); - } -}); +const packages = [ + ["pgstencil", "pgstencil"], + ["@pgstencil/auth", "@pgstencil/auth/better-auth"], +] as const; -// pnpm resolves the workspace override ahead of hosted/package.json, so a bump -// that edits only one of them installs a build the snapshot above never saw. -test("both pinned specifiers name the recorded archives", () => { - const { dependencies } = JSON.parse( - readFileSync("package.json", "utf8"), - ) as { dependencies: Record }; - const workspace = readFileSync("../pnpm-workspace.yaml", "utf8"); - for (const [name, filename] of [ - ["pgstencil", "pgstencil-0.2.0.tgz"], - ["@pgstencil/auth", "pgstencil-auth-0.2.0.tgz"], - ]) { - expect(dependencies[name]).toBe(`file:../vendor/${filename}`); - const override = workspace.match( - new RegExp(`^ {2}'?${name}'?: (\\S+)$`, "m"), - )?.[1]; - expect(override).toBe(`file:vendor/${filename}`); +test("Hosted declares every peer of the installed pgstencil packages", () => { + const { dependencies } = JSON.parse(readFileSync("package.json", "utf8")) as { + dependencies: Record; + }; + for (const [name, entry] of packages) { + const manifest = new URL("../package.json", import.meta.resolve(entry)); + const { peerDependencies = {} } = JSON.parse(readFileSync(manifest, "utf8")) as { + peerDependencies?: Record; + }; + for (const peer of Object.keys(peerDependencies)) + expect(Object.keys(dependencies), `${name} peers on ${peer}`).toContain(peer); } }); -// strictPeerDependencies only rejects an out-of-range peer. pnpm resolves an -// undeclared one itself, where Renovate never sees it and Hosted's own imports -// can get a second copy. -test("Hosted declares every peer of the pinned archives", () => { - const { dependencies } = JSON.parse( - readFileSync("package.json", "utf8"), - ) as { dependencies: Record }; - for (const archive of ["pgstencil-0.2.0.tgz", "pgstencil-auth-0.2.0.tgz"]) { - const manifest = execFileSync( - "tar", - ["-xOf", "../vendor/" + archive, "package/package.json"], - { encoding: "utf8" }, +test("the lockfile resolves both pgstencil packages from npm", () => { + const lockfile = readFileSync("../pnpm-lock.yaml", "utf8"); + const { dependencies } = JSON.parse(readFileSync("package.json", "utf8")) as { + dependencies: Record; + }; + for (const [name] of packages) { + expect(dependencies[name]).toBe("^0.2.1"); + const key = name.startsWith("@") ? `'${name}@0.2.1'` : `${name}@0.2.1`; + expect(lockfile).toContain( + ` ${key}:\n resolution: {integrity: sha512-`, ); - const { peerDependencies = {} } = JSON.parse(manifest) as { - peerDependencies?: Record; - }; - for (const peer of Object.keys(peerDependencies)) - expect(Object.keys(dependencies), `${archive} peers on ${peer}`).toContain( - peer, - ); } + expect(lockfile).not.toMatch(/file:[^\s]*pgstencil/); }); diff --git a/package.json b/package.json index 2268640df..dfab8efef 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "dev:hosted": "pnpm --filter dormouse-hosted dev", "build:hosted": "pnpm --filter dormouse-hosted build", "test:hosted": "pnpm --filter dormouse-hosted test", - "pgstencil:sync": "node scripts/sync-pgstencil.mjs", "build": "pnpm run build:vscode && pnpm --filter dormouse-lib build:pocket && pnpm --filter dormouse-website build && pnpm build:hosted", "test": "node scripts/spec-lint.mjs && node scripts/spec-lint-selftest.mjs && node scripts/public-docs-lint.mjs && node scripts/xterm-lint.mjs && node --test scripts/xterm-bump.test.mjs && node scripts/loopback-lint.mjs && node scripts/loopback-lint-selftest.mjs && node scripts/deploy-lint.mjs && node scripts/deploy-lint-selftest.mjs && node scripts/installer-verify-test.mjs && node scripts/ps1-cmdlet-lint.mjs && node scripts/ps1-cmdlet-lint-selftest.mjs && node scripts/e2e-lint.mjs && node scripts/e2e-lint-selftest.mjs && node scripts/clamp-issue-body-selftest.mjs && node --test scripts/sign-and-deploy.test.mjs && node --test scripts/workflow-audit.test.mjs && node --test scripts/pairing-walkthrough/proc.test.mjs && node --test scripts/security-audit.test.mjs && pnpm --filter dormouse-hosted test:deploy && pnpm -r --filter \"!dormouse-hosted\" run test", "lint:specs": "node scripts/spec-lint.mjs && node scripts/spec-lint-selftest.mjs", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbad1ac75..94dbd4384 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,10 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - pgstencil: file:vendor/pgstencil-0.2.0.tgz - '@pgstencil/auth': file:vendor/pgstencil-auth-0.2.0.tgz - importers: .: @@ -97,8 +93,8 @@ importers: specifier: ^2.0.10 version: 2.1.1(hono@4.13.8) '@pgstencil/auth': - specifier: file:../vendor/pgstencil-auth-0.2.0.tgz - version: file:vendor/pgstencil-auth-0.2.0.tgz(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11) + specifier: ^0.2.1 + version: 0.2.1(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@0.2.1(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11) hono: specifier: ^4.13.8 version: 4.13.8 @@ -106,8 +102,8 @@ importers: specifier: ^0.29.5 version: 0.29.5 pgstencil: - specifier: file:../vendor/pgstencil-0.2.0.tgz - version: file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2) + specifier: ^0.2.1 + version: 0.2.1(kysely@0.29.5)(supports-color@10.2.2) react: specifier: ^19.2.6 version: 19.3.0 @@ -2455,14 +2451,13 @@ packages: cpu: [x64] os: [win32] - '@pgstencil/auth@file:vendor/pgstencil-auth-0.2.0.tgz': - resolution: {integrity: sha512-hrYW0KoGSAOiLxrr4sashv5ImR7DmgzshbcXCol2gaNdA7kw243F+xe7vyuFXjI2l2NC5Th5Di4/gExm1pTqvQ==, tarball: file:vendor/pgstencil-auth-0.2.0.tgz} - version: 0.2.0 + '@pgstencil/auth@0.2.1': + resolution: {integrity: sha512-B5Wu035642KEr0V/VJf+C0MMM9PiAGvpXF9VTeKeiTnBifYQIkO9Cks+RLPiO2laxdBWLlNxo6RDVbjLaIiufA==} engines: {node: '>=24'} peerDependencies: hono: ^4.13.7 kysely: ^0.29.5 - pgstencil: ^0.2.0 + pgstencil: ^0.2.1 '@phosphor-icons/react@2.1.10': resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} @@ -5389,9 +5384,8 @@ packages: pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} - pgstencil@file:vendor/pgstencil-0.2.0.tgz: - resolution: {integrity: sha512-YizN4tswKpHODDQymqiW+TbN+Khe4snCJDy0C/PPbPXrP6/NUpEaSybaMnwP97z8bb2N/oa3TQA5hixqkk3lAQ==, tarball: file:vendor/pgstencil-0.2.0.tgz} - version: 0.2.0 + pgstencil@0.2.1: + resolution: {integrity: sha512-Rvap6rNjtJfncsZUvM97BQwPsvOoi5lpg0R8a+r+PJOmAKh0UfwLNaoV2iONqJxSnZtSa5A+n6xPQ2ojyA1qdw==} engines: {node: '>=24'} peerDependencies: kysely: ^0.29.5 @@ -6112,10 +6106,6 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici@7.28.0: - resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} - engines: {node: '>=20.18.1'} - undici@7.29.0: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} @@ -7952,14 +7942,14 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.21.2': optional: true - '@pgstencil/auth@file:vendor/pgstencil-auth-0.2.0.tgz(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11)': + '@pgstencil/auth@0.2.1(hono@4.13.8)(kysely@0.29.5)(pg@8.23.0)(pgstencil@0.2.1(kysely@0.29.5)(supports-color@10.2.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11)': dependencies: better-auth: 1.7.3(pg@8.23.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(vitest@4.1.11) hono: 4.13.8 jose: 6.2.12 kysely: 0.29.5 openid-client: 6.8.8 - pgstencil: file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2) + pgstencil: 0.2.1(kysely@0.29.5)(supports-color@10.2.2) transitivePeerDependencies: - '@cloudflare/workers-types' - '@lynx-js/react' @@ -9212,7 +9202,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.28.0 + undici: 7.29.0 whatwg-mimetype: 4.0.0 chokidar@5.0.0: @@ -10911,7 +10901,7 @@ snapshots: dependencies: split2: 4.2.0 - pgstencil@file:vendor/pgstencil-0.2.0.tgz(kysely@0.29.5)(supports-color@10.2.2): + pgstencil@0.2.1(kysely@0.29.5)(supports-color@10.2.2): dependencies: cheerio: 1.2.0 kysely: 0.29.5 @@ -11839,8 +11829,6 @@ snapshots: undici-types@7.18.2: {} - undici@7.28.0: {} - undici@7.29.0: {} undici@8.10.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 798567bd9..85191aeae 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -42,6 +42,6 @@ peerDependencyRules: # sockets that would break first. "@hono/node-ws>@hono/node-server": ^2.0.0 minimumReleaseAge: 1440 -overrides: - pgstencil: file:vendor/pgstencil-0.2.0.tgz - '@pgstencil/auth': file:vendor/pgstencil-auth-0.2.0.tgz +minimumReleaseAgeExclude: + - pgstencil + - "@pgstencil/*" diff --git a/scripts/spec-lint.mjs b/scripts/spec-lint.mjs index d7d9ebdc2..ed78b71c3 100644 --- a/scripts/spec-lint.mjs +++ b/scripts/spec-lint.mjs @@ -84,7 +84,7 @@ import { countWords, proseLines as proseLinesOf, SOURCE_EXTENSIONS } from './spe const SPECS_DIR = 'docs/specs'; const TOP_LEVEL_DIRS = [ - 'lib/', 'standalone/', 'vscode-ext/', 'website/', 'relay/', 'hosted/', 'vendor/', + 'lib/', 'standalone/', 'vscode-ext/', 'website/', 'relay/', 'hosted/', 'remote-lib-common/', 'dor/', 'dor-lib-common/', 'canopy/', 'docs/', 'scripts/', 'deploy/', '.github/', '.claude/', '.vscode/', ]; diff --git a/scripts/sync-pgstencil.mjs b/scripts/sync-pgstencil.mjs deleted file mode 100644 index b32354691..000000000 --- a/scripts/sync-pgstencil.mjs +++ /dev/null @@ -1,161 +0,0 @@ -import { execFileSync } from "node:child_process"; -import { - mkdirSync, - mkdtempSync, - readFileSync, - writeFileSync, - copyFileSync, -} from "node:fs"; -import { tmpdir } from "node:os"; -import { join, resolve } from "node:path"; -import { createHash } from "node:crypto"; -import { fileURLToPath } from "node:url"; - -const root = fileURLToPath(new URL("../", import.meta.url)); -const args = process.argv.slice(2); -// There are no flags, so a stray one (such as the removed --working-tree), or an -// empty argument from an unset variable, must not be read as a path or revision. -if ( - args.length < 1 || - args.length > 2 || - args.some((arg) => !arg || arg.startsWith("-")) -) - throw new Error("Usage: pnpm pgstencil:sync /path/to/pgstencil [revision]"); -const [source, revision = "HEAD"] = args; -const repository = resolve(source); -const run = (command, args, cwd, env = process.env) => - execFileSync(command, args, { cwd, stdio: "inherit", env }); -const git = (...args) => - execFileSync("git", args, { cwd: repository, encoding: "utf8" }).trim(); -const succeeds = (attempt) => { - try { - attempt(); - return true; - } catch { - return false; - } -}; -// Every pgstencil pack writes package/dist/provenance.json: the archive's own -// claim of the commit it came from, plus `dirty` when it was packed -// --allow-dirty. Dormouse verifies that claim instead of auditing the packed -// code, which pgstencil audits in its own repository — -// docs/specs/security-hosted.md -> "Deployment boundary". -const provenanceOf = (archive) => { - let raw; - try { - raw = execFileSync( - "tar", - ["-xOf", archive, "package/dist/provenance.json"], - { encoding: "utf8" }, - ); - } catch { - throw new Error(`${archive} carries no package/dist/provenance.json`); - } - let claim; - try { - claim = JSON.parse(raw); - } catch { - throw new Error(`${archive}: package/dist/provenance.json is not JSON`); - } - if (typeof claim?.commit !== "string" || !/^[0-9a-f]{40}$/.test(claim.commit)) - throw new Error(`${archive}: provenance.json names no commit`); - return claim; -}; -const manifest = JSON.parse( - readFileSync(resolve(root, "hosted/package.json"), "utf8"), -); -// pnpm resolves `overrides:` ahead of hosted/package.json, so a bump that edits -// only the manifest would still install the previous tarball while build.json -// below records the new one. Read the block by line rather than adding a YAML -// dependency to a root that has no devDependencies at all. -const workspace = readFileSync(resolve(root, "pnpm-workspace.yaml"), "utf8"); -const overrides = new Map( - (/^overrides:\n((?:[ \t].*\n?|\n)*)/m.exec(workspace)?.[1] ?? "") - .split("\n") - .flatMap((line) => { - const entry = /^ {2}['"]?([^'":]+)['"]?: *(\S+)$/.exec(line); - return entry ? [[entry[1], entry[2]]] : []; - }), -); -// Pack a committed revision in a temporary worktree after a frozen install, so -// nothing uncommitted, untracked or ignored in the pgstencil checkout (a stray -// migration, editor settings, stale build output, a local node_modules) can -// reach an archive, and build.json truthfully records `dirty: false`. To try an -// unfinished pgstencil change, commit it to a local branch and sync that. Each -// archive's own provenance must name `commit`; a pack that reports itself -// dirty (pgstencil's --allow-dirty) is refused below rather than vendored. -const commit = git("rev-parse", "--verify", `${revision}^{commit}`); -// Check the pins before the slow install and before any archive is replaced. -const read = (path) => git("show", `${commit}:${path}`); -const archives = [ - ["pgstencil", "pgstencil"], - ["auth", "@pgstencil/auth"], -].map(([directory, name]) => { - const { version } = JSON.parse(read(`packages/${directory}/package.json`)); - const filename = `${name.replace("@", "").replace("/", "-")}-${version}.tgz`; - if (manifest.dependencies[name] !== `file:../vendor/${filename}`) - throw new Error(`Update hosted/package.json for ${filename}`); - if (overrides.get(name) !== `file:vendor/${filename}`) - throw new Error( - `Update the pnpm-workspace.yaml override for ${name} to file:vendor/${filename}`, - ); - return filename; -}); -const checkout = mkdtempSync(join(tmpdir(), "pgstencil-sync-")); -// pgstencil's scripts locate their project from this variable before the cwd. -const pgstencil = { ...process.env, PGSTENCIL_PROJECT_ROOT: checkout }; -git("worktree", "add", "--detach", checkout, commit); -try { - run("pnpm", ["install", "--frozen-lockfile"], checkout, pgstencil); - run("pnpm", ["packages:pack"], checkout, pgstencil); - mkdirSync(resolve(root, "vendor"), { recursive: true }); - for (const filename of archives) { - const target = resolve(root, "vendor", filename); - copyFileSync(resolve(checkout, "dist/packages", filename), target); - // Both archives are held to the commit packed here, so a stale pack — - // or two archives disagreeing with each other — stops the sync before - // build.json can record a commit the bytes do not carry. - const claim = provenanceOf(target); - if (claim.dirty === true) - throw new Error( - `${filename} was packed --allow-dirty; a dirty pack cannot be vendored`, - ); - if (claim.commit !== commit) - throw new Error( - `${filename} was packed from ${claim.commit}, not the ${commit} packed here`, - ); - } -} finally { - git("worktree", "remove", "--force", checkout); -} -const files = archives.map((filename) => ({ - filename, - sha256: createHash("sha256") - .update(readFileSync(resolve(root, "vendor", filename))) - .digest("hex"), -})); -writeFileSync( - resolve(root, "vendor/build.json"), - JSON.stringify({ commit, dirty: false, files }, null, 2) + "\n", -); -// A changed tarball integrity is resolved by a normal install. --force also -// installs foreign-platform optional binaries and distorts dependency disclosure. -run( - "pnpm", - ["--filter", "dormouse-hosted", "update", "pgstencil", "@pgstencil/auth"], - root, -); -// Dormouse `main` must vendor a pgstencil `main` commit, because the nightly -// audit reads that commit's pgstencil `security-audit` check run — but a -// Dormouse branch may vendor a pgstencil branch while a cross-repo change is -// in flight, so this warns and does not fail. -const fetched = succeeds(() => git("fetch", "origin", "main")); -const onMain = - fetched && - succeeds(() => git("merge-base", "--is-ancestor", commit, "origin/main")); -if (!onMain) - console.warn( - fetched - ? `warning: pgstencil ${commit} is not on origin/main — Dormouse main must vendor a pgstencil main commit, and the nightly audit fails until it does` - : `warning: could not fetch pgstencil origin/main to check ${commit} — Dormouse main must vendor a pgstencil main commit`, - ); diff --git a/vendor/build.json b/vendor/build.json deleted file mode 100644 index 52a85fb35..000000000 --- a/vendor/build.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "commit": "e79cc4d350c0e66066405b1c042f6f6df7526381", - "dirty": false, - "files": [ - { - "filename": "pgstencil-0.2.0.tgz", - "sha256": "014bce9e19c028acf8093a608538cb57bbc04a2da1b4b7e57435e3cf2c7f024f" - }, - { - "filename": "pgstencil-auth-0.2.0.tgz", - "sha256": "f1c07fa34ec45ecbe7f100b1f0ceccce50d882cbef8b22f00a0e1ca81bec88ea" - } - ] -} diff --git a/vendor/pgstencil-0.2.0.tgz b/vendor/pgstencil-0.2.0.tgz deleted file mode 100644 index 85b20e5859393fa6441d542b9f21450440646c13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16412 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 diff --git a/vendor/pgstencil-auth-0.2.0.tgz b/vendor/pgstencil-auth-0.2.0.tgz deleted file mode 100644 index 3ce440a4ab93c1f4c4ad3dddd5bcb22f8b63eaa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37112 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 E0Os@M1^@s6 From 3b7df69c9ae6ba8b06fa8a3880fea1d99d37f860 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 25 Sep 2026 00:23:12 -0700 Subject: [PATCH 05/10] Document pgstencil cooldown exception and unpin artifact check --- docs/specs/security-supply-chain.md | 3 ++- docs/specs/security-supply-chain.rationale.md | 2 ++ docs/specs/security.md | 2 +- hosted/server/tests/artifacts.test.ts | 10 +++++++--- scripts/spec-word-budgets.json | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/specs/security-supply-chain.md b/docs/specs/security-supply-chain.md index 49776de32..9684797d8 100644 --- a/docs/specs/security-supply-chain.md +++ b/docs/specs/security-supply-chain.md @@ -70,9 +70,10 @@ Source of truth: `bundle_node_runtime` / `verify_node_version` in `standalone/sr ## Cooldown and alerts -**Maturity gating runs in both the pnpm configuration and the Renovate configuration.** +**Maturity gating runs in both the pnpm configuration and the Renovate configuration, except for pgstencil releases audited on their main commit, staged, and approved with 2FA.** (rationale) - **FAIL IF** `pnpm-workspace.yaml` is missing `minimumReleaseAge: 1440`. +- **FAIL IF** `minimumReleaseAgeExclude` in `pnpm-workspace.yaml` contains anything except `pgstencil` and `@pgstencil/*`, or a Renovate package rule sets `minimumReleaseAge: null` for any package outside `pgstencil` and `@pgstencil/**`. - **FAIL IF** `.github/renovate.json` is missing `npm` or `cargo` from `enabledManagers` (npm covers `/`; cargo covers `/standalone/src-tauri`), or is missing `minimumReleaseAge` package rules for those managers (rationale). - **FAIL IF** `.github/renovate.json` has no `vulnerabilityAlerts` block, or that block does not set `minimumReleaseAge` **explicitly**. Renovate's built-in default for that block is `minimumReleaseAge: null`, force-applied before lookup, so *omitting* the key drops the cooldown rather than inheriting it from `packageRules`. Keeping it is deliberate (rationale). - **FAIL IF** secret scanning or its push protection is disabled on the repository (`gh api repos/diffplug/dormouse --jq .security_and_analysis`), or Dependabot alerts are off (`GET /repos/diffplug/dormouse/vulnerability-alerts` must answer 204, not 404). Push protection is the one control that acts *before* a credential lands, blocking a push whose diff carries a recognized provider token; it applies to `dormouse-bot` too (rationale). diff --git a/docs/specs/security-supply-chain.rationale.md b/docs/specs/security-supply-chain.rationale.md index 4a56ef032..824e227c7 100644 --- a/docs/specs/security-supply-chain.rationale.md +++ b/docs/specs/security-supply-chain.rationale.md @@ -30,6 +30,8 @@ Why alternate version declarations are excluded: in the pinned [setup-node imple What the `minimumReleaseAge` package rules are: the Renovate equivalent of the pnpm dependency cooldown window, applied per manager. +The pgstencil exception has a different gate: its release workflow stages a version only after a passing `security-audit` on the packaged commit, and a human approves the staged package with npm 2FA before it becomes public. The package carries that commit in `dist/provenance.json`; Hosted verifies matching clean commits for core and auth. The exclusion list is deliberately narrow so other dependencies retain the withdrawal window. + Why the `vulnerabilityAlerts` cooldown is kept rather than dropped for speed: it guards the opposite threat from the alert itself — a compromised release that gets yanked within a day, which a reviewer reading a dependency diff cannot detect the way the ecosystem's own yank process can. Nothing here auto-merges, and the Dependabot alert already makes the vulnerability visible the moment it is published, so what the cooldown costs is a day before the remediation PR appears, not a day before anyone knows. Why push protection covering `dormouse-bot` is the point rather than an incidental: an injected agent pasting a token into a file is exactly the shape it stops. diff --git a/docs/specs/security.md b/docs/specs/security.md index 2090807af..664bea99e 100644 --- a/docs/specs/security.md +++ b/docs/specs/security.md @@ -49,7 +49,7 @@ last column means nothing cheaper does. | **Push, when enabled, cannot be aimed back into the tailnet.** | [What crosses the boundary](./security-remote.md#what-crosses-the-boundary) | `relay/test/push-endpoint.test.mjs` | | **Every dependency that reaches a machine is disclosed** at [dormouse.sh/supply-chain](https://dormouse.sh/supply-chain), and a change without the disclosure fails CI. | [Disclosure](./security-supply-chain.md#disclosure) | `.github/workflows/ci.yml` | | **The bundled runtime is the version disclosed.** The build verifies the binary against the pin. | [Bundled runtime](./security-supply-chain.md#bundled-runtime) | `standalone/src-tauri/build.rs` | -| **No newly published dependency is adopted for 24 hours**, security fixes included. | [Cooldown and alerts](./security-supply-chain.md#cooldown-and-alerts) | audit | +| **Dependencies wait 24 hours**, except audited pgstencil releases approved with 2FA. | [Cooldown and alerts](./security-supply-chain.md#cooldown-and-alerts) | audit | | **Merging to `main` and creating a tag are admin-only**, and every workflow this repository authors pins its actions by commit. | [GitHub Actions Policies](./security-ci.md#github-actions-policies) | audit | | **The bot maintainer cannot merge, tag, or read a release secret**, and its token never enters its own environment. | [Automated Maintainer (tend)](./security-ci.md#automated-maintainer-tend) | `.github/workflows/workflow-audit.yaml`, nightly | | **Publishing the extension takes a second human's approval.** | [VS Code Extension Releases](./security-ci.md#vs-code-extension-releases) | audit | diff --git a/hosted/server/tests/artifacts.test.ts b/hosted/server/tests/artifacts.test.ts index 21d5a6ac1..b9ea52324 100644 --- a/hosted/server/tests/artifacts.test.ts +++ b/hosted/server/tests/artifacts.test.ts @@ -25,9 +25,13 @@ test("the lockfile resolves both pgstencil packages from npm", () => { const { dependencies } = JSON.parse(readFileSync("package.json", "utf8")) as { dependencies: Record; }; - for (const [name] of packages) { - expect(dependencies[name]).toBe("^0.2.1"); - const key = name.startsWith("@") ? `'${name}@0.2.1'` : `${name}@0.2.1`; + for (const [name, entry] of packages) { + const manifest = JSON.parse( + readFileSync(new URL("../package.json", import.meta.resolve(entry)), "utf8"), + ) as { version: string }; + expect(dependencies[name]).toBe(`^${manifest.version}`); + const specifier = `${name}@${manifest.version}`; + const key = name.startsWith("@") ? `'${specifier}'` : specifier; expect(lockfile).toContain( ` ${key}:\n resolution: {integrity: sha512-`, ); diff --git a/scripts/spec-word-budgets.json b/scripts/spec-word-budgets.json index 170eeeb77..cd32df072 100644 --- a/scripts/spec-word-budgets.json +++ b/scripts/spec-word-budgets.json @@ -24,7 +24,7 @@ "docs/specs/security-hosted.md": 600, "docs/specs/security-local.md": 3200, "docs/specs/security-remote.md": 5750, - "docs/specs/security-supply-chain.md": 1150, + "docs/specs/security-supply-chain.md": 1200, "docs/specs/security.md": 1900, "docs/specs/shortcuts.md": 1100, "docs/specs/standalone.md": 11800, From 575316532664a60382e27251f6815625c22a7044 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 25 Sep 2026 00:23:55 -0700 Subject: [PATCH 06/10] Require registry-only pgstencil lockfile entries --- hosted/server/tests/artifacts.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hosted/server/tests/artifacts.test.ts b/hosted/server/tests/artifacts.test.ts index b9ea52324..b9dcf4f06 100644 --- a/hosted/server/tests/artifacts.test.ts +++ b/hosted/server/tests/artifacts.test.ts @@ -22,6 +22,7 @@ test("Hosted declares every peer of the installed pgstencil packages", () => { test("the lockfile resolves both pgstencil packages from npm", () => { const lockfile = readFileSync("../pnpm-lock.yaml", "utf8"); + const lines = lockfile.split("\n"); const { dependencies } = JSON.parse(readFileSync("package.json", "utf8")) as { dependencies: Record; }; @@ -32,8 +33,10 @@ test("the lockfile resolves both pgstencil packages from npm", () => { expect(dependencies[name]).toBe(`^${manifest.version}`); const specifier = `${name}@${manifest.version}`; const key = name.startsWith("@") ? `'${specifier}'` : specifier; - expect(lockfile).toContain( - ` ${key}:\n resolution: {integrity: sha512-`, + const index = lines.indexOf(` ${key}:`); + expect(index, `${name} has a registry resolution`).toBeGreaterThan(-1); + expect(lines[index + 1]).toMatch( + /^ resolution: \{integrity: sha512-[A-Za-z0-9+/=]+\}$/, ); } expect(lockfile).not.toMatch(/file:[^\s]*pgstencil/); From 144d17b48da4ea97c3d77bb6c11dfe10b41b4a21 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 25 Sep 2026 00:26:36 -0700 Subject: [PATCH 07/10] Allow in-range pgstencil lockfile updates --- hosted/server/tests/artifacts.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosted/server/tests/artifacts.test.ts b/hosted/server/tests/artifacts.test.ts index b9dcf4f06..6254e34b1 100644 --- a/hosted/server/tests/artifacts.test.ts +++ b/hosted/server/tests/artifacts.test.ts @@ -30,7 +30,7 @@ test("the lockfile resolves both pgstencil packages from npm", () => { const manifest = JSON.parse( readFileSync(new URL("../package.json", import.meta.resolve(entry)), "utf8"), ) as { version: string }; - expect(dependencies[name]).toBe(`^${manifest.version}`); + expect(dependencies[name]).toMatch(/^\^\d+\.\d+\.\d+$/); const specifier = `${name}@${manifest.version}`; const key = name.startsWith("@") ? `'${specifier}'` : specifier; const index = lines.indexOf(` ${key}:`); From d549a900aab3f9db659d550f90a325a0e5c63eec Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 25 Sep 2026 00:27:24 -0700 Subject: [PATCH 08/10] Check installed registry artifacts without pinning manifest ranges --- hosted/server/tests/artifacts.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hosted/server/tests/artifacts.test.ts b/hosted/server/tests/artifacts.test.ts index 6254e34b1..3f5c69cc6 100644 --- a/hosted/server/tests/artifacts.test.ts +++ b/hosted/server/tests/artifacts.test.ts @@ -23,14 +23,10 @@ test("Hosted declares every peer of the installed pgstencil packages", () => { test("the lockfile resolves both pgstencil packages from npm", () => { const lockfile = readFileSync("../pnpm-lock.yaml", "utf8"); const lines = lockfile.split("\n"); - const { dependencies } = JSON.parse(readFileSync("package.json", "utf8")) as { - dependencies: Record; - }; for (const [name, entry] of packages) { const manifest = JSON.parse( readFileSync(new URL("../package.json", import.meta.resolve(entry)), "utf8"), ) as { version: string }; - expect(dependencies[name]).toMatch(/^\^\d+\.\d+\.\d+$/); const specifier = `${name}@${manifest.version}`; const key = name.startsWith("@") ? `'${specifier}'` : specifier; const index = lines.indexOf(` ${key}:`); From 46bd900a2c6ecd030b76f90058faf5801ff26e2c Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 25 Sep 2026 00:33:07 -0700 Subject: [PATCH 09/10] Audit signed pgstencil provenance and runtime import boundary --- .github/audit/hosted.md | 15 +++++++++++++-- docs/specs/security-hosted.md | 3 ++- docs/specs/security-supply-chain.rationale.md | 2 +- scripts/spec-word-budgets.json | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/audit/hosted.md b/.github/audit/hosted.md index d91575275..f8e6d5898 100644 --- a/.github/audit/hosted.md +++ b/.github/audit/hosted.md @@ -21,8 +21,19 @@ live in those scripts and workflows rather than in the Worker. Verify the installed `pgstencil` and `@pgstencil/auth` packages by reading each `dist/provenance.json`, without auditing package code. Require a 40-hex commit, no `dirty: true`, and the same commit in both packages. Confirm `pnpm-lock.yaml` -resolves both through the npm registry with integrity hashes. Then check the -commit against pgstencil `main` and its audit: +resolves both through the npm registry with integrity hashes. Inspect Hosted's +runtime imports for references to a sibling pgstencil checkout. + +Verify npm's signed SLSA provenance for each installed package/version. Use a +temporary npm consumer of the exact locked versions and `npm audit signatures +--json --include-attestations` (npm does not audit a pnpm-only install). Require +that neither is in `invalid` or `missing`, then decode its verified SLSA DSSE +payload. The signed subject must identify the installed package/version and +its digest; `predicate.buildDefinition.externalParameters.workflow` must name +`https://github.com/diffplug/pgstencil`, `.github/workflows/release.yml`, and +`refs/heads/main`; `resolvedDependencies` must name the same commit as the +installed `dist/provenance.json`. A registry field or unsigned package file +alone is insufficient. Then check the commit against pgstencil `main` and its audit: ```sh gh api repos/diffplug/pgstencil/compare/...main --jq .status diff --git a/docs/specs/security-hosted.md b/docs/specs/security-hosted.md index 11601ef1c..7243f2037 100644 --- a/docs/specs/security-hosted.md +++ b/docs/specs/security-hosted.md @@ -26,7 +26,8 @@ Pinned by `hosted/server/tests/workers.test.ts` and `hosted/server/tests/policy. **Must depend on a released pgstencil** whose installed `dist/provenance.json` names a commit on pgstencil `main` with a passing `security-audit`. The core and auth packages must name the same clean commit. - **FAIL IF** a production Worker exposes the captured-email inbox or deterministic clock controls, or imports the testing injection module; inspect `hosted/server/worker.ts`, the build configuration, and `hosted/server/tests/worker-entry.ts`. -- **FAIL IF** either installed pgstencil package lacks `dist/provenance.json`, records `dirty`, or names a different commit; or `pnpm-lock.yaml` resolves either package from anything but the npm registry. Inspect `verifyPackages` in `hosted/scripts/production.mjs` and `hosted/server/tests/artifacts.test.ts`. +- **FAIL IF** either installed pgstencil package lacks `dist/provenance.json`, records `dirty`, or names a different commit; `pnpm-lock.yaml` resolves either package from outside npm; or a runtime import depends on a sibling pgstencil checkout. Inspect `verifyPackages` in `hosted/scripts/production.mjs`, `hosted/server/tests/artifacts.test.ts`, and Hosted runtime imports. +- **FAIL IF** either package lacks a verified npm SLSA provenance attestation binding its installed version to `diffplug/pgstencil` `.github/workflows/release.yml` on `refs/heads/main` and the same commit as `dist/provenance.json`. Verify the signed subject against the installed registry package before trusting the statement. - **FAIL IF** that commit is not on pgstencil `main` (`gh api repos/diffplug/pgstencil/compare/...main`, status `ahead` or `identical`), or its `security-audit` check runs (`gh api repos/diffplug/pgstencil/commits//check-runs`) include no `success`, or any conclusion other than `success` and `cancelled`. pgstencil audits the released code; Dormouse audits only how Hosted configures it. - **FAIL IF** the local email inbox accepts a foreign Host or Origin or cross-site Fetch Metadata; inspect `allowedDevRequest` in `hosted/server/dev-host-guard.ts`, including the upgrade guard in `hosted/server/dev.ts`. diff --git a/docs/specs/security-supply-chain.rationale.md b/docs/specs/security-supply-chain.rationale.md index 824e227c7..8f5f0baab 100644 --- a/docs/specs/security-supply-chain.rationale.md +++ b/docs/specs/security-supply-chain.rationale.md @@ -30,7 +30,7 @@ Why alternate version declarations are excluded: in the pinned [setup-node imple What the `minimumReleaseAge` package rules are: the Renovate equivalent of the pnpm dependency cooldown window, applied per manager. -The pgstencil exception has a different gate: its release workflow stages a version only after a passing `security-audit` on the packaged commit, and a human approves the staged package with npm 2FA before it becomes public. The package carries that commit in `dist/provenance.json`; Hosted verifies matching clean commits for core and auth. The exclusion list is deliberately narrow so other dependencies retain the withdrawal window. +The pgstencil exception has a different gate: its release workflow stages a version only after a passing `security-audit` on the packaged commit, and a human approves the staged package with npm 2FA before it becomes public. The package carries that commit in `dist/provenance.json`; Hosted verifies matching clean commits for core and auth, and its audit checks npm-signed workflow attestations bind those bytes to that commit. The exclusion list is deliberately narrow so other dependencies retain the withdrawal window. Why the `vulnerabilityAlerts` cooldown is kept rather than dropped for speed: it guards the opposite threat from the alert itself — a compromised release that gets yanked within a day, which a reviewer reading a dependency diff cannot detect the way the ecosystem's own yank process can. Nothing here auto-merges, and the Dependabot alert already makes the vulnerability visible the moment it is published, so what the cooldown costs is a day before the remediation PR appears, not a day before anyone knows. diff --git a/scripts/spec-word-budgets.json b/scripts/spec-word-budgets.json index cd32df072..04373779d 100644 --- a/scripts/spec-word-budgets.json +++ b/scripts/spec-word-budgets.json @@ -21,7 +21,7 @@ "docs/specs/remote-security-model.md": 4750, "docs/specs/security-audit.md": 2100, "docs/specs/security-ci.md": 2950, - "docs/specs/security-hosted.md": 600, + "docs/specs/security-hosted.md": 650, "docs/specs/security-local.md": 3200, "docs/specs/security-remote.md": 5750, "docs/specs/security-supply-chain.md": 1200, From 3a2826af8c3252295ea68d6ebae5a79f24b6a086 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 25 Sep 2026 00:39:27 -0700 Subject: [PATCH 10/10] Bind pgstencil attestations to verified workflow certificate --- .github/audit/hosted.md | 20 ++++++++++++-------- docs/specs/security-hosted.md | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/audit/hosted.md b/.github/audit/hosted.md index f8e6d5898..597e4fbf9 100644 --- a/.github/audit/hosted.md +++ b/.github/audit/hosted.md @@ -26,14 +26,18 @@ runtime imports for references to a sibling pgstencil checkout. Verify npm's signed SLSA provenance for each installed package/version. Use a temporary npm consumer of the exact locked versions and `npm audit signatures ---json --include-attestations` (npm does not audit a pnpm-only install). Require -that neither is in `invalid` or `missing`, then decode its verified SLSA DSSE -payload. The signed subject must identify the installed package/version and -its digest; `predicate.buildDefinition.externalParameters.workflow` must name -`https://github.com/diffplug/pgstencil`, `.github/workflows/release.yml`, and -`refs/heads/main`; `resolvedDependencies` must name the same commit as the -installed `dist/provenance.json`. A registry field or unsigned package file -alone is insufficient. Then check the commit against pgstencil `main` and its audit: +--json --include-attestations` (npm does not audit a pnpm-only install). Each +package **must appear in `verified`** with a SLSA provenance bundle; reject +`invalid` and `missing` entries too. Decode the verified SLSA DSSE payload and +the Fulcio certificate in that bundle. The certificate SAN must be +`https://github.com/diffplug/pgstencil/.github/workflows/release.yml@refs/heads/main`; +its source-repository digest extension `1.3.6.1.4.1.57264.1.13` must equal +the installed `dist/provenance.json` commit. The signed subject must identify +the installed package/version and digest; the payload's +`externalParameters.workflow` and `resolvedDependencies` must agree with the +certificate and commit. npm verifies the signature and subject digest, but +the payload's workflow claim alone is not the signer identity. Then check the +commit against pgstencil `main` and its audit: ```sh gh api repos/diffplug/pgstencil/compare/...main --jq .status diff --git a/docs/specs/security-hosted.md b/docs/specs/security-hosted.md index 7243f2037..7836e406a 100644 --- a/docs/specs/security-hosted.md +++ b/docs/specs/security-hosted.md @@ -27,7 +27,7 @@ Pinned by `hosted/server/tests/workers.test.ts` and `hosted/server/tests/policy. - **FAIL IF** a production Worker exposes the captured-email inbox or deterministic clock controls, or imports the testing injection module; inspect `hosted/server/worker.ts`, the build configuration, and `hosted/server/tests/worker-entry.ts`. - **FAIL IF** either installed pgstencil package lacks `dist/provenance.json`, records `dirty`, or names a different commit; `pnpm-lock.yaml` resolves either package from outside npm; or a runtime import depends on a sibling pgstencil checkout. Inspect `verifyPackages` in `hosted/scripts/production.mjs`, `hosted/server/tests/artifacts.test.ts`, and Hosted runtime imports. -- **FAIL IF** either package lacks a verified npm SLSA provenance attestation binding its installed version to `diffplug/pgstencil` `.github/workflows/release.yml` on `refs/heads/main` and the same commit as `dist/provenance.json`. Verify the signed subject against the installed registry package before trusting the statement. +- **FAIL IF** either installed package lacks a verified npm SLSA provenance attestation whose Fulcio certificate SAN names `diffplug/pgstencil` `.github/workflows/release.yml` on `refs/heads/main`, whose source-repository digest (OID `1.3.6.1.4.1.57264.1.13`) equals `dist/provenance.json`'s commit, or whose signed subject/payload disagrees with the installed package, certificate, or commit. - **FAIL IF** that commit is not on pgstencil `main` (`gh api repos/diffplug/pgstencil/compare/...main`, status `ahead` or `identical`), or its `security-audit` check runs (`gh api repos/diffplug/pgstencil/commits//check-runs`) include no `success`, or any conclusion other than `success` and `cancelled`. pgstencil audits the released code; Dormouse audits only how Hosted configures it. - **FAIL IF** the local email inbox accepts a foreign Host or Origin or cross-site Fetch Metadata; inspect `allowedDevRequest` in `hosted/server/dev-host-guard.ts`, including the upgrade guard in `hosted/server/dev.ts`.