Skip to content

fix: enforce workspace boundary for file tools - #114

Draft
aryansk wants to merge 1 commit into
shauryagangrade:mainfrom
aryansk:feat-workspace-boundary-57
Draft

fix: enforce workspace boundary for file tools#114
aryansk wants to merge 1 commit into
shauryagangrade:mainfrom
aryansk:feat-workspace-boundary-57

Conversation

@aryansk

@aryansk aryansk commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #57

Problem

read_file, write_file, edit_file, and list_dir operate on arbitrary paths (os.path, open(...)) with no check that they stay inside the project/workspace directory. A model with a bad prompt — or a malicious session — can read or overwrite anything the user can on the machine, with only write_file's force flag as a mild guard.

Issue #57 proposes defining a "workspace root" (the --cwd / session cwd) and requiring explicit confirmation or rejection for paths resolving outside it, handling symlinks/.. via Path.resolve().

Change

  • Add gcode/tools.py:_workspace_root() (session cwd via Path(os.getcwd()).resolve(), honors --cwd), _is_within_workspace(path) (via Path.resolve() + is_relative_to, handles .., absolute paths, symlinks), and _check_workspace_boundary(path) (returns error string if outside and not confirmed)
  • Enforce in read_file, write_file, edit_file, list_dir: if _check_workspace_boundary returns error, return it; otherwise proceed. For outside paths, reuse the existing execute_bash y/n gate: prompt Path '...' is outside workspace '...'. Allow? (y/n):; in non-interactive mode (EOF) reject unless AUTO_APPROVE (--yes), mirroring execute_bash non-tty handling
  • In-workspace behavior unchanged

Why this approach

Reuses the existing permission-gate pattern from _run_tool/execute_bash, so the UX is consistent: interactive users get a y/n prompt for outside access, CI/non-tty gets a safe rejection unless --yes. Path.resolve() correctly handles .. and symlinks, and is_relative_to is the stdlib way to test containment.

Testing

command: python3 -m py_compile gcode/tools.py
result: ok

command: git diff --check
result: clean

command: grep -n "_check_workspace_boundary" gcode/tools.py
result: helper + 4 call sites (read/write/edit/list)

command: manual
result: `read_file("/etc/passwd")` outside workspace prompts y/n; with `echo n |` rejects; with `--yes` allows; `read_file("gcode/tools.py")` inside proceeds

Documentation and release impact

  • User-facing behavior updated (workspace boundary)
  • Changelog/release note needed: security fix
  • Migration or compatibility note needed
  • No documentation impact

Review notes

  • Known limitations: none beyond boundary check
  • Follow-up issue, if any: none
  • Security/licensing considerations: security fix, no new deps

Fixes shauryagangrade#57

File tools (read_file, write_file, edit_file, list_dir) operated on
arbitrary paths with no check that they stay inside the project
workspace. A model with a bad prompt could read or overwrite anything.

Define workspace root as the session cwd (honors --cwd) and check each
path via Path.resolve() (handles .., absolute paths, and symlinks).
For paths outside the workspace, reuse the existing y/n permission gate
(and non-interactive EOF handling) from execute_bash: prompt
"Path ... outside workspace ... Allow? (y/n)"; in non-interactive
mode reject unless --yes/AUTO_APPROVE, mirroring execute_bash. In-workspace
paths proceed unchanged.

Validation: py_compile passes, git diff --check clean; boundary helper
handles symlink/.. via resolve() and is_relative_to.
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.

[Security]: File tools have no workspace boundary — can read/write anywhere on disk

1 participant