fix(server): open symlinked files (e.g. .env) in the file explorer#36
Merged
Conversation
readFile rejected any in-tree file whose symlink target resolved outside the workspace root, so opening a `.env` symlinked into a git worktree from the main checkout (a common secrets-sharing pattern) failed with WorkspaceFilePathEscapeError — "error when opening the file". writeFile had no such check and already wrote straight through the symlink, so the file could be saved but not opened. Drop readFile's realpath-containment check so it follows symlinks like writeFile (and every editor) does. The lexical containment in resolveRelativePathWithinRoot still rejects `..`/absolute relativePaths, so that remains the security boundary; the explorer already lets users re-root anywhere (Home/root bar), so blocking in-tree symlinks added no real confidentiality boundary. Fixes #35 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New t3code/* worktrees ship without node_modules; document the one-time pnpm install and the isolated single-file test/typecheck loop (plus the Effect.either/console.log gotchas) so future agents can reproduce server-behavior bugs in seconds instead of rediscovering the basics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
readFile(workspace file explorer/preview) no longer rejects an in-tree file whose symlink target resolves outside the workspace root. It now follows the symlink and reads the file it points at, mirroring whatwriteFilealready does.Also added an
AGENTS.mdsection documenting how to test in a fresh worktree (one-timepnpm install, isolated single-file test/typecheck loop, Effect/vitest gotchas), since that was the main friction hitting this bug.Why
Fixes #35 ("Cannot open file in file explorer" —
.env).The dotfiles feature made
.envvisible in the explorer, but opening one could still error withWorkspaceFilePathEscapeError("error when opening the file"). Root cause:readFilehad arealpath-based containment check that rejected any in-tree file whose symlink target pointed outside the root..envis very commonly a symlink — and especially so in this project's worktree-heavy workflow, where.envis symlinked into each git worktree from the main checkout so branches share secrets. In a worktree the symlink target resolves outside the worktree root, so it was rejected.The tell was the asymmetry:
writeFilehas no such check and writes straight through the symlink. So the same.envcould be saved but not opened — matching the reporter's "error when opening… should be able to move the file" and their hunch that it was "an issue with the files themselves."The lexical containment in
resolveRelativePathWithinRootstill rejects../absoluterelativePaths and remains the security boundary. The explorer already lets users re-root anywhere (Home / root bar), so blocking in-tree symlinks added no real confidentiality boundary — it only broke reads.How it was verified
Reproduced against the real server services with a scratch test (plain
.envread fine; symlinked-outside.envfailed with the escape error; listing surfaced.envcorrectly). After the fix:..traversal is still rejected.apps/server/src/workspacesuite: 44 passingtsgo --noEmit(server): cleanvp lint(workspace): cleanNo UI change (server-side only), so no before/after images.