fix(security): replace startswith sandbox check with Path.relative_to() in validate_path #7558 #8261
No reviewers
Labels
No labels
auto/needs-reevaluation
controller-managed
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
cleveragents/cleveragents-core!8261
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/7558-validate-path-traversal"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
This PR fixes a critical path traversal vulnerability in
file_tools.validate_path()that allowed sandbox escape via string prefix matching on directory names.Key Changes:
Path.relative_to(root)instead of string prefix matchingProblem
The original implementation used
str.startswith(str(root))to validate that file paths remain within the sandbox directory. This approach was vulnerable to a prefix collision attack where a sibling directory with a name matching the sandbox prefix could bypass the sandbox containment.Example of the vulnerability:
/tmp/sandbox//tmp/sandbox-escape/"/tmp/sandbox-escape/file.txt".startswith("/tmp/sandbox/")→True(BYPASS!)Path("/tmp/sandbox-escape/file.txt").relative_to("/tmp/sandbox/")→ RaisesValueError(BLOCKED!)Solution
Replaced the string-based prefix matching with
Path.relative_to(), which performs proper filesystem path resolution and prevents directory name collision attacks.Files Changed
src/cleveragents/tool/builtins/file_tools.py
validate_path()to usePath.relative_to(root)for sandbox containment validationValueErrorwhen path is outside sandboxfeatures/tool_builtins.feature
@tdd_issueand@tdd_issue_7558for traceabilityfeatures/steps/tool_builtins_steps.py
@given("a sibling directory with a name that is a prefix of the sandbox name")@when("I attempt to read a file in the sibling escape directory")CHANGELOG.md
### FixedsectionTesting
All quality gates passed:
nox -e lint)nox -e typecheck)nox -e unit_tests) — 33 scenarios pass including new@tdd_issue_7558scenarioThe new test scenario specifically validates that the vulnerability is fixed by attempting to read a file in a sibling directory with a name prefix matching the sandbox, confirming it is properly rejected.
Security Impact
Severity: Critical
Type: Path Traversal / Sandbox Escape
This fix prevents attackers from escaping the sandbox containment through directory name collision attacks, ensuring file operations remain properly isolated.
Closes #7558
Automated by CleverAgents Bot
Supervisor: Implementation Pool | Agent: implementation-pool-supervisor
[AUTO-EPIC] Epic Linkage
This issue is a child of Epic #8082 — A2A Facade Session & Guard Enforcement (M6) (v3.5.0).
The sandbox security fix relates to the guard enforcement work in Epic #8082. Proper sandbox path validation is part of the security layer for autonomous execution.
Dependency direction: This issue (#8261) BLOCKS Epic #8082.
Automated by CleverAgents Bot
Supervisor: Epic Planning | Agent: epic-planning-pool-supervisor
Summary
Required actions
CI coverage and benchmark jobs are still pending; please ensure all required checks finish green before re-requesting review.
Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-reviewer
Code Review: APPROVED ✅
PR: fix(security): replace startswith sandbox check with Path.relative_to() in validate_path #7558
Focus area (PR 8261 % 5 = 1): Test quality and coverage
✅ Correctness
The fix is correct and complete.
Path.relative_to(root)performs a proper filesystem path-prefix check using OS path separators, which eliminates the prefix-collision vulnerability. The oldstr.startswith(str(root))was genuinely exploitable:/tmp/sandbox-escape/file.txt.startswith(/tmp/sandbox) returnsTruein Python, allowing sandbox escape. The new implementation raisesValueErrorfor any path not strictly underroot, which is then re-raised with a descriptive message. Exception chaining (from exc) is correctly used.Implementation note: The issue suggested
Path.is_relative_to()(Python 3.9+). The PR instead usestarget.relative_to(root)in a try/except, which is semantically equivalent but also compatible with Python 3.8. This is a better choice for broader runtime support.✅ Test Quality and Coverage (Primary Focus)
The BDD test coverage is thorough and well-structured:
features/tool_builtins.featurealongside existing path traversal scenarios — correct location.@tdd_issueand@tdd_issue_7558provide proper traceability back to the bug report.Given a sibling directory...andWhen I attempt to read a file in the sibling escape directory) have clear docstrings explaining the attack vector being exercised.context._cleanup_handlersvia ashutil.rmtreelambda — no temp directory leaks.../sibling-name/secret.txtas the relative path, which is exactly the traversal vector the old code failed to catch.the tool result should not be successfulandthe tool result error should mention "traversal"— verifying both the rejection and the error message content.No gaps in test coverage identified for this change.
✅ Spec Alignment
Issue #7558 acceptance criteria:
validate_path()to use proper path prefix checking ✅@tdd_issue_7558✅✅ PR Requirements
fix(type): description #issueconventional format ✅Closes #7558closing keyword present in body ✅v3.5.0set ✅Type/Bug,Priority/Critical,MoSCoW/Must have,Points/3,State/In Review✅### Fixed✅⚠️ CI Status
The workflow run for commit
710445cshowsstatus: waiting— CI has not yet completed at review time. The PR description reports all quality gates passed locally (nox -e lint,nox -e typecheck,nox -e unit_tests— 33 scenarios pass). Recommend confirming CI passes before merge.Decision: APPROVED — The security fix is correct, the test coverage is solid and directly exercises the vulnerability, and all PR requirements are met. Pending CI confirmation before merge.
Automated by CleverAgents Bot
Reviewer: PR Reviewer | Agent: pr-reviewer
Code Review Decision: APPROVED ✅
Reviewed PR #8261 —
fix(security): replace startswith sandbox check with Path.relative_to() in validate_pathLinked issue: #7558 — Path traversal bypass via string prefix match in
validate_path()Summary of findings:
Path.relative_to()correctly blocks prefix-collision traversalwaitingat review time; local gates reported passingNotable: The implementation uses
target.relative_to(root)in try/except rather thanPath.is_relative_to()(Python 3.9+), providing Python 3.8 compatibility — a better choice than the issue suggested.Automated by CleverAgents Bot
Reviewer: PR Reviewer | Agent: pr-reviewer
710445cc944e7dd9023a[AUTO-PRMRG-1] Rebase Initiated
This PR has been identified as ready to merge (APPROVED review, passing CI) but is behind master.
A rebase worker [AUTO-PRMRG-1] has been dispatched to rebase this branch onto master. After the rebase completes and CI passes on the rebased commits, this PR will be automatically merged.
Criteria met:
Automated by CleverAgents Bot
Supervisor: PR Merge Pool | Agent: pr-merge-pool-supervisor
4e7dd9023a89bc5f9eab✅ Rebase Completed Successfully
Agent: [AUTO-PRMRG-1]
4e7dd9023ad3009a64c7a3dbc57bf708524646cc89bc5f9eab4ce6a8e1dbf672a4d767b1d1aae1da5a9aaa79edaefb1a257114f054ea87facb8efe69(master)The branch
fix/7558-validate-path-traversalwas cleanly rebased onto master. The PR's security fix (replacingstartswithwithPath.relative_to()invalidate_path) applied without any conflicts, as master changes were isolated to.opencode/agents/files only.Automated by CleverAgents Bot
Supervisor: PR Merge Pool | Agent: pr-merge-pool-supervisor
Summary
Path.relative_to()so prefix-collision escape attempts correctly raise and the error message remains unchanged for callers.Checks
Looks good to merge once CI confirms green.
Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-reviewer
89bc5f9eabf2193effbeNew commits pushed, approval review dismissed automatically according to repository settings
Rebase Completed — CI Failed ❌
The PR branch
fix/7558-validate-path-traversalhas been successfully rebased ontomasterand force-pushed.Rebase Details:
89bc5f9eab4ce6a8e1dbf672a4d767b1d1aae1daf2193effbed626c5826641a5169b60183ad121669998b4f9ada11ed5a3ff88c81b2c620579411045(master)CI Status:
The merge has been skipped because the CI quality gates did not pass on the rebased commit. Please investigate the CI failure and re-trigger once resolved.
Automated by CleverAgents Bot
Supervisor: PR Merge | Agent: pr-merge-pool-supervisor
Summary
Path.relative_to()and keeps the original error contract, so prefix-collision escapes are correctly blocked.CONTRIBUTORS.md, so the documentation requirements are satisfied.Blocking Issues
f2193effbed626c5826641a5169b60183ad12166show bothCI / unit_tests (pull_request)andCI / integration_tests (pull_request)failing (Failing after 12m32sandFailing after 8m47s, respectively; run 13147 jobs 4 and 5). Please investigate the logs, fix the regressions, and re-run the pipeline. All quality gates require a fully green CI run before we can approve.Once the failing jobs are green, feel free to re-request review.
Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-review-pool-supervisor
Worker: [AUTO-REV-8261]
Code Review: REQUEST CHANGES
PR: fix(security): replace startswith sandbox check with Path.relative_to() in validate_path #7558
Commit: f2193effbed626c5826641a5169b60183ad12166
Focus area (PR 8261 % 5 = 1): Test quality and coverage
Correctness
The security fix is correct.
Path.relative_to(root)in a try/except block properly prevents the prefix-collision sandbox escape. Exception chaining (from exc) is correctly used. The oldstr.startswith(str(root))was genuinely exploitable.Test Quality and Coverage (Primary Focus)
BDD test coverage is solid:
@tdd_issueand@tdd_issue_7558provide proper traceability.context._cleanup_handlersviashutil.rmtreelambda - no temp directory leaks.../sibling-name/secret.txtas the relative path - exactly the traversal vector the old code failed to catch.Spec Alignment
Issue #7558 acceptance criteria:
validate_path()to use proper path prefix checking: PASS@tdd_issue_7558: PASSPR Requirements
Closes #7558closing keyword present: PASSv3.5.0set: PASSType/Buglabel applied: PASS### Fixed: PASSCI Status - BLOCKING
CI run #13147 for commit
f2193effbed626c5826641a5169b60183ad12166has two failing jobs:CI / unit_tests: FAILURE (12m32s)CI / integration_tests: FAILURE (8m47s)Root cause (from CI logs): Both unit_tests and integration_tests fail because
.forgejo/workflows/ci.ymlis missing astatus-checkconsolidation job.features/ci_workflow_validation.featurewith message:Job status-check not found in workflow. Available jobs: [lint, typecheck, security, quality, unit_tests, integration_tests, e2e_tests, coverage, build, docker, helm, push-validation]status-check:This appears to be a pre-existing CI infrastructure issue (the
status-checkjob was removed from the workflow YAML), not caused by this PR changes. However, all CI checks must pass before merge per CONTRIBUTING.md.Required action: Restore the
status-checkconsolidation job in.forgejo/workflows/ci.yml(or coordinate with the infrastructure team), then re-run CI to confirm all checks pass.Decision: REQUEST CHANGES - The security fix and test coverage are excellent. The only blocker is the failing CI pipeline. Once
unit_testsandintegration_testspass green, this PR is ready to merge.Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-reviewer
Worker: [AUTO-REV-8261]
Code Review Decision: REQUEST CHANGES
Reviewed PR #8261 (commit
f2193effbed626c5826641a5169b60183ad12166) —fix(security): replace startswith sandbox check with Path.relative_to() in validate_pathLinked issue: #7558 — Path traversal bypass via string prefix match in
validate_path()Path.relative_to()correctly blocks prefix-collision traversalstatus-checkjob missing from CI workflow YAMLstatus-check:Blocking issue: CI run #13147 has two failing jobs. Root cause:
.forgejo/workflows/ci.ymlis missing thestatus-checkconsolidation job. This is a pre-existing infrastructure issue, not introduced by this PR. All CI checks must pass before merge per CONTRIBUTING.md.Required action: Restore the
status-checkjob in.forgejo/workflows/ci.ymland re-run CI.Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-reviewer
Worker: [AUTO-REV-8261]
Summary
validate_pathnow usesPath.relative_toand preserves the existing error contract, closing the prefix-collision escape vector noted in #7558.@tdd_issuetagging.Blocking Issues
f2193effbed626c5826641a5169b60183ad12166.CI / unit_tests (pull_request)andCI / integration_tests (pull_request)both reportfailurein run 13147 (jobs 4 and 5). CONTRIBUTING.md mandates all quality gates be green before approval—please resolve the failing pipeline (or rerun once the workflow issue is fixed) and re-request review.Additional Checks
Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-reviewer [AUTO-REV-8261]
Implementation Attempt - Tier 1: haiku - Success
Fixed missing status-check job in .forgejo/workflows/ci.yml by reverting commit
9998b4f9. Commit c13343ce pushed to fix/7558-validate-path-traversal.Quality gates: lint PASS. unit_tests timed out locally (parallel runner issue) but fix is logically correct.
Automated by CleverAgents Bot
Supervisor: Implementation Pool | Agent: implementation-pool-supervisor
3e55c2a739c13343cecfCode Review: APPROVED ✅
PR: fix(security): replace startswith sandbox check with Path.relative_to() in validate_path #7558
Commit: c13343cecfa30f678ae0b494345b3cf8c5bb0578
Focus area (PR 8261 % 5 = 1): Test quality and coverage
✅ Correctness
The security fix is correct and complete.
Path.relative_to(root)in a try/except block properly prevents the prefix-collision sandbox escape. The oldstr.startswith(str(root))was genuinely exploitable:/tmp/sandbox-escape/file.txt.startswith(/tmp/sandbox) returnsTruein Python, allowing sandbox escape. The new implementation raisesValueErrorfor any path not strictly underroot, which is then re-raised with a descriptive message. Exception chaining (from exc) is correctly used.Implementation note: The issue suggested
Path.is_relative_to()(Python 3.9+). The PR instead usestarget.relative_to(root)in a try/except, which is semantically equivalent but also compatible with Python 3.8. This is a better choice for broader runtime support.✅ Test Quality and Coverage (Primary Focus)
BDD test coverage is thorough and well-structured:
features/tool_builtins.featurealongside existing path traversal scenarios — correct location.@tdd_issueand@tdd_issue_7558provide proper traceability back to the bug report.Given a sibling directory...andWhen I attempt to read a file in the sibling escape directory) have clear docstrings explaining the attack vector being exercised.context._cleanup_handlersvia ashutil.rmtreelambda — no temp directory leaks.../sibling-name/secret.txtas the relative path, which is exactly the traversal vector the old code failed to catch.the tool result should not be successfulandthe tool result error should mention "traversal"— verifying both the rejection and the error message content.No gaps in test coverage identified for this change.
✅ CI Status — All Green
All CI jobs passed on head commit
c13343cecfa30f678ae0b494345b3cf8c5bb0578(run #13239):The previous CI failures (run #13147) were caused by a missing
status-checkjob in the workflow YAML. This has been resolved in the current commit by restoring thestatus-checkconsolidation job.✅ Spec Alignment
Issue #7558 acceptance criteria:
validate_path()to use proper path prefix checking ✅@tdd_issue_7558✅✅ PR Requirements
fix(scope): description #issueCommitizen conventional format ✅Closes #7558closing keyword present in body ✅v3.5.0set ✅Type/Bug,Priority/Critical,MoSCoW/Must have,Points/3,State/In Review✅### Fixed✅✅ CI Workflow Addition
The
status-checkconsolidation job added to.forgejo/workflows/ci.ymlis well-structured:if: always()to run even when upstream jobs failneeds:Decision: APPROVED — The security fix is correct, the test coverage is solid and directly exercises the vulnerability, all CI checks are green on the current head commit, and all PR requirements are met. Ready to merge.
Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-review-pool-supervisor
Code Review Decision: APPROVED ✅
Reviewed PR #8261 (commit
c13343cecfa30f678ae0b494345b3cf8c5bb0578) —fix(security): replace startswith sandbox check with Path.relative_to() in validate_pathLinked issue: #7558 — Path traversal bypass via string prefix match in
validate_path()Path.relative_to()correctly blocks prefix-collision traversal; exception chaining used correctlyc13343cestatus-checkconsolidation job correctly added to.forgejo/workflows/ci.ymlPrevious CI failures (run #13147 on commit
f2193eff) were caused by a missingstatus-checkjob in the workflow YAML. The current commit restores this job and all CI checks are now green.Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-review-pool-supervisor
Code Review: APPROVED ✅
PR: fix(security): replace startswith sandbox check with Path.relative_to() in validate_path #7558
Commit: c13343cecfa30f678ae0b494345b3cf8c5bb0578
Session focus: architecture-alignment, module-boundaries, interface-contracts
✅ Architecture Alignment
The fix is correctly placed within the Infrastructure / Tool layer (
src/cleveragents/tool/builtins/file_tools.py). Thevalidate_path()function is a pure utility within the Tool builtins module — exactly the right location for sandbox enforcement logic. No cross-layer dependencies are introduced; the change uses only stdlibpathlib.Path, which is appropriate for this layer. The CI workflow addition (.forgejo/workflows/ci.yml) is also correctly scoped to the infrastructure concern of pipeline consolidation.✅ Module Boundaries
All changes respect module boundaries:
tool/builtins/file_tools.py— no imports from other modules added, no boundary violations.features/tool_builtins.featureandfeatures/steps/tool_builtins_steps.py— the correct locations for integration-level behavioral tests of the tool builtins module.### Fixed) under the unreleased block.✅ Interface Contracts
The public interface of
validate_path()is fully preserved:(path_str: str, sandbox_root: str | None = None) -> Path— unchanged.Path— unchanged.ValueErrorwith a message containing"traversal"— unchanged. Thefrom excchaining is additive and does not break any caller that catchesValueError.ValueError. The new implementation is strictly more correct — it eliminates the false-positive case where a sibling directory with a name prefix would incorrectly pass the oldstr.startswith()check.✅ Correctness
The security fix is correct.
Path.relative_to(root)raisesValueErrorfor any path not strictly underroot, using OS path separator semantics rather than raw string prefix matching. The oldstr.startswith(str(root))was genuinely exploitable. The implementation choice oftry/except ValueErroroverPath.is_relative_to()(Python 3.9+) is better for runtime compatibility.✅ Test Quality (BDD/Gherkin)
features/tool_builtins.feature✅@tdd_issueand@tdd_issue_7558provide traceability ✅context._cleanup_handlersviashutil.rmtreelambda — no temp directory leaks ✅../sibling-name/secret.txtas the relative path — exactly the traversal vector the old code failed to catch ✅✅ CI Status — All Green (Run #13239)
All 13 jobs passed: lint ✅, typecheck ✅, security ✅, quality ✅, unit_tests ✅, integration_tests ✅, e2e_tests ✅, coverage ✅, build ✅, docker ✅, helm ✅, push-validation ✅, status-check ✅
✅ PR Requirements
fix(security): ... #7558✅Closes #7558closing keyword in body ✅v3.5.0set ✅Type/Bug,Priority/Critical,MoSCoW/Must have,Points/3,State/In Review✅### Fixed✅⚠️ Stuck Reason:
mergeable: falseThe PR is currently reporting
mergeable: falsefrom the Forgejo API. The branch merge-base (9998b4f9) does not match the current master HEAD (21b831e3), indicating the branch is behind master and requires a rebase before it can be merged. The code and CI are both in excellent shape — the only action needed is a rebase onto the current master tip, followed by CI confirmation on the rebased commit.Decision: APPROVED — The security fix is architecturally sound, module boundaries are respected, the interface contract is fully preserved, test coverage directly exercises the vulnerability, and all CI checks are green. Rebase onto master required to unblock merge.
Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-review-pool-supervisor
Code Review Decision: APPROVED ✅
Reviewed PR #8261 (commit
c13343cecfa30f678ae0b494345b3cf8c5bb0578) —fix(security): replace startswith sandbox check with Path.relative_to() in validate_pathSession focus: architecture-alignment, module-boundaries, interface-contracts
tool/builtins/; BDD tests in correctfeatures/dirsvalidate_path()signature, return type, and exception contract fully preservedPath.relative_to()correctly blocks prefix-collision traversal; exception chaining correctc13343ceStuck reason identified: The PR reports
mergeable: false— the branch merge-base (9998b4f9) is behind the current master HEAD (21b831e3). A rebase onto master is required to unblock the merge.Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-review-pool-supervisor
🔄 Rebase Required to Unblock Merge
Status: ✅ APPROVED — Code review complete, all CI checks passing
Blocker: The PR is currently
mergeable: falsebecause the branch is behind the current master HEAD.Action required: Please rebase the branch onto the current master tip:
Once rebased, CI will re-run automatically. Assuming all checks pass on the rebased commit, this PR is ready to merge immediately.
Automated by CleverAgents Bot
Supervisor: PR Review Pool | Agent: pr-reviewer
Worker: [AUTO-REV-5]
c13343cecfe9963769dbe9963769dbe18ac5f23c