Files
wehub-resource-sync 7a0da7932b
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
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

467 lines
15 KiB
Python

"""
Comprehensive tests for security/file_write_verifier.py
Tests cover:
- _sanitize_sensitive_data function
- write_file_verified function
- write_json_verified function
- FileWriteSecurityError exception
"""
import json
from pathlib import Path
from unittest.mock import patch
import pytest
class TestSanitizeSensitiveData:
"""Tests for the _sanitize_sensitive_data function."""
def test_sanitizes_password_key(self):
"""Test that password keys are redacted."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {"username": "admin", "password": "secret123"}
result = _sanitize_sensitive_data(data)
assert result["username"] == "admin"
assert result["password"] == "[REDACTED]"
def test_sanitizes_api_key(self):
"""Test that api_key is redacted."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {"api_key": "sk-123456", "name": "test"}
result = _sanitize_sensitive_data(data)
assert result["api_key"] == "[REDACTED]"
assert result["name"] == "test"
def test_sanitizes_various_sensitive_keys(self):
"""Test that all sensitive key variations are redacted."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {
"apikey": "value1",
"api-key": "value2",
"secret": "value3",
"secret_key": "value4",
"token": "value5",
"access_token": "value6",
"refresh_token": "value7",
"private_key": "value8",
"credentials": "value9",
"auth": "value10",
"authorization": "value11",
}
result = _sanitize_sensitive_data(data)
for key in data:
assert result[key] == "[REDACTED]"
def test_case_insensitive_matching(self):
"""Test that matching is case insensitive."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {"PASSWORD": "secret", "Api_Key": "key123", "TOKEN": "tok"}
result = _sanitize_sensitive_data(data)
assert result["PASSWORD"] == "[REDACTED]"
assert result["Api_Key"] == "[REDACTED]"
assert result["TOKEN"] == "[REDACTED]"
def test_sanitizes_nested_dicts(self):
"""Test that nested dictionaries are sanitized."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {
"config": {
"api_key": "secret",
"settings": {"password": "hidden", "name": "test"},
}
}
result = _sanitize_sensitive_data(data)
assert result["config"]["api_key"] == "[REDACTED]"
assert result["config"]["settings"]["password"] == "[REDACTED]"
assert result["config"]["settings"]["name"] == "test"
def test_sanitizes_lists_of_dicts(self):
"""Test that lists containing dicts are sanitized."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {
"users": [
{"name": "user1", "password": "pass1"},
{"name": "user2", "password": "pass2"},
]
}
result = _sanitize_sensitive_data(data)
assert result["users"][0]["password"] == "[REDACTED]"
assert result["users"][1]["password"] == "[REDACTED]"
assert result["users"][0]["name"] == "user1"
def test_preserves_non_sensitive_data(self):
"""Test that non-sensitive data is preserved."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {"name": "test", "value": 123, "flag": True, "items": [1, 2, 3]}
result = _sanitize_sensitive_data(data)
assert result == data
def test_handles_primitive_values(self):
"""Test that primitive values pass through unchanged."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
assert _sanitize_sensitive_data("string") == "string"
assert _sanitize_sensitive_data(123) == 123
assert _sanitize_sensitive_data(True) is True
assert _sanitize_sensitive_data(None) is None
def test_handles_list_of_primitives(self):
"""Test that lists of primitives pass through."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = [1, 2, "test", True]
result = _sanitize_sensitive_data(data)
assert result == data
def test_handles_non_string_keys(self):
"""Test handling of non-string dictionary keys."""
from local_deep_research.security.file_write_verifier import (
_sanitize_sensitive_data,
)
data = {1: "value1", "password": "secret"}
result = _sanitize_sensitive_data(data)
assert result[1] == "value1"
assert result["password"] == "[REDACTED]"
class TestFileWriteSecurityError:
"""Tests for the FileWriteSecurityError exception."""
def test_exception_can_be_raised(self):
"""Test that the exception can be raised and caught."""
from local_deep_research.security.file_write_verifier import (
FileWriteSecurityError,
)
with pytest.raises(FileWriteSecurityError) as exc_info:
raise FileWriteSecurityError("Test error message")
assert "Test error message" in str(exc_info.value)
def test_exception_inherits_from_exception(self):
"""Test that FileWriteSecurityError inherits from Exception."""
from local_deep_research.security.file_write_verifier import (
FileWriteSecurityError,
)
assert issubclass(FileWriteSecurityError, Exception)
class TestWriteFileVerified:
"""Tests for the write_file_verified function."""
@pytest.fixture
def mock_get_setting(self):
"""Fixture to mock the get_setting_from_snapshot function."""
with patch(
"local_deep_research.config.search_config.get_setting_from_snapshot"
) as mock:
yield mock
def test_writes_file_when_setting_matches(self, tmp_path, mock_get_setting):
"""Test that file is written when setting matches required value."""
from local_deep_research.security.file_write_verifier import (
write_file_verified,
)
filepath = tmp_path / "test.txt"
content = "test content"
mock_get_setting.return_value = True
write_file_verified(
filepath, content, "test.setting", required_value=True
)
assert filepath.exists()
assert filepath.read_text() == content
def test_raises_error_when_setting_mismatch(
self, tmp_path, mock_get_setting
):
"""Test that error is raised when setting doesn't match."""
from local_deep_research.security.file_write_verifier import (
write_file_verified,
FileWriteSecurityError,
)
filepath = tmp_path / "test.txt"
mock_get_setting.return_value = False
with pytest.raises(FileWriteSecurityError) as exc_info:
write_file_verified(
filepath,
"content",
"test.setting",
required_value=True,
context="test operation",
)
assert "test operation" in str(exc_info.value)
assert "test.setting=True" in str(exc_info.value)
def test_raises_error_when_setting_not_found(
self, tmp_path, mock_get_setting
):
"""Test that error is raised when setting doesn't exist."""
from local_deep_research.security.file_write_verifier import (
write_file_verified,
FileWriteSecurityError,
)
filepath = tmp_path / "test.txt"
mock_get_setting.side_effect = KeyError("Setting not found")
with pytest.raises(FileWriteSecurityError):
write_file_verified(filepath, "content", "nonexistent.setting")
def test_writes_binary_file(self, tmp_path, mock_get_setting):
"""Test writing binary content."""
from local_deep_research.security.file_write_verifier import (
write_file_verified,
)
filepath = tmp_path / "test.bin"
content = b"\x00\x01\x02\x03"
mock_get_setting.return_value = True
write_file_verified(filepath, content, "test.setting", mode="wb")
assert filepath.exists()
assert filepath.read_bytes() == content
def test_uses_custom_encoding(self, tmp_path, mock_get_setting):
"""Test writing with custom encoding."""
from local_deep_research.security.file_write_verifier import (
write_file_verified,
)
filepath = tmp_path / "test.txt"
content = "日本語テスト"
mock_get_setting.return_value = True
write_file_verified(filepath, content, "test.setting", encoding="utf-8")
assert filepath.exists()
assert filepath.read_text(encoding="utf-8") == content
def test_passes_settings_snapshot(self, tmp_path, mock_get_setting):
"""Test that settings_snapshot is passed to get_setting_from_snapshot."""
from local_deep_research.security.file_write_verifier import (
write_file_verified,
)
filepath = tmp_path / "test.txt"
snapshot = {"test.setting": True}
mock_get_setting.return_value = True
write_file_verified(
filepath, "content", "test.setting", settings_snapshot=snapshot
)
mock_get_setting.assert_called_once_with(
"test.setting", settings_snapshot=snapshot
)
def test_accepts_path_object(self, tmp_path, mock_get_setting):
"""Test that Path objects are accepted."""
from local_deep_research.security.file_write_verifier import (
write_file_verified,
)
filepath = Path(tmp_path) / "test.txt"
mock_get_setting.return_value = True
write_file_verified(filepath, "content", "test.setting")
assert filepath.exists()
class TestWriteJsonVerified:
"""Tests for the write_json_verified function."""
@pytest.fixture
def mock_get_setting(self):
"""Fixture to mock the get_setting_from_snapshot function."""
with patch(
"local_deep_research.config.search_config.get_setting_from_snapshot"
) as mock:
yield mock
def test_writes_json_when_setting_matches(self, tmp_path, mock_get_setting):
"""Test that JSON is written when setting matches."""
from local_deep_research.security.file_write_verifier import (
write_json_verified,
)
filepath = tmp_path / "test.json"
data = {"key": "value", "number": 42}
mock_get_setting.return_value = True
write_json_verified(filepath, data, "test.setting")
assert filepath.exists()
written_data = json.loads(filepath.read_text())
assert written_data == data
def test_sanitizes_sensitive_data(self, tmp_path, mock_get_setting):
"""Test that sensitive data is sanitized before writing."""
from local_deep_research.security.file_write_verifier import (
write_json_verified,
)
filepath = tmp_path / "test.json"
data = {"name": "test", "password": "secret123", "api_key": "key456"}
mock_get_setting.return_value = True
write_json_verified(filepath, data, "test.setting")
written_data = json.loads(filepath.read_text())
assert written_data["name"] == "test"
assert written_data["password"] == "[REDACTED]"
assert written_data["api_key"] == "[REDACTED]"
def test_uses_default_indent(self, tmp_path, mock_get_setting):
"""Test that default indent of 2 is used."""
from local_deep_research.security.file_write_verifier import (
write_json_verified,
)
filepath = tmp_path / "test.json"
data = {"key": "value"}
mock_get_setting.return_value = True
write_json_verified(filepath, data, "test.setting")
content = filepath.read_text()
# With indent=2, there should be newlines and spaces
assert "\n" in content
assert " " in content
def test_custom_json_kwargs(self, tmp_path, mock_get_setting):
"""Test that custom JSON kwargs are passed through."""
from local_deep_research.security.file_write_verifier import (
write_json_verified,
)
filepath = tmp_path / "test.json"
data = {"b": 2, "a": 1}
mock_get_setting.return_value = True
write_json_verified(
filepath, data, "test.setting", sort_keys=True, indent=4
)
content = filepath.read_text()
# With sort_keys, 'a' should come before 'b'
assert content.index('"a"') < content.index('"b"')
# With indent=4, should have 4 spaces
assert " " in content
def test_writes_list_data(self, tmp_path, mock_get_setting):
"""Test writing list data."""
from local_deep_research.security.file_write_verifier import (
write_json_verified,
)
filepath = tmp_path / "test.json"
data = [{"name": "item1"}, {"name": "item2", "password": "secret"}]
mock_get_setting.return_value = True
write_json_verified(filepath, data, "test.setting")
written_data = json.loads(filepath.read_text())
assert len(written_data) == 2
assert written_data[1]["password"] == "[REDACTED]"
def test_raises_error_when_setting_mismatch(
self, tmp_path, mock_get_setting
):
"""Test that error is raised when setting doesn't match."""
from local_deep_research.security.file_write_verifier import (
write_json_verified,
FileWriteSecurityError,
)
filepath = tmp_path / "test.json"
mock_get_setting.return_value = False
with pytest.raises(FileWriteSecurityError):
write_json_verified(filepath, {"key": "value"}, "test.setting")
class TestSensitiveKeys:
"""Tests for SENSITIVE_KEYS constant."""
def test_sensitive_keys_is_frozenset(self):
"""Test that SENSITIVE_KEYS is a frozenset."""
from local_deep_research.security.file_write_verifier import (
SENSITIVE_KEYS,
)
assert isinstance(SENSITIVE_KEYS, frozenset)
def test_sensitive_keys_contains_expected_values(self):
"""Test that SENSITIVE_KEYS contains expected sensitive key names."""
from local_deep_research.security.file_write_verifier import (
SENSITIVE_KEYS,
)
expected_keys = {
"password",
"api_key",
"secret",
"token",
"credentials",
}
for key in expected_keys:
assert key in SENSITIVE_KEYS