feat(banner): Consent-Historie/Widerruf live erkennen (Borlabs-Stil, #62)

consent_history.detect_consent_history: erkennt CMP-Anbieter (Borlabs/
Usercentrics/OneTrust/Cookiebot/…) aus Storage+Cookies, versionierten Consent
(historie-fähig) + dauerhaftes Widerruf-/Einstellungs-Widget. consent_scanner
ruft es in Phase A; scan_matrix_summary surft summary.consent_history;
browser_cross_finding: positiver Befund wenn vorhanden, sonst Best-Practice-LOW
(„Nutzer sehen, wann sie welcher Version zugestimmt haben"); BrowserBehaviorView
zeigt es im Engine-Detail. Tests: 7 (classify/versioned) + 2 Cross-Finding.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-13 16:38:38 +02:00
parent 8d0da710d5
commit d92dd3b5fc
7 changed files with 208 additions and 4 deletions
@@ -8,7 +8,8 @@ from compliance.services.browser_cross_finding import build_cross_findings
def _row(pid, label, engine, *, before=0, reject_ok=True,
impressum=True, dse=True, with_summary=True, track_before=0):
impressum=True, dse=True, with_summary=True, track_before=0,
consent_history=None):
if not with_summary:
return {"profile_id": pid, "label": label, "engine": engine,
"summary": None, "error": "launch failed"}
@@ -22,6 +23,7 @@ def _row(pid, label, engine, *, before=0, reject_ok=True,
# before_consent = nicht-essentielles Tracking vor Consent (das
# rechtlich relevante Signal, NICHT die Cookie-Rohzahl `before`).
"violations": {"before_consent": track_before},
"consent_history": consent_history or {},
},
}
@@ -99,5 +101,21 @@ def test_clean_matrix_no_violations():
_row("chromium-headed-de", "Chromium", "blink"),
_row("firefox-headed-de", "Firefox", "gecko"),
]}
# Alles sauber → keine Verstoß-Befunde (Impressum/DSE vorhanden).
assert build_cross_findings(m) == []
f = build_cross_findings(m)
# Keine HIGH/MEDIUM-Verstöße; nur der Consent-Historie-Best-Practice-Hinweis (LOW).
assert [x for x in f if x["severity"] in ("HIGH", "MEDIUM")] == []
def test_consent_history_present_is_positive():
ch = {"provider": "Borlabs", "history_capable": True, "withdraw_ui": True}
m = {"browser_matrix": [
_row("chromium-headed-de", "Chromium", "blink", consent_history=ch),
]}
hit = [x for x in build_cross_findings(m) if "Historie / Widerruf vorhanden" in x["title"]]
assert hit and "Borlabs" in hit[0]["detail"]
def test_consent_history_absent_is_best_practice_low():
m = {"browser_matrix": [_row("chromium-headed-de", "Chromium", "blink")]}
hit = [x for x in build_cross_findings(m) if "Keine sichtbare Einwilligungs-Historie" in x["title"]]
assert hit and hit[0]["severity"] == "LOW"