Files
ITWorx_MCP_Hub-Public/tests/test_wp220_cve_exceptions.py
T

161 lines
6.1 KiB
Python

"""WP-220E regression tests: CVE exception drafting and gate enforcement."""
from __future__ import annotations
import json
from datetime import date, timedelta
from pathlib import Path
from scripts.check_cve_exceptions import REQUIRED_FIELDS, check
ROOT = Path(__file__).resolve().parents[1]
TODAY = date(2026, 8, 15)
def _scan(*rows: dict) -> dict:
return {
"schemaVersion": "1.0",
"scannedAt": "2026-08-15T00:00:00Z",
"trivyVersion": "0.72.0",
"unresolvedCriticalOrHigh": list(rows),
}
def _exception(**overrides) -> dict:
base = {
"id": "pkg-CVE-2026-1",
"cve": "CVE-2026-1",
"package": "pkg",
"installedVersion": "1.0",
"severity": "HIGH",
"scanner": "trivy 0.72.0",
"scanDate": "2026-08-01",
"affectedImageDigests": {"control-api": "sha256:" + "a" * 64},
"reachabilityAnalysis": "base OS package",
"compensatingControls": "network egress restricted",
"riskOwner": "jens",
"approver": "jens",
"approvalStatus": "approved",
"expiryDate": "2026-12-01",
"mandatoryRescanDate": "2026-09-01",
"remediationTrigger": "upstream fix",
"evidenceReference": "evidence/WP-200/x.md",
}
base.update(overrides)
return base
def test_real_wp220_registry_has_every_required_field() -> None:
"""The actual drafted registry this packet ships must be schema-complete."""
doc = json.loads(
(ROOT / "docs" / "09-release" / "cve-exceptions.json").read_text(encoding="utf-8")
)
assert doc["exceptions"], "registry must not be empty"
for exception in doc["exceptions"]:
for field in REQUIRED_FIELDS:
assert field in exception, f"{exception.get('id')} missing {field}"
def test_finding_with_no_exception_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
errors = check(scan, {"exceptions": []}, None, TODAY)
assert any("UNAPPROVED" in e and "no exception recorded at all" in e for e in errors)
def test_pending_exception_still_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {
"exceptions": [
_exception(approvalStatus="pending", riskOwner="", approver="", compensatingControls="")
]
}
errors = check(scan, exceptions, None, TODAY)
assert any("not 'approved'" in e for e in errors)
def test_rejected_exception_still_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {"exceptions": [_exception(approvalStatus="rejected")]}
errors = check(scan, exceptions, None, TODAY)
assert any("not 'approved'" in e for e in errors)
def test_fully_approved_current_exception_passes() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {"exceptions": [_exception()]}
errors = check(scan, exceptions, None, TODAY)
assert errors == []
def test_approved_but_missing_risk_owner_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {"exceptions": [_exception(riskOwner="")]}
errors = check(scan, exceptions, None, TODAY)
assert any("no riskOwner recorded" in e for e in errors)
def test_approved_but_missing_compensating_controls_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {"exceptions": [_exception(compensatingControls="")]}
errors = check(scan, exceptions, None, TODAY)
assert any("no compensating controls" in e for e in errors)
def test_expired_exception_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {"exceptions": [_exception(expiryDate=(TODAY - timedelta(days=1)).isoformat())]}
errors = check(scan, exceptions, None, TODAY)
assert any("EXPIRED" in e for e in errors)
def test_overdue_rescan_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {
"exceptions": [_exception(mandatoryRescanDate=(TODAY - timedelta(days=1)).isoformat())]
}
errors = check(scan, exceptions, None, TODAY)
assert any("RESCAN OVERDUE" in e for e in errors)
def test_changed_digest_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {
"exceptions": [_exception(affectedImageDigests={"control-api": "sha256:" + "a" * 64})]
}
current_digests = {"control-api": "sha256:" + "b" * 64}
errors = check(scan, exceptions, current_digests, TODAY)
assert any("DIGEST CHANGED" in e for e in errors)
def test_matching_digest_passes() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
exceptions = {
"exceptions": [_exception(affectedImageDigests={"control-api": "sha256:" + "a" * 64})]
}
current_digests = {"control-api": "sha256:" + "a" * 64}
errors = check(scan, exceptions, current_digests, TODAY)
assert errors == []
def test_malformed_exception_missing_required_field_fails() -> None:
scan = _scan({"package": "pkg", "cves": ["CVE-2026-1"], "severity": "HIGH"})
broken = _exception()
del broken["evidenceReference"]
errors = check(scan, {"exceptions": [broken]}, None, TODAY)
assert any("missing required fields" in e for e in errors)
# And the finding itself is still reported unapproved, since the
# malformed entry was excluded from lookup rather than trusted.
assert any("UNAPPROVED" in e for e in errors)
def test_multiple_cves_in_one_package_row_each_need_their_own_exception() -> None:
scan = _scan(
{"package": "perl-base", "cves": ["CVE-2026-1", "CVE-2026-2"], "severity": "CRITICAL"}
)
exceptions = {
"exceptions": [_exception(id="perl-base-CVE-2026-1", cve="CVE-2026-1", package="perl-base")]
}
errors = check(scan, exceptions, None, TODAY)
assert not any("CVE-2026-1" in e for e in errors)
assert any("CVE-2026-2" in e and "UNAPPROVED" in e for e in errors)