chore(deps): untangle the vitest/Storybook exact-pin knot to clear GHSA-p63j-vcc4-9vmv (#1839) (#1899)

GHSA-p63j-vcc4-9vmv (**critical**) — `@vitest/browser` Browser Mode provider
commands accept a file path from the browser and act on it without checking the
`allowWrite` permission gate. Patched in 4.1.10; `clients/web` was pinned at
4.1.0.

Dev-scope only — `@vitest/*` is absent from the published `files` allowlist, and
Browser Mode here runs this project's own Storybook play functions, never a
third-party page. That is why #1837 deferred it rather than attempting a
toolchain upgrade immediately before an irreversible publish.

## The knot, and what actually unties it

`^4.1.0` already admits 4.1.10, so the declared ranges were never the problem.
The family is welded by *exact* peer pins — `vitest` pins
`@vitest/browser-playwright`, which pins `@vitest/browser` — so nothing moves
alone.

The issue predicted that an explicit coordinated `npm i -D` would fix it. It
does not. With `clients/web/package-lock.json` in place, npm ERESOLVEs even when
every member of the family plus the Storybook packages are named in one command
and `node_modules` is deleted first:

    Found: @storybook/addon-a11y@10.2.19
    Could not resolve dependency: dev @storybook/addon-a11y@"^10.5.5"
    Conflicting peer dependency: storybook@10.5.5

npm resolves against the existing lock tree and will not move the whole
constellation at once. **Regenerating `clients/web/package-lock.json` is the
only thing that unties it** — a clean resolve places all four at 4.1.10 with no
peer complaints at all.

## Versions

    vitest                       4.1.0    -> 4.1.10
    @vitest/browser              4.1.0    -> 4.1.10   (transitive)
    @vitest/browser-playwright   4.1.0    -> 4.1.10
    @vitest/coverage-v8          4.1.0    -> 4.1.10
    storybook + @storybook/*     10.2.19  -> 10.5.5
    eslint-plugin-storybook      10.2.19  -> 10.5.5
    @chromatic-com/storybook     5.0.1    -> 5.2.1

`clients/{cli,tui,launcher}` were already resolving 4.1.9 (no browser pins, so
they floated freely) and were never vulnerable. Their declared `^4.1.0` still
*permitted* 4.1.0 on a fresh resolve, so the floor is raised to `^4.1.10` in all
four manifests — same reasoning as the vite floor in #1841. Pinning only the
lockfile would have left a regression path open.

## The vite 8 peer override

Resolved, as a consequence rather than a workaround.
`@joshwooding/vite-plugin-react-docgen-typescript@0.6.4` declared
`peer vite@"^3 || ^4 || ^5 || ^6 || ^7"` against our vite 8. It is not a direct
dependency — it arrives through `@storybook/react-vite`, which at 10.5.5 depends
on `^0.7.0`, and 0.7.0 adds `^8.0.0` to that range. So taking Storybook 10.5.5
for the peer set drops the override too; nothing is forced.

## Two holds the regeneration forced, each with a tracked issue

Regenerating the lock floats every other in-range dependency to current. Most of
that is welcome — it is what takes `clients/web` from 19 advisories to 1 — but
two floats broke the gate and are unrelated to this security fix:

- **zod 4.3.6 -> 4.4.3** makes `clients/web`'s `tsc -b` die with
  `FATAL ERROR: Ineffective mark-compacts near heap limit` at the ~4GB default.
  Reverting zod alone makes it exit 0, so the attribution is unambiguous.
  Held at `~4.3.6`. See #1896.
- **eslint-plugin-react-hooks 7.0.1 -> 7.1.1** enables `set-state-in-effect`,
  which fails `lint` on 8 pre-existing violations across 7 components. Fixing
  them is a real refactor with interaction-behavior consequences, not a
  suppression. Held at `~7.0.1`. See #1897.

Both `~` pins are deliberate and temporary, and both issues say so — they are
constraints to remove, not preferences.

Storybook 10.5.5 also reports that `setProjectAnnotations` is applied
automatically since 10.3, making `.storybook/vitest.setup.ts` redundant. Left in
place: `./preview` carries the Mantine decorator and the a11y annotations drive
the play-function assertions, and if automatic provisioning missed either, the
462 stories would render unthemed with a11y checks inert and very likely still
pass. That silent-failure risk does not belong in a security bump. See #1898.

## Audit delta

`clients/web`, where the advisory lived:

    before   19 (4 critical, 6 high, 6 moderate, 3 low)
    after     1 (1 low — esbuild-in-tsup, Windows dev server only)

All four critical entries — `vitest`, `@vitest/browser`,
`@vitest/browser-playwright`, `@vitest/coverage-v8` — are gone. The other 14 are
transitive dev-tree advisories (hono, ws, js-yaml, flatted, brace-expansion, ...)
cleared as a side effect of the clean resolve. Elsewhere: cli 6 -> 5,
launcher 3 -> 2, tui 4 -> 4, root unchanged at 9 (its lock is untouched). Those
remainders are pre-existing and unrelated.

## Verification

`npm run ci` green end to end on the upgraded stack — the point of caring here,
since this *is* the test infrastructure:

- coverage gate (>=90 per file, all four dimensions) passes on all four clients;
  no file regressed, so no gate was lowered and no `v8 ignore` added
- 4802 web tests, 304 cli, 282 tui, 5 launcher
- 462 Storybook play functions across 109 files, on vitest 4.1.10 Browser Mode
- verify:build-gate, and all five smokes including `smoke:tui` (real TTY here)

Claude-Session: https://claude.ai/code/session_01YAt8rqxysNbhYWLhoRm3fU

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cliff Hall 2026-08-01 19:50:57 -04:00 • committed by GitHub
commit 23e0c39d11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 7865 additions and 6278 deletions

View file

@ -29,7 +29,7 @@
"@eslint/js": "^9.39.4",
"@types/node": "^24.12.4",
"@types/react": "^19.2.14",
"@vitest/coverage-v8": "^4.1.0",
"@vitest/coverage-v8": "^4.1.10",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"globals": "^17.4.0",
@ -41,7 +41,7 @@
"typescript-eslint": "^8.56.1",
"vite": "^8.1.5",
"vite-node": "^6.0.0",
"vitest": "^4.1.0"
"vitest": "^4.1.10"
}
},
"node_modules/@alcalzone/ansi-tokenize": {
@ -2376,14 +2376,14 @@
}
},
"node_modules/@vitest/coverage-v8": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz",
"integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz",
"integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
"@vitest/utils": "4.1.9",
"@vitest/utils": "4.1.10",
"ast-v8-to-istanbul": "^1.0.0",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
@ -2397,8 +2397,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "4.1.9",
"vitest": "4.1.9"
"@vitest/browser": "4.1.10",
"vitest": "4.1.10"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@ -2407,16 +2407,16 @@
}
},
"node_modules/@vitest/expect": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz",
"integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
"integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2",
"@vitest/spy": "4.1.9",
"@vitest/utils": "4.1.9",
"@vitest/spy": "4.1.10",
"@vitest/utils": "4.1.10",
"chai": "^6.2.2",
"tinyrainbow": "^3.1.0"
},
@ -2425,13 +2425,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz",
"integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
"integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.1.9",
"@vitest/spy": "4.1.10",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@ -2452,9 +2452,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz",
"integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
"integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2465,13 +2465,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz",
"integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
"integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.1.9",
"@vitest/utils": "4.1.10",
"pathe": "^2.0.3"
},
"funding": {
@ -2479,14 +2479,14 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz",
"integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
"integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.9",
"@vitest/utils": "4.1.9",
"@vitest/pretty-format": "4.1.10",
"@vitest/utils": "4.1.10",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@ -2495,9 +2495,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz",
"integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
"integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
"dev": true,
"license": "MIT",
"funding": {
@ -2505,13 +2505,13 @@
}
},
"node_modules/@vitest/utils": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz",
"integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
"integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.9",
"@vitest/pretty-format": "4.1.10",
"convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0"
},
@ -3438,9 +3438,9 @@
}
},
"node_modules/expect-type": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
"integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -5304,9 +5304,9 @@
"license": "MIT"
},
"node_modules/std-env": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
"integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz",
"integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
"dev": true,
"license": "MIT"
},
@ -5503,9 +5503,9 @@
}
},
"node_modules/tinyrainbow": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
"integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz",
"integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==",
"dev": true,
"license": "MIT",
"engines": {
@ -6351,19 +6351,19 @@
}
},
"node_modules/vitest": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz",
"integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==",
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
"integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "4.1.9",
"@vitest/mocker": "4.1.9",
"@vitest/pretty-format": "4.1.9",
"@vitest/runner": "4.1.9",
"@vitest/snapshot": "4.1.9",
"@vitest/spy": "4.1.9",
"@vitest/utils": "4.1.9",
"@vitest/expect": "4.1.10",
"@vitest/mocker": "4.1.10",
"@vitest/pretty-format": "4.1.10",
"@vitest/runner": "4.1.10",
"@vitest/snapshot": "4.1.10",
"@vitest/spy": "4.1.10",
"@vitest/utils": "4.1.10",
"es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0",
"magic-string": "^0.30.21",
@ -6391,12 +6391,12 @@
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.1.9",
"@vitest/browser-preview": "4.1.9",
"@vitest/browser-webdriverio": "4.1.9",
"@vitest/coverage-istanbul": "4.1.9",
"@vitest/coverage-v8": "4.1.9",
"@vitest/ui": "4.1.9",
"@vitest/browser-playwright": "4.1.10",
"@vitest/browser-preview": "4.1.10",
"@vitest/browser-webdriverio": "4.1.10",
"@vitest/coverage-istanbul": "4.1.10",
"@vitest/coverage-v8": "4.1.10",
"@vitest/ui": "4.1.10",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
@ -6441,9 +6441,9 @@
}
},
"node_modules/vitest/node_modules/tinyexec": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
"integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz",
"integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==",
"dev": true,
"license": "MIT",
"engines": {

View file

@ -48,7 +48,7 @@
"@eslint/js": "^9.39.4",
"@types/node": "^24.12.4",
"@types/react": "^19.2.14",
"@vitest/coverage-v8": "^4.1.0",
"@vitest/coverage-v8": "^4.1.10",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"globals": "^17.4.0",
@ -60,6 +60,6 @@
"typescript-eslint": "^8.56.1",
"vite": "^8.1.5",
"vite-node": "^6.0.0",
"vitest": "^4.1.0"
"vitest": "^4.1.10"
}
}