fix(mc-audit): TOM/AVV case-mismatch + Ausnahmen-Pattern Wortabstand
- _PROCESS_INTERNAL_PATTERNS: Patterns wurden gegen lowercased Blob geprueft, aber Case-sensitive geschrieben (TOM/AVV/SCC). Matchen nie. Auf lowercase normalisiert. - "Ausnahmen ... dokumentieren": Pattern war zu eng, verlangte direkte Adjazenz. Jetzt bis zu 60 Zeichen Wortabstand. - Test-Suite mit 22 kuratierten DSGVO/AI-Act/eCall-MC-Labels. Alle gruen (vorher 2/22 FAIL — beide vom User explizit als Beispiele genannt: TOM, Ausnahmen). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,13 +38,15 @@ _PROCESS_INTERNAL_PATTERNS = [
|
||||
r"\bsensibilisier",
|
||||
# Vertraege intern
|
||||
r"\bauftragsverarbeitungsvertrag\b",
|
||||
r"\bAVV\b\s+abgeschlossen",
|
||||
r"\bavv\b\s+abgeschlossen",
|
||||
r"\bvertrag.*abgeschlossen",
|
||||
r"\bdpa\s+(geschlossen|abgeschlossen|vorhanden)",
|
||||
r"\bSCC\s+(geschlossen|abgeschlossen|implementiert)",
|
||||
# Technisch-organisatorische Massnahmen (intern)
|
||||
r"\bscc\s+(geschlossen|abgeschlossen|implementiert)",
|
||||
# Technisch-organisatorische Massnahmen (intern). Lowercase: blob
|
||||
# ist bereits .lower(); Case-sensitive Patterns (TOM/AVV/SCC) matchen
|
||||
# nie. Daher hier explizit klein.
|
||||
r"\btechnisch[-\s]*organisatorische\s+ma(ß|ss)nahmen?\b",
|
||||
r"\bTOM\s+(umgesetzt|dokumentiert|implementiert)",
|
||||
r"\btom\s+(umgesetzt|dokumentiert|implementiert)",
|
||||
r"\bverschluesselung\s+(implementiert|aktiv)",
|
||||
r"\bpseudonymisierung\s+(implementiert|aktiv)",
|
||||
r"\bbackup[s]?\s+(eingerichtet|vorhanden)",
|
||||
@@ -68,7 +70,11 @@ _PROCESS_INTERNAL_PATTERNS = [
|
||||
r"\bbitte\s+(intern\s+)?dokumentieren",
|
||||
r"\bin\s+der\s+verfahrens",
|
||||
r"\bnach\s+innen\s+geh",
|
||||
r"\bausnahmen\s+(dokumentieren|protokollieren)",
|
||||
# "Ausnahmen ... dokumentieren" — Wortabstand bis 60 Zeichen erlauben,
|
||||
# damit "ausnahmen bei bereits vorhandenen informationen dokumentieren"
|
||||
# matcht. Sonst Pattern zu eng → process-internal-MC bleibt FAIL.
|
||||
r"\bausnahmen\b[^\n]{0,60}\b(dokumentieren|protokollieren)\b",
|
||||
r"\bdokumentations[\s-]?pflicht",
|
||||
r"\bkostenfrei\s+(zur\s+verfuegung|gewaehren|ermoegli)",
|
||||
r"\bunentgeltlich\s+(zur\s+verfuegung)",
|
||||
# Vertragsleistung / Service-Level (intern)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
"""Heuristic-coverage tests for mc_audit_type.classify_mc_audit_type.
|
||||
|
||||
Hand-kuratierte Beispiele aus DSGVO-/AI-Act-/eCall-Kontext, die nahe an
|
||||
den real-world FAILs aus VW-/BMW-Audits liegen. Wenn ein Pattern hier
|
||||
fehlschlaegt, fehlt _PROCESS_INTERNAL_PATTERNS bzw. _VERIFIABLE_PATTERNS
|
||||
eine entsprechende Regel.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from compliance.services.mc_audit_type import classify_mc_audit_type
|
||||
|
||||
|
||||
VERIFIABLE_CASES = [
|
||||
("Datenschutzerklaerung nennt Kontaktdaten des Verantwortlichen",
|
||||
"Steht im Impressum / DSE der Name + Adresse + Email?"),
|
||||
("Cookie-Banner enthaelt Ablehnen-Button",
|
||||
"Ist im Cookie-Banner ein 'Ablehnen' Button sichtbar?"),
|
||||
("Datenschutzerklaerung verweist auf Recht auf Auskunft",
|
||||
"Wird in der Datenschutzerklaerung Art. 15 DSGVO erwaehnt?"),
|
||||
("Cookie-Richtlinie listet alle Drittanbieter",
|
||||
"Sind alle Vendors in der Cookie-Richtlinie aufgefuehrt?"),
|
||||
("Impressum nennt Telefonnummer",
|
||||
"Steht im Impressum eine Kontakttelefonnummer?"),
|
||||
("Datenschutzerklaerung nennt Speicherdauer",
|
||||
"Sind in der DSE die Aufbewahrungsfristen angegeben?"),
|
||||
("Cookie-Banner Save-Label entspricht UWG-Vorgabe",
|
||||
"Ist der Speichern-Button im Banner korrekt beschriftet?"),
|
||||
]
|
||||
|
||||
PROCESS_INTERNAL_CASES = [
|
||||
("Mitarbeiter-Schulung zu Datenschutz durchgefuehrt",
|
||||
"Wurden alle Mitarbeiter zum Thema Datenschutz geschult?"),
|
||||
("AVV mit allen Auftragsverarbeitern abgeschlossen",
|
||||
"Liegt fuer jeden Dienst ein Auftragsverarbeitungsvertrag vor?"),
|
||||
("TOM dokumentiert und implementiert",
|
||||
"Sind die technisch-organisatorischen Massnahmen umgesetzt?"),
|
||||
("DSFA fuer Hochrisiko-Verarbeitung durchgefuehrt",
|
||||
"Wurde eine DSFA durchgefuehrt und dokumentiert?"),
|
||||
("Rollenkonzept eingerichtet",
|
||||
"Existiert ein Berechtigungskonzept mit klaren Rollen?"),
|
||||
("Pseudonymisierung von Daten implementiert",
|
||||
"Wird Pseudonymisierung aktiv eingesetzt?"),
|
||||
("Backup-Strategie eingerichtet",
|
||||
"Sind Backups vorhanden und werden regelmaessig getestet?"),
|
||||
("Abschaltung der Standortdatenverarbeitung kostenfrei ermoeglichen",
|
||||
"Kann der Nutzer die Standortdatenverarbeitung kostenfrei abschalten?"),
|
||||
("Ausnahmen bei bereits vorhandenen Informationen dokumentieren",
|
||||
"Werden Ausnahmen intern dokumentiert?"),
|
||||
("Sensibilisierung neue Mitarbeiter",
|
||||
"Erhalten neue Mitarbeiter eine Awareness-Schulung?"),
|
||||
("72-Stunden-Meldung an Aufsichtsbehoerde eingehalten",
|
||||
"Wird die Meldepflicht innerhalb von 72 Stunden umgesetzt?"),
|
||||
]
|
||||
|
||||
DOC_INTERNAL_CASES = [
|
||||
("Verzeichnis von Verarbeitungstaetigkeiten gefuehrt",
|
||||
"Existiert ein VVT mit allen Pflichtangaben?"),
|
||||
("Subprozessor-Liste aktualisiert",
|
||||
"Wird die Sub-Prozessor-Liste gepflegt?"),
|
||||
("Auftragsverarbeitungsverzeichnis vollstaendig",
|
||||
"Sind alle AVVs im Auftragsverarbeitungsverzeichnis erfasst?"),
|
||||
("Aufbewahrungskonzept dokumentiert",
|
||||
"Existiert ein dokumentiertes Aufbewahrungskonzept?"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("title,question", VERIFIABLE_CASES)
|
||||
def test_verifiable_cases(title: str, question: str) -> None:
|
||||
"""These MCs MUST be classified as verifiable — they describe things
|
||||
visible from the outside (website / banner / DSE)."""
|
||||
result = classify_mc_audit_type(title, question)
|
||||
assert result == "verifiable", (
|
||||
f"Expected 'verifiable' for {title!r}, got {result!r}"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("title,question", PROCESS_INTERNAL_CASES)
|
||||
def test_process_internal_cases(title: str, question: str) -> None:
|
||||
"""These MCs MUST be classified as process_internal — they describe
|
||||
internal customer processes that we cannot verify from the outside."""
|
||||
result = classify_mc_audit_type(title, question)
|
||||
assert result == "process_internal", (
|
||||
f"Expected 'process_internal' for {title!r}, got {result!r}"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("title,question", DOC_INTERNAL_CASES)
|
||||
def test_doc_internal_cases(title: str, question: str) -> None:
|
||||
"""These MCs MUST be classified as doc_internal — they describe
|
||||
internal documentation (VVT, AVV-Verzeichnis) we cannot inspect."""
|
||||
result = classify_mc_audit_type(title, question)
|
||||
assert result == "doc_internal", (
|
||||
f"Expected 'doc_internal' for {title!r}, got {result!r}"
|
||||
)
|
||||
|
||||
|
||||
def test_empty_input_is_ambiguous() -> None:
|
||||
assert classify_mc_audit_type("", "") == "ambiguous"
|
||||
assert classify_mc_audit_type(None, None) == "ambiguous"
|
||||
Reference in New Issue
Block a user