[security] read_file/write_file tools have no path sandboxing or validation #676

Closed
opened 2026-06-02 23:41:34 +02:00 by sleepy · 1 comment
Owner

Security: read_file/write_file tools have no path sandboxing

Files: src/tool_execution.py lines 375-416

The read_file and write_file tools in _direct_fallback() accept arbitrary filesystem paths with no restrictions:

if tool == "read_file":
    path = content.split("\n", 1)[0].strip()
    with open(path, "r", encoding="utf-8", errors="replace") as f:
        return f.read(MAX_READ_CHARS + 1)

if tool == "write_file":
    path = lines[0].strip()
    os.makedirs(d, exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        f.write(body)

Issues

  • No path validation: A model-prompted tool call can read /etc/shadow, /proc/self/environ, ~/.ssh/id_rsa, or any file on the system
  • No path sandboxing: write_file can write to /etc/cron.d/backdoor, ~/.bashrc, /etc/passwd, or any arbitrary path
  • Directory traversal: ../../ sequences are not checked
  • The security model relies entirely on the LLM not being jailbroken into reading sensitive files

Bash tool already runs arbitrary commands

Yes, but bash is a known escape hatch. The file tools should be safer than bash — they're designed for document/code workflows. At minimum, non-admin users should be restricted to a working directory.

Suggested fix

  • Add an allowlist/denylist for path prefixes (e.g. deny /etc/, /proc/, /sys/, ~/.ssh/, ~/.gnupg/)
  • Optionally restrict to a configurable working directory sandbox
  • At minimum, block reading of known sensitive paths (private keys, credentials, shadow file)
  • Log all file access for audit
## Security: read_file/write_file tools have no path sandboxing ### Files: `src/tool_execution.py` lines 375-416 The `read_file` and `write_file` tools in `_direct_fallback()` accept arbitrary filesystem paths with no restrictions: ```python if tool == "read_file": path = content.split("\n", 1)[0].strip() with open(path, "r", encoding="utf-8", errors="replace") as f: return f.read(MAX_READ_CHARS + 1) if tool == "write_file": path = lines[0].strip() os.makedirs(d, exist_ok=True) with open(path, "w", encoding="utf-8") as f: f.write(body) ``` ### Issues - **No path validation**: A model-prompted tool call can read `/etc/shadow`, `/proc/self/environ`, `~/.ssh/id_rsa`, or any file on the system - **No path sandboxing**: `write_file` can write to `/etc/cron.d/backdoor`, `~/.bashrc`, `/etc/passwd`, or any arbitrary path - **Directory traversal**: `../../` sequences are not checked - The security model relies entirely on the LLM not being jailbroken into reading sensitive files ### Bash tool already runs arbitrary commands Yes, but bash is a known escape hatch. The file tools should be **safer** than bash — they're designed for document/code workflows. At minimum, non-admin users should be restricted to a working directory. ### Suggested fix - Add an allowlist/denylist for path prefixes (e.g. deny `/etc/`, `/proc/`, `/sys/`, `~/.ssh/`, `~/.gnupg/`) - Optionally restrict to a configurable working directory sandbox - At minimum, block reading of known sensitive paths (private keys, credentials, shadow file) - Log all file access for audit
Author
Owner

Fixed via PR #892 — added validate_file_path() with sandbox dirs, blocked system paths, and TOOL_SANDBOX_DIRS env var. 23 new tests.

Fixed via PR #892 — added validate_file_path() with sandbox dirs, blocked system paths, and TOOL_SANDBOX_DIRS env var. 23 new tests.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sleepy/odysseus#676
No description provided.