From 81fa3328e3c16000480feeb695ab76446ba1b239 Mon Sep 17 00:00:00 2001 From: chh-ay Date: Sun, 26 Jul 2026 16:34:32 +0700 Subject: [PATCH 1/2] fix(deps): take the patched postcss for the source-map traversal advisory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GHSA-r28c-9q8g-f849 (high, CVSS 7.5) covers postcss <= 8.5.17: a `sourceMappingURL` comment in parsed CSS is resolved with `path.join` and read from disk, so `../` segments escape the stylesheet's directory and any reachable `.map` file can be disclosed through `result.map`. The advisory reached the database on 2026-07-24 and has been failing `audit:js` on every branch since, blocking all pull requests. postcss is transitive here and only ever parses our own stylesheets, so the practical exposure is low — but the audit is fail-closed by policy and the fix costs nothing. An override rather than a direct dependency: nothing in this repository imports postcss, and declaring it would misrepresent the dependency graph. --- bun.lock | 23 +++++++++++++---------- package.json | 5 ++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/bun.lock b/bun.lock index 185c72aa..15ae109d 100644 --- a/bun.lock +++ b/bun.lock @@ -24,7 +24,7 @@ }, "bench": { "name": "@sheetwrite/bench", - "version": "0.0.0", + "version": "0.0.1", "dependencies": { "@sheetwrite/core": "workspace:*", "@sheetwrite/wasm": "workspace:*", @@ -39,7 +39,7 @@ }, "docs": { "name": "@sheetwrite/docs-start", - "version": "0.0.0", + "version": "0.0.1", "dependencies": { "@base-ui/react": "^1.6.0", "@floating-ui/dom": "^1.8.0", @@ -95,14 +95,14 @@ }, "packages/core": { "name": "@sheetwrite/core", - "version": "0.1.0", + "version": "0.3.1", "dependencies": { "@sheetwrite/wasm": "workspace:*", }, }, "packages/react": { "name": "@sheetwrite/react", - "version": "0.1.0", + "version": "0.3.1", "dependencies": { "@sheetwrite/core": "workspace:*", }, @@ -112,7 +112,7 @@ }, "packages/svelte": { "name": "@sheetwrite/svelte", - "version": "0.1.0", + "version": "0.3.1", "dependencies": { "@sheetwrite/core": "workspace:*", }, @@ -126,7 +126,7 @@ }, "packages/vue": { "name": "@sheetwrite/vue", - "version": "0.1.0", + "version": "0.3.1", "dependencies": { "@sheetwrite/core": "workspace:*", }, @@ -136,11 +136,11 @@ }, "packages/wasm": { "name": "@sheetwrite/wasm", - "version": "0.1.0", + "version": "0.3.1", }, "packages/xlsx": { "name": "@sheetwrite/xlsx", - "version": "0.1.0", + "version": "0.3.1", "dependencies": { "@sheetwrite/core": "workspace:*", "fflate": "0.8.3", @@ -150,6 +150,9 @@ "trustedDependencies": [ "@biomejs/biome", ], + "overrides": { + "postcss": "^8.5.18", + }, "packages": { "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], @@ -1105,7 +1108,7 @@ "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], - "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + "nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], "node-releases": ["node-releases@2.0.51", "", {}, "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ=="], @@ -1157,7 +1160,7 @@ "playwright-core": ["playwright-core@1.61.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg=="], - "postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="], + "postcss": ["postcss@8.5.23", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg=="], "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], diff --git a/package.json b/package.json index e32c9d96..ce75ad7d 100644 --- a/package.json +++ b/package.json @@ -92,5 +92,8 @@ }, "trustedDependencies": [ "@biomejs/biome" - ] + ], + "overrides": { + "postcss": "^8.5.18" + } } From e6e4a6ece9e378a275e26babbc2390f8d0a00d49 Mon Sep 17 00:00:00 2001 From: chh-ay Date: Sun, 26 Jul 2026 17:05:32 +0700 Subject: [PATCH 2/2] fix(ci): remove the unavailable self-hosted performance gate Required CI waited unconditionally for a runner label backed by zero registered runners, so every pull request remained queued forever. A single non-ephemeral workstation is not a reliable merge prerequisite. Keep deterministic benchmark smoke and safety ceilings in ordinary CI. Keep the ten-round matched protocol as a documented, fail-closed local command for deliberate performance work, but remove it from the required workflow and its contract assertions. --- .changeset/fuzzy-mails-march.md | 4 ++ .github/workflows/ci.yml | 50 ------------------- bench/test/matched-formula-evidence.test.ts | 4 +- .../docs/guides/performance-resources.md | 4 +- scripts/docs.ts | 4 +- scripts/toolchain-contract.test.ts | 20 -------- scripts/workflow-contract.test.ts | 4 -- 7 files changed, 11 insertions(+), 79 deletions(-) create mode 100644 .changeset/fuzzy-mails-march.md diff --git a/.changeset/fuzzy-mails-march.md b/.changeset/fuzzy-mails-march.md new file mode 100644 index 00000000..d1384ada --- /dev/null +++ b/.changeset/fuzzy-mails-march.md @@ -0,0 +1,4 @@ +--- +--- + +Remove the unavailable self-hosted benchmark from required CI while retaining its local protocol. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c0a108b..7dd97833 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -405,53 +405,6 @@ jobs: playwright-report/ if-no-files-found: warn - controlled-performance: - name: Matched Zero-Regression Performance - needs: artifact-build - runs-on: sheetwrite-perf-i9-12900h-cachyos - timeout-minutes: 90 - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Setup Node - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 - with: - node-version: ${{ env.NODE_VERSION }} - - name: Setup Bun - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 - with: - bun-version: ${{ env.BUN_VERSION }} - - name: Install exact dependencies and benchmark browser - run: | - bun install --frozen-lockfile - bunx playwright install chromium - - name: Build controlled render harness - run: bun run --filter '@sheetwrite/bench' bench:render:prepare - - name: Capture repeated full matched samples - working-directory: bench - run: | - bun run src/render-driver.ts \ - --rounds 10 \ - --output results/render-fresh.json \ - --markdown-output results/render-fresh.md - - name: Require matched zero-regression decision - working-directory: bench - run: | - bun run src/check.ts \ - --baseline results/render-baseline.json \ - --fresh results/render-fresh.json \ - --power-mode balanced \ - --concurrency 1 - - name: Upload controlled raw samples - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: controlled-performance-raw-${{ github.sha }} - path: | - bench/results/render-fresh.json - bench/results/render-fresh.md - if-no-files-found: error - retention-days: 30 required: @@ -466,7 +419,6 @@ jobs: - delivery-size - docs-build - browser-smoke - - controlled-performance runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -480,7 +432,6 @@ jobs: SIZE: ${{ needs.delivery-size.result }} DOCS: ${{ needs.docs-build.result }} BROWSER: ${{ needs.browser-smoke.result }} - PERFORMANCE: ${{ needs.controlled-performance.result }} DOCS_REQUIRED: ${{ needs.preflight.outputs.docs_required }} run: | test "$PREFLIGHT" = success @@ -489,7 +440,6 @@ jobs: test "$PACKED" = success test "$BUNDLERS" = success test "$SIZE" = success - test "$PERFORMANCE" = success if [ "$DOCS_REQUIRED" = "true" ]; then test "$DOCS" = success test "$BROWSER" = success diff --git a/bench/test/matched-formula-evidence.test.ts b/bench/test/matched-formula-evidence.test.ts index 25f7a5f2..79a268c5 100644 --- a/bench/test/matched-formula-evidence.test.ts +++ b/bench/test/matched-formula-evidence.test.ts @@ -29,9 +29,11 @@ function validate(value: MatchedFormulaEvidence): void { } describe("matched formula evidence contract", () => { + // The success path validates the complete bootstrap artifact. Tamper cases + // below fail early, but this one deliberately traverses every sample. test("accepts the freshly generated canonical artifact", () => { expect(() => validate(copy())).not.toThrow(); - }); + }, 60_000); for (const [label, mutate] of [ [ diff --git a/docs/src/content/docs/guides/performance-resources.md b/docs/src/content/docs/guides/performance-resources.md index 5760d983..b601d6d4 100644 --- a/docs/src/content/docs/guides/performance-resources.md +++ b/docs/src/content/docs/guides/performance-resources.md @@ -4,9 +4,9 @@ description: "Freshness-gated benchmark and package-size evidence for Sheetwrite --- Every number on this page comes from a validated local protocol artifact captured on a clean tree; nothing is published from an unvalidated or protocol-mismatched artifact. Every expected cell carries either a validated timing or its recorded failure - a run that did not complete is shown as a failure, never converted into a timing. -## Matched regression gate +## Matched local regression check -The release gate does not treat a competitor comparison or a smoke ceiling as regression evidence. On the controlled performance runner it captures ten fresh matched rounds, retains every raw sample, and compares the fresh artifact with the committed baseline. Any unapproved slowdown fails the required CI job. +Timing comparisons run deliberately on a controlled local machine, not as a required CI job. Capture ten fresh matched rounds, retain every raw sample, and compare the fresh artifact with the committed baseline. Any unapproved slowdown fails the local command. ```sh verify title="Zero-regression benchmark" bun run --filter @sheetwrite/bench bench:render:prepare diff --git a/scripts/docs.ts b/scripts/docs.ts index b7295e25..62282a8d 100644 --- a/scripts/docs.ts +++ b/scripts/docs.ts @@ -1632,9 +1632,9 @@ export async function renderEvidencePage(sizeHistoryOverride?: SizeHistory): Pro ).trimEnd(), "Every number on this page comes from a validated local protocol artifact captured on a clean tree; nothing is published from an unvalidated or protocol-mismatched artifact. Every expected cell carries either a validated timing or its recorded failure - a run that did not complete is shown as a failure, never converted into a timing.", "", - "## Matched regression gate", + "## Matched local regression check", "", - "The release gate does not treat a competitor comparison or a smoke ceiling as regression evidence. On the controlled performance runner it captures ten fresh matched rounds, retains every raw sample, and compares the fresh artifact with the committed baseline. Any unapproved slowdown fails the required CI job.", + "Timing comparisons run deliberately on a controlled local machine, not as a required CI job. Capture ten fresh matched rounds, retain every raw sample, and compare the fresh artifact with the committed baseline. Any unapproved slowdown fails the local command.", "", '```sh verify title="Zero-regression benchmark"', "bun run --filter @sheetwrite/bench bench:render:prepare", diff --git a/scripts/toolchain-contract.test.ts b/scripts/toolchain-contract.test.ts index 74f4389e..b51b57b6 100644 --- a/scripts/toolchain-contract.test.ts +++ b/scripts/toolchain-contract.test.ts @@ -158,26 +158,6 @@ describe("contributor and CI toolchain contract", () => { expect(commands).toContain("test:browser"); }); - it("requires a full matched comparison on the pinned performance runner", () => { - const jobs = parsedWorkflow.jobs ?? {}; - const controlled = jobs["controlled-performance"]; - expect(controlled?.["runs-on"]).toBe("sheetwrite-perf-i9-12900h-cachyos"); - expect(controlled?.needs).toBe("artifact-build"); - const commands = controlled?.steps?.flatMap((step) => (step.run ? [step.run] : [])).join("\n"); - expect(commands).toContain("--rounds 10"); - expect(commands).toContain("src/check.ts"); - expect(commands).toContain("--power-mode balanced"); - expect(commands).toContain("--concurrency 1"); - expect(commands).not.toContain("--report-only"); - expect(commands).not.toContain("bench:verify"); - const upload = controlled?.steps?.find((step) => - step.uses?.startsWith("actions/upload-artifact@"), - ); - expect(upload?.if).toBe("always()"); - expect(upload?.with?.path).toContain("render-fresh.json"); - expect(JSON.stringify(jobs.required)).toContain("controlled-performance"); - }); - it("gates the exact docs artifact without weakening Required CI", () => { const jobs = parsedWorkflow.jobs ?? {}; const preflight = jobs.preflight; diff --git a/scripts/workflow-contract.test.ts b/scripts/workflow-contract.test.ts index 6beb1988..436f3d19 100644 --- a/scripts/workflow-contract.test.ts +++ b/scripts/workflow-contract.test.ts @@ -179,10 +179,6 @@ describe("CI and release workflow contracts", () => { expect(commands(workflows().ci.jobs[jobName]!)).toContain("bun run browser:install:chromium"); } - const controlledPerformance = workflows().ci.jobs["controlled-performance"]!; - expect(controlledPerformance["runs-on"]).toBe("sheetwrite-perf-i9-12900h-cachyos"); - expect(commands(controlledPerformance)).toContain("bunx playwright install chromium"); - const projectByName = Object.fromEntries( (playwrightConfig.projects ?? []).map((project) => [project.name, project]), );