feat(cookie): A — Findings auditfest an Controls verdrahten

Jeder Cookie-Befund traegt jetzt ein strukturiertes control-Feld
(control_id aus doc_check_controls + regulation + article) statt nur
hardcodeter Strings: vague_duration->AUTH-2051-A03 (Art.5(1)e+13),
tracker_as_necessary->DATA-2851-A05 (§25 TDDDG), third_country->
DATA-1624-A04 (Art.44). Kette Regulation->Article->Control->Finding.
Frontend zeigt die Rechtsgrundlage je Befund. (Controls tragen
regulation/article noch NULL -> hier mitgeliefert bis gepflegt.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-11 08:44:19 +02:00
parent 4c45f11e43
commit 901de1ca97
3 changed files with 38 additions and 0 deletions
@@ -22,6 +22,19 @@ from compliance.services.cookie_knowledge_db import lookup_cookie
_TRACKER_CATS = {"marketing", "statistics", "social_media", "targeting"}
# A — auditfeste Verdrahtung: jeder Befund-Typ → echter Control (control_id aus
# doc_check_controls) + legal_basis. Die Controls tragen regulation/article noch
# NULL, daher liefern wir die Rechtsgrundlage hier strukturiert mit (bis sie in
# den Controls gepflegt ist). Kette: Regulation → Article → Control → Finding.
_CONTROL_MAP = {
"vague_duration": {"control_id": "AUTH-2051-A03", "regulation": "DSGVO", "article": "Art. 5 Abs. 1 lit. e + Art. 13"},
"excessive_lifetime": {"control_id": "AUTH-2051-A02", "regulation": "DSGVO", "article": "Art. 5 Abs. 1 lit. e"},
"tracker_as_necessary": {"control_id": "DATA-2851-A05", "regulation": "TDDDG", "article": "§ 25 Abs. 1"},
"missing_purpose": {"control_id": "AUTH-2053-A05", "regulation": "DSGVO", "article": "Art. 13"},
"third_country": {"control_id": "DATA-1624-A04", "regulation": "DSGVO", "article": "Art. 44 ff."},
"eu_alternative": {"control_id": None, "regulation": "", "article": "kommerzielle Empfehlung"},
}
def load_big_library(db, names: list[str]) -> dict:
"""Batch-Lookup der grossen Open-Cookie-Database (compliance.cookie_library,
@@ -219,6 +232,9 @@ def analyze_cookies(vendors: list[dict], big_lib: dict | None = None) -> dict:
),
})
# A: jeden Befund an seinen Control + Rechtsgrundlage haengen (auditfest).
for f in findings:
f["control"] = _CONTROL_MAP.get(f["type"], {})
findings.sort(key=lambda f: _SEV_ORDER.get(f["severity"], 3))
return {
"summary": {
@@ -79,6 +79,21 @@ def test_excessive_lifetime():
assert el and "Art. 5" in el[0]["remediation"]
def test_findings_carry_control_and_legal_basis():
# A: jeder Befund traegt control_id + Rechtsgrundlage (auditfest).
out = analyze_cookies([{
"name": "Google", "category": "necessary",
"cookies": [{"name": "_ga", "purpose": "x",
"expiry": "Wird solange gespeichert, bis es deaktiviert wird."}],
}])
assert out["findings"], "es sollte Befunde geben"
for f in out["findings"]:
assert "control" in f
vd = next(f for f in out["findings"] if f["type"] == "vague_duration")
assert vd["control"]["control_id"] == "AUTH-2051-A03"
assert "Art. 5" in vd["control"]["article"]
def test_vague_duration_flagged_concrete_ok():
# User-Beispiel Salesforce: "bis der Nutzer es deaktiviert" = vage.
out = analyze_cookies([{