d646288142
CI / push-validation (pull_request) Successful in 34s
CI / helm (pull_request) Successful in 37s
CI / build (pull_request) Successful in 48s
CI / lint (pull_request) Successful in 1m8s
CI / typecheck (pull_request) Successful in 1m14s
CI / quality (pull_request) Successful in 1m7s
CI / security (pull_request) Successful in 1m15s
CI / integration_tests (pull_request) Successful in 3m7s
CI / unit_tests (pull_request) Successful in 4m54s
CI / docker (pull_request) Successful in 1m26s
CI / coverage (pull_request) Successful in 11m59s
CI / status-check (pull_request) Successful in 3s
147 lines
5.9 KiB
Python
147 lines
5.9 KiB
Python
"""Tests for ``validate_path`` security fixes.
|
|
|
|
Covers:
|
|
- Absolute path rejection (pathlib ``/`` join bypass)
|
|
- Path traversal via ``..`` components
|
|
- Empty and whitespace-only paths
|
|
- Valid relative paths continue to function normally
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
class ValidatePathTests:
|
|
"""Security-focused tests for the ``validate_path`` function."""
|
|
|
|
@staticmethod
|
|
def _make_sandbox() -> str:
|
|
"""Create a temporary sandbox directory and return its path."""
|
|
return tempfile.mkdtemp()
|
|
|
|
# ------------------------------------------------------------------
|
|
# Absolute path rejection (CVE-style bypass prevention)
|
|
# ------------------------------------------------------------------
|
|
|
|
def test_absolute_leading_slash_rejected(self):
|
|
"""An absolute path like ``/etc/passwd`` must be rejected."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Absolute paths"):
|
|
validate_path("/etc/passwd", sandbox_root=sandbox)
|
|
|
|
def test_absolute_slash_only_rejected(self):
|
|
"""Root path ``/`` is absolute and must be rejected."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Absolute paths"):
|
|
validate_path("/", sandbox_root=sandbox)
|
|
|
|
def test_absolute_nested_rejected(self):
|
|
"""A deeply nested absolute path must be rejected."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Absolute paths"):
|
|
validate_path("/var/lib/../../etc/shadow", sandbox_root=sandbox)
|
|
|
|
def test_absolute_double_slash_rejected(self):
|
|
"""Paths starting with ``//`` are treated as absolute."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Absolute paths"):
|
|
validate_path("//etc/passwd", sandbox_root=sandbox)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Path traversal via ``..`` components
|
|
# ------------------------------------------------------------------
|
|
|
|
def test_double_dot_traversal_rejected(self):
|
|
"""A single ``..`` at the top level escapes the sandbox."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Path traversal"):
|
|
validate_path("../outside", sandbox_root=sandbox)
|
|
|
|
def test_double_dot_traversal_multiple_rejected(self):
|
|
"""Multiple ``..`` components that escape the sandbox are rejected."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Path traversal"):
|
|
validate_path("../../../etc/passwd", sandbox_root=sandbox)
|
|
|
|
def test_traversal_inside_subdirs_rejected(self):
|
|
"""Traversal after entering a subdirectory is detected."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
# First go into a subdir, then escape up twice (exceeds root)
|
|
with pytest.raises(ValueError, match="Path traversal"):
|
|
validate_path("a/b/../../..", sandbox_root=sandbox)
|
|
|
|
def test_double_dot_last_component_rejected(self):
|
|
"""A path ending in ``..`` that escapes root is rejected."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Path traversal"):
|
|
validate_path("..", sandbox_root=sandbox)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Empty and whitespace handling
|
|
# ------------------------------------------------------------------
|
|
|
|
def test_empty_string_rejected(self):
|
|
"""An empty path string must be rejected."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
with pytest.raises(ValueError, match="Empty file path"):
|
|
validate_path("", sandbox_root=sandbox)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Valid relative paths still work
|
|
# ------------------------------------------------------------------
|
|
|
|
def test_simple_relative_file_accepted(self):
|
|
"""A normal relative path inside the sandbox is accepted."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
result = validate_path("myfile.txt", sandbox_root=sandbox)
|
|
assert str(result).startswith(sandbox)
|
|
|
|
def test_nested_relative_accepted(self):
|
|
"""A nested relative path inside the sandbox is accepted."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
result = validate_path("subdir/nested/file.txt", sandbox_root=sandbox)
|
|
assert str(result).startswith(sandbox)
|
|
|
|
def test_dot_self_reference_accepted(self):
|
|
"""``./`` prefix paths are accepted as valid relative paths."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
result = validate_path("./myfile.txt", sandbox_root=sandbox)
|
|
assert str(result).startswith(sandbox)
|
|
|
|
def test_traversal_within_sandbox_accepted(self):
|
|
"""``..`` components that stay inside the sandbox are valid."""
|
|
from cleveragents.tool.builtins.file_tools import validate_path
|
|
|
|
sandbox = self._make_sandbox()
|
|
# Go into a subdir and back out stays within root
|
|
result = validate_path("subdir/..", sandbox_root=sandbox)
|
|
assert str(result).startswith(sandbox)
|