Files
learningcircuit--local-deep…/tests/security/test_path_validator_gaps_coverage.py
wehub-resource-sync 7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

187 lines
8.7 KiB
Python

"""
Tests targeting specific uncovered lines in path_validator.py.
Covers:
- Line 140: Malformed Windows path on win32
- Line 153: safe_join returns None for unix absolute path
- Lines 157-162: Windows absolute path handling (drive letter parsing)
- Lines 185-186: Windows system directory restrictions
- Line 274: validate_model_path when validate_safe_path returns None
- Line 308: validate_data_path when validate_safe_path returns None
- Lines 375-385: Absolute config path validation (suffix + existence)
- Line 400: Relative config path when validate_safe_path returns None
"""
from pathlib import Path
from unittest.mock import patch
import pytest
from local_deep_research.security.path_validator import PathValidator
# ---------------------------------------------------------------------------
# Line 140: Malformed Windows path on win32
# The function does `import sys` locally (line 112), so we patch `sys.platform`
# on the actual sys module.
# ---------------------------------------------------------------------------
class TestMalformedWindowsPath:
"""Line 140: raise ValueError('Malformed Windows path input')"""
@patch("sys.platform", "win32")
def test_malformed_windows_path_slash_colon(self):
"""'/C:/Windows' on win32 triggers the malformed path guard."""
with pytest.raises(ValueError, match="Malformed Windows path input"):
PathValidator.validate_local_filesystem_path("/C:/Windows")
@patch("sys.platform", "win32")
def test_malformed_windows_path_backslash_colon(self):
"""'\\C:\\Windows' on win32 triggers the malformed path guard."""
with pytest.raises(ValueError, match="Malformed Windows path input"):
PathValidator.validate_local_filesystem_path("\\C:\\Windows")
# ---------------------------------------------------------------------------
# Line 153: safe_join returns None for unix absolute path
# ---------------------------------------------------------------------------
class TestSafeJoinReturnsNoneUnix:
"""Line 153: raise ValueError('Invalid path - failed security validation')"""
@patch(
"local_deep_research.security.path_validator.safe_join",
return_value=None,
)
def test_safe_join_none_for_absolute_path(self, mock_safe_join):
with pytest.raises(ValueError, match="failed security validation"):
PathValidator.validate_local_filesystem_path("/home/user/docs")
# ---------------------------------------------------------------------------
# Lines 157-162: Windows absolute path handling (drive letter parsing)
# On Linux, a path like 'C:\Users\test' is not absolute (no leading /),
# so it enters the drive-letter branch at line 155.
# ---------------------------------------------------------------------------
class TestWindowsDriveLetterParsing:
"""Lines 157-162: Windows drive-letter absolute path branch."""
@patch("local_deep_research.security.path_validator.safe_join")
def test_windows_drive_letter_path_success(self, mock_safe_join):
"""Drive-letter path where safe_join succeeds (lines 157-162)."""
mock_safe_join.return_value = "/tmp/fake_resolved"
result = PathValidator.validate_local_filesystem_path("C:\\Users\\test")
assert result is not None
@patch(
"local_deep_research.security.path_validator.safe_join",
return_value=None,
)
def test_windows_drive_letter_safe_join_none(self, mock_safe_join):
"""Drive-letter path where safe_join returns None (line 160-161)."""
with pytest.raises(ValueError, match="failed security validation"):
PathValidator.validate_local_filesystem_path("C:\\Users\\test")
# ---------------------------------------------------------------------------
# Lines 185-186: Windows system directory restrictions
# When sys.platform == "win32" and restricted_dirs is None, extra Windows
# system dirs are added. We mock safe_join and Path.resolve so that the
# validated path falls inside one of those restricted dirs.
# ---------------------------------------------------------------------------
class TestWindowsSystemDirRestrictions:
"""Lines 184-193: Windows restricted directories added on win32."""
@patch("sys.platform", "win32")
@patch("local_deep_research.security.path_validator.safe_join")
def test_windows_restricted_dirs_block_access(self, mock_safe_join):
"""On win32, C:\\Windows is restricted by default."""
# The path 'C:\Windows\System32\cmd.exe' has len>2, [1]==':',
# so it enters the drive-letter branch.
# It does NOT start with / or \, so the malformed guard (line 135-140)
# is skipped.
fake = Path("C:\\Windows\\System32\\cmd.exe")
mock_safe_join.return_value = str(fake)
with patch.object(Path, "resolve", return_value=fake):
with patch.object(Path, "is_relative_to", return_value=True):
with pytest.raises(
ValueError, match="Cannot access system directories"
):
PathValidator.validate_local_filesystem_path(
"C:\\Windows\\System32\\cmd.exe"
)
# ---------------------------------------------------------------------------
# Line 274: validate_model_path - validate_safe_path returns None
# ---------------------------------------------------------------------------
class TestValidateModelPathNone:
"""Line 274: raise ValueError('Invalid model path')"""
@patch.object(PathValidator, "validate_safe_path", return_value=None)
def test_model_path_returns_none(self, mock_vsp):
with pytest.raises(ValueError, match="Invalid model path"):
PathValidator.validate_model_path(
"bad_model.bin", model_root="/tmp"
)
# ---------------------------------------------------------------------------
# Line 308: validate_data_path - validate_safe_path returns None
# ---------------------------------------------------------------------------
class TestValidateDataPathNone:
"""Line 308: raise ValueError('Invalid data path')"""
@patch.object(PathValidator, "validate_safe_path", return_value=None)
def test_data_path_returns_none(self, mock_vsp):
with pytest.raises(ValueError, match="Invalid data path"):
PathValidator.validate_data_path("bad_file.dat", data_root="/tmp")
# ---------------------------------------------------------------------------
# Lines 375-385: Absolute config path validation
# safe_join("/", config_path) returns None when config_path starts with "/",
# so on Unix these lines are unreachable through normal flow.
# We mock safe_join to return a value so we can exercise lines 375-385.
# ---------------------------------------------------------------------------
class TestAbsoluteConfigPath:
"""Lines 375-385: absolute config path suffix and existence checks."""
@patch("local_deep_research.security.path_validator.safe_join")
def test_absolute_config_bad_extension(self, mock_safe_join):
"""Line 378-379: suffix not in CONFIG_EXTENSIONS."""
mock_safe_join.return_value = "/tmp/config.exe"
with pytest.raises(ValueError, match="Invalid config file type"):
PathValidator.validate_config_path("/tmp/config.exe")
@patch("local_deep_research.security.path_validator.safe_join")
def test_absolute_config_does_not_exist(self, mock_safe_join, tmp_path):
"""Line 382-383: valid extension but file does not exist."""
nonexistent = str(tmp_path / "missing.json")
mock_safe_join.return_value = nonexistent
with pytest.raises(ValueError, match="Config file not found"):
PathValidator.validate_config_path(nonexistent)
@patch("local_deep_research.security.path_validator.safe_join")
def test_absolute_config_valid(self, mock_safe_join, tmp_path):
"""Lines 375-385: happy path - valid extension, file exists."""
cfg = tmp_path / "settings.yaml"
cfg.write_text("key: value")
cfg_str = str(cfg)
mock_safe_join.return_value = cfg_str
result = PathValidator.validate_config_path(cfg_str)
assert result == Path(cfg_str)
# ---------------------------------------------------------------------------
# Line 400: Relative config path - validate_safe_path returns None
# ---------------------------------------------------------------------------
class TestRelativeConfigPathNone:
"""Line 400: raise ValueError('Invalid config path: ...')"""
@patch.object(PathValidator, "validate_safe_path", return_value=None)
def test_relative_config_path_none(self, mock_vsp):
with pytest.raises(ValueError, match="Invalid config path"):
PathValidator.validate_config_path(
"my_config.yaml", config_root="/tmp"
)