fix: pre-landing review fixes

Testing specialist: the drift test asserts the bundled DOMPurify is the override and the bundle carries no Google API key; deps-smoke refuses ip-address below 10.7.4 and checks every bumped package against its OSV-fixed floor in both lockfiles. Adversarial: build.ts fails when any AIza key survives the strip. Outside voice and red team: the CHANGELOG hash and scope lines match the shipped tree.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-26 09:27:37 +00:00
1 parent 3ebce9a0dd
commit 96df9eaa8a
5 files changed
+53 -7

No files matched your search

+7 -3
View File
@@ -2,15 +2,19 @@
## [Unreleased]
**The weekly OSV scan on main is green again, so the weekly release cut can run.** The 2026-10-26 scan ([run 40288353843](https://github.com/garrytan/gstack/actions/runs/40288353843), #3462) found 14 fixable vulnerabilities in six packages across both lockfiles, and the release workflow skips any main commit with a red check run. Every one is bumped to its fixed release; no code changed, because gstack imports none of these packages directly (the MCP SDK and zod arrive through `@anthropic-ai/claude-agent-sdk`, body-parser through its express, fast-uri through ajv, ip-address through socks and express-rate-limit, dompurify through mermaid).
**The weekly OSV scan on main is green again, so the weekly release cut can run.** The 2026-10-26 scan ([run 40288353843](https://github.com/garrytan/gstack/actions/runs/40288353843), #3462) found 14 fixable vulnerabilities in six packages across both lockfiles, and the release workflow skips any main commit with a red check run. Every one is bumped to its fixed release; no application code changed, because gstack imports none of these packages directly (the MCP SDK and zod arrive through `@anthropic-ai/claude-agent-sdk`, body-parser through its express, fast-uri through ajv, ip-address through socks and express-rate-limit, dompurify through mermaid).
### Fixed
- **dompurify 3.4.16 → 3.4.17 in `lib/diagram-render`** (GHSA-cg2g-xh64-v4q7, CVSS 9.2; GHSA-mrv2-3pqr-j5jr; GHSA-c6gr-qvv4-5v3f). The override moves and the committed bundle is rebuilt with Bun 1.4.2 (deterministic: two builds, one sha256 `4d1c10a0…`), so the shipped page runs the fixed sanitizer instead of a bundled copy of the old one. Mermaid, excalidraw and React pins are unchanged.
- **dompurify 3.4.16 → 3.4.17 in `lib/diagram-render`** (GHSA-cg2g-xh64-v4q7, CVSS 9.2; GHSA-mrv2-3pqr-j5jr; GHSA-c6gr-qvv4-5v3f). The override moves and the committed bundle is rebuilt with Bun 1.4.2 (two builds, one sha256 `c2d37f9f…`), so the shipped page runs the fixed sanitizer instead of a bundled copy of the old one. Mermaid, excalidraw and React pins are unchanged.
- **@modelcontextprotocol/sdk 1.31.0 → 1.32.0** (GHSA-22jm-h49p-29qw, GHSA-46jr-4wx8-v453, GHSA-rvq5-wwqv-78pq, GHSA-6prh-2h8m-c8cw), **fast-uri 3.1.8 → 3.1.9** (GHSA-w74x-v6f9-j3xg, GHSA-wjr9-j795-jxcr, GHSA-333x-w4r6-6crp, GHSA-xhq7-xmqj-769h) and **ip-address 10.7.3 → 10.7.4** (GHSA-rxp7-6hxx-x8q5) through the existing `overrides` pins.
- **zod 4.5.4 → 4.6.7** (GHSA-mrhx-c7xc-m3w5, fixed in 4.6.6) and **body-parser 2.3.0 → 2.4.0** (GHSA-27r3-jxmh-p828) through the lockfile alone; both sit inside their consumers' ranges, so no override was added. body-parser 2.4.0 brings a nested qs 6.16.1.
### Changed
- **`lib/diagram-render/scripts/build.ts`** empties the one `VITE_APP_FIREBASE_CONFIG` literal that `@excalidraw/excalidraw`'s npm bundle carries (excalidraw.com's public Firebase client key, which nothing in the package reads and the offline render page never uses). The build fails if the literal is not matched exactly once or any `AIza…` key survives, so an excalidraw bump that changes its shape cannot put the key back silently. Without this, every rebuild of the committed bundle failed the pre-gate secret scan.
### Tests
- `test/deps-smoke.test.ts` names the new ip-address pin. OSV-Scanner 2.3.8 with the workflow's own arguments (`--config=.osv-scanner.toml --include-git-root --recursive ./`) reports no issues on this tree; the katex ignore is the only filtered advisory.
- `test/diagram-render-drift.test.ts` asserts the bundle's DOMPurify is the `package.json` override and no other version, and that it carries no Google API key; its path constants are repo-relative segment runs so the pre-gate's import graph selects it for the bundle, `BUILD_INFO.json` and the build script.
- `test/deps-smoke.test.ts` refuses `ip-address` below 10.7.4 and checks every resolved version of the six packages against its OSV-fixed floor in both lockfiles (all six fail on main's lockfiles). OSV-Scanner 2.3.8 with the workflow's own arguments (`--config=.osv-scanner.toml --include-git-root --recursive ./`) reports no issues on this tree; the katex ignore is the only filtered advisory.
## [1.91.235.0] - 2026-10-26
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "gstack-diagram-render",
"sha256": "c2d37f9f924baae2a146bc109bf9b4c308d89f49e08b1c35793edc310b579444",
"srcSha256": "0c2c0f262cad7e32aa8afa430024bb7ff1e026e3eb7a182ba9c4744a4d825d64",
"srcSha256": "17d8921086e091252425c63f06cab01b4626d8e0fa9fe8738267e1ac4ade52e8",
"bytes": 7884979,
"bunVersion": "1.4.2",
"deps": {
+4
View File
@@ -55,6 +55,10 @@ if (firebaseHits !== 1) {
process.exit(1);
}
const strippedJs = js.replace(FIREBASE_CONFIG, "VITE_APP_FIREBASE_CONFIG:'{}'");
if (/AIza[0-9A-Za-z_-]{35}/.test(strippedJs)) {
console.error("a Google API key literal survived the strip; update the strip rule in scripts/build.ts");
process.exit(1);
}
// Escape inline-script terminators (see header note).
const inlineJs = strippedJs.replaceAll("</scri", "<\\/scri");
+27 -3
View File
@@ -27,13 +27,37 @@ describe("dependency-wave smoke", () => {
expect(outMeta.width).toBe(4);
});
test("ip-address 10.7 override: both nested consumers resolve the fixed version", async () => {
test("ip-address 10.7.4 override: both nested consumers resolve the fixed version", async () => {
const lock = await Bun.file(`${import.meta.dir}/../bun.lock`).text();
// No vulnerable ip-address node may survive anywhere in the tree
// (socks pulled 10.2.0; express-rate-limit exact-pinned 10.1.0 — the
// override must defeat both).
// override must defeat both), and 10.7.3 (GHSA-rxp7-6hxx-x8q5) is vulnerable too.
expect(lock).not.toMatch(/ip-address@10\.[0-6]\./);
expect(lock).toMatch(/ip-address@10\.7\./);
expect(lock).not.toMatch(/ip-address@10\.7\.[0-3]"/);
expect(lock).toMatch(/ip-address@10\.7\.(?:[4-9]|\d{2,})/);
});
// The 2026-10-26 OSV scan (#3462) named the fixed release of each package
// below; a lockfile that resolves any of them lower regresses the scan.
const floors: Array<[lockfile: string, pkg: string, min: string]> = [
["bun.lock", "@modelcontextprotocol/sdk", "1.32.0"],
["bun.lock", "fast-uri", "3.1.9"],
["bun.lock", "ip-address", "10.7.4"],
["bun.lock", "zod", "4.6.6"],
["bun.lock", "body-parser", "2.4.0"],
["lib/diagram-render/bun.lock", "dompurify", "3.4.17"],
];
const cmp = (a: string, b: string) => {
const [x, y] = [a, b].map(v => v.split(".").map(Number));
for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i];
return 0;
};
test.each(floors)("%s resolves every %s at or above its OSV-fixed release %s", async (lockfile, pkg, min) => {
const lock = await Bun.file(`${import.meta.dir}/../${lockfile}`).text();
const escaped = pkg.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
const resolved = [...lock.matchAll(new RegExp(`"${escaped}@(\\d+\\.\\d+\\.\\d+)"`, "g"))].map(m => m[1]);
expect(resolved.length).toBeGreaterThan(0);
for (const v of resolved) expect(cmp(v, min), `${pkg}@${v} is below ${min}`).toBeGreaterThanOrEqual(0);
});
test("marked stays importable and parses (direct-dep bump)", async () => {
+14
View File
@@ -40,6 +40,20 @@ describe("diagram-render bundle drift", () => {
expect(info.deps).toEqual(pkg.dependencies);
});
test("the bundled DOMPurify is the package.json override, and no other version", async () => {
const html = await Bun.file(DIST_HTML).text();
const pkg = await Bun.file(path.join(ROOT, "package.json")).json();
const versions = [...html.matchAll(/@license DOMPurify (\d+\.\d+\.\d+)/g)].map(m => m[1]);
expect(versions).toEqual([pkg.overrides.dompurify]);
expect(html).toContain(`version="${pkg.overrides.dompurify}"`);
});
test("the bundle carries no Google API key: excalidraw's Firebase config literal is emptied", async () => {
const html = await Bun.file(DIST_HTML).text();
expect(html).not.toMatch(/AIza[0-9A-Za-z_-]{35}/);
expect(html).toContain("VITE_APP_FIREBASE_CONFIG:'{}'");
});
test("BUILD_INFO srcSha256 matches src on disk (edited-src-forgot-rebuild guard)", async () => {
// The deep rebuild check below needs node_modules, which CI doesn't
// install for this nested package — this tier-1.5 fingerprint catches a