Files
derp/tests/test_crtsh.py
user f48b32cd65 fix: resolve test_crtsh.py import for plugins/ directory
Same importlib fix as test_username.py -- load plugins.crtsh from
file path since plugins/ is not a Python package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 06:18:06 +01:00

122 lines
3.9 KiB
Python

"""Tests for the crt.sh certificate transparency plugin."""
import importlib.util
import sys
from datetime import datetime, timezone
from pathlib import Path
# plugins/ is not a Python package -- load the module from file path
_spec = importlib.util.spec_from_file_location(
"plugins.crtsh", Path(__file__).resolve().parent.parent / "plugins" / "crtsh.py",
)
_mod = importlib.util.module_from_spec(_spec)
sys.modules[_spec.name] = _mod
_spec.loader.exec_module(_mod)
from plugins.crtsh import ( # noqa: E402
deduplicate,
format_result,
is_expired,
is_live_cert_expired,
parse_crtsh_ts,
)
class TestDeduplicate:
def test_removes_duplicate_serials(self):
certs = [
{"serial_number": "AAA", "common_name": "a.example.com"},
{"serial_number": "BBB", "common_name": "b.example.com"},
{"serial_number": "AAA", "common_name": "a.example.com (dup)"},
]
result = deduplicate(certs)
assert len(result) == 2
serials = {c["serial_number"] for c in result}
assert serials == {"AAA", "BBB"}
def test_keeps_first_occurrence(self):
certs = [
{"serial_number": "AAA", "common_name": "first"},
{"serial_number": "AAA", "common_name": "second"},
]
result = deduplicate(certs)
assert result[0]["common_name"] == "first"
def test_empty_input(self):
assert deduplicate([]) == []
def test_no_serial_field(self):
certs = [{"common_name": "no-serial"}, {"serial_number": "", "common_name": "empty"}]
result = deduplicate(certs)
assert len(result) == 0
def test_all_unique(self):
certs = [
{"serial_number": "A", "common_name": "a"},
{"serial_number": "B", "common_name": "b"},
{"serial_number": "C", "common_name": "c"},
]
assert len(deduplicate(certs)) == 3
class TestExpiredCheck:
def test_expired_cert(self):
cert = {"not_after": "2020-01-01T00:00:00"}
assert is_expired(cert) is True
def test_valid_cert(self):
cert = {"not_after": "2099-12-31T23:59:59"}
assert is_expired(cert) is False
def test_missing_not_after(self):
assert is_expired({}) is False
assert is_expired({"not_after": ""}) is False
def test_fractional_seconds(self):
cert = {"not_after": "2020-06-15T12:30:45.123"}
assert is_expired(cert) is True
def test_parse_timestamp_basic(self):
dt = parse_crtsh_ts("2024-03-15T10:30:00")
assert dt == datetime(2024, 3, 15, 10, 30, 0, tzinfo=timezone.utc)
def test_parse_timestamp_fractional(self):
dt = parse_crtsh_ts("2024-03-15T10:30:00.500")
assert dt.microsecond == 500000
class TestLiveCertExpired:
def test_expired_live_cert(self):
cert = {"notAfter": "Jan 1 00:00:00 2020 GMT"}
assert is_live_cert_expired(cert) is True
def test_valid_live_cert(self):
cert = {"notAfter": "Dec 31 23:59:59 2099 GMT"}
assert is_live_cert_expired(cert) is False
def test_missing_field(self):
assert is_live_cert_expired({}) is False
class TestFormatResult:
def test_basic(self):
line = format_result("example.com", 100, 10, 90, None)
assert line == "example.com -- 100 certs (10 expired, 90 valid)"
def test_live_expired(self):
line = format_result("bad.com", 50, 5, 45, True)
assert "live cert EXPIRED" in line
def test_live_ok_with_expired_certs(self):
line = format_result("ok.com", 50, 5, 45, False)
assert "live cert ok" in line
def test_live_ok_no_expired(self):
"""No live check annotation when zero expired certs."""
line = format_result("clean.com", 50, 0, 50, False)
assert "live cert" not in line
def test_zero_certs(self):
line = format_result("empty.com", 0, 0, 0, None)
assert line == "empty.com -- 0 certs (0 expired, 0 valid)"