feat(cookie): Findings bearbeitbar — gruppiert nach Typ + Matrix + Hinweise-Split

CookieFindings: Umschalter [Nach Fehlertyp] (je Typ: Maßnahme + betroffene
Cookies + Ticket-Text) ↔ [Matrix] (Cookie×Typ, ✗ Handlung / ⚠ Hinweis).
Trennung FINDINGS (zu beheben) vs HINWEISE (neutral, gegen DSE zu prüfen).
Backend: kind-Klassifikation (third_country/eu_alternative=hinweis); Drittland-
Remediation neutral formuliert (pro Verarbeiter prüfen, keine 'in DSE benennen'-
Befehle, da DSE-Abdeckung wie BMWs 'in der Regel SCC' oft unzureichend).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-11 11:02:34 +02:00
parent b0115cb10b
commit 39cb6afc23
6 changed files with 346 additions and 87 deletions
@@ -39,6 +39,11 @@ _CONTROL_MAP = {
"eu_alternative": {"control_id": None, "regulation": "", "article": "kommerzielle Empfehlung"},
}
# Advisory-Typen: keine bestätigten Verstöße, sondern Hinweise, die der
# Cross-Finding-Agent gegen die DSE abgleicht (Drittland kann dort bereits via
# SCC/Art. 49/Angemessenheit abgedeckt sein → dann unterdrücken).
_HINWEIS_TYPES = {"third_country", "eu_alternative"}
def load_big_library(db, names: list[str]) -> dict:
"""Batch-Lookup der grossen Open-Cookie-Database (compliance.cookie_library,
@@ -222,9 +227,13 @@ def analyze_cookies(vendors: list[dict], big_lib: dict | None = None) -> dict:
"declared": country or "",
"library_purpose": schrems or f"Anbieter-Sitz {country}",
"remediation": (
f"{vname} überträgt in ein Drittland ({country or 'außerhalb EWR'}) — "
f"SCC (Art. 46) oder DPF-Zertifizierung prüfen und in der "
f"Datenschutzerklärung benennen (Art. 44 ff. DSGVO)."
f"Neutrales Finding: {vname} kann Daten außerhalb der EU "
f"({country or 'Drittland'}) verarbeiten. Für jeden solchen "
f"Verarbeiter geeignete Garantien konkret nachweisen (SCC Art. 46 / "
f"Angemessenheitsbeschluss / Art. 49) und ggf. eine "
f"Transfer-Folgenabschätzung (TIA). Pauschale DSE-Formulierungen "
f"('in der Regel SCC') genügen nicht — pro Verarbeiter prüfen "
f"(Art. 44 ff. DSGVO). Interne Verträge können wir nicht einsehen."
),
})
@@ -262,9 +271,11 @@ def analyze_cookies(vendors: list[dict], big_lib: dict | None = None) -> dict:
),
})
# A: jeden Befund an seinen Control + Rechtsgrundlage haengen (auditfest).
# A: jeden Befund an Control + Rechtsgrundlage haengen + als echtes Finding
# (zu beheben) oder Hinweis (advisory, gegen DSE abzugleichen) klassifizieren.
for f in findings:
f["control"] = _CONTROL_MAP.get(f["type"], {})
f["kind"] = "hinweis" if f["type"] in _HINWEIS_TYPES else "finding"
findings.sort(key=lambda f: _SEV_ORDER.get(f["severity"], 3))
return {
"summary": {
@@ -58,6 +58,22 @@ def test_third_country_and_eu_alternative_for_us_tracker():
assert "eu_alternative" in t
def test_kind_splits_findings_from_hinweise():
# third_country/eu_alternative = Hinweis (advisory); Rest = Finding.
out = analyze_cookies([{
"name": "Google", "category": "necessary",
"cookies": [{"name": "_ga", "purpose": "", "expiry": "2 Jahre"}],
}])
by = {f["type"]: f["kind"] for f in out["findings"]}
assert by.get("third_country") == "hinweis"
assert by.get("eu_alternative") == "hinweis"
assert by.get("tracker_as_necessary") == "finding"
# Drittland-Wording: neutral, pro Verarbeiter, keine "in DSE benennen"-Befehle.
tc = next(f for f in out["findings"] if f["type"] == "third_country")
assert "pro Verarbeiter" in tc["remediation"]
assert "benennen" not in tc["remediation"]
def test_third_country_deduped_per_vendor():
out = analyze_cookies([{
"name": "Google", "category": "marketing",