mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
v1.91.241.0 fix(deps): bump dompurify, MCP SDK, zod, body-parser, fast-uri, ip-address to OSV-fixed releases (#3462) (#3463)
* fix(deps): bump dompurify, @modelcontextprotocol/sdk, zod, body-parser, fast-uri and ip-address to their OSV-fixed releases (#3462) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * build(diagram-render): empty excalidraw's public Firebase config literal in the bundle; declare BUILD_INFO.json's test lane Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(diagram-render): name the bundle, BUILD_INFO and build script by repo path so the pre-gate sees their lane Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore(pregate): drop the BUILD_INFO.json lane declaration; the drift test names the file now Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * 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> * docs(diagram-render): document the Firebase-literal strip and the new drift checks Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
021d817c36
commit
a1487458d3
13 files changed
+701
-615
No files matched your search
@@ -1,5 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## [1.91.241.0] - 2026-10-26
|
||||
|
||||
**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 (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/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.240.0] - 2026-10-26
|
||||
|
||||
**Proactive skill suggestions default to the four core skills.** `proactive` now takes `core` (the default: only /office-hours, /review, /qa and /ship auto-invoke when a request matches), `all` (any matching skill, the old behavior) or `off`. `true` and `false` keep working as `all` and `off`. The sentiment research (GSTA-93) found auto-invocation of every skill is what makes gstack collide with other skill packs; the four core skills are where the praise is, so they keep the nudge. Garry, 2026-10-25: "do it".
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# gstack digest v1.91.240.0 — regenerate/re-copy after upgrading gstack
|
||||
# gstack digest v1.91.241.0 — regenerate/re-copy after upgrading gstack
|
||||
|
||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||
for agent hosts without a full skill install. The full skills add workflows,
|
||||
|
||||
@@ -31,12 +31,12 @@
|
||||
"playwright-core@1.62.1": "patches/playwright-core@1.62.1.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"@modelcontextprotocol/sdk": "1.31.0",
|
||||
"@modelcontextprotocol/sdk": "1.32.0",
|
||||
"adm-zip": "^0.6.1",
|
||||
"fast-uri": "3.1.8",
|
||||
"fast-uri": "3.1.9",
|
||||
"hono": "4.13.13",
|
||||
"image-size": "2.0.4",
|
||||
"ip-address": "10.7.3",
|
||||
"ip-address": "10.7.4",
|
||||
"proxy-addr": "2.0.8",
|
||||
"sharp": "0.35.5",
|
||||
},
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.31.0", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-UvTMgnNlnIBO/22ob2RcVGDlcvOslQs8T59+FTGdA0L27a39fdGF/EDETNtDVK4DZGpwomlsYpRdA8UXcVL/pw=="],
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.32.0", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-8BviX/hK4Gd2eL1KdTwm0gfl4d3wdoErW0Jd00h6nomUAtk6IJk5vwQJc6kfWtJTzN5OEV/lgIiJKI8fiDGAlA=="],
|
||||
|
||||
"@ngrok/ngrok": ["@ngrok/ngrok@1.7.0", "", { "optionalDependencies": { "@ngrok/ngrok-android-arm64": "1.7.0", "@ngrok/ngrok-darwin-arm64": "1.7.0", "@ngrok/ngrok-darwin-universal": "1.7.0", "@ngrok/ngrok-darwin-x64": "1.7.0", "@ngrok/ngrok-freebsd-x64": "1.7.0", "@ngrok/ngrok-linux-arm-gnueabihf": "1.7.0", "@ngrok/ngrok-linux-arm64-gnu": "1.7.0", "@ngrok/ngrok-linux-arm64-musl": "1.7.0", "@ngrok/ngrok-linux-x64-gnu": "1.7.0", "@ngrok/ngrok-linux-x64-musl": "1.7.0", "@ngrok/ngrok-win32-arm64-msvc": "1.7.0", "@ngrok/ngrok-win32-ia32-msvc": "1.7.0", "@ngrok/ngrok-win32-x64-msvc": "1.7.0" } }, "sha512-P06o9TpxrJbiRbHQkiwy/rUrlXRupc+Z8KT4MiJfmcdWxvIdzjCaJOdnNkcOTs6DMyzIOefG5tvk/HLdtjqr0g=="],
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
|
||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||
|
||||
"body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="],
|
||||
"body-parser": ["body-parser@2.4.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.1.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.3", "on-finished": "^2.4.1", "qs": "^6.16.1", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ=="],
|
||||
|
||||
"browser-split": ["browser-split@0.0.1", "", {}, "sha512-JhvgRb2ihQhsljNda3BI8/UcRHVzrVwo3Q+P8vDtSiyobXuFpuZ9mq+MbRGMnC22CjW3RrfXdg6j6ITX8M+7Ow=="],
|
||||
|
||||
@@ -323,7 +323,7 @@
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.8", "", {}, "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg=="],
|
||||
"fast-uri": ["fast-uri@3.1.9", "", {}, "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ=="],
|
||||
|
||||
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||
|
||||
@@ -383,7 +383,7 @@
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ip-address": ["ip-address@10.7.3", "", {}, "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg=="],
|
||||
"ip-address": ["ip-address@10.7.4", "", {}, "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
@@ -581,7 +581,7 @@
|
||||
|
||||
"xterm-addon-fit": ["xterm-addon-fit@0.8.0", "", { "peerDependencies": { "xterm": "^5.0.0" } }, "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw=="],
|
||||
|
||||
"zod": ["zod@4.5.4", "", {}, "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA=="],
|
||||
"zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||
|
||||
@@ -597,6 +597,8 @@
|
||||
|
||||
"body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="],
|
||||
|
||||
"body-parser/qs": ["qs@6.16.1", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg=="],
|
||||
|
||||
"dom-serializer/domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
|
||||
|
||||
@@ -14,6 +14,8 @@ The built page is **committed** (eng-review D2): rendering works with zero
|
||||
network at install time and render time, and there is no npm supply-chain
|
||||
surface in `./setup`. The drift test (`test/diagram-render-drift.test.ts`)
|
||||
fails CI if `dist/` is edited by hand or falls out of sync with `BUILD_INFO.json`.
|
||||
It also checks that the bundled DOMPurify is the `package.json` override and that
|
||||
the bundle carries no Google API key.
|
||||
|
||||
## Page API (window functions)
|
||||
|
||||
@@ -41,6 +43,12 @@ bun run build
|
||||
# 3. commit package.json + bun.lock + dist/ together
|
||||
```
|
||||
|
||||
`scripts/build.ts` empties the one `VITE_APP_FIREBASE_CONFIG` literal in
|
||||
`@excalidraw/excalidraw`'s npm bundle (a public Firebase client key the offline page
|
||||
never uses), because a key-shaped literal fails the pre-gate secret scan. The build
|
||||
fails if that literal is not matched exactly once or an `AIza…` key survives, so an
|
||||
excalidraw bump that changes its shape needs the strip rule updated.
|
||||
|
||||
Render contract details (securityLevel strict, htmlLabels false, print-css font
|
||||
lock, `<base href>` + `</scri` escaping) are documented in `src/entry.ts` and
|
||||
`scripts/build.ts` — read both before touching either.
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"overrides": {
|
||||
"chokidar": "4.0.3",
|
||||
"dompurify": "3.4.16",
|
||||
"dompurify": "3.4.17",
|
||||
"lodash-es": "4.18.1",
|
||||
"nanoid": "5.1.16",
|
||||
"source-map-js": "1.2.2",
|
||||
@@ -281,7 +281,7 @@
|
||||
|
||||
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
||||
|
||||
"dompurify": ["dompurify@3.4.16", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ=="],
|
||||
"dompurify": ["dompurify@3.4.17", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w=="],
|
||||
|
||||
"es-toolkit": ["es-toolkit@1.52.0", "", {}, "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA=="],
|
||||
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "gstack-diagram-render",
|
||||
"sha256": "ec759ca41239ff888acdd0654c3f6ef505e3314b87c6e5de11903f804fc8a859",
|
||||
"srcSha256": "07238fae312bc0444f62b0a0a3404a8a38c45cef505aa1528c60a0ded17cbe06",
|
||||
"bytes": 7884089,
|
||||
"sha256": "c2d37f9f924baae2a146bc109bf9b4c308d89f49e08b1c35793edc310b579444",
|
||||
"srcSha256": "17d8921086e091252425c63f06cab01b4626d8e0fa9fe8738267e1ac4ade52e8",
|
||||
"bytes": 7884979,
|
||||
"bunVersion": "1.4.2",
|
||||
"deps": {
|
||||
"@excalidraw/excalidraw": "0.18.1",
|
||||
|
||||
+585
-585
File diff suppressed because it is too large.
Load diff
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"overrides": {
|
||||
"chokidar": "4.0.3",
|
||||
"dompurify": "3.4.16",
|
||||
"dompurify": "3.4.17",
|
||||
"nanoid": "5.1.16",
|
||||
"source-map-js": "1.2.2",
|
||||
"lodash-es": "4.18.1"
|
||||
|
||||
@@ -42,8 +42,26 @@ if (!result.success) {
|
||||
}
|
||||
const js = await result.outputs[0].text();
|
||||
|
||||
// @excalidraw/excalidraw's npm bundle carries excalidraw.com's public Firebase
|
||||
// client config (an `AIza…` key) in an env literal nothing in the package
|
||||
// reads. The offline page never opens a collab session, and a key-shaped
|
||||
// literal in a committed file fails the pre-gate secret scan on every rebuild,
|
||||
// so the literal is emptied. Exactly one match is expected: a bump that changes
|
||||
// the shape must fail here rather than put the key back silently.
|
||||
const FIREBASE_CONFIG = /VITE_APP_FIREBASE_CONFIG:'\{[^']*\}'/g;
|
||||
const firebaseHits = js.match(FIREBASE_CONFIG)?.length ?? 0;
|
||||
if (firebaseHits !== 1) {
|
||||
console.error(`expected exactly one VITE_APP_FIREBASE_CONFIG literal in the excalidraw bundle, found ${firebaseHits}; update the strip rule in scripts/build.ts`);
|
||||
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 = js.replaceAll("</scri", "<\\/scri");
|
||||
const inlineJs = strippedJs.replaceAll("</scri", "<\\/scri");
|
||||
|
||||
const head = `<!doctype html>
|
||||
<html>
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gstack",
|
||||
"version": "1.91.240",
|
||||
"version": "1.91.241",
|
||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -103,12 +103,12 @@
|
||||
"typescript": "7.0.2"
|
||||
},
|
||||
"overrides": {
|
||||
"@modelcontextprotocol/sdk": "1.31.0",
|
||||
"@modelcontextprotocol/sdk": "1.32.0",
|
||||
"adm-zip": "^0.6.1",
|
||||
"fast-uri": "3.1.8",
|
||||
"fast-uri": "3.1.9",
|
||||
"hono": "4.13.13",
|
||||
"image-size": "2.0.4",
|
||||
"ip-address": "10.7.3",
|
||||
"ip-address": "10.7.4",
|
||||
"proxy-addr": "2.0.8",
|
||||
"sharp": "0.35.5"
|
||||
},
|
||||
|
||||
+29
-5
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Dependency-override smoke tests (v1.78.0.0 dependency wave).
|
||||
*
|
||||
* The wave's `overrides` entries (package.json: ip-address 10.7.3 (raised
|
||||
* from 10.3.1 for GHSA-2vr4/h3mg/j6r3/rpw4, Oct 2026),
|
||||
* The wave's `overrides` entries (package.json: ip-address 10.7.4 (raised
|
||||
* from 10.3.1 for GHSA-2vr4/h3mg/j6r3/rpw4, then 10.7.3 for GHSA-rxp7, Oct 2026),
|
||||
* sharp 0.35.0; lib/diagram-render: nanoid 5.x, lodash-es 4.18.x) defeat
|
||||
* nested exact pins, so a green unit suite alone does not prove the forced
|
||||
* versions actually work for their consumers. These smokes exercise the
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -17,9 +17,13 @@ import { createHash } from "node:crypto";
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, "..", "lib", "diagram-render");
|
||||
const DIST_HTML = path.join(ROOT, "dist", "diagram-render.html");
|
||||
const BUILD_INFO = path.join(ROOT, "dist", "BUILD_INFO.json");
|
||||
// Repo-relative segment runs, so the pre-gate's import graph sees that this
|
||||
// test owns the committed bundle, its BUILD_INFO and the build script.
|
||||
const REPO = path.resolve(import.meta.dir, "..");
|
||||
const ROOT = path.join(REPO, "lib", "diagram-render");
|
||||
const DIST_HTML = path.join(REPO, "lib", "diagram-render", "dist", "diagram-render.html");
|
||||
const BUILD_INFO = path.join(REPO, "lib", "diagram-render", "dist", "BUILD_INFO.json");
|
||||
const BUILD_SCRIPT = path.join(REPO, "lib", "diagram-render", "scripts", "build.ts");
|
||||
|
||||
describe("diagram-render bundle drift", () => {
|
||||
test("dist hash matches BUILD_INFO (tamper check)", async () => {
|
||||
@@ -36,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
|
||||
@@ -43,7 +61,7 @@ describe("diagram-render bundle drift", () => {
|
||||
const info = await Bun.file(BUILD_INFO).json();
|
||||
const srcSha = createHash("sha256")
|
||||
.update(await Bun.file(path.join(ROOT, "src", "entry.ts")).text())
|
||||
.update(await Bun.file(path.join(ROOT, "scripts", "build.ts")).text())
|
||||
.update(await Bun.file(BUILD_SCRIPT).text())
|
||||
.digest("hex");
|
||||
expect(srcSha).toBe(info.srcSha256);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user