feat: 4 remaining tasks — EU institutions, banner integration, JS-sites, Caritas fixes
Build + Deploy / build-ai-sdk (push) Failing after 36s
Build + Deploy / build-developer-portal (push) Successful in 8s
Build + Deploy / build-tts (push) Successful in 7s
Build + Deploy / build-document-crawler (push) Successful in 7s
Build + Deploy / build-admin-compliance (push) Successful in 8s
Build + Deploy / build-backend-compliance (push) Successful in 8s
CI / nodejs-build (push) Successful in 3m14s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Failing after 46s
CI / test-python-backend (push) Successful in 43s
CI / test-python-document-crawler (push) Successful in 29s
CI / test-python-dsms-gateway (push) Successful in 30s
CI / validate-canonical-controls (push) Successful in 16s
Build + Deploy / build-dsms-gateway (push) Successful in 8s
Build + Deploy / build-dsms-node (push) Successful in 8s
CI / branch-name (push) Has been skipped
Build + Deploy / trigger-orca (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 17s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped

1. EU Institution Checks (Verordnung 2018/1725):
   - New doc_type "eu_institution" with 9 L1 + 15 L2 checks
   - Both German + English patterns (EU institutions are multilingual)
   - Auto-detection via "2018/1725", "EDSB", "EDPS" keywords
   - Correct article references (Art. 15 instead of 13, Art. 5 instead of 6)

2. Banner Check Integration:
   - banner_runner.py maps scan results to 36 L1/L2 structured checks
   - BannerCheckTab shows hierarchical ChecklistView with hints
   - 3-phase summary (cookies/scripts before/after consent)
   - /scan endpoint now includes structured_checks in response

3. JS-heavy Website Fixes (dm, Zalando, HWK):
   - dsi_helpers.py: goto_resilient (networkidle→domcontentloaded fallback)
   - try_dismiss_consent_banner before text extraction
   - PDF redirect detection (dm.de redirects to GCS PDF)

4. Caritas False Positive Fixes:
   - Phone regex allows parentheses: +49 (0)761 → now matches
   - "Recht auf Widerspruch" (3 words) + §23 KDG → matches Art. 21
   - Church authorities: "Katholisches Datenschutzzentrum" recognized

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-08 01:10:10 +02:00
parent 89af88ef7d
commit 686834cea0
11 changed files with 1039 additions and 171 deletions
@@ -329,6 +329,7 @@ SECTION_TYPE_MAP = [
(r"datenschutzfolge|dsfa|risikoanalyse", "dsfa"),
(r"^social\s*media$|^soziale\s+(?:medien|netzwerke)$", "social_media"),
(r"datenschutzerkl(?:ae|ä)rung.*social|datenschutz\s+f(?:ue|ü)r\s+social", "social_media"),
(r"(?:verordnung|regulation)\s*\(?eu\)?\s*2018\s*/?\s*1725", "eu_institution"),
]
@@ -2,7 +2,7 @@
doc_checks — Legal document compliance checkers.
Provides checklists and functions for verifying legal documents
(DSI, AGB, Impressum, Cookie, Widerruf, Social Media, DSFA)
(DSI, AGB, Impressum, Cookie, Widerruf, Social Media, DSFA, EU Institution)
against their mandatory content requirements.
Two check levels:
@@ -18,6 +18,7 @@ from .impressum_checks import IMPRESSUM_CHECKLIST
from .cookie_checks import COOKIE_CHECKLIST
from .social_media_checks import JOINT_CONTROLLER_CHECKLIST
from .dsfa_checks import DSFA_CHECKLIST
from .eu_institution_checks import EU_INSTITUTION_CHECKLIST
__all__ = [
"check_document_completeness",
@@ -29,4 +30,5 @@ __all__ = [
"COOKIE_CHECKLIST",
"JOINT_CONTROLLER_CHECKLIST",
"DSFA_CHECKLIST",
"EU_INSTITUTION_CHECKLIST",
]
@@ -47,8 +47,9 @@ ART13_CHECKLIST = [
"label": "Telefonnummer des Verantwortlichen",
"level": 2, "parent": "controller",
"patterns": [
r"(?:tel(?:efon)?|phone|fon)\s*[.:]\s*[\+\d][\d\s/\-]{6,}",
r"\+49\s*[\d\s/\-]{8,}",
r"(?:tel(?:efon)?|phone|fon)\s*[.:]\s*[\+\d][\d\s/\-\(\)]{6,}",
r"\+49\s*[\d\s/\-\(\)]{8,}",
r"0\d{2,4}\s*[\(/\-\s]\s*\d{3,}",
],
"severity": "MEDIUM",
"hint": "EuGH (C-298/17, 'Verein fuer Konsumenteninformation') verlangt effektive Kontaktmoeglichkeit. Telefon ist nicht zwingend, aber empfohlen — fehlt sie, muss ein gleichwertiger Kanal (z.B. Chat, Rueckruf) angeboten werden.",
@@ -345,7 +346,7 @@ ART13_CHECKLIST = [
"id": "rights_art21",
"label": "Widerspruchsrecht (Art. 21)",
"level": 2, "parent": "rights",
"patterns": [r"art\.\s*21", r"widerspruchsrecht", r"right\s+to\s+object"],
"patterns": [r"art\.\s*21", r"widerspruchsrecht", r"recht\s+auf\s+widerspruch", r"§\s*23\s+kdg", r"right\s+to\s+object"],
"severity": "LOW",
"hint": "Art. 21(4) DSGVO: Der Widerspruchshinweis muss spaetestens zum Zeitpunkt der ersten Kommunikation GESONDERT und in klarer Sprache erfolgen. Haeufiger Fehler: Widerspruchsrecht nur im Fliesstext versteckt — eigener Abschnitt/Hervorhebung noetig.",
},
@@ -386,6 +387,9 @@ ART13_CHECKLIST = [
r"l(?:an)?fdi\s+\w+",
r"bfdi",
r"(?:bayerische|hessische|s(?:ae|ä)chsische|berliner)\s+(?:datenschutz|aufsicht)",
r"(?:katholisch|evangelisch|kirchlich)\w*\s+datenschutz",
r"datenschutzzentrum",
r"kd(?:oe|ö)r",
],
"severity": "LOW",
"hint": "Vollstaendigen Namen, Adresse und Website der Aufsichtsbehoerde angeben. Haeufiger Fehler: 'die zustaendige Aufsichtsbehoerde' ohne Konkretisierung. Korrekt z.B.: 'LfDI BW, Koenigstrasse 10a, 70173 Stuttgart, www.baden-wuerttemberg.datenschutz.de'.",
@@ -0,0 +1,500 @@
"""
EU Institution checks — Verordnung (EU) 2018/1725.
Applies to EU institutions, bodies, offices and agencies instead of DSGVO.
Key differences: Art. 15 (not 13), Art. 5 (not 6), EDSB (not national DPAs).
L1: Pflichtangabe erwaehnt? L2: Pflichtangabe korrekt/vollstaendig?
"""
EU_INSTITUTION_CHECKLIST = [
# == L1: Verantwortlicher (Controller) =================================
{
"id": "eu_controller",
"label": "Verantwortlicher (Art. 15(1)(a) VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"verantwortlich\w*\s+(?:ist|im sinne|fuer|f(?:ue|ü)r)",
r"kontaktdaten\s+des\s+verantwortlichen",
r"name\s+(?:und|&)\s+kontaktdaten\s+des",
r"controller", r"verantwortliche\s+stelle",
r"responsible\s+(?:party|for)",
r"data\s+controller",
r"identity\s+(?:of\s+)?(?:the\s+)?controller",
],
"severity": "HIGH",
"hint": (
"Art. 15(1)(a) VO 2018/1725 verlangt die Identitaet des Verantwortlichen. "
"Bei EU-Organen: Vollstaendiger Name der Institution (z.B. 'Europaeische Kommission, "
"GD DIGIT'), Dienstadresse und funktionale E-Mail-Adresse. "
"Haeufiger Fehler: Nur Abkuerzung ohne vollstaendigen Institutionsnamen."
),
},
{
"id": "eu_controller_address",
"label": "Dienstadresse des Verantwortlichen",
"level": 2, "parent": "eu_controller",
"patterns": [
r"(?:rue|avenue|boulevard|strasse|stra(?:ss|ß)e)\s+\w+",
r"\d{4,5}\s+(?:bruxelles|brussels|br(?:ue|ü)ssel|luxembourg|luxemburg|strasbourg|stra(?:ss|ß)burg)",
r"b[\-\s]?\d{4}\s+\w+",
r"l[\-\s]?\d{4}\s+\w+",
],
"severity": "MEDIUM",
"hint": (
"Angabe der Dienstadresse der EU-Institution (typisch: Bruessel, Luxemburg "
"oder Strassburg). Format z.B. 'Rue de la Loi 200, B-1049 Bruxelles'. "
"Haeufiger Fehler: Nur Postfach ohne physische Adresse."
),
},
{
"id": "eu_controller_email",
"label": "E-Mail-Adresse des Verantwortlichen",
"level": 2, "parent": "eu_controller",
"patterns": [
r"[a-z0-9._%+\-]+@[a-z0-9.\-]+\.europa\.eu",
r"[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}",
],
"severity": "MEDIUM",
"hint": (
"Eine funktionale E-Mail-Adresse ist Pflicht (Art. 15(1)(a) VO 2018/1725). "
"Bei EU-Organen typischerweise @ec.europa.eu, @europarl.europa.eu o.ae. "
"Ein reines Kontaktformular genuegt nicht als unmittelbarer Kommunikationskanal."
),
},
# == L1: Datenschutzbeauftragter (DPO) =================================
{
"id": "eu_dpo",
"label": "Datenschutzbeauftragter (Art. 15(1)(b) / Art. 43 VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"datenschutzbeauftragt",
r"data\s+protection\s+officer",
r"kontaktdaten\s+de[rs]\s+datenschutz",
r"dpo",
r"d(?:ae|ä)legu(?:e|é)\s+(?:a|à)\s+la\s+protection\s+des\s+donn(?:e|é)es",
],
"severity": "HIGH",
"hint": (
"Art. 43-44 VO 2018/1725: Jedes EU-Organ MUSS einen DSB (DPO) benennen. "
"Dies ist — anders als unter der DSGVO — keine Frage der Mitarbeiterzahl, "
"sondern absolute Pflicht fuer alle EU-Organe. Die Kontaktdaten muessen in "
"jeder Datenschutzerklaerung angegeben werden (Art. 15(1)(b))."
),
},
{
"id": "eu_dpo_contact",
"label": "DPO-Kontaktdaten (E-Mail oder Adresse)",
"level": 2, "parent": "eu_dpo",
"patterns": [
r"(?:data\s+protection\s+officer|dpo|datenschutzbeauftragt)[\s\S]{0,300}[a-z0-9._%+\-]+@",
r"dpo[\s\S]{0,100}@",
r"data[\-\.]?protection@",
r"dpo@\w+\.europa\.eu",
],
"severity": "MEDIUM",
"hint": (
"Art. 44(7) VO 2018/1725: Die Kontaktdaten des DPO muessen veroeffentlicht werden. "
"Mindestens eine funktionale E-Mail-Adresse angeben (z.B. DATA-PROTECTION-OFFICER@ec.europa.eu). "
"Den Namen des DPO muessen Sie nicht nennen."
),
},
{
"id": "eu_dpo_function",
"label": "DPO-Funktion / -Rolle beschrieben",
"level": 2, "parent": "eu_dpo",
"patterns": [
r"(?:aufgaben|role|function|zustaendig).*(?:dpo|datenschutzbeauftragt|data\s+protection\s+officer)",
r"(?:dpo|datenschutzbeauftragt|data\s+protection\s+officer).*(?:aufgaben|role|function|zustaendig)",
r"art(?:icle)?\s*44\s+(?:vo|regulation|verordnung)",
],
"severity": "LOW",
"hint": (
"Art. 44 VO 2018/1725 beschreibt die Aufgaben des DPO bei EU-Organen: "
"Beratung, Ueberwachung, Zusammenarbeit mit dem EDSB. "
"Es empfiehlt sich, kurz die Rolle des DPO zu erlaeutern, damit "
"Betroffene wissen, wofuer der DPO zustaendig ist."
),
},
# == L1: Zwecke und Rechtsgrundlage ====================================
{
"id": "eu_purposes",
"label": "Zwecke der Verarbeitung (Art. 15(1)(c) VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"zweck\w*\s+(?:der|und|die)\s+(?:verarbeitung|datenerhebung|datenverarbeitung)",
r"purpose\w*\s+(?:of|for)\s+(?:the\s+)?(?:processing|data)",
r"zu\s+welch\w+\s+zweck",
r"(?:data|personal\s+data)\s+(?:is|are)\s+(?:collected|processed)\s+(?:for|to)",
],
"severity": "HIGH",
"hint": (
"Art. 15(1)(c) VO 2018/1725 verlangt konkrete Zweckangaben. "
"EU-Organe muessen jeden Verarbeitungszweck einzeln auffuehren: z.B. "
"'Verwaltung von Bewerbungen', 'Zugangsmanagement zum Gebaeude', "
"'Webanalyse der Internetseite'. Pauschalformulierungen sind unzulaessig."
),
},
{
"id": "eu_purposes_specific",
"label": "Konkrete Verarbeitungszwecke benannt",
"level": 2, "parent": "eu_purposes",
"patterns": [
r"(?:recruitment|selection|verwaltung|management|administration|monitoring|evaluation)",
r"(?:human\s+resources|hr|personal|bewerbung|grant|procurement|vergabe)",
r"(?:access|zugang|building|gebaeude|website|webseite|intranet)",
],
"severity": "LOW",
"hint": (
"Mindestens 2 konkrete Zwecke benennen, jeweils mit zugehoeriger "
"Rechtsgrundlage. Typische EU-Organ-Zwecke: Personalverwaltung, "
"Gebaeudezugang, IT-Sicherheitsmonitoring, Vergabeverfahren, "
"Evaluierung von Foerderprogrammen. Pauschalformulierungen genuegen "
"nicht dem Bestimmtheitsgrundsatz."
),
},
# == L1: Rechtsgrundlage (Art. 5 statt Art. 6 DSGVO) ==================
{
"id": "eu_legal_basis",
"label": "Rechtsgrundlage (Art. 5 VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"rechtsgrundlage",
r"art\.\s*5\s*(?:abs|absatz)?\s*\.?\s*1",
r"legal\s+basis",
r"lawfulness\s+of\s+processing",
r"art(?:icle)?\s*5\s*(?:\(1\))?\s*(?:\([a-d]\))?",
r"auf\s+grundlage\s+(?:von|des|der)\s+art",
r"regulation\s*\(eu\)\s*2018\s*/?\s*1725",
r"verordnung\s*\(eu\)\s*2018\s*/?\s*1725",
],
"severity": "HIGH",
"hint": (
"Art. 5(1) VO 2018/1725 enthaelt die Rechtsgrundlagen fuer EU-Organe: "
"(a) Einwilligung, (b) Vertrag, (c) rechtliche Verpflichtung, "
"(d) im oeffentlichen Interesse/Ausuebung oeffentlicher Gewalt. "
"WICHTIG: Art. 5(1)(d) ist der haeufigste Tatbestand bei EU-Organen — "
"er entspricht etwa Art. 6(1)(e) DSGVO. Art. 6(1)(f) DSGVO "
"(berechtigtes Interesse) existiert in der VO 2018/1725 NICHT."
),
},
{
"id": "eu_legal_basis_public_interest",
"label": "Art. 5(1)(a) — Oeffentliches Interesse / oeffentliche Gewalt",
"level": 2, "parent": "eu_legal_basis",
"patterns": [
r"art\.\s*5\s*(?:\(1\))?\s*\(?(?:1\s*)?(?:let(?:ter)?\.?\s*)?a\)?",
r"(?:oeffentlich|öffentlich).*(?:interesse|gewalt|aufgabe)",
r"public\s+interest",
r"(?:exercise|performance)\s+of\s+(?:official|public)\s+(?:authority|task)",
],
"severity": "LOW",
"hint": (
"Art. 5(1)(a) VO 2018/1725 ist die Hauptrechtsgrundlage fuer EU-Organe. "
"Verlangt einen konkreten Rechtsakt als Grundlage (z.B. Verordnung, "
"Beschluss, Basisrechtsakt der Institution). Benennen Sie den spezifischen "
"Rechtsakt, nicht nur pauschal 'oeffentliches Interesse'."
),
},
{
"id": "eu_legal_basis_consent",
"label": "Art. 5(1)(d) — Einwilligung",
"level": 2, "parent": "eu_legal_basis",
"patterns": [
r"art\.\s*5\s*(?:\(1\))?\s*\(?(?:1\s*)?(?:let(?:ter)?\.?\s*)?d\)?",
r"einwilligung\s+(?:gem|nach|i\.?\s*s\.?\s*d\.?)",
r"consent\s+(?:of|given\s+by)\s+the\s+data\s+subject",
],
"severity": "LOW",
"hint": (
"Bei Einwilligung (Art. 5(1)(d) VO 2018/1725) muss auf das jederzeitige "
"Widerrufsrecht hingewiesen werden (Art. 7(3) VO 2018/1725). "
"Achtung: EU-Organe sollten Einwilligung nur als Rechtsgrundlage waehlen, "
"wenn keine andere Grundlage greift — wegen des Machtungleichgewichts "
"zwischen Institution und Einzelperson (EDSB-Leitlinien)."
),
},
# == L1: Empfaenger ====================================================
{
"id": "eu_recipients",
"label": "Empfaenger (Art. 15(1)(e) VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"empf(?:ae|ä)nger",
r"(?:ueber|über|weiter)mitt(?:el|l)ung",
r"recipient",
r"weitergabe\s+(?:an|von)\s+daten",
r"data\s+(?:will\s+be|are|is)\s+(?:shared|disclosed|transferred|transmitted)\s+to",
r"auftragsverarbeit",
r"processor",
],
"severity": "MEDIUM",
"hint": (
"Art. 15(1)(e) VO 2018/1725: Empfaenger oder Empfaengerkategorien benennen. "
"Typisch bei EU-Organen: andere EU-Institutionen (z.B. OLAF, Rechnungshof), "
"Mitgliedstaaten-Behoerden, IT-Dienstleister. Auftragsverarbeiter muessen "
"nach Art. 29 VO 2018/1725 vertraglich gebunden sein."
),
},
{
"id": "eu_recipients_processor",
"label": "Auftragsverarbeiter / Processor (Art. 29 VO 2018/1725)",
"level": 2, "parent": "eu_recipients",
"patterns": [
r"auftragsverarbeit(?:er|ung)",
r"art\.\s*29\s+(?:vo|verordnung|regulation)",
r"art(?:icle)?\s*29",
r"processor",
r"sub[\-\s]?processor",
],
"severity": "LOW",
"hint": (
"Art. 29 VO 2018/1725 (entspricht Art. 28 DSGVO): "
"Auftragsverarbeiter muessen vertraglich gebunden werden. "
"Erwaehnen Sie, dass ein Auftragsverarbeitungsvertrag besteht. "
"Bei Cloud-Diensten (z.B. Microsoft 365, AWS): Vertrag muss "
"die Vorgaben von Art. 29(3) VO 2018/1725 einhalten."
),
},
# == L1: Drittlandtransfer =============================================
{
"id": "eu_third_country",
"label": "Drittlandtransfer (Art. 46-50 VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"drittland",
r"dritt\s*staat",
r"third\s+countr",
r"angemessenheitsbeschluss",
r"adequacy\s+decision",
r"standard\s*(?:vertragsklausel|contractual\s+clause)",
r"(?:transfer|uebermittlung|übermittlung).*(?:ausserhalb|außerhalb|outside)",
r"(?:europ(?:ae|ä)ischen\s+wirtschaftsraum|ewr|eea)",
r"art(?:icle)?\s*4[6-9]",
r"art\.\s*50",
],
"severity": "MEDIUM",
"hint": (
"Art. 46-50 VO 2018/1725 (entspricht Art. 44-49 DSGVO): "
"Drittlandtransfers erfordern Angemessenheitsbeschluss (Art. 47), "
"geeignete Garantien (Art. 48) oder Ausnahmen (Art. 50). "
"EDSB-Empfehlung: EU-Organe muessen besonders streng pruefen, "
"da sie eine Vorbildfunktion fuer die Mitgliedstaaten haben."
),
},
{
"id": "eu_third_country_mechanism",
"label": "Transfermechanismus benannt (Art. 47-48 VO 2018/1725)",
"level": 2, "parent": "eu_third_country",
"patterns": [
r"standard\s*vertragsklausel|scc|standard\s+contractual",
r"angemessenheitsbeschluss|adequacy\s+decision",
r"art(?:icle)?\s*4[7-8]",
r"data\s+privacy\s+framework|dpf",
r"appropriate\s+safeguards",
r"geeignete\s+garantien",
],
"severity": "MEDIUM",
"hint": (
"Art. 48 VO 2018/1725: Bei fehlender Angemessenheit koennen "
"geeignete Garantien (z.B. SCC, verbindliche Verwaltungsvereinbarungen) "
"den Transfer absichern. Der EDSB hat 2020 eigene Leitlinien zu "
"Drittlandtransfers fuer EU-Organe veroeffentlicht."
),
},
# == L1: Speicherdauer =================================================
{
"id": "eu_retention",
"label": "Speicherdauer (Art. 15(1)(g) VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"speicherdauer",
r"aufbewahrungsfrist",
r"retention\s+period",
r"(?:how\s+long|storage\s+period|data\s+retention)",
r"l(?:oe|ö)sch(?:ung|frist)",
r"daten\s+werden\s+gel(?:oe|ö)scht",
r"(?:\d+\s+(?:tage|monate|jahre|days|months|years))",
r"dauer\s+der\s+speicherung",
r"data\s+will\s+be\s+(?:kept|stored|retained)\s+(?:for|until|during)",
],
"severity": "HIGH",
"hint": (
"Art. 15(1)(g) VO 2018/1725 verlangt die Speicherdauer oder "
"Kriterien zu deren Festlegung. EU-Organe haben oft interne "
"Aufbewahrungsrichtlinien (retention schedules). Nennen Sie die "
"konkreten Fristen oder verweisen Sie auf die interne Richtlinie "
"mit Dokumentenreferenz."
),
},
{
"id": "eu_retention_periods",
"label": "Konkrete Zeitangaben",
"level": 2, "parent": "eu_retention",
"patterns": [
r"\d+\s+(?:tage?|monate?|jahre?|days?|months?|years?)",
r"(?:after|nach)\s+(?:the\s+)?(?:end|closure|completion|ablauf|beendigung)",
r"retention\s+(?:schedule|policy|period)\s+(?:of|for)\s+\d+",
],
"severity": "MEDIUM",
"hint": (
"Konkrete Fristen pro Datenkategorie nennen. EU-Organe folgen "
"typischerweise der Common Retention List (CRL) der Kommission. "
"Beispiel: Bewerbungsdaten 2 Jahre, Finanzunterlagen 7 Jahre, "
"Gebaeudezugangslogs 6 Monate."
),
},
# == L1: Betroffenenrechte (Art. 17-24 statt Art. 15-22 DSGVO) =========
{
"id": "eu_rights",
"label": "Betroffenenrechte (Art. 17-24 VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"recht\s+auf\s+auskunft",
r"recht\s+auf\s+l(?:oe|ö)schung",
r"recht\s+auf\s+berichtigung",
r"widerspruchsrecht",
r"right\s+to\s+(?:access|erasure|rectification|object|restrict)",
r"betroffenenrecht",
r"rechte\s+(?:des|der)\s+betroffenen",
r"(?:your|data\s+subject)\s+rights",
r"art(?:icle)?\s*(?:17|18|19|20|21|22|23|24)\s+(?:vo|regulation|verordnung)",
],
"severity": "HIGH",
"hint": (
"Art. 15(1)(h) VO 2018/1725 verlangt Nennung der Betroffenenrechte: "
"Auskunft (Art. 17), Berichtigung (Art. 18), Loeschung (Art. 19), "
"Einschraenkung (Art. 20), Datenportabilitaet (Art. 22), "
"Widerspruch (Art. 23). Achtung: Die Artikelnummern unterscheiden sich "
"von der DSGVO (Art. 15-22)! Haeufiger Fehler: DSGVO-Artikel "
"statt VO 2018/1725 Artikel zitieren."
),
},
{
"id": "eu_rights_access",
"label": "Recht auf Auskunft (Art. 17 VO 2018/1725)",
"level": 2, "parent": "eu_rights",
"patterns": [
r"art(?:icle)?\s*17\s+(?:vo|regulation|verordnung)",
r"art\.\s*17",
r"recht\s+auf\s+(?:\w+\s+)?auskunft",
r"right\s+(?:of|to)\s+access",
],
"severity": "LOW",
"hint": (
"Art. 17 VO 2018/1725 (entspricht Art. 15 DSGVO): Betroffene koennen "
"Auskunft und eine Kopie ihrer Daten verlangen. Antwortfrist: 1 Monat "
"(Art. 14(3) VO 2018/1725). Anfragen gehen typischerweise an den DPO "
"der Institution."
),
},
{
"id": "eu_rights_erasure",
"label": "Recht auf Loeschung (Art. 19 VO 2018/1725)",
"level": 2, "parent": "eu_rights",
"patterns": [
r"art(?:icle)?\s*19\s+(?:vo|regulation|verordnung)",
r"art\.\s*19",
r"recht\s+auf\s+(?:\w+\s+)?l(?:oe|ö)schung",
r"right\s+to\s+erasure",
r"right\s+to\s+be\s+forgotten",
],
"severity": "LOW",
"hint": (
"Art. 19 VO 2018/1725 (entspricht Art. 17 DSGVO): "
"Recht auf Loeschung bei Zweckentfall, Widerruf der Einwilligung "
"oder unrechtmaessiger Verarbeitung. Erwaehnen Sie auch die "
"Ausnahmen fuer EU-Organe: Archivzwecke im oeffentlichen Interesse, "
"gesetzliche Aufbewahrungspflichten."
),
},
{
"id": "eu_rights_restriction",
"label": "Recht auf Einschraenkung (Art. 20 VO 2018/1725)",
"level": 2, "parent": "eu_rights",
"patterns": [
r"art(?:icle)?\s*20\s+(?:vo|regulation|verordnung)",
r"art\.\s*20",
r"einschr(?:ae|ä)nkung\s+der\s+verarbeitung",
r"right\s+to\s+restrict(?:ion)?",
],
"severity": "LOW",
"hint": (
"Art. 20 VO 2018/1725 (entspricht Art. 18 DSGVO): "
"Recht auf Einschraenkung der Verarbeitung bei bestrittener "
"Richtigkeit, unrechtmaessiger Verarbeitung oder laufendem "
"Widerspruch. Wird am haeufigsten vergessen."
),
},
{
"id": "eu_rights_automated",
"label": "Automatisierte Entscheidungen (Art. 24 VO 2018/1725)",
"level": 2, "parent": "eu_rights",
"patterns": [
r"art(?:icle)?\s*24\s+(?:vo|regulation|verordnung)",
r"art\.\s*24",
r"automatisierte\s+entscheidung",
r"automated\s+(?:decision|individual)",
r"profiling",
],
"severity": "LOW",
"hint": (
"Art. 24 VO 2018/1725 (entspricht Art. 22 DSGVO): "
"Bei automatisierten Einzelentscheidungen muessen Logik, "
"Tragweite und Auswirkungen erklaert werden. Falls kein "
"Profiling stattfindet, explizit verneinen."
),
},
# == L1: Beschwerderecht beim EDSB =====================================
{
"id": "eu_complaint",
"label": "Beschwerderecht beim EDSB (Art. 15(1)(i) VO 2018/1725)",
"level": 1, "parent": None,
"patterns": [
r"beschwerderecht",
r"right\s+to\s+lodge\s+a\s+complaint",
r"beschwerde.*(?:edsb|edps)",
r"edsb",
r"edps",
r"europ(?:ae|ä)isch\w*\s+datenschutzbeauftragt",
r"european\s+data\s+protection\s+supervisor",
r"contr(?:o|ô)leur\s+europ(?:e|é)en",
r"art(?:icle)?\s*63",
],
"severity": "HIGH",
"hint": (
"Art. 15(1)(i) VO 2018/1725: Bei EU-Organen ist der EDSB "
"(Europaeischer Datenschutzbeauftragter / European Data Protection "
"Supervisor) die zustaendige Aufsichtsbehoerde — NICHT die nationalen "
"Datenschutzbehoerden. Kontakt: edps@edps.europa.eu, "
"Rue Wiertz 60, B-1047 Bruxelles. Haeufiger Fehler: Verweis auf "
"nationale Aufsichtsbehoerde statt EDSB."
),
},
{
"id": "eu_complaint_edps_contact",
"label": "EDSB-Kontaktdaten angegeben",
"level": 2, "parent": "eu_complaint",
"patterns": [
r"edps@edps\.europa\.eu",
r"edps\.europa\.eu",
r"edsb.*(?:kontakt|anschrift|adresse|e[\-\s]?mail|wiertz)",
r"edps.*(?:contact|address|e[\-\s]?mail|wiertz)",
r"rue\s+wiertz",
],
"severity": "MEDIUM",
"hint": (
"Vollstaendige EDSB-Kontaktdaten angeben: "
"Europaeischer Datenschutzbeauftragter (EDSB), "
"Rue Wiertz 60, B-1047 Bruxelles/Bruessel, "
"edps@edps.europa.eu, https://edps.europa.eu. "
"Haeufiger Fehler: Nur 'EDSB' erwaehnt ohne Kontaktdaten."
),
},
]
@@ -15,6 +15,7 @@ from .impressum_checks import IMPRESSUM_CHECKLIST
from .cookie_checks import COOKIE_CHECKLIST
from .social_media_checks import JOINT_CONTROLLER_CHECKLIST
from .dsfa_checks import DSFA_CHECKLIST
from .eu_institution_checks import EU_INSTITUTION_CHECKLIST
logger = logging.getLogger(__name__)
@@ -35,6 +36,7 @@ _CHECKLIST_MAP = {
"social_media": (JOINT_CONTROLLER_CHECKLIST, "Art. 26 DSGVO"),
"joint_controller": (JOINT_CONTROLLER_CHECKLIST, "Art. 26 DSGVO"),
"dsfa": (DSFA_CHECKLIST, "Art. 35 DSGVO"),
"eu_institution": (EU_INSTITUTION_CHECKLIST, "VO (EU) 2018/1725"),
}
@@ -218,6 +220,11 @@ def classify_document_type(title: str, url: str) -> str:
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"
# EU institution check BEFORE generic privacy — 2018/1725 is more specific
if any(kw in combined for kw in ["2018/1725", "2018 1725", "regulation (eu)",
"verordnung (eu)", "edsb", "edps",
"european data protection supervisor"]):
return "eu_institution"
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"]):