BUG-HUNT: [security] file_tools.validate_path() path traversal bypass — /sandbox-escape/ escapes /sandbox/ via string prefix match #7558

Closed
opened 2026-04-10 21:49:43 +00:00 by HAL9000 · 5 comments
Owner

Bug Report: [security] — Path Traversal Bypass in validate_path()

Severity Assessment

  • Impact: An attacker can provide a path to /tmp/sandbox-escape/evil that bypasses the sandbox check for /tmp/sandbox because str.startswith() returns True for prefix substrings. This allows escaping the sandbox to read/write files in sibling directories.
  • Likelihood: Medium — requires a sibling directory with matching name prefix.
  • Priority: Critical

Location

  • File: src/cleveragents/tool/builtins/file_tools.py
  • Function/Class: validate_path
  • Lines: 76-87

Description

The path containment check uses string startswith():

def validate_path(path_str, sandbox_root=None):
    root = Path(sandbox_root).resolve()
    target = (root / path_str).resolve()
    if not str(target).startswith(str(root)):  # BUG!
        raise ValueError(...)

Proof of concept:

root = "/tmp/sandbox"
target = "/tmp/sandbox-escape/evil"
str(target).startswith(str(root))  # Returns True! (WRONG)

"/tmp/sandbox-escape".startswith("/tmp/sandbox") is True because sandbox-escape starts with sandbox as a string prefix — the check fails to verify that root is an actual PATH prefix (with separator).

Expected Behavior

Use Path.is_relative_to(root) (Python 3.9+) or check str(root) + os.sep as prefix.

Actual Behavior

String prefix match allows sibling directories like /sandbox-escape/ to bypass the sandbox.

Suggested Fix

# Using Path.is_relative_to() (Python 3.9+):
if not target.is_relative_to(root):
    raise ValueError(...)

Category

security

TDD Note

After this bug issue is verified, a Type/Testing issue will be created with @tdd_expected_fail tags.


Automated by CleverAgents Bot
Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor

## Bug Report: [security] — Path Traversal Bypass in validate_path() ### Severity Assessment - **Impact**: An attacker can provide a path to /tmp/sandbox-escape/evil that bypasses the sandbox check for /tmp/sandbox because str.startswith() returns True for prefix substrings. This allows escaping the sandbox to read/write files in sibling directories. - **Likelihood**: Medium — requires a sibling directory with matching name prefix. - **Priority**: Critical ### Location - **File**: src/cleveragents/tool/builtins/file_tools.py - **Function/Class**: validate_path - **Lines**: 76-87 ### Description The path containment check uses string startswith(): ```python def validate_path(path_str, sandbox_root=None): root = Path(sandbox_root).resolve() target = (root / path_str).resolve() if not str(target).startswith(str(root)): # BUG! raise ValueError(...) ``` **Proof of concept:** ```python root = "/tmp/sandbox" target = "/tmp/sandbox-escape/evil" str(target).startswith(str(root)) # Returns True! (WRONG) ``` "/tmp/sandbox-escape".startswith("/tmp/sandbox") is True because sandbox-escape starts with sandbox as a string prefix — the check fails to verify that root is an actual PATH prefix (with separator). ### Expected Behavior Use Path.is_relative_to(root) (Python 3.9+) or check str(root) + os.sep as prefix. ### Actual Behavior String prefix match allows sibling directories like /sandbox-escape/ to bypass the sandbox. ### Suggested Fix ```python # Using Path.is_relative_to() (Python 3.9+): if not target.is_relative_to(root): raise ValueError(...) ``` ### Category security ### TDD Note After this bug issue is verified, a Type/Testing issue will be created with @tdd_expected_fail tags. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor
HAL9000 added this to the v3.5.0 milestone 2026-04-10 23:05:48 +00:00
Author
Owner

Issue triaged by project owner:

  • State: Verified
  • Priority: Critical — Path traversal security vulnerability in file_tools.validate_path(). Sandbox escape is a critical security issue.
  • Milestone: v3.5.0 (M6: Autonomy Hardening) — Sandbox security is core to M6 guard enforcement
  • Story Points: 3 (M) — Security fix with clear reproduction
  • MoSCoW: Must Have — Sandbox security must be enforced for autonomous operation

Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

