Skip to content

fix(deps): raise vite to 8.1.5 to close three dev-server file-read advisories; 2.0.0-rc.3 - #1841

Merged
cliffhall merged 1 commit into
mainfrom
fix/vite-dev-server-advisories
Jul 28, 2026
Merged

fix(deps): raise vite to 8.1.5 to close three dev-server file-read advisories; 2.0.0-rc.3#1841
cliffhall merged 1 commit into
mainfrom
fix/vite-dev-server-advisories

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Part of #1818. Closes five vite advisories, three of them high, and bumps to 2.0.0-rc.3.

What the sweep missed

#1837 moved vite to 8.0.16 in the root and tui manifests, but clients/web stayed pinned at 8.0.0. npm update vite reported "up to date" and only an explicit install would move it — nothing in the peer graph actually pinned it, npm was just being conservative.

Advisory Sev Issue
GHSA-fx2h-pf6j-xcff high server.fs.deny bypass via Windows alternate data streams
GHSA-v2wj-q39q-566r high server.fs.deny bypassed with queries
GHSA-p9ff-h696-f583 high Arbitrary file read via the Vite dev server
GHSA-v6wh-96g9-6wx3 medium launch-editor NTLMv2 hash disclosure via UNC path
GHSA-4w7w-66w2-5vf9 medium Path traversal in optimized deps

Why these are worth fixing rather than dismissing

The other remaining dev-scope alerts are being dismissed as tolerable — these are not, and the distinction matters.

server.fs.deny bypass is remotely triggerable: a malicious page can read arbitrary files from anyone running npm run dev. Published-package users are unaffected (they get the built dist and never run vite), but contributors to this repo are directly exposed. That is a materially different risk profile from a lint-time glob DoS.

The declared floor, not just the pin

Also raises the declared range to ^8.1.5 in all three manifests that declare vite. The root previously declared ^8.0.0, which still permitted the vulnerable 8.0.0 on a fresh resolve — so updating the lockfile alone would have left the regression path open.

Manifest Before After
root ^8.0.0 ^8.1.5
clients/web ^8.0.0 (pinned 8.0.0) ^8.1.5
clients/tui ^8.0.16 ^8.1.5

Why rc.3 rather than folding into 2.0.0

vite builds the shipped bundle, so changing it changes the artifact. rc.2 validated a different tree. The discipline held throughout this phase is that the tree which ships is the tree that was validated against the live registry — so this gets its own RC rather than riding along untested.

Verification

npm run ciexit 0. Full chain: validatecoverage (per-file ≥90) → build-gate → smokes → Storybook (462 tests, 109 files).

What remains after this

Only @vitest/browser (3 critical — the exact-pin knot, #1839) and esbuild (3 low). Both dev-scope, both genuinely blocked, both to be dismissed as tolerable_risk. The eslint cluster was auto-dismissed by GitHub itself; see #1838 for why npm audit still reports it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Txmv2qqv3yeKgRzoqXytzD

…visories; 2.0.0-rc.3

The #1837 sweep moved vite to 8.0.16 in the root and tui manifests but left
`clients/web` pinned at **8.0.0** — `npm update` reported "up to date" and only
an explicit install would move it. That left five open advisories, three high:

  GHSA-fx2h-pf6j-xcff  high    `server.fs.deny` bypass via Windows ADS
  GHSA-v2wj-q39q-566r  high    `server.fs.deny` bypassed with queries
  GHSA-p9ff-h696-f583  high    arbitrary file read via the dev server
  GHSA-v6wh-96g9-6wx3  medium  launch-editor NTLMv2 hash disclosure
  GHSA-4w7w-66w2-5vf9  medium  path traversal in optimized deps

These are worth fixing rather than dismissing as "dev-only". Unlike the other
remaining dev-scope alerts, `server.fs.deny` bypass is *remotely triggerable*: a
malicious page can read arbitrary files from anyone running `npm run dev`.
Published-package users are unaffected — they get the built `dist` and never
run vite — but contributors are.

Also raises the declared floor to `^8.1.5` in all three manifests that declare
vite. The root previously declared `^8.0.0`, which still *permitted* the
vulnerable 8.0.0 on a fresh resolve, so pinning the lockfile alone would have
left a regression path open.

Bumped to rc.3 rather than folding this into 2.0.0: vite builds the shipped
bundle, so changing it changes the artifact, and the tree that ships should be
the tree that was validated against the live registry. rc.2 validated a
different tree.

`npm run ci` passes — full chain including the coverage gate, build gate,
smokes, and 462 Storybook tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Txmv2qqv3yeKgRzoqXytzD
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Jul 28, 2026
@cliffhall
cliffhall merged commit 73fe059 into main Jul 28, 2026
6 checks passed
@cliffhall
cliffhall deleted the fix/vite-dev-server-advisories branch July 28, 2026 06:43
@cliffhall cliffhall mentioned this pull request Jul 28, 2026
cliffhall added a commit that referenced this pull request Jul 28, 2026
The v2 go-live release (#1818, phase 5 of #1804).

This is byte-identical to 2.0.0-rc.3 apart from dropping the prerelease suffix,
which is what makes rc.3's validation meaningful: the tree that ships is the
tree that was validated against the live registry.

`2.0.0` has no hyphen, so the derivation added in #1834 resolves it to `latest`
— this is the release that takes the tag from 1.0.1.

Validated across three release candidates:

- rc.1 caught two publish-path defects invisible to every local check —
  `NODE_AUTH_TOKEN` pointing at a non-existent secret (shadowing OIDC trusted
  publishing) and a missing npm CLI upgrade (trusted publishing needs
  >= 11.5.1; Node 22 bundles 10.x). Both fixed in #1836.
- rc.2 validated the dependency sweep (#1837) that cleared every prod-scope
  advisory using lockfile-only changes.
- rc.3 validated the vite 8.1.5 bump (#1841) closing three high-severity
  dev-server file-read advisories.

Each RC was verified with a cold `node:22` container install driving the
published tarball end to end — install, `--help`, and a real `--cli tools/list`
against a live stdio server. `latest` remained on 1.0.1 throughout, proving the
`--tag` derivation.

Zero open Dependabot alerts at time of release.


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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
cliffhall added a commit that referenced this pull request Aug 1, 2026
…8.1.5) (#1881)

The 2.0.0-rc series shipped these dependency bumps to `main` only, so
`v2/main` was developing against older versions and would have met them
for the first time at the milestone merge.

- `@hono/node-server` ^1.19.14 -> ^2.0.12 (root, clients/web) — a major.
  v2.0.0's only breaking changes are dropping Node 18 (the repo already
  requires >=22.19.0) and removing the `@hono/node-server/vercel` adapter
  (unused here). The used surface — `serve()`, `serveStatic`, `ServerType`
  — is unchanged, so no source edits were needed.
- `vite` ^8.0.0 -> ^8.1.5 (root, clients/web) and ^8.0.16 -> ^8.1.5
  (clients/tui) — the security bumps from #1841.

`npm install` at the root (postinstall cascades into every client)
regenerated the three touched lockfiles. `npm run ci` passes.


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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
cliffhall added a commit that referenced this pull request Aug 1, 2026
…SA-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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant