fix(workspace-fs): confine host-service filesystem ops to the workspace root (GHSA-6223-9p9j-gwf2, GHSA-9v86-rjhr-mqw6, GHSA-rg8v-v9gh-vwpq)
Summary
Confirms and fixes three advisories (rg8v duplicates 9v86): host-service filesystem.readFile/listDirectory/getMetadata could read arbitrary absolute paths, and movePath/copyPath could escape the workspace root via symlink ancestors.
Fix (well-factored)
One shared confinement gate (resolve realpath, assert it stays within the workspace root) applied across read/list/getMetadata/move/copy, so a new fs op is confined by default.
Proof
Exploit PoC 0/6 → 6/6 blocked; path-confinement.test.ts 5/7 → 12/0; workspace-fs 158 pass; desktop typecheck clean; biome clean. One failing test verified pre-existing on a clean tree.
Write-up: https://app.superset.sh/page/p1-a-host-service-filesystem-path-confinement-7uq4tb
Reviewer decision
The read-confinement half touches an existing test that pins "reads are host-wide" as intended policy — flagged for a human trust-model call, not decided here.
Merge note
Squash-merge only (earlier commits held local repro artifacts, now untracked).
Summary by cubic
Confines readFile, listDirectory, getMetadata, movePath, and copyPath to the workspace root, closing GHSA-6223-9p9j-gwf2, GHSA-9v86-rjhr-mqw6, and its duplicate GHSA-rg8v-v9gh-vwpq. Previously the read ops accepted any absolute path and move/copy only checked lexical containment, so a symlinked ancestor inside the worktree could escape it. All five ops now resolve the real path and reject anything outside the root.
Breaking change
- Reads are no longer host-wide: a workspace-scoped read that points outside the root now throws. This reverses the old documented policy;
browseHostandstatPathremain the host-wide escape hatches. A few tests that pinned the old read policy were inverted.
Summary by CodeRabbit
- Bug Fixes
- Workspace file operations are now confined to the workspace, including reads, directory listings, metadata checks, writes, moves, and copies.
- Attempts to access files outside the workspace or through symlinks that escape it are rejected. In-workspace files and symlinks can still be accessed according to the operation.