Issue triaged by project owner: - **State**: Verified - **Priority**: Critical — Path traversal security vulnerability in file_tools.validate_path(). Sandbox escape is a critical security issue. - **Milestone**: v3.5.0 (M6: Autonomy Hardening) — Sandbox security is core to M6 guard enforcement - **Story Points**: 3 (M) — Security fix with clear reproduction - **MoSCoW**: Must Have — Sandbox security must be enforced for autonomous operation --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

[CLAIM] Issue claimed by implementation-worker

Claim Details:

  • Agent: implementation-worker
  • Session ID: impl-session-7558
  • Claim ID: claim-7558-20260412-1
  • Timestamp: 2026-04-12T00:00:00Z

This issue is now being worked on. Other agents should not start work on this issue.


Automated by CleverAgents Bot
Supervisor: Implementation | Agent: implementation-worker

[CLAIM] Issue claimed by implementation-worker **Claim Details:** - Agent: implementation-worker - Session ID: impl-session-7558 - Claim ID: claim-7558-20260412-1 - Timestamp: 2026-04-12T00:00:00Z This issue is now being worked on. Other agents should not start work on this issue. --- **Automated by CleverAgents Bot** Supervisor: Implementation | Agent: implementation-worker
Author
Owner

Implementation Plan for Issue #7558

Parallel Wave Plan

Wave 1 (1 subtask): Harden file_tools.validate_path to enforce sandbox containment using canonical path comparison (Path.is_relative_to with legacy fallback) and preserve deny list handling.

Wave 2 (1 subtask, depends on Wave 1): Add Behave coverage for sandbox path validation edge cases (tdd_issue_7558 tag) including traversal attempt regression and happy-path behavior.

Rationale

The behavioral tests rely on the hardened validator to pass deterministically; therefore they execute after the core fix.

Reference Context

Security sandbox invariants in spec sections on:

  • Tool-Based Resource Modification
  • Tool Execution Flow
  • Sandbox Isolation

Commit Hash for Traceability

ca2eaab02d65ed684cc21d14a568474af1a917fa


Automated by CleverAgents Bot
Supervisor: Implementation | Agent: issue-note-writer

## Implementation Plan for Issue #7558 ### Parallel Wave Plan **Wave 1 (1 subtask)**: Harden `file_tools.validate_path` to enforce sandbox containment using canonical path comparison (`Path.is_relative_to` with legacy fallback) and preserve deny list handling. **Wave 2 (1 subtask, depends on Wave 1)**: Add Behave coverage for sandbox path validation edge cases (`tdd_issue_7558` tag) including traversal attempt regression and happy-path behavior. ### Rationale The behavioral tests rely on the hardened validator to pass deterministically; therefore they execute after the core fix. ### Reference Context Security sandbox invariants in spec sections on: - Tool-Based Resource Modification - Tool Execution Flow - Sandbox Isolation ### Commit Hash for Traceability `ca2eaab02d65ed684cc21d14a568474af1a917fa` --- **Automated by CleverAgents Bot** Supervisor: Implementation | Agent: issue-note-writer
Author
Owner

Implementation Attempt — Tier 1: haiku — Starting [AUTO-IMP-ISSUE-7558]

What I'm attempting:
Fixing the path traversal bypass in file_tools.validate_path(). The bug uses str.startswith() for sandbox containment which allows sibling directories (e.g., /tmp/sandbox-escape/) to bypass the /tmp/sandbox/ check.

Plan:

  1. Wave 1: Harden validate_path using Path.is_relative_to() (Python 3.9+) with legacy fallback
  2. Wave 2: Add Behave BDD tests with tdd_issue_7558 tag covering traversal regression and happy-path

Escalation Tier: Tier 1: haiku


Automated by CleverAgents Bot
Supervisor: Implementation Pool | Agent: implementation-pool-supervisor

## Implementation Attempt — Tier 1: haiku — Starting [AUTO-IMP-ISSUE-7558] **What I'm attempting:** Fixing the path traversal bypass in `file_tools.validate_path()`. The bug uses `str.startswith()` for sandbox containment which allows sibling directories (e.g., `/tmp/sandbox-escape/`) to bypass the `/tmp/sandbox/` check. **Plan:** 1. Wave 1: Harden `validate_path` using `Path.is_relative_to()` (Python 3.9+) with legacy fallback 2. Wave 2: Add Behave BDD tests with `tdd_issue_7558` tag covering traversal regression and happy-path **Escalation Tier:** Tier 1: haiku --- **Automated by CleverAgents Bot** Supervisor: Implementation Pool | Agent: implementation-pool-supervisor
Author
Owner

Implementation Attempt — Tier 1: haiku — Success [AUTO-IMP-ISSUE-7558]

What was done:

Wave 1 — Security Fix:

  • Fixed validate_path() in src/cleveragents/tool/builtins/file_tools.py
  • Replaced str.startswith(str(root)) with Path.relative_to(root) which performs proper OS-level path prefix comparison using path separators
  • The old check allowed /tmp/sandbox-escape/ to bypass /tmp/sandbox/ because "sandbox-escape".startswith("sandbox") is True as a string

Wave 2 — BDD Regression Test:

  • Added scenario "Path traversal with sandbox name prefix collision is rejected" to features/tool_builtins.feature
  • Tagged with @tdd_issue @tdd_issue_7558 for traceability
  • Added step definitions in features/steps/tool_builtins_steps.py:
    • @given("a sibling directory with a name that is a prefix of the sandbox name") — dynamically creates a sibling dir with <sandbox_name>-escape suffix
    • @when("I attempt to read a file in the sibling escape directory") — exercises the prefix collision bypass
  • Updated CHANGELOG.md with security fix entry

Quality Gates:

  • nox -e lint — passed
  • nox -e typecheck — passed (0 errors)
  • nox -e unit_tests (tool_builtins.feature) — 33 scenarios passed, 0 failed

PR Created: #8261

  • Branch: fix/7558-validate-path-traversal
  • Milestone: v3.5.0 — M6: Autonomy Hardening
  • Labels: Type/Bug, Priority/Critical, State/In Review, MoSCoW/Must have, Points/3

Automated by CleverAgents Bot
Supervisor: Implementation Pool | Agent: implementation-pool-supervisor

## Implementation Attempt — Tier 1: haiku — **Success** ✅ [AUTO-IMP-ISSUE-7558] **What was done:** **Wave 1 — Security Fix:** - Fixed `validate_path()` in `src/cleveragents/tool/builtins/file_tools.py` - Replaced `str.startswith(str(root))` with `Path.relative_to(root)` which performs proper OS-level path prefix comparison using path separators - The old check allowed `/tmp/sandbox-escape/` to bypass `/tmp/sandbox/` because `"sandbox-escape".startswith("sandbox")` is `True` as a string **Wave 2 — BDD Regression Test:** - Added scenario `"Path traversal with sandbox name prefix collision is rejected"` to `features/tool_builtins.feature` - Tagged with `@tdd_issue @tdd_issue_7558` for traceability - Added step definitions in `features/steps/tool_builtins_steps.py`: - `@given("a sibling directory with a name that is a prefix of the sandbox name")` — dynamically creates a sibling dir with `<sandbox_name>-escape` suffix - `@when("I attempt to read a file in the sibling escape directory")` — exercises the prefix collision bypass - Updated `CHANGELOG.md` with security fix entry **Quality Gates:** - ✅ `nox -e lint` — passed - ✅ `nox -e typecheck` — passed (0 errors) - ✅ `nox -e unit_tests` (tool_builtins.feature) — 33 scenarios passed, 0 failed **PR Created:** https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/8261 - Branch: `fix/7558-validate-path-traversal` - Milestone: v3.5.0 — M6: Autonomy Hardening - Labels: Type/Bug, Priority/Critical, State/In Review, MoSCoW/Must have, Points/3 --- **Automated by CleverAgents Bot** Supervisor: Implementation Pool | Agent: implementation-pool-supervisor
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
cleveragents/cleveragents-core#7558
No description provided.