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
239 lines
8.1 KiB
Python
239 lines
8.1 KiB
Python
"""Tests for security/file_integrity/base_verifier.py."""
|
|
|
|
import pytest
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
|
|
class TestFileTypeEnum:
|
|
"""Tests for FileType enum."""
|
|
|
|
def test_faiss_index_value(self):
|
|
"""Test FAISS_INDEX enum value."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
FileType,
|
|
)
|
|
|
|
assert FileType.FAISS_INDEX.value == "faiss_index"
|
|
|
|
def test_pdf_value(self):
|
|
"""Test PDF enum value."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
FileType,
|
|
)
|
|
|
|
assert FileType.PDF.value == "pdf"
|
|
|
|
def test_export_value(self):
|
|
"""Test EXPORT enum value."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
FileType,
|
|
)
|
|
|
|
assert FileType.EXPORT.value == "export"
|
|
|
|
def test_file_type_is_str_enum(self):
|
|
"""Test that FileType is a string enum."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
FileType,
|
|
)
|
|
|
|
assert isinstance(FileType.PDF, str)
|
|
assert FileType.PDF == "pdf"
|
|
|
|
|
|
class TestBaseFileVerifierAbstract:
|
|
"""Tests for BaseFileVerifier abstract class."""
|
|
|
|
def test_cannot_instantiate_directly(self):
|
|
"""Test that BaseFileVerifier cannot be instantiated directly."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
BaseFileVerifier,
|
|
)
|
|
|
|
with pytest.raises(TypeError, match="abstract"):
|
|
BaseFileVerifier()
|
|
|
|
def test_defines_abstract_methods(self):
|
|
"""Test that abstract methods are defined."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
BaseFileVerifier,
|
|
)
|
|
|
|
# Check that the class has the expected abstract methods
|
|
assert hasattr(BaseFileVerifier, "should_verify")
|
|
assert hasattr(BaseFileVerifier, "get_file_type")
|
|
assert hasattr(BaseFileVerifier, "allows_modifications")
|
|
|
|
|
|
class TestConcreteVerifier:
|
|
"""Tests using a concrete implementation of BaseFileVerifier."""
|
|
|
|
@pytest.fixture
|
|
def concrete_verifier(self):
|
|
"""Create a concrete verifier implementation for testing."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
BaseFileVerifier,
|
|
FileType,
|
|
)
|
|
|
|
class TestVerifier(BaseFileVerifier):
|
|
def should_verify(self, file_path: Path) -> bool:
|
|
return file_path.suffix == ".test"
|
|
|
|
def get_file_type(self) -> FileType:
|
|
return FileType.PDF
|
|
|
|
def allows_modifications(self) -> bool:
|
|
return False
|
|
|
|
return TestVerifier()
|
|
|
|
def test_calculate_checksum_returns_sha256_hex(self, concrete_verifier):
|
|
"""Test that calculate_checksum returns a SHA256 hex string."""
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".test") as f:
|
|
f.write(b"test content")
|
|
f.flush()
|
|
file_path = Path(f.name)
|
|
|
|
try:
|
|
checksum = concrete_verifier.calculate_checksum(file_path)
|
|
|
|
# SHA256 hex string is 64 characters
|
|
assert len(checksum) == 64
|
|
assert all(c in "0123456789abcdef" for c in checksum)
|
|
finally:
|
|
file_path.unlink()
|
|
|
|
def test_calculate_checksum_is_deterministic(self, concrete_verifier):
|
|
"""Test that same content produces same checksum."""
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".test") as f:
|
|
f.write(b"identical content")
|
|
f.flush()
|
|
file_path = Path(f.name)
|
|
|
|
try:
|
|
checksum1 = concrete_verifier.calculate_checksum(file_path)
|
|
checksum2 = concrete_verifier.calculate_checksum(file_path)
|
|
|
|
assert checksum1 == checksum2
|
|
finally:
|
|
file_path.unlink()
|
|
|
|
def test_calculate_checksum_different_for_different_content(
|
|
self, concrete_verifier
|
|
):
|
|
"""Test that different content produces different checksum."""
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".test") as f1:
|
|
f1.write(b"content one")
|
|
f1.flush()
|
|
file_path1 = Path(f1.name)
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".test") as f2:
|
|
f2.write(b"content two")
|
|
f2.flush()
|
|
file_path2 = Path(f2.name)
|
|
|
|
try:
|
|
checksum1 = concrete_verifier.calculate_checksum(file_path1)
|
|
checksum2 = concrete_verifier.calculate_checksum(file_path2)
|
|
|
|
assert checksum1 != checksum2
|
|
finally:
|
|
file_path1.unlink()
|
|
file_path2.unlink()
|
|
|
|
def test_calculate_checksum_raises_for_missing_file(
|
|
self, concrete_verifier
|
|
):
|
|
"""Test that FileNotFoundError is raised for missing file."""
|
|
with pytest.raises(FileNotFoundError):
|
|
concrete_verifier.calculate_checksum(Path("/nonexistent/file.test"))
|
|
|
|
def test_get_algorithm_returns_sha256(self, concrete_verifier):
|
|
"""Test that get_algorithm returns sha256."""
|
|
assert concrete_verifier.get_algorithm() == "sha256"
|
|
|
|
def test_should_verify_with_matching_file(self, concrete_verifier):
|
|
"""Test should_verify returns True for matching file type."""
|
|
assert (
|
|
concrete_verifier.should_verify(Path("/path/to/file.test")) is True
|
|
)
|
|
|
|
def test_should_verify_with_non_matching_file(self, concrete_verifier):
|
|
"""Test should_verify returns False for non-matching file type."""
|
|
assert (
|
|
concrete_verifier.should_verify(Path("/path/to/file.txt")) is False
|
|
)
|
|
|
|
def test_get_file_type_returns_expected(self, concrete_verifier):
|
|
"""Test get_file_type returns the expected FileType."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
FileType,
|
|
)
|
|
|
|
assert concrete_verifier.get_file_type() == FileType.PDF
|
|
|
|
def test_allows_modifications_returns_false(self, concrete_verifier):
|
|
"""Test allows_modifications returns False."""
|
|
assert concrete_verifier.allows_modifications() is False
|
|
|
|
|
|
class TestChecksumWithLargeFile:
|
|
"""Tests for checksum calculation with larger files."""
|
|
|
|
@pytest.fixture
|
|
def verifier(self):
|
|
"""Create a concrete verifier for testing."""
|
|
from local_deep_research.security.file_integrity.base_verifier import (
|
|
BaseFileVerifier,
|
|
FileType,
|
|
)
|
|
|
|
class LargeFileVerifier(BaseFileVerifier):
|
|
def should_verify(self, file_path: Path) -> bool:
|
|
return True
|
|
|
|
def get_file_type(self) -> FileType:
|
|
return FileType.EXPORT
|
|
|
|
def allows_modifications(self) -> bool:
|
|
return True
|
|
|
|
return LargeFileVerifier()
|
|
|
|
def test_handles_file_larger_than_chunk_size(self, verifier):
|
|
"""Test that files larger than 4096 bytes are handled correctly."""
|
|
# Create a file larger than the chunk size (4096 bytes)
|
|
content = b"x" * 10000 # 10KB
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
|
f.write(content)
|
|
f.flush()
|
|
file_path = Path(f.name)
|
|
|
|
try:
|
|
checksum = verifier.calculate_checksum(file_path)
|
|
|
|
# Should still return valid SHA256
|
|
assert len(checksum) == 64
|
|
assert all(c in "0123456789abcdef" for c in checksum)
|
|
finally:
|
|
file_path.unlink()
|
|
|
|
def test_empty_file_has_valid_checksum(self, verifier):
|
|
"""Test that empty files have a valid checksum."""
|
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
|
# Don't write anything - empty file
|
|
file_path = Path(f.name)
|
|
|
|
try:
|
|
checksum = verifier.calculate_checksum(file_path)
|
|
|
|
# Empty file should have the SHA256 of empty string
|
|
# This is the well-known SHA256 hash of "" (not a secret)
|
|
empty_string_sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" # DevSkim: ignore DS173237
|
|
assert checksum == empty_string_sha256
|
|
finally:
|
|
file_path.unlink()
|