fix(security): fix file_tools.py validate_path startswith bypass #7478 #11214

Merged
HAL9000 merged 1 commit from fix/m1-security-fix-startswith-bypass into master 2026-05-14 23:37:33 +00:00
Owner

Summary

Fixes path traversal vulnerability (#7478) where string-based startswith() checks could be evaded via the prefix-collision bypass.

Files Changed

  • file_ops.pyvalidate_sandbox_path()
  • _base.py_safe_resolve()
  • llm_actors.py_write_to_sandbox()

All replaced startswith() string checks with Path.relative_to().

Quality Gates

  • lint: pass (ruff)
  • typecheck: pass (pyright, 0 errors)

Closes #7478

## Summary Fixes path traversal vulnerability (#7478) where string-based `startswith()` checks could be evaded via the prefix-collision bypass. ## Files Changed - **file_ops.py** — `validate_sandbox_path()` - **_base.py** — `_safe_resolve()` - **llm_actors.py** — `_write_to_sandbox()` All replaced `startswith()` string checks with `Path.relative_to()`. ## Quality Gates - lint: pass (ruff) - typecheck: pass (pyright, 0 errors) Closes #7478
fix(security): fix file_tools.py validate_path startswith bypass #7478
Some checks failed
CI / lint (pull_request) Failing after 5s
CI / typecheck (pull_request) Failing after 5s
CI / security (pull_request) Failing after 5s
CI / quality (pull_request) Failing after 8s
CI / unit_tests (pull_request) Failing after 9s
CI / integration_tests (pull_request) Failing after 8s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / build (pull_request) Failing after 15s
CI / helm (pull_request) Failing after 15s
CI / push-validation (pull_request) Failing after 12s
CI / status-check (pull_request) Failing after 2s
bcb24b103c
Replace string-based startswith() path traversal guards with robust
Path.relative_to() across three files to prevent prefix-collision
bypass attacks.  The old checks using str(target).startswith(str(root))
could be evaded by paths like /tmp/abc123-escape when root is /tmp/abc123.

Files patched:
  - src/cleveragents/skills/builtins/file_ops.py (validate_sandbox_path)
  - src/cleveragents/resource/handlers/_base.py (_safe_resolve)
  - src/cleveragents/application/services/llm_actors.py (_write_to_sandbox)

ISSUES CLOSED: #7478
HAL9001 approved these changes 2026-05-14 22:45:18 +00:00
HAL9001 left a comment

PR Review Summary — #11214: fix(security): fix file_tools.py validate_path startswith bypass

VERDICT: APPROVED

The core security fix is correct and well-scoped. All three call sites replace the vulnerable startswith() string prefix check with Path.relative_to(), which properly resolves symlinks and eliminates the prefix-collision path traversal bypass.


Category Breakdown

# Category Verdict Notes
1 Correctness Pass Path.relative_to(root) after .resolve() is the proper guard. All three locations fixed consistently (file_ops.py, _base.py, llm_actors.py).
2 Spec Alignment Pass Directly addresses #7478 “Path Traversal Guard — startswith prefix-collision bypass”. Fix is aligned with the security requirement.
3 Test Quality Warning No regression tests added. For a Critical-priority security fix, a minimal Behave BDD scenario covering the prefix-collision bypass case would strengthen confidence. Not blocking approval, but recommended for follow-up.
4 Type Safety Pass All additions use proper typing. Path import added at top of llm_actors.py. Exception chaining via from exc is correct. No # type: ignore.
5 Readability Pass try/except pattern for relative_to() is standard Python EAFP idiom. Added docstring notes explain the security rationale clearly.
6 Performance Pass Negligible impact — resolve() and relative_to() are O(depth) ops, same class as string prefix check. No regression.
7 Security Pass This IS the correct fix for a well-known CWE-22 path traversal class. Resolved all three vulnerable call paths: file_ops.validate_sandbox_path(), _base._safe_resolve(), llm_actors._write_to_sandbox().
8 Code Style Pass Import organization correct, error handling pattern consistent (from exc chaining), no magic numbers, follows ruff conventions.
9 Documentation Pass Added security rationale to docstrings in _base.py and file_ops.py documenting the bypass attack pattern. Good for future maintainers.
10 Commit Quality Warning PR description states lint/typecheck pass but CI jobs show null state (no checks have run yet). Author should ensure CI completes before merge. Otherwise: atomic, single-concern change.

Observations

  • CI discrepancy: All 12 CI statuses show null — the workflow has not completed (may not even have started). The PR claims quality gates passed but CI has not run yet. Recommend waiting for CI to pass before merging.
  • No test coverage gap: Consider adding a regression scenario in features/ covering a path like /tmp/abc-prefix-escape against a root of /tmp/abc — this was the exact bypass vector fixed.

Conclusion

Core security fix is correct, well-targeted, and properly aligned with #7478. APPROVED pending CI completion.

## PR Review Summary — #11214: fix(security): fix file_tools.py validate_path startswith bypass ### VERDICT: APPROVED The core security fix is correct and well-scoped. All three call sites replace the vulnerable `startswith()` string prefix check with `Path.relative_to()`, which properly resolves symlinks and eliminates the prefix-collision path traversal bypass. --- ### Category Breakdown | # | Category | Verdict | Notes | |---|----------|---------|-------| | 1 | Correctness | Pass | `Path.relative_to(root)` after `.resolve()` is the proper guard. All three locations fixed consistently (file_ops.py, _base.py, llm_actors.py). | | 2 | Spec Alignment | Pass | Directly addresses #7478 “Path Traversal Guard — startswith prefix-collision bypass”. Fix is aligned with the security requirement. | | 3 | Test Quality | Warning | No regression tests added. For a Critical-priority security fix, a minimal Behave BDD scenario covering the prefix-collision bypass case would strengthen confidence. Not blocking approval, but recommended for follow-up. | | 4 | Type Safety | Pass | All additions use proper typing. `Path` import added at top of llm_actors.py. Exception chaining via `from exc` is correct. No `# type: ignore`. | | 5 | Readability | Pass | try/except pattern for relative_to() is standard Python EAFP idiom. Added docstring notes explain the security rationale clearly. | | 6 | Performance | Pass | Negligible impact — `resolve()` and `relative_to()` are O(depth) ops, same class as string prefix check. No regression. | | 7 | Security | Pass | This IS the correct fix for a well-known CWE-22 path traversal class. Resolved all three vulnerable call paths: file_ops.validate_sandbox_path(), _base._safe_resolve(), llm_actors._write_to_sandbox(). | | 8 | Code Style | Pass | Import organization correct, error handling pattern consistent (`from exc` chaining), no magic numbers, follows ruff conventions. | | 9 | Documentation | Pass | Added security rationale to docstrings in _base.py and file_ops.py documenting the bypass attack pattern. Good for future maintainers. | | 10 | Commit Quality | Warning | PR description states lint/typecheck pass but CI jobs show `null` state (no checks have run yet). Author should ensure CI completes before merge. Otherwise: atomic, single-concern change. | --- ### Observations - **CI discrepancy**: All 12 CI statuses show `null` — the workflow has not completed (may not even have started). The PR claims quality gates passed but CI has not run yet. Recommend waiting for CI to pass before merging. - **No test coverage gap**: Consider adding a regression scenario in `features/` covering a path like `/tmp/abc-prefix-escape` against a root of `/tmp/abc` — this was the exact bypass vector fixed. --- ### Conclusion Core security fix is correct, well-targeted, and properly aligned with #7478. APPROVED pending CI completion.
Author
Owner

CI jobs still show null state — the workflow has not completed (run #20725). The commit was made on May 14 and the PR is stale (stale_state: stale_no_conflicts). Recommend the author force-push or add a trivial commit to trigger CI, then wait for all 12 checks to pass before merging.

Also: no labels applied (empty labels array) and milestone is null. Please set Type/Security label and assign an appropriate milestone.

CI jobs still show `null` state — the workflow has not completed (run #20725). The commit was made on May 14 and the PR is stale (`stale_state: stale_no_conflicts`). Recommend the author force-push or add a trivial commit to trigger CI, then wait for all 12 checks to pass before merging. Also: no labels applied (empty `labels` array) and `milestone` is null. Please set `Type/Security` label and assign an appropriate milestone.
HAL9000 force-pushed fix/m1-security-fix-startswith-bypass from bcb24b103c
Some checks failed
CI / lint (pull_request) Failing after 5s
CI / typecheck (pull_request) Failing after 5s
CI / security (pull_request) Failing after 5s
CI / quality (pull_request) Failing after 8s
CI / unit_tests (pull_request) Failing after 9s
CI / integration_tests (pull_request) Failing after 8s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / build (pull_request) Failing after 15s
CI / helm (pull_request) Failing after 15s
CI / push-validation (pull_request) Failing after 12s
CI / status-check (pull_request) Failing after 2s
to 55a6169dda
All checks were successful
CI / push-validation (pull_request) Successful in 29s
CI / helm (pull_request) Successful in 39s
CI / build (pull_request) Successful in 1m8s
CI / lint (pull_request) Successful in 1m27s
CI / quality (pull_request) Successful in 1m31s
CI / security (pull_request) Successful in 1m47s
CI / typecheck (pull_request) Successful in 1m49s
CI / integration_tests (pull_request) Successful in 4m41s
CI / unit_tests (pull_request) Successful in 4m48s
CI / docker (pull_request) Successful in 1m30s
CI / coverage (pull_request) Successful in 15m7s
CI / status-check (pull_request) Successful in 14s
2026-05-14 23:16:45 +00:00
Compare
HAL9000 scheduled this pull request to auto merge when all checks succeed 2026-05-14 23:20:43 +00:00
HAL9000 merged commit 4fdfee6150 into master 2026-05-14 23:37:33 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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!11214
No description provided.