3f90e40807
Korrektheit (§ 25 TDDDG): "Cookies vor Consent" ist KEIN Verstoss per se — technisch notwendige Cookies inkl. des Consent-Cookies (speichert die Ablehnung) sind nach Abs. 2 erlaubt. Verstoss ist nur nicht-essentielles TRACKING vor Consent. - browser_cross_finding: Befund haengt jetzt an violations.before_consent (Tracking), nicht an der Cookie-Rohzahl; § 25 Abs. 2-Hinweis im Detail. Regressionstest: Cookies-ohne-Tracking → KEIN Befund. - multi_browser_scanner._extract_dimensions: Score nutzt Tracking-Violations + reject_respected-Verdikt statt Rohzahl (Fallback erhalten). - BrowserBehaviorView: "Cookies vor Consent" nur rot/⚠ bei Tracking, "nach Ablehnen" neutral (Verdikt = reject-Spalte); erklaerende Zeile. Speed: run_consent_test ueberspringt im Matrix-Modus (browser_profile gesetzt) die teuren Phasen C/D-F/G — nur A+B noetig. Verhindert das 504 beim Multi-Engine-Scan (BMW 4 Engines lief sonst in den 338s-Gateway-Timeout). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
104 lines
4.2 KiB
Python
104 lines
4.2 KiB
Python
"""Cross-Browser-Befunde (deterministische Matrix-Sicht, Phase 4).
|
|
|
|
Sichert die Inkonsistenz-Erkennung ZWISCHEN Engines + die Einordnung, dass
|
|
Browser-Tracking-Schutz (Safari/ITP, Brave/Shields, Firefox/ETP) Verstöße
|
|
clientseitig maskiert (kein Compliance-Beleg)."""
|
|
|
|
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):
|
|
if not with_summary:
|
|
return {"profile_id": pid, "label": label, "engine": engine,
|
|
"summary": None, "error": "launch failed"}
|
|
return {
|
|
"profile_id": pid, "label": label, "engine": engine,
|
|
"summary": {
|
|
"cookies_before_consent": before,
|
|
"cookies_after_reject": 0 if reject_ok else 2,
|
|
"reject_respected": reject_ok,
|
|
"surface": {"has_impressum_link": impressum, "has_dse_link": dse},
|
|
# before_consent = nicht-essentielles Tracking vor Consent (das
|
|
# rechtlich relevante Signal, NICHT die Cookie-Rohzahl `before`).
|
|
"violations": {"before_consent": track_before},
|
|
},
|
|
}
|
|
|
|
|
|
def _titles(findings):
|
|
return " | ".join(f["title"] for f in findings)
|
|
|
|
|
|
def test_empty_matrix():
|
|
assert build_cross_findings(None) == []
|
|
assert build_cross_findings({"browser_matrix": []}) == []
|
|
|
|
|
|
def test_tracking_before_consent_in_all_engines_high():
|
|
# Nicht-essentielles Tracking vor Consent (track_before>0) in allen Engines.
|
|
m = {"browser_matrix": [
|
|
_row("chromium-headed-de", "Chromium", "blink", before=5, track_before=2),
|
|
_row("firefox-headed-de", "Firefox", "gecko", before=3, track_before=1),
|
|
]}
|
|
f = build_cross_findings(m)
|
|
hit = [x for x in f if "Tracking vor der Einwilligung — in allen" in x["title"]]
|
|
assert hit and hit[0]["severity"] == "HIGH"
|
|
assert "§ 25 Abs. 2" in hit[0]["detail"] # essentielle Cookies ausgenommen
|
|
|
|
|
|
def test_cookies_before_consent_without_tracking_is_no_finding():
|
|
# KERN-REGRESSION (User-Frage): Cookies vor Consent vorhanden, aber KEIN
|
|
# Tracking (z.B. nur das Consent-Cookie selbst) → KEIN Verstoß-Befund.
|
|
m = {"browser_matrix": [
|
|
_row("chromium-headed-de", "Chromium", "blink", before=5, track_before=0),
|
|
_row("firefox-headed-de", "Firefox", "gecko", before=4, track_before=0),
|
|
]}
|
|
f = build_cross_findings(m)
|
|
assert [x for x in f if "Tracking vor" in x["title"]] == []
|
|
|
|
|
|
def test_tracking_before_consent_inconsistent_flags_browser_protection():
|
|
# Chrome (nachgiebig) lässt Tracker vor Consent, Safari/WebKit (ITP) nicht.
|
|
m = {"browser_matrix": [
|
|
_row("chrome-channel-desktop-de", "Chrome", "blink", before=4, track_before=3),
|
|
_row("webkit-headed-de", "Safari/WebKit", "webkit", before=0, track_before=0),
|
|
]}
|
|
f = build_cross_findings(m)
|
|
hit = [x for x in f if "nur in manchen" in x["title"]]
|
|
assert hit and hit[0]["severity"] == "HIGH"
|
|
# Einordnung: ITP maskiert clientseitig → kein Compliance-Beleg.
|
|
assert "ITP" in hit[0]["detail"]
|
|
assert "Chrome" in hit[0]["affected"]
|
|
|
|
|
|
def test_reject_inconsistent_high():
|
|
m = {"browser_matrix": [
|
|
_row("chrome-channel-desktop-de", "Chrome", "blink", reject_ok=False),
|
|
_row("brave-default-de", "Brave", "blink", reject_ok=True),
|
|
]}
|
|
f = build_cross_findings(m)
|
|
hit = [x for x in f if "nicht in allen Browsern respektiert" in x["title"]]
|
|
assert hit and hit[0]["severity"] == "HIGH"
|
|
assert "Chrome" in hit[0]["affected"]
|
|
|
|
|
|
def test_missing_engines_coverage_hint():
|
|
m = {"browser_matrix": [
|
|
_row("chromium-headed-de", "Chromium", "blink"),
|
|
_row("brave-default-de", "Brave", "blink", with_summary=False),
|
|
]}
|
|
f = build_cross_findings(m)
|
|
hit = [x for x in f if "Nicht alle Browser getestet" in x["title"]]
|
|
assert hit and hit[0]["severity"] == "LOW"
|
|
assert "Brave" in hit[0]["affected"]
|
|
|
|
|
|
def test_clean_matrix_no_violations():
|
|
m = {"browser_matrix": [
|
|
_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) == []
|