Files
BoltClam/tests/test_quarantine.py
T
TheMajesticMagicianandClaude Code addd00364d BoltClam 1.0.0: ClamAV desktop protection with Bolt sign-in
Adds the PyQt6 Windows client (Homepage, Scan & results, About & updates, Settings), ClamAV scanning and quarantine, browser-download watch with an exclusive scan gate, Bolt loopback/PKCE sign-in with DPAPI key storage, Gitea release feed with checksum verification, opt-in diagnostics, and Windows/portable build tooling.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-25 16:21:14 -06:00

101 lines
3.2 KiB
Python

from __future__ import annotations
import json
import os
import stat
from pathlib import Path
import pytest
from boltclam.quarantine import QuarantineError, QuarantineService, delete_file, hash_file
from boltclam.storage import Storage
def _service(tmp_path) -> QuarantineService:
storage = Storage(tmp_path / "history.db")
return QuarantineService(storage, tmp_path / "quarantine")
def test_quarantine_moves_records_and_writes_metadata(tmp_path):
service = _service(tmp_path)
source = tmp_path / "malware.exe"
source.write_bytes(b"malware bytes")
digest = hash_file(source)
item = service.quarantine_file(source, "Win.Test", scan_id=7, reason="Unit test")
assert not source.exists()
destination = Path(item.quarantine_path)
assert destination.is_file()
assert destination.name.endswith(".quarantine")
assert item.sha256 == digest
assert service.storage.get_quarantine(item.id).signature == "Win.Test"
metadata = json.loads((service.root / f"{item.id}.json").read_text())
assert metadata["original_path"] == str(source)
assert metadata["reason"] == "Unit test"
def test_restore_to_original_or_chosen_path(tmp_path):
service = _service(tmp_path)
source = tmp_path / "bad.exe"
source.write_bytes(b"x")
item = service.quarantine_file(source, "Sig")
target = service.restore(item.id)
assert target.read_bytes() == b"x"
assert service.storage.get_quarantine(item.id).status == "restored"
source.write_bytes(b"y")
item2 = service.quarantine_file(source, "Sig")
chosen = tmp_path / "restored" / "safe.exe"
restored = service.restore(item2.id, chosen)
assert restored == chosen
assert chosen.read_bytes() == b"y"
def test_delete_quarantined_item_and_missing_original(tmp_path):
service = _service(tmp_path)
source = tmp_path / "bad.exe"
source.write_bytes(b"x")
item = service.quarantine_file(source, "Sig")
service.delete(item.id)
assert not Path(item.quarantine_path).exists()
assert service.storage.get_quarantine(item.id).status == "deleted"
with pytest.raises(QuarantineError):
service.restore(item.id)
def test_refuses_symlink_and_directory(tmp_path):
service = _service(tmp_path)
directory = tmp_path / "folder"
directory.mkdir()
with pytest.raises(QuarantineError):
service.quarantine_file(directory, "Sig")
real = tmp_path / "real.exe"
real.write_bytes(b"x")
link = tmp_path / "link.exe"
try:
link.symlink_to(real)
except OSError:
pytest.skip("symlinks unavailable")
with pytest.raises(QuarantineError):
service.quarantine_file(link, "Sig")
with pytest.raises(QuarantineError):
delete_file(directory)
def test_delete_file_clears_readonly_bit(tmp_path):
target = tmp_path / "readonly.exe"
target.write_bytes(b"x")
os.chmod(target, stat.S_IREAD)
delete_file(target)
assert not target.exists()
def test_restore_refuses_to_overwrite(tmp_path):
service = _service(tmp_path)
source = tmp_path / "bad.exe"
source.write_bytes(b"x")
item = service.quarantine_file(source, "Sig")
source.write_bytes(b"replacement")
with pytest.raises(QuarantineError):
service.restore(item.id)