Skip to content

Share workspace roots with list_dir and delete_file bounds - #1009

Merged
TheGreatAxios merged 3 commits into
mainfrom
cl-6729-share-workspace-roots-with-list_dir-and-delete_file-bounds
Sep 14, 2026
Merged

TheGreatAxios merged 3 commits into
mainfrom
cl-6729-share-workspace-roots-with-list_dir-and-delete_file-bounds

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Summary

Implements Linear CL-6729: listDirectory and deleteFilePlugin resolve through the shared workspace-root containment instead of cwd alone, so registered sibling worktree roots list and delete while genuinely outside paths stay refused. Session roots realpath before the lexical check, fixing aliased-tmp false denies on macOS. Wiring threads the worktree roots provider at both tool sites; yolo/allowOutside and allow/deny semantics unchanged (availability fix only).

Parallel-stack note: shares path-restriction and wiring surface with CL-6727/CL-6730 in flight — kept to a minimal diff.

Fixes CL-6729

Verification

  • Red: new CL-6729 tests fail on unfixed code (22 tests across the two files: 18 pass, 4 fail)
  • Green: bun run check exit 0 — lint, typecheck, build, projects-dir-guard, full suite 7312 pass, 0 fail
  • Post-rebase full run hit one unrelated timing-flaky watchdog case; isolated file run 47 pass, 0 fail, and a full re-run is green (7312 pass, 0 fail)
  • DO NOT MERGE

listDirectory and deleteFilePlugin confine to cwd alone: sibling worktree roots are refused, aliased session roots false-deny, and delete_file has no roots provider. The new CL-6729 cases pin the availability gap before the fix.
listDirectory and deleteFilePlugin resolve through the shared workspace-root containment instead of cwd alone, so sibling worktree roots list and delete while genuinely outside paths stay refused. Session roots realpath before the lexical check, fixing aliased-tmp false denies. Wiring threads the worktree roots provider at both tool sites; yolo and allowOutside behavior is unchanged.
@linear-code

linear-code Bot commented Sep 13, 2026

Copy link
Copy Markdown

CL-6729

@TheGreatAxios

Copy link
Copy Markdown
Collaborator Author

Critic review · Comment

Correct direction: both bounds now go through resolveWorkspacePath, one shared rootsProvider instance in posix-tool-plugins.ts:18, alias false-denies fixed by realpathing the session root, yolo/allowOutside semantics untouched. Tests are real (sibling allow, outside deny, alias, relative traversal). No escape widened.

Findings

  • src/plugins/delete-file-plugin.ts:84-93 — should-fix, availability narrowing this diff introduces (verified, not vibes): the containment check resolves the full target path, while the old check resolved only dirname(target). A dangling symlink inside cwd now returns undefined from resolveWorkspacePath (UNRESOLVABLE) and a symlink inside cwd pointing outside resolves outside, so delete_file refuses both with "resolves outside the working directory". The old code allowed them, and allowing was safe: lstat/unlink never follow the final component, so unlinking the link itself cannot escape. Probe on worktree 6def6e3a: resolveWorkspacePath(dir, dangling-link) → undefined; link-to-outside → undefined. A user asking to clean up a broken link now gets a refusal plus a misleading message. Suggest keying delete containment off the parent dir (nearest-ancestor resolve of dirname), since the operation unlinks the name, not the referent. Not blocking — it narrows rather than widens — but it is a behavior change beyond "availability fix" for siblings.
  • src/util/list-dir.ts:57-73 — genuinely fine: uses the resolver's canonical return directly for readdir, closing the check-to-read symlink retarget the old two-step compare left open. Non-existent paths still surface as "cannot list …" via the readdir failure, same as before.
  • Wiring (src/agent/posix-tool-plugins.ts:15-30, src/agent/tools.ts:584-587) — genuinely fine: both sites threaded, single shared provider instance in the posix stack, optional rootsProvider defaults to cwd-alone so the API change is backwards compatible. No sync→async drift.
  • Hygiene — clean: bespoke isWithin deleted with its relative/sep/isAbsolute imports, no duplication, comments describe the current code.

Notes

  • Keeper tests for the suite: deleting a dangling symlink inside cwd succeeds; deleting a link whose referent is outside removes the link without touching the referent. (Routing to testsmith/builder, not committing from here.)
  • Stack: shares path-restriction surface with CL-6727/CL-6730; this diff stays minimal, no cross-branch conflict introduced here.
  • Probe scripts under tmp/critique-tests/ removed after verification. No commits made.

@TheGreatAxios

Copy link
Copy Markdown
Collaborator Author

Addressed the delete_file should-fix: containment is now keyed off the parent directory (the operation unlinks the name; lstat/unlink never follow the final component), so a dangling symlink in cwd and a link with an outside referent delete the link itself while the referent stays untouched. Sibling admission, outside refusal, alias handling, and yolo/allowOutside are unchanged.

Summary

  • Keyed delete_file containment off dirname(target) through the shared workspace resolver.
  • Added keeper tests: dangling symlink in cwd deletes; link with outside referent removes the link, referent untouched.

Verification

  • bun test src/plugins/delete-file-plugin.test.ts: 13 pass, 0 fail
  • bun test src/list-dir.test.ts src/plugins/path-escape-plugin.test.ts: 24 pass, 0 fail
  • bun run check: 7314 pass, 0 fail across 501 files
  • Fix commit 02a9ec7 pushed, not merged.

@TheGreatAxios

Copy link
Copy Markdown
Collaborator Author

Critic re-check · Green

Fix commit 02a9ec7 resolves the should-fix with no escape widened.

Fix reviewed: src/plugins/delete-file-plugin.ts now keys containment off dirname(target) through the shared workspace resolver instead of the full target path. Correct because lstat/unlink never follow the final component — unlinking the link itself cannot escape, so refusing dangling/outside-referent links was availability narrowing, not protection. Sibling admission, outside refusal, alias handling, and yolo/allowOutside are untouched; directory refusal via lstat still follows the check.

Tests run (worktree at 02a9ec7):

  • bun test src/plugins/delete-file-plugin.test.ts: 13 pass, 0 fail — includes the 2 new keeper tests (dangling symlink deletes; outside-referent link removes the link, referent untouched) plus the no-widening guards: traversal blocked, file reached through a directory symlink outside still refused, genuinely-outside file still refused with roots registered.
  • bun test src/list-dir.test.ts src/plugins/path-escape-plugin.test.ts: 24 pass, 0 fail — list_dir boundary unchanged.

Verdict: Green. No remaining findings; no commits made.

@TheGreatAxios
TheGreatAxios merged commit d69c64c into main Sep 14, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant