BUG-HUNT: [boundary] CopyOnWriteSandbox.get_path() path traversal check only splits on '/' but not on os.sep allowing Windows-style traversal #7356

Open
opened 2026-04-10 18:03:11 +00:00 by HAL9000 · 1 comment
Owner

Bug Report: [boundary] CopyOnWriteSandbox.get_path() path traversal check uses '/' split but not os.sep allowing OS-specific bypass

Severity Assessment

  • Impact: On Windows or systems where path separators differ, ..\\..\\etc\\passwd would not be detected as a traversal attempt, allowing files outside the sandbox to be read/written
  • Likelihood: Low — primarily affects Windows deployments, but the check is logically incomplete even on Linux for edge cases
  • Priority: Medium

Location

  • File: src/cleveragents/infrastructure/sandbox/copy_on_write.py
  • Function/Class: CopyOnWriteSandbox.get_path()
  • Lines: ~160-175

Description

The get_path() method performs path traversal detection with this check:

if ".." in resource_path.split("/"):
    raise ValueError(f"Path traversal not allowed: {resource_path}")

This has several issues:

  1. Only checks forward slash: resource_path.split("/") only splits on /. On Windows, paths use \. A path like ..\.. would not be split and ..\.. is not .., so the check passes.

  2. Incomplete component check: Even on Linux, the path ../secret_file splits into ['..', 'secret_file'] which is correctly detected. But .. as a single-component path directly also works. However, URL-encoded variants or Unicode look-alike characters might bypass this.

  3. Better approach exists: The proper way to check for path traversal is to resolve the path and check if it starts with the sandbox root, which is what file_tools.validate_path() does correctly. The CoW sandbox reinvents this check incorrectly.

Evidence

def get_path(self, resource_path: str) -> str:
    """Translate a resource-relative path to a sandbox copy path."""
    # ...
    if ".." in resource_path.split("/"):  # BUG: Only splits on '/', not os.sep!
        raise ValueError(f"Path traversal not allowed: {resource_path}")
    # ...
    return os.path.join(self._sandbox_path, resource_path)

Compare with the correct implementation in file_tools.py:

def validate_path(path_str: str, sandbox_root: str | None = None) -> Path:
    """Validate a path to prevent path traversal attacks."""
    root = Path(sandbox_root) if sandbox_root else Path.cwd()
    root = root.resolve()
    target = (root / path_str).resolve()
    if not str(target).startswith(str(root)):
        raise ValueError(f"Path traversal detected: '{path_str}' escapes sandbox root")
    return target

The file_tools.py version properly resolves the path and checks the absolute result. The copy_on_write.py version only checks for literal .. components split on /.

Expected Behavior

get_path() should use the same robust path traversal check as file_tools.validate_path():

def get_path(self, resource_path: str) -> str:
    # ...
    if self._sandbox_path is None:
        raise SandboxStateError("Sandbox path not set")
    
    sandbox_root = Path(self._sandbox_path).resolve()
    target = (sandbox_root / resource_path).resolve()
    if not str(target).startswith(str(sandbox_root)):
        raise ValueError(f"Path traversal not allowed: {resource_path!r}")
    return str(target)

Actual Behavior

The path traversal check only detects literal .. components when the path is split on /. This would miss:

  • ..\\secret on Windows
  • ../ trailing-slash variants
  • Paths with mixed separators

Category

boundary

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_, and @tdd_expected_fail to prove the bug exists before fixing it.


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

## Bug Report: [boundary] CopyOnWriteSandbox.get_path() path traversal check uses '/' split but not os.sep allowing OS-specific bypass ### Severity Assessment - **Impact**: On Windows or systems where path separators differ, `..\\..\\etc\\passwd` would not be detected as a traversal attempt, allowing files outside the sandbox to be read/written - **Likelihood**: Low — primarily affects Windows deployments, but the check is logically incomplete even on Linux for edge cases - **Priority**: Medium ### Location - **File**: `src/cleveragents/infrastructure/sandbox/copy_on_write.py` - **Function/Class**: `CopyOnWriteSandbox.get_path()` - **Lines**: ~160-175 ### Description The `get_path()` method performs path traversal detection with this check: ```python if ".." in resource_path.split("/"): raise ValueError(f"Path traversal not allowed: {resource_path}") ``` This has several issues: 1. **Only checks forward slash**: `resource_path.split("/")` only splits on `/`. On Windows, paths use `\`. A path like `..\..` would not be split and `..\..` is not `..`, so the check passes. 2. **Incomplete component check**: Even on Linux, the path `../secret_file` splits into `['..', 'secret_file']` which is correctly detected. But `..` as a single-component path directly also works. However, URL-encoded variants or Unicode look-alike characters might bypass this. 3. **Better approach exists**: The proper way to check for path traversal is to resolve the path and check if it starts with the sandbox root, which is what `file_tools.validate_path()` does correctly. The CoW sandbox reinvents this check incorrectly. ### Evidence ```python def get_path(self, resource_path: str) -> str: """Translate a resource-relative path to a sandbox copy path.""" # ... if ".." in resource_path.split("/"): # BUG: Only splits on '/', not os.sep! raise ValueError(f"Path traversal not allowed: {resource_path}") # ... return os.path.join(self._sandbox_path, resource_path) ``` Compare with the correct implementation in `file_tools.py`: ```python def validate_path(path_str: str, sandbox_root: str | None = None) -> Path: """Validate a path to prevent path traversal attacks.""" root = Path(sandbox_root) if sandbox_root else Path.cwd() root = root.resolve() target = (root / path_str).resolve() if not str(target).startswith(str(root)): raise ValueError(f"Path traversal detected: '{path_str}' escapes sandbox root") return target ``` The `file_tools.py` version properly resolves the path and checks the absolute result. The `copy_on_write.py` version only checks for literal `..` components split on `/`. ### Expected Behavior `get_path()` should use the same robust path traversal check as `file_tools.validate_path()`: ```python def get_path(self, resource_path: str) -> str: # ... if self._sandbox_path is None: raise SandboxStateError("Sandbox path not set") sandbox_root = Path(self._sandbox_path).resolve() target = (sandbox_root / resource_path).resolve() if not str(target).startswith(str(sandbox_root)): raise ValueError(f"Path traversal not allowed: {resource_path!r}") return str(target) ``` ### Actual Behavior The path traversal check only detects literal `..` components when the path is split on `/`. This would miss: - `..\\secret` on Windows - `../` trailing-slash variants - Paths with mixed separators ### Category boundary ### TDD Note After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_<this-issue-number>, and @tdd_expected_fail to prove the bug exists before fixing it. --- **Automated by CleverAgents Bot** Supervisor: Bug Detection Pool | Agent: bug-hunt-pool-supervisor
HAL9000 added this to the v3.5.0 milestone 2026-04-10 18:43:33 +00:00
Author
Owner

Issue triaged by project owner:

  • State: Verified — Path traversal check using string split is a real security bug
  • Priority: Priority/Critical — sandbox escape is a security vulnerability
  • Milestone: v3.5.0 — CopyOnWriteSandbox is part of the sandbox infrastructure needed for Autonomy Hardening
  • Type: Type/Bug
  • MoSCoW: Must Have — sandbox security is a hard requirement for any execution milestone

The fix is clear: use Path.is_relative_to() instead of string split, consistent with file_tools.validate_path().


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

Issue triaged by project owner: - **State**: Verified — Path traversal check using string split is a real security bug - **Priority**: Priority/Critical — sandbox escape is a security vulnerability - **Milestone**: v3.5.0 — CopyOnWriteSandbox is part of the sandbox infrastructure needed for Autonomy Hardening - **Type**: Type/Bug - **MoSCoW**: Must Have — sandbox security is a hard requirement for any execution milestone The fix is clear: use `Path.is_relative_to()` instead of string split, consistent with `file_tools.validate_path()`. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-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#7356
No description provided.