feat: Art. 26 Joint Controller + DSFA checklists for Social Media sections

New checklists:
- JOINT_CONTROLLER_CHECKLIST (Art. 26 DSGVO, 7 checks):
  Joint parties, arrangement, contact point, processing split,
  data categories, third-country transfer (USA), rights
- DSFA_CHECKLIST (Art. 35 DSGVO, 5 checks):
  Description, necessity, risk assessment, measures, DSB involvement

Section detection: 'Datenschutzerklaerung fuer Social Media' → social_media,
'Datenschutzfolgeabschaetzung/Risikoanalyse' → dsfa

classify_document_type: DSFA and social_media detected before generic DSE

Frontend: DOC_TYPES dropdown + ChecklistView labels updated

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-07 10:49:32 +02:00
parent 5188411828
commit 3853a0838a
4 changed files with 68 additions and 3 deletions
@@ -263,9 +263,8 @@ SECTION_TYPE_MAP = [
(r"widerrufsrecht|widerrufsbelehrung", "widerruf"), # Widerruf → §355 BGB
(r"^impressum$", "impressum"), # Impressum → §5 TMG
(r"^(?:agb|allgemeine geschäftsbedingungen|nutzungsbedingungen)$", "agb"),
# NOTE: Social Media, DSFA, Datensicherheit, Betroffenenrechte are NOT
# separate documents — they are sections within the parent DSI.
# DSFA needs its own checklist (RAG-based) — Phase 2.
(r"datenschutzerkl.*social|datenschutz.*social\s*media", "social_media"),
(r"datenschutzfolge|dsfa|risikoanalyse.*social", "dsfa"),
]
@@ -196,6 +196,58 @@ COOKIE_CHECKLIST = [
"patterns": [r"(?:widerspruch|opt.?out|ablehnen|deaktivieren).*cookie", r"cookie.*(?:ablehnen|deaktivieren|loeschen)"]},
]
# Art. 26 DSGVO Joint Controller (Social Media DSE)
JOINT_CONTROLLER_CHECKLIST = [
{"id": "joint_parties", "label": "Gemeinsam Verantwortliche benannt (Art. 26(1))",
"patterns": [r"gemeinsam.*verantwortlich", r"joint.*controller", r"gemeinsame\s+verantwortlichkeit",
r"art\.\s*26", r"mitverantwortlich"]},
{"id": "arrangement", "label": "Vereinbarung nach Art. 26 DSGVO",
"patterns": [r"vereinbarung.*art\.\s*26", r"art\.\s*26.*vereinbarung",
r"page\s*controller", r"fanpage", r"insights",
r"gemeinsame.*verantwortung.*(?:vertrag|vereinbarung)"]},
{"id": "contact_point", "label": "Anlaufstelle fuer Betroffene (Art. 26(1) S.3)",
"patterns": [r"anlaufstelle", r"kontaktstelle", r"ansprechpartner.*betroffene",
r"rechte.*(?:gegenueber|gegenüber)\s+(?:uns|beiden)",
r"rechte.*(?:sowohl|grundsaetzlich|grundsätzlich).*(?:uns|als auch)"]},
{"id": "processing_split", "label": "Verarbeitungsaufteilung (wer macht was)",
"patterns": [r"(?:wir|betreiber).*(?:verarbeiten|erheben|nutzen).*(?:daten|informationen)",
r"(?:facebook|meta|google|youtube|instagram|linkedin|twitter|x\.com).*(?:verarbeit|erhebt|nutzt|speichert)",
r"bei\s+besuch\s+(?:unserer|der)\s+(?:seite|fanpage|profil)"]},
{"id": "social_data_types", "label": "Kategorien verarbeiteter Daten",
"patterns": [r"(?:nutzungsstatistik|insight|reichweite|interaktion|klick|aufruf)",
r"(?:ip.?adresse|standort|browser|geraet|alter|geschlecht).*(?:social|profil|seite|fanpage)",
r"(?:personenbezogen|daten).*(?:social|netzwerk|plattform)"]},
{"id": "third_country", "label": "Drittlandtransfer (USA bei Social Media)",
"patterns": [r"(?:usa|vereinigte\s+staaten|drittland|drittstaaten).*(?:social|facebook|meta|google)",
r"(?:social|facebook|meta|google).*(?:usa|drittland|drittstaaten)",
r"privacy\s+shield|data\s+privacy\s+framework|angemessenheitsbeschluss",
r"standardvertragsklausel|standard.*contractual"]},
{"id": "rights", "label": "Betroffenenrechte (Art. 15-21)",
"patterns": [r"recht\s+auf\s+auskunft", r"recht\s+auf\s+l(?:oe|ö)schung",
r"art\.\s*1[5-9]", r"betroffenenrecht",
r"ihre\s+rechte", r"rechte.*betroffene"]},
]
# DSFA minimal checklist (Art. 35 DSGVO) — basic structure checks
DSFA_CHECKLIST = [
{"id": "description", "label": "Beschreibung der Verarbeitungsvorgaenge (Art. 35(7)(a))",
"patterns": [r"beschreibung.*verarbeitung", r"verarbeitungsvorg(?:ae|ä)ng",
r"systematische\s+beschreibung", r"gegenstand.*verarbeitung"]},
{"id": "necessity", "label": "Notwendigkeit und Verhaeltnismaessigkeit (Art. 35(7)(b))",
"patterns": [r"notwendigkeit", r"verh(?:ae|ä)ltnism(?:ae|ä)ssigkeit",
r"erforderlichkeit", r"zweckbindung"]},
{"id": "risks", "label": "Risikobewertung fuer Betroffene (Art. 35(7)(c))",
"patterns": [r"risiko.*(?:bewertung|analyse|einsch(?:ae|ä)tzung)",
r"risiken.*(?:rechte|freiheit)", r"eintrittswahrscheinlichkeit",
r"schwere.*(?:risiko|auswirkung)"]},
{"id": "measures", "label": "Abhilfemassnahmen (Art. 35(7)(d))",
"patterns": [r"abhilfe", r"massnahmen.*risiko", r"schutzma(?:ss|ß)nahm",
r"(?:technisch|organisatorisch).*massnahm", r"tom"]},
{"id": "stakeholders", "label": "Einbeziehung des DSB (Art. 35(2))",
"patterns": [r"datenschutzbeauftragt.*(?:einbez|konsult|beteilig|rat)",
r"dsb.*(?:konsult|einbez|rat)", r"stellungnahme.*dsb"]},
]
def check_document_completeness(
text: str,
@@ -255,6 +307,12 @@ def check_document_completeness(
elif doc_type in ("cookie",):
checklist = COOKIE_CHECKLIST
label = "§25 TDDDG"
elif doc_type in ("social_media", "joint_controller"):
checklist = JOINT_CONTROLLER_CHECKLIST
label = "Art. 26 DSGVO"
elif doc_type in ("dsfa",):
checklist = DSFA_CHECKLIST
label = "Art. 35 DSGVO"
else:
checklist = ART13_CHECKLIST # Default: check as DSE
label = "Art. 13 DSGVO"
@@ -323,6 +381,11 @@ def classify_document_type(title: str, url: str) -> str:
"""Classify a document by its title/URL into a legal document type."""
combined = f"{title} {url}".lower()
if any(kw in combined for kw in ["datenschutzfolge", "dsfa", "risikoanalyse für nutzung"]):
return "dsfa"
if any(kw in combined for kw in ["social media", "facebook", "instagram", "linkedin", "fanpage"]):
if any(kw in combined for kw in ["datenschutzerkl", "datenschutz für", "datenschutzinformation"]):
return "social_media"
if any(kw in combined for kw in ["datenschutz", "privacy", "dsgvo", "data protection", "données"]):
return "dse"
if any(kw in combined for kw in ["widerruf", "withdrawal", "rétractation", "desistimiento"]):