feat: Add actionable hints to all 138 compliance checks
Build + Deploy / build-admin-compliance (push) Successful in 1m40s
Build + Deploy / build-backend-compliance (push) Successful in 7s
Build + Deploy / build-ai-sdk (push) Successful in 35s
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 8s
Build + Deploy / build-dsms-gateway (push) Successful in 7s
Build + Deploy / build-dsms-node (push) Successful in 8s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 16s
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
CI / nodejs-build (push) Successful in 2m50s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Failing after 40s
CI / test-python-backend (push) Successful in 37s
CI / test-python-document-crawler (push) Successful in 25s
CI / test-python-dsms-gateway (push) Successful in 23s
CI / validate-canonical-controls (push) Successful in 15s
Build + Deploy / trigger-orca (push) Successful in 2m28s

Each check now has a "hint" field explaining what is missing and
what the customer should do to fix it. Hints are shown in the
frontend below failed checks in red text.

Examples:
- "Bei Verarbeitung auf Basis von Art. 6(1)(f) muss dokumentiert
  werden, warum Ihr berechtigtes Interesse die Rechte der
  Betroffenen ueberwiegt."
- "Die ladungsfaehige Anschrift fehlt. Erforderlich: Strasse,
  Hausnummer, PLZ und Ort."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-07 14:05:01 +02:00
parent 912684644e
commit 293c58d0dd
10 changed files with 151 additions and 0 deletions
@@ -11,6 +11,7 @@ interface CheckItem {
level?: number level?: number
parent?: string | null parent?: string | null
skipped?: boolean skipped?: boolean
hint?: string
} }
interface DocResult { interface DocResult {
@@ -177,6 +178,11 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
&quot;...{g.check.matched_text}...&quot; &quot;...{g.check.matched_text}...&quot;
</div> </div>
)} )}
{!g.check.passed && g.check.hint && (
<div className="text-xs text-red-600/80 mt-0.5">
{g.check.hint}
</div>
)}
</div> </div>
</div> </div>
@@ -199,6 +205,11 @@ export function ChecklistView({ results }: { results: DocResult[] }) {
&quot;...{ch.matched_text}...&quot; &quot;...{ch.matched_text}...&quot;
</div> </div>
)} )}
{!ch.passed && !ch.skipped && ch.hint && (
<div className="text-xs text-red-500/80 mt-0.5">
{ch.hint}
</div>
)}
</div> </div>
</div> </div>
))} ))}
@@ -51,6 +51,7 @@ class CheckItem(BaseModel):
level: int = 1 level: int = 1
parent: str | None = None parent: str | None = None
skipped: bool = False skipped: bool = False
hint: str = ""
class DocCheckResult(BaseModel): class DocCheckResult(BaseModel):
@@ -247,6 +248,7 @@ def _run_checklist(text: str, doc_type: str, label: str, url: str, word_count: i
level=c.get("level", 1), level=c.get("level", 1),
parent=c.get("parent"), parent=c.get("parent"),
skipped=c.get("skipped", False), skipped=c.get("skipped", False),
hint=c.get("hint", ""),
)) ))
completeness = f.get("completeness_pct", 0) completeness = f.get("completeness_pct", 0)
correctness = f.get("correctness_pct", 0) correctness = f.get("correctness_pct", 0)
@@ -16,6 +16,7 @@ AGB_CHECKLIST = [
r"diese\s+(?:agb|bedingungen)\s+gelten", r"diese\s+(?:agb|bedingungen)\s+gelten",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Ihre AGB enthalten keinen Geltungsbereich. Ergaenzen Sie einen Abschnitt, der festlegt, fuer welche Vertraege und Parteien die AGB gelten.",
}, },
{ {
"id": "incorporation_clause", "id": "incorporation_clause",
@@ -27,6 +28,7 @@ AGB_CHECKLIST = [
r"§\s*305", r"§\s*305",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Es fehlt eine Einbeziehungsklausel gemaess §305 BGB. Stellen Sie klar, dass der Kunde vor Vertragsschluss auf die AGB hingewiesen wird und ihnen zustimmt.",
}, },
# ── L1: Vertragsschluss ─────────────────────────────────────────── # ── L1: Vertragsschluss ───────────────────────────────────────────
@@ -39,6 +41,7 @@ AGB_CHECKLIST = [
r"contract\s+formation", r"angebot\s+und\s+annahme", r"contract\s+formation", r"angebot\s+und\s+annahme",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die AGB beschreiben nicht, wie ein Vertrag zustande kommt. Ergaenzen Sie eine Regelung zu Angebot und Annahme (z.B. Bestellung als Angebot, Bestaetigung als Annahme).",
}, },
# ── L1: Haftung ─────────────────────────────────────────────────── # ── L1: Haftung ───────────────────────────────────────────────────
@@ -51,6 +54,7 @@ AGB_CHECKLIST = [
r"schadensersatz", r"haftungsbeschr(?:ae|ä)nkung", r"schadensersatz", r"haftungsbeschr(?:ae|ä)nkung",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Es fehlt eine Haftungsregelung. Nehmen Sie einen Abschnitt auf, der die Haftung regelt und dabei die gesetzlichen Mindestrechte (Vorsatz, grobe Fahrlaessigkeit, Personenschaeden) unberuehrt laesst.",
}, },
# ── L1: Gerichtsstand / Anwendbares Recht ───────────────────────── # ── L1: Gerichtsstand / Anwendbares Recht ─────────────────────────
@@ -63,6 +67,7 @@ AGB_CHECKLIST = [
r"jurisdiction", r"governing\s+law", r"jurisdiction", r"governing\s+law",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die AGB enthalten keine Angabe zum anwendbaren Recht oder Gerichtsstand. Benennen Sie das geltende Recht (z.B. deutsches Recht) und ggf. den Gerichtsstand.",
}, },
{ {
"id": "dispute_odr_link", "id": "dispute_odr_link",
@@ -74,6 +79,7 @@ AGB_CHECKLIST = [
r"(?:online[\-\s]?streitbeilegung|online\s+dispute\s+resolution)", r"(?:online[\-\s]?streitbeilegung|online\s+dispute\s+resolution)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Der Link zur EU-Online-Streitbeilegungsplattform (https://ec.europa.eu/consumers/odr) fehlt. Dieser ist fuer Online-Haendler nach EU-Verordnung 524/2013 Pflicht.",
}, },
{ {
"id": "choice_of_law_specific", "id": "choice_of_law_specific",
@@ -86,6 +92,7 @@ AGB_CHECKLIST = [
r"un[\-\s]kaufrecht.*(?:ausgeschlossen|findet\s+keine\s+anwendung)", r"un[\-\s]kaufrecht.*(?:ausgeschlossen|findet\s+keine\s+anwendung)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es wird kein konkretes anwendbares Recht benannt. Geben Sie explizit an, welches Recht gilt (z.B. 'Es gilt das Recht der Bundesrepublik Deutschland') und ob das UN-Kaufrecht ausgeschlossen ist.",
}, },
# ── L1: Zahlungsbedingungen ─────────────────────────────────────── # ── L1: Zahlungsbedingungen ───────────────────────────────────────
@@ -99,6 +106,7 @@ AGB_CHECKLIST = [
r"zahlungsweise", r"rechnungsstellung", r"zahlungsweise", r"rechnungsstellung",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die AGB enthalten keine Zahlungsbedingungen. Ergaenzen Sie Angaben zu Preisen, Zahlungsarten und Faelligkeit.",
}, },
{ {
"id": "payment_methods", "id": "payment_methods",
@@ -108,6 +116,7 @@ AGB_CHECKLIST = [
r"(?:vorkasse|nachnahme|lastschrift|sepa|(?:ue|ü)berweisung|kreditkarte|paypal|sofort(?:ue|ü)berweisung|klarna|rechnung|giropay|apple\s*pay|google\s*pay)", r"(?:vorkasse|nachnahme|lastschrift|sepa|(?:ue|ü)berweisung|kreditkarte|paypal|sofort(?:ue|ü)berweisung|klarna|rechnung|giropay|apple\s*pay|google\s*pay)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es werden keine konkreten Zahlungsarten aufgefuehrt. Listen Sie die akzeptierten Zahlungsmethoden auf (z.B. Kreditkarte, PayPal, SEPA-Lastschrift, Ueberweisung).",
}, },
{ {
"id": "payment_due_date", "id": "payment_due_date",
@@ -119,6 +128,7 @@ AGB_CHECKLIST = [
r"zahlungsfrist", r"zahlungsfrist",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es fehlt eine Angabe zur Faelligkeit der Zahlung. Geben Sie an, wann die Zahlung faellig ist (z.B. 'sofort nach Rechnungsstellung' oder 'innerhalb von 14 Tagen').",
}, },
# ── L1: Lieferung ───────────────────────────────────────────────── # ── L1: Lieferung ─────────────────────────────────────────────────
@@ -132,6 +142,7 @@ AGB_CHECKLIST = [
r"(?:zugang|zugriff).*(?:dienst|leistung)", r"(?:zugang|zugriff).*(?:dienst|leistung)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die AGB regeln nicht die Lieferung oder Leistungserbringung. Ergaenzen Sie Angaben zu Liefergebiet, Versandart und Lieferfristen.",
}, },
{ {
"id": "delivery_timeframe", "id": "delivery_timeframe",
@@ -143,6 +154,7 @@ AGB_CHECKLIST = [
r"(?:liefer|versand).*\d+[\-\s]+(?:\d+\s+)?(?:werk)?tage", r"(?:liefer|versand).*\d+[\-\s]+(?:\d+\s+)?(?:werk)?tage",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es wird keine konkrete Lieferzeit angegeben. Nennen Sie die voraussichtliche Lieferfrist (z.B. '3-5 Werktage nach Zahlungseingang').",
}, },
# ── L1: Gewaehrleistung ─────────────────────────────────────────── # ── L1: Gewaehrleistung ───────────────────────────────────────────
@@ -156,6 +168,7 @@ AGB_CHECKLIST = [
r"gew(?:ae|ä)hrleistungsfrist", r"gew(?:ae|ä)hrleistungsfrist",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die AGB enthalten keine Gewaehrleistungsregelung. Nehmen Sie einen Abschnitt zu Maengelrechten auf, der die gesetzlichen Gewaehrleistungsansprueche beschreibt.",
}, },
{ {
"id": "warranty_period", "id": "warranty_period",
@@ -167,6 +180,7 @@ AGB_CHECKLIST = [
r"(?:gew(?:ae|ä)hrleistung|m(?:ae|ä)ngel|verj(?:ae|ä)hrung).*(?:2|zwei)\s+jahre", r"(?:gew(?:ae|ä)hrleistung|m(?:ae|ä)ngel|verj(?:ae|ä)hrung).*(?:2|zwei)\s+jahre",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die Gewaehrleistungsfrist wird nicht genannt. Geben Sie die Frist an (gesetzlich: 2 Jahre bei Neuware, 1 Jahr bei Gebrauchtwaren gegenueber Verbrauchern moeglich).",
}, },
# ── L1: Kuendigung ──────────────────────────────────────────────── # ── L1: Kuendigung ────────────────────────────────────────────────
@@ -180,6 +194,7 @@ AGB_CHECKLIST = [
r"k(?:ue|ü)ndigungsfrist", r"k(?:ue|ü)ndigungsfrist",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Es fehlt eine Kuendigungsregelung. Beschreiben Sie, wie und unter welchen Bedingungen der Vertrag gekuendigt werden kann.",
}, },
{ {
"id": "termination_period", "id": "termination_period",
@@ -191,6 +206,7 @@ AGB_CHECKLIST = [
r"(?:k(?:ue|ü)ndig|frist).*\d+\s+(?:tage?|wochen?|monate?)", r"(?:k(?:ue|ü)ndig|frist).*\d+\s+(?:tage?|wochen?|monate?)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es wird keine konkrete Kuendigungsfrist angegeben. Nennen Sie die Frist (z.B. '4 Wochen zum Monatsende') und den Zeitpunkt, ab dem sie gilt.",
}, },
{ {
"id": "termination_form", "id": "termination_form",
@@ -201,6 +217,7 @@ AGB_CHECKLIST = [
r"(?:schriftlich|textform).*k(?:ue|ü)ndigung", r"(?:schriftlich|textform).*k(?:ue|ü)ndigung",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die Formvorschrift fuer Kuendigungen fehlt. Geben Sie an, in welcher Form eine Kuendigung erfolgen muss (z.B. Textform per E-Mail oder schriftlich per Post).",
}, },
# ── L1: Datenschutzhinweis in AGB ───────────────────────────────── # ── L1: Datenschutzhinweis in AGB ─────────────────────────────────
@@ -215,6 +232,7 @@ AGB_CHECKLIST = [
r"dsgvo.*(?:agb|vertrag)", r"dsgvo.*(?:agb|vertrag)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die AGB enthalten keinen Verweis auf den Datenschutz. Ergaenzen Sie einen Hinweis, dass personenbezogene Daten gemaess Ihrer Datenschutzerklaerung verarbeitet werden, und verlinken Sie diese.",
}, },
# ── Neue L1: Salvatorische Klausel ──────────────────────────────── # ── Neue L1: Salvatorische Klausel ────────────────────────────────
@@ -229,6 +247,7 @@ AGB_CHECKLIST = [
r"(?:uebrigen|übrigen)\s+bestimmungen.*(?:unberuehrt|unberührt|wirksam|bestehen)", r"(?:uebrigen|übrigen)\s+bestimmungen.*(?:unberuehrt|unberührt|wirksam|bestehen)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es fehlt eine salvatorische Klausel. Ergaenzen Sie eine Regelung, dass die uebrigen Bestimmungen wirksam bleiben, falls einzelne Klauseln unwirksam sein sollten.",
}, },
# ── Neue L1: Aenderungsklausel ──────────────────────────────────── # ── Neue L1: Aenderungsklausel ────────────────────────────────────
@@ -243,6 +262,7 @@ AGB_CHECKLIST = [
r"(?:neue\s+fassung|neufassung).*(?:agb|bedingung)", r"(?:neue\s+fassung|neufassung).*(?:agb|bedingung)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die AGB enthalten keine Aenderungsklausel. Beschreiben Sie, wie und wann die AGB geaendert werden koennen und wie Kunden ueber Aenderungen informiert werden.",
}, },
# ── Neue L1: Verbraucherrechte §309 ─────────────────────────────── # ── Neue L1: Verbraucherrechte §309 ───────────────────────────────
@@ -257,5 +277,6 @@ AGB_CHECKLIST = [
r"(?:verbrauch|konsument).*(?:recht|anspruch|schutz)", r"(?:verbrauch|konsument).*(?:recht|anspruch|schutz)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es fehlt ein Hinweis, dass zwingende Verbraucherrechte (§309 BGB) unberuehrt bleiben. Stellen Sie klar, dass gesetzliche Verbraucherrechte durch die AGB nicht eingeschraenkt werden.",
}, },
] ]
@@ -16,6 +16,7 @@ COOKIE_CHECKLIST = [
r"cookie.*(?:art|typ|kategori)", r"cookie.*(?:art|typ|kategori)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Ihre Cookie-Richtlinie muss die verschiedenen Arten von Cookies auflisten (z.B. notwendige, funktionale, Statistik-, Marketing-Cookies). Ergaenzen Sie eine Kategorisierung aller eingesetzten Cookie-Typen.",
}, },
{ {
"id": "cookie_names_listed", "id": "cookie_names_listed",
@@ -27,6 +28,7 @@ COOKIE_CHECKLIST = [
r"name\s+des\s+cookie", r"name\s+des\s+cookie",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Neben den Kategorien sollten auch die konkreten Cookie-Namen aufgefuehrt werden (z.B. _ga, _gid, PHPSESSID). Listen Sie jeden einzelnen Cookie mit seinem technischen Namen auf.",
}, },
{ {
"id": "cookie_essential_justified", "id": "cookie_essential_justified",
@@ -37,6 +39,7 @@ COOKIE_CHECKLIST = [
r"(?:unbedingt|zwingend)\s+erforderlich", r"(?:unbedingt|zwingend)\s+erforderlich",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Fuer essenzielle/notwendige Cookies muss begruendet werden, warum sie technisch erforderlich sind (z.B. Warenkorb, Session, Sicherheit). Ergaenzen Sie eine kurze Begruendung je Cookie.",
}, },
# ── L1: Zwecke der Cookies ──────────────────────────────────────── # ── L1: Zwecke der Cookies ────────────────────────────────────────
@@ -53,6 +56,7 @@ COOKIE_CHECKLIST = [
r"cookies?\s+(?:dienen|helfen|erm(?:oe|ö)glichen)", r"cookies?\s+(?:dienen|helfen|erm(?:oe|ö)glichen)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die Cookie-Richtlinie muss erklaeren, zu welchem Zweck Cookies eingesetzt werden (z.B. Analyse, Marketing, Funktionalitaet). Beschreiben Sie den Zweck fuer jede Cookie-Kategorie.",
}, },
{ {
"id": "cookie_providers_named", "id": "cookie_providers_named",
@@ -63,6 +67,7 @@ COOKIE_CHECKLIST = [
r"(?:anbieter|provider|dienst)\s*[:\|]\s*[A-Z]", r"(?:anbieter|provider|dienst)\s*[:\|]\s*[A-Z]",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die konkreten Anbieter und Dienste, die Cookies setzen, muessen namentlich genannt werden (z.B. Google Analytics, Meta Pixel, Hotjar). Ergaenzen Sie alle Drittanbieter-Dienste mit Namen.",
}, },
{ {
"id": "cookie_analytics_named", "id": "cookie_analytics_named",
@@ -72,6 +77,7 @@ COOKIE_CHECKLIST = [
r"google\s+analytics|matomo|piwik|plausible|fathom|adobe\s+analytics|microsoft\s+clarity|hotjar|etracker", r"google\s+analytics|matomo|piwik|plausible|fathom|adobe\s+analytics|microsoft\s+clarity|hotjar|etracker",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Falls Sie Analyse-/Statistik-Tools einsetzen, muessen diese konkret benannt werden (z.B. Google Analytics, Matomo, Hotjar). Fuehren Sie jedes eingesetzte Analysetool namentlich auf.",
}, },
{ {
"id": "cookie_marketing_named", "id": "cookie_marketing_named",
@@ -81,6 +87,7 @@ COOKIE_CHECKLIST = [
r"(?:facebook|meta)\s+pixel|google\s+ads|linkedin\s+insight|tiktok\s+pixel|pinterest\s+tag|criteo|adroll|taboola", r"(?:facebook|meta)\s+pixel|google\s+ads|linkedin\s+insight|tiktok\s+pixel|pinterest\s+tag|criteo|adroll|taboola",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Falls Sie Marketing- oder Tracking-Tools einsetzen, muessen diese konkret benannt werden (z.B. Meta Pixel, Google Ads, LinkedIn Insight Tag). Listen Sie alle Marketing-Dienste namentlich auf.",
}, },
# ── L1: Speicherdauer ───────────────────────────────────────────── # ── L1: Speicherdauer ─────────────────────────────────────────────
@@ -93,6 +100,7 @@ COOKIE_CHECKLIST = [
r"cookie.*(?:\d+\s+(?:tag|monat|jahr)|session)", r"cookie.*(?:\d+\s+(?:tag|monat|jahr)|session)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Cookie-Richtlinie muss Angaben zur Speicherdauer der Cookies enthalten. Ergaenzen Sie fuer jede Cookie-Kategorie oder jeden Cookie, wie lange er gespeichert wird (z.B. Session, 30 Tage, 1 Jahr).",
}, },
{ {
"id": "cookie_duration_values", "id": "cookie_duration_values",
@@ -104,6 +112,7 @@ COOKIE_CHECKLIST = [
r"(?:ablauf|expiry|laufzeit)\s*[:\|]\s*\d+", r"(?:ablauf|expiry|laufzeit)\s*[:\|]\s*\d+",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Neben einer allgemeinen Angabe sollten konkrete Speicherdauern pro Cookie angegeben werden (z.B. _ga: 2 Jahre, Session-Cookie: bis Browser geschlossen). Ergaenzen Sie die exakte Laufzeit fuer jeden Cookie.",
}, },
# ── L1: Drittanbieter ───────────────────────────────────────────── # ── L1: Drittanbieter ─────────────────────────────────────────────
@@ -116,6 +125,7 @@ COOKIE_CHECKLIST = [
r"(?:google|facebook|meta|microsoft).*cookie", r"(?:google|facebook|meta|microsoft).*cookie",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Falls Drittanbieter-Cookies eingesetzt werden, muss dies in der Cookie-Richtlinie erwaehnt werden. Geben Sie an, welche Drittanbieter Cookies auf Ihrer Website setzen.",
}, },
{ {
"id": "cookie_legal_basis", "id": "cookie_legal_basis",
@@ -128,6 +138,7 @@ COOKIE_CHECKLIST = [
r"ttdsg|tdddg|§\s*25", r"ttdsg|tdddg|§\s*25",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Fuer nicht-essentielle Cookies muss die Rechtsgrundlage genannt werden (§25 TDDDG bzw. Art. 6 Abs. 1 lit. a DSGVO — Einwilligung). Ergaenzen Sie die Rechtsgrundlage, insbesondere den Verweis auf die Einwilligung.",
}, },
# ── L1: Widerspruch ─────────────────────────────────────────────── # ── L1: Widerspruch ───────────────────────────────────────────────
@@ -140,6 +151,7 @@ COOKIE_CHECKLIST = [
r"cookie.*(?:ablehnen|deaktivieren|l(?:oe|ö)schen)", r"cookie.*(?:ablehnen|deaktivieren|l(?:oe|ö)schen)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Cookie-Richtlinie muss eine Widerspruchsmoeglichkeit beschreiben — also wie Nutzer Cookies ablehnen, deaktivieren oder loeschen koennen. Ergaenzen Sie einen Abschnitt zum Opt-out.",
}, },
{ {
"id": "cookie_consent_mechanism", "id": "cookie_consent_mechanism",
@@ -151,6 +163,7 @@ COOKIE_CHECKLIST = [
r"einwilligung\s+(?:jederzeit|widerrufen|zurueckziehen|zur(?:ue|ü)ckziehen)", r"einwilligung\s+(?:jederzeit|widerrufen|zurueckziehen|zur(?:ue|ü)ckziehen)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Beschreiben Sie das eingesetzte Consent-Tool oder Cookie-Banner und erklaeren Sie, wie Nutzer ihre Einwilligung jederzeit widerrufen koennen (z.B. ueber das Cookie-Banner oder eine Einstellungsseite).",
}, },
{ {
"id": "cookie_browser_settings", "id": "cookie_browser_settings",
@@ -162,6 +175,7 @@ COOKIE_CHECKLIST = [
r"(?:chrome|firefox|safari|edge).*(?:cookie|einstellung)", r"(?:chrome|firefox|safari|edge).*(?:cookie|einstellung)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Weisen Sie Nutzer darauf hin, dass sie Cookies auch ueber die Browser-Einstellungen verwalten, blockieren oder loeschen koennen. Nennen Sie idealerweise die gaengigen Browser (Chrome, Firefox, Safari, Edge).",
}, },
# ── Neue L1: Cookie-Tabelle ─────────────────────────────────────── # ── Neue L1: Cookie-Tabelle ───────────────────────────────────────
@@ -175,5 +189,6 @@ COOKIE_CHECKLIST = [
r"(?:first[\-\s]?party|third[\-\s]?party)\s*[\|\t]", r"(?:first[\-\s]?party|third[\-\s]?party)\s*[\|\t]",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Eine strukturierte Cookie-Tabelle oder -Liste mit Spalten wie Name, Anbieter, Zweck und Speicherdauer erleichtert die Uebersichtlichkeit und wird von Aufsichtsbehoerden empfohlen. Ergaenzen Sie eine tabellarische Uebersicht aller Cookies.",
}, },
] ]
@@ -19,6 +19,7 @@ ART13_CHECKLIST = [
r"responsible\s+(?:party|for)", r"responsible\s+(?:party|for)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Der Name und die Kontaktdaten des Verantwortlichen muessen gemaess Art. 13(1)(a) DSGVO angegeben werden. Ergaenzen Sie: Firmenname, Rechtsform, Anschrift, E-Mail und Telefon.",
}, },
{ {
"id": "controller_address", "id": "controller_address",
@@ -29,6 +30,7 @@ ART13_CHECKLIST = [
r"[a-z\u00c0-\u017e]\w+(?:str|stra(?:ss|ß)e|weg|platz|allee|gasse|ring|damm)\s*\.?\s*\d", r"[a-z\u00c0-\u017e]\w+(?:str|stra(?:ss|ß)e|weg|platz|allee|gasse|ring|damm)\s*\.?\s*\d",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die ladungsfaehige Anschrift des Verantwortlichen fehlt oder ist unvollstaendig. Erforderlich: Strasse, Hausnummer, PLZ und Ort. Ein Postfach allein genuegt nicht.",
}, },
{ {
"id": "controller_email", "id": "controller_email",
@@ -38,6 +40,7 @@ ART13_CHECKLIST = [
r"[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}", r"[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Eine E-Mail-Adresse des Verantwortlichen muss angegeben werden, damit Betroffene ihre Rechte ausueben koennen.",
}, },
{ {
"id": "controller_phone", "id": "controller_phone",
@@ -48,6 +51,7 @@ ART13_CHECKLIST = [
r"\+49\s*[\d\s/\-]{8,}", r"\+49\s*[\d\s/\-]{8,}",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Eine Telefonnummer sollte angegeben werden, um eine niedrigschwellige Kontaktaufnahme zu ermoeglichen.",
}, },
# ── L1: Datenschutzbeauftragter ─────────────────────────────────── # ── L1: Datenschutzbeauftragter ───────────────────────────────────
@@ -61,6 +65,7 @@ ART13_CHECKLIST = [
r"dsb", r"dpo", r"dsb", r"dpo",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Falls ein Datenschutzbeauftragter bestellt ist (Pflicht ab 20 Personen regelmaessig mit Datenverarbeitung), muessen dessen Kontaktdaten angegeben werden (Art. 13(1)(b) DSGVO).",
}, },
{ {
"id": "dpo_contact", "id": "dpo_contact",
@@ -72,6 +77,7 @@ ART13_CHECKLIST = [
r"datenschutz@", r"datenschutz@",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Fuer den DSB muss mindestens eine direkte Kontaktmoeglichkeit angegeben sein (E-Mail-Adresse oder Telefon). Empfehlung: datenschutz@ihredomain.de",
}, },
# ── L1: Zwecke der Verarbeitung ─────────────────────────────────── # ── L1: Zwecke der Verarbeitung ───────────────────────────────────
@@ -87,6 +93,7 @@ ART13_CHECKLIST = [
r"daten\s+werden\s+(?:zu|fuer|für)\s+(?:folgende|diese)", r"daten\s+werden\s+(?:zu|fuer|für)\s+(?:folgende|diese)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Art. 13(1)(c) verlangt eine Angabe der Zwecke, fuer die personenbezogene Daten verarbeitet werden. Listen Sie alle Verarbeitungszwecke konkret auf (z.B. Vertragserfuellung, Newsletter, Webanalyse).",
}, },
{ {
"id": "purposes_specific", "id": "purposes_specific",
@@ -97,6 +104,7 @@ ART13_CHECKLIST = [
r"(?:bereitstellung|betrieb|sicherheit|optimierung)\s+(?:der|des|unserer|unseres)", r"(?:bereitstellung|betrieb|sicherheit|optimierung)\s+(?:der|des|unserer|unseres)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Allgemeine Formulierungen wie 'Wir verarbeiten Daten' genuegen nicht. Nennen Sie konkrete Zwecke: z.B. Vertragsabwicklung, Kontaktanfragen, Website-Analyse, Newsletter-Versand.",
}, },
# ── L1: Rechtsgrundlage ─────────────────────────────────────────── # ── L1: Rechtsgrundlage ───────────────────────────────────────────
@@ -114,6 +122,7 @@ ART13_CHECKLIST = [
r"einwilligung\s+gem", r"einwilligung\s+gem",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Fuer jeden Verarbeitungszweck muss die Rechtsgrundlage nach Art. 6(1) DSGVO genannt werden: Einwilligung (a), Vertrag (b), rechtliche Pflicht (c), lebenswichtige Interessen (d), oeffentliches Interesse (e) oder berechtigtes Interesse (f).",
}, },
{ {
"id": "legal_basis_consent_6a", "id": "legal_basis_consent_6a",
@@ -124,6 +133,7 @@ ART13_CHECKLIST = [
r"einwilligung\s+(?:gem|nach|i\.?\s*s\.?\s*d\.?)", r"einwilligung\s+(?:gem|nach|i\.?\s*s\.?\s*d\.?)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Wenn Daten auf Basis einer Einwilligung verarbeitet werden (z.B. Newsletter, Cookies), sollte Art. 6(1)(a) DSGVO als Rechtsgrundlage genannt und auf das Widerrufsrecht hingewiesen werden.",
}, },
{ {
"id": "legal_basis_contract_6b", "id": "legal_basis_contract_6b",
@@ -135,6 +145,7 @@ ART13_CHECKLIST = [
r"durchf(?:ue|ü)hrung\s+(?:eines|des|vorvertragliche)", r"durchf(?:ue|ü)hrung\s+(?:eines|des|vorvertragliche)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Daten, die zur Vertragserfuellung oder vorvertraglichen Massnahmen verarbeitet werden, sollten auf Art. 6(1)(b) DSGVO gestuetzt werden.",
}, },
{ {
"id": "legal_basis_interest_6f", "id": "legal_basis_interest_6f",
@@ -145,6 +156,7 @@ ART13_CHECKLIST = [
r"berechtigte[sn]?\s+interesse", r"berechtigte[sn]?\s+interesse",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Wenn Sie sich auf ein berechtigtes Interesse (Art. 6(1)(f)) stuetzen, muss dieses Interesse konkret benannt werden (z.B. Betrugspraevention, IT-Sicherheit, Direktwerbung).",
}, },
{ {
"id": "legal_basis_balancing", "id": "legal_basis_balancing",
@@ -156,6 +168,7 @@ ART13_CHECKLIST = [
r"abw(?:ae|ä)gung.*(?:recht|interesse|freiheit)", r"abw(?:ae|ä)gung.*(?:recht|interesse|freiheit)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Bei Verarbeitung auf Basis von Art. 6(1)(f) muss dokumentiert werden, warum Ihr berechtigtes Interesse die Rechte der Betroffenen ueberwiegt. Ergaenzen Sie eine Interessenabwaegung oder verweisen Sie auf eine solche.",
}, },
# ── L1: Empfaenger ──────────────────────────────────────────────── # ── L1: Empfaenger ────────────────────────────────────────────────
@@ -170,6 +183,7 @@ ART13_CHECKLIST = [
r"auftragsverarbeit", r"auftragsverarbeit",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Empfaenger oder Kategorien von Empfaengern der Daten muessen benannt werden (Art. 13(1)(e) DSGVO). Beispiele: Hosting-Anbieter, Zahlungsdienstleister, Steuerberater.",
}, },
{ {
"id": "recipients_categories", "id": "recipients_categories",
@@ -180,6 +194,7 @@ ART13_CHECKLIST = [
r"(?:dienstleister|auftragnehmer|subunternehmer).*(?:fuer|für|im bereich)", r"(?:dienstleister|auftragnehmer|subunternehmer).*(?:fuer|für|im bereich)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Listen Sie konkrete Empfaenger-Kategorien auf: z.B. IT-Dienstleister, Hosting-Anbieter, Zahlungsabwickler, Versandunternehmen, Steuerberater. 'Dritte' allein genuegt nicht.",
}, },
{ {
"id": "recipients_processor", "id": "recipients_processor",
@@ -191,6 +206,7 @@ ART13_CHECKLIST = [
r"avv|av-vertrag|auftragsverarbeitungsvertrag", r"avv|av-vertrag|auftragsverarbeitungsvertrag",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Falls Auftragsverarbeiter eingesetzt werden (z.B. Cloud-Hosting, E-Mail-Service), sollte dies erwaehnt und auf bestehende AVVs nach Art. 28 DSGVO hingewiesen werden.",
}, },
# ── L1: Drittlandtransfer ───────────────────────────────────────── # ── L1: Drittlandtransfer ─────────────────────────────────────────
@@ -207,6 +223,7 @@ ART13_CHECKLIST = [
r"privacy\s+shield", r"data\s+privacy\s+framework", r"privacy\s+shield", r"data\s+privacy\s+framework",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Falls Daten ausserhalb des EWR uebermittelt werden (z.B. USA-basierte Dienste wie Google, Microsoft, AWS), muss dies angegeben werden — inkl. des Empfaengerlandes und der Schutzgarantien.",
}, },
{ {
"id": "third_country_mechanism", "id": "third_country_mechanism",
@@ -219,6 +236,7 @@ ART13_CHECKLIST = [
r"art\.\s*4[5-9]", r"art\.\s*4[5-9]",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Bei Drittlandtransfers muss der konkrete Schutzmechanismus benannt werden: Angemessenheitsbeschluss (Art. 45), Standardvertragsklauseln/SCC (Art. 46(2)(c)) oder EU-US Data Privacy Framework. Ohne Angabe ist der Transfer nicht DSGVO-konform dokumentiert.",
}, },
# ── L1: Speicherdauer ───────────────────────────────────────────── # ── L1: Speicherdauer ─────────────────────────────────────────────
@@ -237,6 +255,7 @@ ART13_CHECKLIST = [
r"gesetzliche.*aufbewahrung", r"gesetzliche.*aufbewahrung",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die Speicherdauer oder die Kriterien zur Festlegung der Dauer muessen angegeben werden (Art. 13(2)(a) DSGVO). Nennen Sie konkrete Fristen (z.B. '10 Jahre steuerrechtliche Aufbewahrung') oder Loeschkriterien.",
}, },
{ {
"id": "retention_periods", "id": "retention_periods",
@@ -248,6 +267,7 @@ ART13_CHECKLIST = [
r"(?:nach|innerhalb)\s+(?:von\s+)?\d+\s+(?:tag|monat|jahr)", r"(?:nach|innerhalb)\s+(?:von\s+)?\d+\s+(?:tag|monat|jahr)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Statt allgemeiner Aussagen ('so lange wie noetig') sollten konkrete Fristen stehen: z.B. 'Logfiles: 7 Tage', 'Vertragsdaten: 10 Jahre (§257 HGB)', 'Bewerbungen: 6 Monate nach Absage'.",
}, },
{ {
"id": "retention_deletion", "id": "retention_deletion",
@@ -259,6 +279,7 @@ ART13_CHECKLIST = [
r"nach\s+(?:ablauf|wegfall).*(?:gel(?:oe|ö)scht|l(?:oe|ö)sch)", r"nach\s+(?:ablauf|wegfall).*(?:gel(?:oe|ö)scht|l(?:oe|ö)sch)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Beschreiben Sie, wie und wann Daten geloescht werden: z.B. 'Nach Ablauf der Aufbewahrungsfrist werden die Daten routinemaessig geloescht.' oder Verweis auf ein internes Loeschkonzept.",
}, },
# ── L1: Betroffenenrechte ───────────────────────────────────────── # ── L1: Betroffenenrechte ─────────────────────────────────────────
@@ -275,6 +296,7 @@ ART13_CHECKLIST = [
r"ihnen\s+(?:stehen|steht)\s+(?:ein|folgende)\s+recht", r"ihnen\s+(?:stehen|steht)\s+(?:ein|folgende)\s+recht",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die Betroffenenrechte (Art. 15-22 DSGVO) muessen vollstaendig aufgezaehlt werden: Auskunft, Berichtigung, Loeschung, Einschraenkung, Datenportabilitaet, Widerspruch und ggf. automatisierte Entscheidungen.",
}, },
{ {
"id": "rights_art15", "id": "rights_art15",
@@ -282,6 +304,7 @@ ART13_CHECKLIST = [
"level": 2, "parent": "rights", "level": 2, "parent": "rights",
"patterns": [r"art\.\s*15", r"recht\s+auf\s+auskunft", r"right\s+(?:of|to)\s+access"], "patterns": [r"art\.\s*15", r"recht\s+auf\s+auskunft", r"right\s+(?:of|to)\s+access"],
"severity": "LOW", "severity": "LOW",
"hint": "Ergaenzen Sie den Hinweis auf das Auskunftsrecht nach Art. 15 DSGVO: Betroffene koennen eine Kopie aller ueber sie gespeicherten Daten anfordern.",
}, },
{ {
"id": "rights_art16", "id": "rights_art16",
@@ -289,6 +312,7 @@ ART13_CHECKLIST = [
"level": 2, "parent": "rights", "level": 2, "parent": "rights",
"patterns": [r"art\.\s*16", r"recht\s+auf\s+berichtigung", r"right\s+to\s+rectification"], "patterns": [r"art\.\s*16", r"recht\s+auf\s+berichtigung", r"right\s+to\s+rectification"],
"severity": "LOW", "severity": "LOW",
"hint": "Ergaenzen Sie das Recht auf Berichtigung nach Art. 16 DSGVO: Betroffene koennen die Korrektur unrichtiger Daten verlangen.",
}, },
{ {
"id": "rights_art17", "id": "rights_art17",
@@ -296,6 +320,7 @@ ART13_CHECKLIST = [
"level": 2, "parent": "rights", "level": 2, "parent": "rights",
"patterns": [r"art\.\s*17", r"recht\s+auf\s+l(?:oe|ö)schung", r"right\s+to\s+erasure"], "patterns": [r"art\.\s*17", r"recht\s+auf\s+l(?:oe|ö)schung", r"right\s+to\s+erasure"],
"severity": "LOW", "severity": "LOW",
"hint": "Ergaenzen Sie das Recht auf Loeschung ('Recht auf Vergessenwerden') nach Art. 17 DSGVO.",
}, },
{ {
"id": "rights_art18", "id": "rights_art18",
@@ -303,6 +328,7 @@ ART13_CHECKLIST = [
"level": 2, "parent": "rights", "level": 2, "parent": "rights",
"patterns": [r"art\.\s*18", r"einschr(?:ae|ä)nkung\s+der\s+verarbeitung", r"right\s+to\s+restriction"], "patterns": [r"art\.\s*18", r"einschr(?:ae|ä)nkung\s+der\s+verarbeitung", r"right\s+to\s+restriction"],
"severity": "LOW", "severity": "LOW",
"hint": "Ergaenzen Sie das Recht auf Einschraenkung der Verarbeitung nach Art. 18 DSGVO.",
}, },
{ {
"id": "rights_art20", "id": "rights_art20",
@@ -310,6 +336,7 @@ ART13_CHECKLIST = [
"level": 2, "parent": "rights", "level": 2, "parent": "rights",
"patterns": [r"art\.\s*20", r"daten(?:ue|ü)bertragbarkeit|datenportabilit", r"right\s+to\s+data\s+portability"], "patterns": [r"art\.\s*20", r"daten(?:ue|ü)bertragbarkeit|datenportabilit", r"right\s+to\s+data\s+portability"],
"severity": "LOW", "severity": "LOW",
"hint": "Ergaenzen Sie das Recht auf Datenuebertragbarkeit nach Art. 20 DSGVO: Betroffene koennen ihre Daten in einem maschinenlesbaren Format erhalten.",
}, },
{ {
"id": "rights_art21", "id": "rights_art21",
@@ -317,6 +344,7 @@ ART13_CHECKLIST = [
"level": 2, "parent": "rights", "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"right\s+to\s+object"],
"severity": "LOW", "severity": "LOW",
"hint": "Ergaenzen Sie das Widerspruchsrecht nach Art. 21 DSGVO, insbesondere bei Verarbeitung auf Basis von Art. 6(1)(e) oder (f). Der Hinweis muss gesondert und in klarer Sprache erfolgen.",
}, },
{ {
"id": "rights_art22_profiling", "id": "rights_art22_profiling",
@@ -327,6 +355,7 @@ ART13_CHECKLIST = [
r"profiling", r"automated\s+(?:decision|individual)", r"profiling", r"automated\s+(?:decision|individual)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Falls automatisierte Entscheidungen oder Profiling stattfinden, muss dies offengelegt werden (Art. 22 DSGVO). Falls nicht: Ergaenzen Sie 'Es findet keine automatisierte Entscheidungsfindung einschliesslich Profiling statt.'",
}, },
# ── L1: Beschwerderecht ─────────────────────────────────────────── # ── L1: Beschwerderecht ───────────────────────────────────────────
@@ -343,6 +372,7 @@ ART13_CHECKLIST = [
r"(?:zust(?:ae|ä)ndige|competent)\s+(?:beh(?:oe|ö)rde|authority)", r"(?:zust(?:ae|ä)ndige|competent)\s+(?:beh(?:oe|ö)rde|authority)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Betroffene muessen auf ihr Recht hingewiesen werden, sich bei einer Aufsichtsbehoerde zu beschweren (Art. 77 DSGVO). Nennen Sie idealerweise die zustaendige Landesbehoerde.",
}, },
{ {
"id": "complaint_authority_named", "id": "complaint_authority_named",
@@ -355,5 +385,6 @@ ART13_CHECKLIST = [
r"(?:bayerische|hessische|s(?:ae|ä)chsische|berliner)\s+(?:datenschutz|aufsicht)", r"(?:bayerische|hessische|s(?:ae|ä)chsische|berliner)\s+(?:datenschutz|aufsicht)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Nennen Sie die zustaendige Aufsichtsbehoerde mit Name und Kontakt. Z.B.: 'Der Landesbeauftragte fuer den Datenschutz und die Informationsfreiheit Baden-Wuerttemberg' mit Adresse und Website.",
}, },
] ]
@@ -19,6 +19,7 @@ DSFA_CHECKLIST = [
r"folgen.*(?:verarbeitung|schutz).*personenbezogen", r"folgen.*(?:verarbeitung|schutz).*personenbezogen",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Es fehlt eine Schwellwertanalyse gemaess Art. 35 Abs. 1 DSGVO. Beschreiben Sie, warum die Verarbeitung voraussichtlich ein hohes Risiko fuer die Rechte und Freiheiten natuerlicher Personen birgt und eine DSFA erforderlich ist.",
}, },
# ── L1: Beschreibung der Verarbeitungsvorgaenge ─────────────────── # ── L1: Beschreibung der Verarbeitungsvorgaenge ───────────────────
@@ -33,6 +34,7 @@ DSFA_CHECKLIST = [
r"(?:kan(?:ae|ä)le|plattform).*(?:facebook|twitter|instagram|youtube|linkedin|xing)", r"(?:kan(?:ae|ä)le|plattform).*(?:facebook|twitter|instagram|youtube|linkedin|xing)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Eine systematische Beschreibung der geplanten Verarbeitungsvorgaenge fehlt. Fuegen Sie einen Abschnitt hinzu, der Art, Umfang, Umstaende und Zweck der Verarbeitung detailliert beschreibt.",
}, },
{ {
"id": "processing_named", "id": "processing_named",
@@ -43,6 +45,7 @@ DSFA_CHECKLIST = [
r"(?:verarbeitung|erhebung|speicherung)\s+(?:von|der)\s+(?:nutzerdaten|personenbezogen|besucher|mitglieder)", r"(?:verarbeitung|erhebung|speicherung)\s+(?:von|der)\s+(?:nutzerdaten|personenbezogen|besucher|mitglieder)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Der konkrete Verarbeitungsvorgang ist nicht namentlich benannt. Benennen Sie den spezifischen Vorgang (z.B. Betrieb einer Fanpage, Verwaltung eines Social-Media-Kanals) explizit im Dokument.",
}, },
# ── L1: Notwendigkeit / Verhaeltnismaessigkeit ──────────────────── # ── L1: Notwendigkeit / Verhaeltnismaessigkeit ────────────────────
@@ -58,6 +61,7 @@ DSFA_CHECKLIST = [
r"freiwillig\s+angegeben", r"freiwillig\s+angegeben",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die Bewertung der Notwendigkeit und Verhaeltnismaessigkeit der Verarbeitung fehlt. Begruenden Sie, warum die Verarbeitung erforderlich ist und in einem angemessenen Verhaeltnis zum Zweck steht.",
}, },
{ {
"id": "legal_basis_dsfa", "id": "legal_basis_dsfa",
@@ -69,6 +73,7 @@ DSFA_CHECKLIST = [
r"(?:einwilligung|vertrag|berechtigt).*(?:rechtsgrundlage|grundlage)", r"(?:einwilligung|vertrag|berechtigt).*(?:rechtsgrundlage|grundlage)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die Rechtsgrundlage der Verarbeitung ist nicht angegeben. Nennen Sie die einschlaegige Rechtsgrundlage nach Art. 6 Abs. 1 DSGVO (z.B. Einwilligung, berechtigtes Interesse, Vertragserfuellung).",
}, },
# ── L1: Risikobewertung ─────────────────────────────────────────── # ── L1: Risikobewertung ───────────────────────────────────────────
@@ -85,6 +90,7 @@ DSFA_CHECKLIST = [
r"systematische\s+beobachtung", r"systematische\s+beobachtung",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Eine Risikobewertung fuer die Rechte und Freiheiten der Betroffenen fehlt. Fuehren Sie eine strukturierte Risikoanalyse durch, die moegliche Schaeden und deren Auswirkungen auf die betroffenen Personen beschreibt.",
}, },
{ {
"id": "risk_probability", "id": "risk_probability",
@@ -96,6 +102,7 @@ DSFA_CHECKLIST = [
r"(?:gering|mittel|hoch)\w*\s+(?:wahrscheinlichkeit|eintritt)", r"(?:gering|mittel|hoch)\w*\s+(?:wahrscheinlichkeit|eintritt)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Eintrittswahrscheinlichkeit der identifizierten Risiken ist nicht bewertet. Ordnen Sie jedem Risiko eine Eintrittswahrscheinlichkeit zu (z.B. gering, mittel, hoch).",
}, },
{ {
"id": "risk_severity", "id": "risk_severity",
@@ -107,6 +114,7 @@ DSFA_CHECKLIST = [
r"(?:physisch|materiell|immateriell)\w*\s+(?:schaden|nachteil|beeintr(?:ae|ä)chtigung)", r"(?:physisch|materiell|immateriell)\w*\s+(?:schaden|nachteil|beeintr(?:ae|ä)chtigung)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Schwere der moeglichen Auswirkungen auf Betroffene ist nicht bewertet. Bewerten Sie fuer jedes Risiko die Schadenshoehe (z.B. gering, mittel, hoch, kritisch) und unterscheiden Sie physische, materielle und immaterielle Schaeden.",
}, },
# ── L1: Abhilfemassnahmen ───────────────────────────────────────── # ── L1: Abhilfemassnahmen ─────────────────────────────────────────
@@ -122,6 +130,7 @@ DSFA_CHECKLIST = [
r"risiko.*(?:minim|reduz|begrenzen)", r"risiko.*(?:minim|reduz|begrenzen)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Abhilfemassnahmen zur Risikominderung fehlen. Beschreiben Sie die geplanten technischen und organisatorischen Massnahmen (TOMs), mit denen die identifizierten Risiken eingedaemmt werden sollen.",
}, },
{ {
"id": "tom_encryption", "id": "tom_encryption",
@@ -132,6 +141,7 @@ DSFA_CHECKLIST = [
r"(?:transport|ende[\-\s]zu[\-\s]ende)[\-\s]?verschl(?:ue|ü)sselung", r"(?:transport|ende[\-\s]zu[\-\s]ende)[\-\s]?verschl(?:ue|ü)sselung",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Verschluesselung ist nicht als Schutzmassnahme aufgefuehrt. Ergaenzen Sie, ob und welche Verschluesselungsverfahren eingesetzt werden (z.B. TLS-Transportverschluesselung, Ende-zu-Ende-Verschluesselung).",
}, },
{ {
"id": "tom_pseudonymization", "id": "tom_pseudonymization",
@@ -142,6 +152,7 @@ DSFA_CHECKLIST = [
r"(?:pseudonymisiert|anonymisiert).*(?:daten|verarbeit)", r"(?:pseudonymisiert|anonymisiert).*(?:daten|verarbeit)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Pseudonymisierung oder Anonymisierung ist nicht als Massnahme erwaehnt. Pruefen Sie, ob personenbezogene Daten pseudonymisiert oder anonymisiert werden koennen, und dokumentieren Sie dies.",
}, },
{ {
"id": "tom_access_control", "id": "tom_access_control",
@@ -153,6 +164,7 @@ DSFA_CHECKLIST = [
r"(?:need[\-\s]to[\-\s]know|least\s+privilege|minimalprinzip)", r"(?:need[\-\s]to[\-\s]know|least\s+privilege|minimalprinzip)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Zugriffskontrollmassnahmen sind nicht dokumentiert. Beschreiben Sie, wie der Zugriff auf personenbezogene Daten beschraenkt wird (z.B. Berechtigungskonzept, Rollenmodell, Need-to-know-Prinzip).",
}, },
{ {
"id": "tom_logging", "id": "tom_logging",
@@ -163,6 +175,7 @@ DSFA_CHECKLIST = [
r"(?:zugriff|(?:ae|ä)nderung).*(?:protokoll|logging|nachvollzieh)", r"(?:zugriff|(?:ae|ä)nderung).*(?:protokoll|logging|nachvollzieh)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Protokollierung und Nachvollziehbarkeit sind nicht als Massnahme aufgefuehrt. Ergaenzen Sie, wie Zugriffe und Aenderungen an personenbezogenen Daten protokolliert und nachvollziehbar gemacht werden.",
}, },
# ── L1: Landesbehoerden ─────────────────────────────────────────── # ── L1: Landesbehoerden ───────────────────────────────────────────
@@ -177,6 +190,7 @@ DSFA_CHECKLIST = [
r"(?:aufsichtsbeh(?:oe|ö)rde|beh(?:oe|ö)rde).*(?:richtlinie|empfehlung|vorgabe)", r"(?:aufsichtsbeh(?:oe|ö)rde|beh(?:oe|ö)rde).*(?:richtlinie|empfehlung|vorgabe)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Es fehlt ein Verweis auf die Richtlinien der zustaendigen Landesbehoerde (LfDI). Pruefen Sie, ob Ihre Landesdatenschutzbehoerde spezifische Vorgaben oder Empfehlungen fuer diese Verarbeitung veroeffentlicht hat, und beruecksichtigen Sie diese.",
}, },
# ── L1: Einbeziehung DSB ────────────────────────────────────────── # ── L1: Einbeziehung DSB ──────────────────────────────────────────
@@ -191,6 +205,7 @@ DSFA_CHECKLIST = [
r"(?:rat|empfehlung).*datenschutzbeauftragt", r"(?:rat|empfehlung).*datenschutzbeauftragt",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Einbeziehung des Datenschutzbeauftragten (DSB) ist nicht dokumentiert. Gemaess Art. 35 Abs. 2 DSGVO muss der DSB bei der DSFA konsultiert werden — dokumentieren Sie dessen Beteiligung und Stellungnahme.",
}, },
{ {
"id": "dsb_opinion_documented", "id": "dsb_opinion_documented",
@@ -202,6 +217,7 @@ DSFA_CHECKLIST = [
r"(?:empfehlung|beurteilung|einsch(?:ae|ä)tzung)\s+(?:des|der)\s+(?:dsb|datenschutzbeauftragt)", r"(?:empfehlung|beurteilung|einsch(?:ae|ä)tzung)\s+(?:des|der)\s+(?:dsb|datenschutzbeauftragt)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die Stellungnahme des Datenschutzbeauftragten ist nicht im Dokument enthalten. Fuegen Sie die schriftliche Stellungnahme oder Empfehlung des DSB zur DSFA hinzu.",
}, },
# ── L1: Dokumentation ───────────────────────────────────────────── # ── L1: Dokumentation ─────────────────────────────────────────────
@@ -215,6 +231,7 @@ DSFA_CHECKLIST = [
r"vorliegend.*(?:dsfa|analyse|bewertung|absch(?:ae|ä)tzung)", r"vorliegend.*(?:dsfa|analyse|bewertung|absch(?:ae|ä)tzung)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Ergebnisse der DSFA sind nicht zusammenfassend dokumentiert. Erstellen Sie einen Ergebnisabschnitt, der die Schlussfolgerungen der Folgenabschaetzung und die Gesamtbewertung des Restrisikos festhält.",
}, },
{ {
"id": "review_cycle", "id": "review_cycle",
@@ -226,5 +243,6 @@ DSFA_CHECKLIST = [
r"n(?:ae|ä)chste\s+(?:ueberpr(?:ue|ü)fung|überprüfung|review)", r"n(?:ae|ä)chste\s+(?:ueberpr(?:ue|ü)fung|überprüfung|review)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Ein Ueberpruefungszyklus fuer die DSFA ist nicht festgelegt. Definieren Sie, in welchem Turnus die DSFA ueberprueft und aktualisiert wird (z.B. jaehrlich oder bei wesentlichen Aenderungen der Verarbeitung).",
}, },
] ]
@@ -16,6 +16,7 @@ IMPRESSUM_CHECKLIST = [
r"firma", r"unternehmen", r"firma", r"unternehmen",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Der vollstaendige Name des Unternehmens oder der Person muss im Impressum stehen (z.B. 'Musterfirma GmbH'). Bei Einzelunternehmen genuegt der vollstaendige Vor- und Nachname.",
}, },
# ── L1: Anschrift ───────────────────────────────────────────────── # ── L1: Anschrift ─────────────────────────────────────────────────
@@ -28,6 +29,7 @@ IMPRESSUM_CHECKLIST = [
r"d-\d{5}", r"\d{5}\s+\w+", r"d-\d{5}", r"\d{5}\s+\w+",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Eine vollstaendige ladungsfaehige Anschrift (Strasse, Hausnummer, PLZ, Ort) muss im Impressum angegeben werden. Ein Postfach genuegt nicht.",
}, },
{ {
"id": "address_zip_city", "id": "address_zip_city",
@@ -37,6 +39,7 @@ IMPRESSUM_CHECKLIST = [
r"(?:d[\-\s]?)?\d{5}\s+[a-z\u00c0-\u017e]\w{2,}", r"(?:d[\-\s]?)?\d{5}\s+[a-z\u00c0-\u017e]\w{2,}",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Anschrift muss eine fuenfstellige Postleitzahl und den Ortsnamen enthalten (z.B. '10115 Berlin').",
}, },
{ {
"id": "address_street_number", "id": "address_street_number",
@@ -47,6 +50,7 @@ IMPRESSUM_CHECKLIST = [
r"\w+\s+(?:str|stra(?:ss|ß)e|weg|platz|allee)\s*\.?\s*\d+", r"\w+\s+(?:str|stra(?:ss|ß)e|weg|platz|allee)\s*\.?\s*\d+",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Bitte den Strassennamen und die Hausnummer angeben (z.B. 'Musterstrasse 12'). Ohne Hausnummer ist die Anschrift nicht ladungsfaehig.",
}, },
# ── L1: Kontaktdaten ────────────────────────────────────────────── # ── L1: Kontaktdaten ──────────────────────────────────────────────
@@ -59,6 +63,7 @@ IMPRESSUM_CHECKLIST = [
r"\+?\d[\d\s/\-]{8,}", r"\+?\d[\d\s/\-]{8,}",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Das Impressum muss mindestens eine E-Mail-Adresse und eine Telefonnummer enthalten, damit Nutzer schnell Kontakt aufnehmen koennen.",
}, },
{ {
"id": "contact_email_format", "id": "contact_email_format",
@@ -68,6 +73,7 @@ IMPRESSUM_CHECKLIST = [
r"[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}", r"[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die E-Mail-Adresse muss in einem gueltigen Format vorliegen (z.B. 'info@beispiel.de'). Bitte pruefen Sie, ob ein '@'-Zeichen und eine Domain vorhanden sind.",
}, },
{ {
"id": "contact_phone_format", "id": "contact_phone_format",
@@ -79,6 +85,7 @@ IMPRESSUM_CHECKLIST = [
r"0\d{2,4}\s*[/\-\s]\s*\d{4,}", r"0\d{2,4}\s*[/\-\s]\s*\d{4,}",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Bitte eine Telefonnummer mit Vorwahl angeben (z.B. '+49 30 12345678' oder '030 / 12345678'). Ein reines Kontaktformular reicht nicht aus.",
}, },
# ── L1: Handelsregister ─────────────────────────────────────────── # ── L1: Handelsregister ───────────────────────────────────────────
@@ -91,6 +98,7 @@ IMPRESSUM_CHECKLIST = [
r"register.*(?:nr|nummer)", r"register.*(?:nr|nummer)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Falls das Unternehmen im Handelsregister eingetragen ist, muessen Registergericht und Registernummer angegeben werden (z.B. 'Amtsgericht Muenchen, HRB 12345').",
}, },
{ {
"id": "register_court", "id": "register_court",
@@ -101,6 +109,7 @@ IMPRESSUM_CHECKLIST = [
r"ag\s+[A-Z\u00c0-\u017e]\w+", r"ag\s+[A-Z\u00c0-\u017e]\w+",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Bitte das zustaendige Registergericht benennen (z.B. 'Amtsgericht Muenchen'). Die alleinige Angabe der Registernummer ohne Gericht ist unvollstaendig.",
}, },
{ {
"id": "register_number", "id": "register_number",
@@ -110,6 +119,7 @@ IMPRESSUM_CHECKLIST = [
r"(?:hrb|hra)\s*\d+", r"(?:hrb|hra)\s*\d+",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die Registernummer muss mit dem Praefix HRB oder HRA und der zugehoerigen Nummer angegeben werden (z.B. 'HRB 12345').",
}, },
# ── L1: USt-IdNr ────────────────────────────────────────────────── # ── L1: USt-IdNr ──────────────────────────────────────────────────
@@ -122,6 +132,7 @@ IMPRESSUM_CHECKLIST = [
r"vat.*id", r"de\s*\d{9}", r"vat.*id", r"de\s*\d{9}",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Falls eine Umsatzsteuer-Identifikationsnummer vorhanden ist, muss diese im Impressum angegeben werden. Die Steuernummer allein genuegt nicht als Ersatz.",
}, },
{ {
"id": "vat_de_format", "id": "vat_de_format",
@@ -131,6 +142,7 @@ IMPRESSUM_CHECKLIST = [
r"de\s*\d{9}", r"de\s*\d{9}",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die USt-IdNr. muss im Format 'DE' gefolgt von 9 Ziffern angegeben werden (z.B. 'DE123456789'). Bitte pruefen Sie, ob die Nummer vollstaendig ist.",
}, },
# ── L1: Vertretungsberechtigte ──────────────────────────────────── # ── L1: Vertretungsberechtigte ────────────────────────────────────
@@ -143,6 +155,7 @@ IMPRESSUM_CHECKLIST = [
r"vorstand", r"inhaber", r"vorstand", r"inhaber",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Bei juristischen Personen (GmbH, AG etc.) muss die vertretungsberechtigte Person namentlich genannt werden (z.B. 'Geschaeftsfuehrer: Max Mustermann').",
}, },
{ {
"id": "representative_person", "id": "representative_person",
@@ -153,6 +166,7 @@ IMPRESSUM_CHECKLIST = [
r"(?:vertreten\s+durch|repr(?:ae|ä)sentiert)\s*:?\s*[A-Z\u00c0-\u017e]", r"(?:vertreten\s+durch|repr(?:ae|ä)sentiert)\s*:?\s*[A-Z\u00c0-\u017e]",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Bitte den vollstaendigen Vor- und Nachnamen der vertretungsberechtigten Person angeben (z.B. 'Geschaeftsfuehrer: Max Mustermann'). Eine reine Funktionsbezeichnung reicht nicht.",
}, },
# ── Neue L1: Redaktionell Verantwortlicher ──────────────────────── # ── Neue L1: Redaktionell Verantwortlicher ────────────────────────
@@ -166,6 +180,7 @@ IMPRESSUM_CHECKLIST = [
r"§\s*18\s+m(?:edien)?st(?:aat)?v", r"§\s*18\s+m(?:edien)?st(?:aat)?v",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Wenn die Website journalistisch-redaktionelle Inhalte enthaelt, muss ein inhaltlich Verantwortlicher mit Name und Anschrift benannt werden (§18 MStV, 'V.i.S.d.P.').",
}, },
# ── Neue L1: Streitbeilegung ────────────────────────────────────── # ── Neue L1: Streitbeilegung ──────────────────────────────────────
@@ -181,5 +196,6 @@ IMPRESSUM_CHECKLIST = [
r"alternative\s+streitbeilegung", r"alternative\s+streitbeilegung",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Online-Haendler muessen einen Link zur EU-Streitbeilegungsplattform (https://ec.europa.eu/consumers/odr) angeben und erklaeren, ob sie zur Teilnahme an Streitbeilegungsverfahren bereit oder verpflichtet sind.",
}, },
] ]
@@ -129,6 +129,7 @@ def check_document_completeness(
"passed": passed, "severity": check.get("severity", "MEDIUM"), "passed": passed, "severity": check.get("severity", "MEDIUM"),
"matched_text": _extract_context(text_lower, match), "matched_text": _extract_context(text_lower, match),
"level": 1, "parent": None, "skipped": False, "level": 1, "parent": None, "skipped": False,
"hint": check.get("hint", ""),
}) })
# ── Pass 2: L2 checks (only if parent L1 passed) ───────────────── # ── Pass 2: L2 checks (only if parent L1 passed) ─────────────────
@@ -165,6 +166,7 @@ def check_document_completeness(
"passed": passed, "severity": check.get("severity", "MEDIUM"), "passed": passed, "severity": check.get("severity", "MEDIUM"),
"matched_text": matched_text, "matched_text": matched_text,
"level": 2, "parent": parent, "skipped": skipped, "level": 2, "parent": parent, "skipped": skipped,
"hint": check.get("hint", ""),
}) })
# ── Summary ─────────────────────────────────────────────────────── # ── Summary ───────────────────────────────────────────────────────
@@ -19,6 +19,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:betreiber|netzwerk).*verantwortlich", r"(?:betreiber|netzwerk).*verantwortlich",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Es fehlt der Hinweis auf die gemeinsame Verantwortlichkeit nach Art. 26 DSGVO. Benennen Sie ausdruecklich, dass Sie und die jeweilige Plattform gemeinsam Verantwortliche fuer die Datenverarbeitung sind.",
}, },
{ {
"id": "facebook_meta_named", "id": "facebook_meta_named",
@@ -29,6 +30,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"meta\s+platforms.*(?:verantwortlich|controller|betreiber)", r"meta\s+platforms.*(?:verantwortlich|controller|betreiber)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Facebook/Meta ist nicht namentlich als gemeinsam Verantwortlicher aufgefuehrt. Nennen Sie die vollstaendige Firmenbezeichnung (z.B. Meta Platforms Ireland Limited) als Mitverantwortlichen.",
}, },
# ── L1: Vereinbarung Art. 26 ────────────────────────────────────── # ── L1: Vereinbarung Art. 26 ──────────────────────────────────────
@@ -43,6 +45,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"addendum|nachtrag|seiten.*insights", r"addendum|nachtrag|seiten.*insights",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Es fehlt ein Verweis auf die Vereinbarung nach Art. 26 DSGVO zwischen Ihnen und der Plattform. Erwaehnen Sie die bestehende Vereinbarung (z.B. Page Controller Addendum bei Facebook) und deren wesentlichen Inhalt.",
}, },
{ {
"id": "insights_referenced", "id": "insights_referenced",
@@ -54,6 +57,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:statistik|nutzungsstatistik).*(?:facebook|meta|fanpage|seite)", r"(?:statistik|nutzungsstatistik).*(?:facebook|meta|fanpage|seite)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Verarbeitung von Seiten-Insights bzw. Page Insights wird nicht erwaehnt. Erlaeutern Sie, dass bei Nutzung Ihrer Social-Media-Seite Insights-Daten erhoben und verarbeitet werden.",
}, },
{ {
"id": "page_controller_addendum", "id": "page_controller_addendum",
@@ -65,6 +69,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:addendum|nachtrag|erg(?:ae|ä)nzung).*(?:controller|verantwortlich)", r"(?:addendum|nachtrag|erg(?:ae|ä)nzung).*(?:controller|verantwortlich)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Das Page Controller Addendum (bzw. die Seiten-Insights-Ergaenzung) wird nicht namentlich erwaehnt. Verweisen Sie konkret auf das Addendum und verlinken Sie es, damit Betroffene die Vereinbarung nachvollziehen koennen.",
}, },
# ── L1: Anlaufstelle ────────────────────────────────────────────── # ── L1: Anlaufstelle ──────────────────────────────────────────────
@@ -80,6 +85,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"wenden\s+sie\s+sich", r"wenden\s+sie\s+sich",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Es fehlt eine Anlaufstelle fuer Betroffene gemaess Art. 26(1) S.3 DSGVO. Geben Sie an, an wen sich Betroffene zur Wahrnehmung ihrer Rechte wenden koennen.",
}, },
{ {
"id": "contact_both_parties", "id": "contact_both_parties",
@@ -91,6 +97,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:facebook|meta|google|plattform).*(?:als\s+auch|oder|und).*(?:uns|bei\s+uns)", r"(?:facebook|meta|google|plattform).*(?:als\s+auch|oder|und).*(?:uns|bei\s+uns)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die Kontaktdaten beider Verantwortlicher sind nicht vollstaendig angegeben. Stellen Sie klar, dass Betroffene ihre Rechte sowohl bei Ihnen als auch bei der Plattform geltend machen koennen, und nennen Sie die jeweiligen Kontaktwege.",
}, },
# ── L1: Verarbeitungsaufteilung ─────────────────────────────────── # ── L1: Verarbeitungsaufteilung ───────────────────────────────────
@@ -105,6 +112,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:nutzungsstatistik|statistik|insight).*(?:betreiber|netzwerk)", r"(?:nutzungsstatistik|statistik|insight).*(?:betreiber|netzwerk)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Es fehlt eine Beschreibung, welche Datenverarbeitungen Sie und welche die Plattform vornimmt. Erlaeutern Sie die Aufgabenverteilung (z.B. wer Insights-Daten erhebt und wer sie auswertet).",
}, },
# ── L1: Datenkategorien ─────────────────────────────────────────── # ── L1: Datenkategorien ───────────────────────────────────────────
@@ -119,6 +127,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:nutzername|beitr(?:ae|ä)g|profil|like|kommentar)", r"(?:nutzername|beitr(?:ae|ä)g|profil|like|kommentar)",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die Kategorien der verarbeiteten Daten sind nicht aufgefuehrt. Listen Sie auf, welche Daten verarbeitet werden (z.B. IP-Adresse, Standort, Geraetedaten, Interaktionen, demografische Daten).",
}, },
# ── L1: Plattformen ─────────────────────────────────────────────── # ── L1: Plattformen ───────────────────────────────────────────────
@@ -132,6 +141,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"social\s*media.*(?:angebot|pr(?:ae|ä)senz|auftritte)", r"social\s*media.*(?:angebot|pr(?:ae|ä)senz|auftritte)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die genutzten Social-Media-Plattformen werden nicht aufgelistet. Fuehren Sie alle Plattformen namentlich auf, auf denen Sie Praesenzen betreiben (z.B. Facebook, Instagram, LinkedIn, YouTube).",
}, },
{ {
"id": "platform_dse_links", "id": "platform_dse_links",
@@ -143,6 +153,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:privacy\s+policy|datenschutzerkl(?:ae|ä)rung).*(?:finden\s+sie|abrufbar|unter)", r"(?:privacy\s+policy|datenschutzerkl(?:ae|ä)rung).*(?:finden\s+sie|abrufbar|unter)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es fehlen Links zu den Datenschutzerklaerungen der einzelnen Plattformen. Verlinken Sie die jeweilige Privacy Policy (z.B. von Meta, Google, LinkedIn), damit Nutzer sich dort informieren koennen.",
}, },
# ── L1: Drittlandtransfer ───────────────────────────────────────── # ── L1: Drittlandtransfer ─────────────────────────────────────────
@@ -157,6 +168,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:(?:ue|ü)bermittlung|(?:ueber|über)mittlung).*(?:usa|drittland|au(?:ss|ß)erhalb)", r"(?:(?:ue|ü)bermittlung|(?:ueber|über)mittlung).*(?:usa|drittland|au(?:ss|ß)erhalb)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Es fehlt ein Hinweis auf den Drittlandtransfer in die USA. Informieren Sie darueber, dass Daten in die USA uebermittelt werden, und nennen Sie die Rechtsgrundlage dafuer (z.B. Angemessenheitsbeschluss, Standardvertragsklauseln).",
}, },
{ {
"id": "usa_transfer_scc", "id": "usa_transfer_scc",
@@ -167,6 +179,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"standard\s+contractual\s+clause", r"standard\s+contractual\s+clause",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Standardvertragsklauseln (SCC) als Garantie fuer den US-Datentransfer werden nicht erwaehnt. Falls SCC als Rechtsgrundlage genutzt werden, fuehren Sie diese ausdruecklich auf.",
}, },
{ {
"id": "usa_transfer_dpf", "id": "usa_transfer_dpf",
@@ -178,6 +191,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"adequacy\s+decision", r"adequacy\s+decision",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Das EU-US Data Privacy Framework (DPF) wird nicht als Transfergrundlage erwaehnt. Falls sich die Plattform auf den Angemessenheitsbeschluss stuetzt, erwaehnen Sie dies und verweisen Sie auf die DPF-Zertifizierung.",
}, },
# ── L1: Rechtsgrundlage ─────────────────────────────────────────── # ── L1: Rechtsgrundlage ───────────────────────────────────────────
@@ -191,6 +205,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"einwilligung.*art\.\s*6", r"lit\.\s*[a-f]", r"einwilligung.*art\.\s*6", r"lit\.\s*[a-f]",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Rechtsgrundlage fuer die Datenverarbeitung nach Art. 6 DSGVO fehlt. Nennen Sie die einschlaegige Rechtsgrundlage (z.B. berechtigtes Interesse nach Art. 6(1) lit. f oder Einwilligung nach Art. 6(1) lit. a).",
}, },
{ {
"id": "legal_basis_specific_lit", "id": "legal_basis_specific_lit",
@@ -200,6 +215,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"art\.\s*6\s*(?:abs\.\s*)?1\s*(?:s\.\s*1\s*)?(?:lit\.\s*)?[a-f]", r"art\.\s*6\s*(?:abs\.\s*)?1\s*(?:s\.\s*1\s*)?(?:lit\.\s*)?[a-f]",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Der konkrete Buchstabe in Art. 6(1) DSGVO ist nicht angegeben. Nennen Sie den spezifischen Erlaubnistatbestand (z.B. lit. a fuer Einwilligung oder lit. f fuer berechtigtes Interesse).",
}, },
# ── L1: Betroffenenrechte ───────────────────────────────────────── # ── L1: Betroffenenrechte ─────────────────────────────────────────
@@ -214,6 +230,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"widerspruchsrecht", r"widerspruchsrecht",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Betroffenenrechte nach Art. 15-21 DSGVO sind nicht aufgefuehrt. Listen Sie die Rechte auf (Auskunft, Berichtigung, Loeschung, Einschraenkung, Datenuebertragbarkeit, Widerspruch) und erklaeren Sie, wie Betroffene diese ausueben koennen.",
}, },
{ {
"id": "opt_out_social", "id": "opt_out_social",
@@ -225,6 +242,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:abmelden|abschalten).*(?:tracking|statistik|insight)", r"(?:abmelden|abschalten).*(?:tracking|statistik|insight)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Eine konkrete Opt-Out-Moeglichkeit fuer das Social-Media-Tracking fehlt. Beschreiben Sie, wie Nutzer dem Tracking widersprechen oder es deaktivieren koennen (z.B. ueber Plattform-Einstellungen oder Cookie-Opt-Out).",
}, },
# ── L1: Social Bookmarks vs Plugins ─────────────────────────────── # ── L1: Social Bookmarks vs Plugins ───────────────────────────────
@@ -238,6 +256,7 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:link|verweis|weiterleitung).*(?:dienst|anbieter|netzwerk)", r"(?:link|verweis|weiterleitung).*(?:dienst|anbieter|netzwerk)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Es fehlt ein Hinweis, ob Social-Media-Plugins oder nur einfache Links (Bookmarks) eingebunden sind. Stellen Sie klar, ob beim Seitenaufruf bereits Daten an die Plattformen uebertragen werden oder erst nach Klick.",
}, },
{ {
"id": "two_click_solution", "id": "two_click_solution",
@@ -249,5 +268,6 @@ JOINT_CONTROLLER_CHECKLIST = [
r"(?:erst|nur)\s+(?:bei|nach|durch)\s+(?:klick|aktivierung).*(?:daten|verbindung)", r"(?:erst|nur)\s+(?:bei|nach|durch)\s+(?:klick|aktivierung).*(?:daten|verbindung)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die verwendete technische Loesung (z.B. 2-Klick-Loesung oder Shariff) wird nicht beschrieben. Erlaeutern Sie, welche datenschutzfreundliche Technik eingesetzt wird, um den sofortigen Datentransfer an Plattformen zu verhindern.",
}, },
] ]
@@ -17,6 +17,7 @@ WIDERRUF_CHECKLIST = [
r"recht\s+(?:zum|auf)\s+widerruf", r"recht\s+(?:zum|auf)\s+widerruf",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die Widerrufsbelehrung muss ausdruecklich ueber das Bestehen eines Widerrufsrechts informieren. Ergaenzen Sie einen Abschnitt, der den Verbraucher klar auf sein Widerrufsrecht hinweist.",
}, },
# ── L1: Widerrufsfrist ──────────────────────────────────────────── # ── L1: Widerrufsfrist ────────────────────────────────────────────
@@ -29,6 +30,7 @@ WIDERRUF_CHECKLIST = [
r"14\s+days", r"fourteen\s+days", r"14\s+days", r"fourteen\s+days",
], ],
"severity": "HIGH", "severity": "HIGH",
"hint": "Die gesetzliche Widerrufsfrist von 14 Tagen muss explizit genannt werden. Fuegen Sie die Angabe '14 Tage' oder 'vierzehn Tage' in Ihre Widerrufsbelehrung ein.",
}, },
{ {
"id": "deadline_calendar_days", "id": "deadline_calendar_days",
@@ -39,6 +41,7 @@ WIDERRUF_CHECKLIST = [
r"14\s+calendar\s+days", r"14\s+calendar\s+days",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Zur Klarstellung sollte 'Kalendertage' statt nur 'Tage' angegeben werden. Aendern Sie die Formulierung zu '14 Kalendertage', um Missverstaendnisse auszuschliessen.",
}, },
{ {
"id": "deadline_receipt_trigger", "id": "deadline_receipt_trigger",
@@ -50,6 +53,7 @@ WIDERRUF_CHECKLIST = [
r"beginnt\s+(?:mit|ab)\s+(?:dem\s+)?(?:zugang|erhalt)", r"beginnt\s+(?:mit|ab)\s+(?:dem\s+)?(?:zugang|erhalt)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Der Fristbeginn muss klar definiert sein (z.B. ab Erhalt der Ware oder ab Vertragsschluss). Ergaenzen Sie eine Angabe wie 'Die Frist beginnt ab dem Tag des Erhalts der Ware'.",
}, },
# ── L1: Form des Widerrufs ──────────────────────────────────────── # ── L1: Form des Widerrufs ────────────────────────────────────────
@@ -62,6 +66,7 @@ WIDERRUF_CHECKLIST = [
r"withdrawal\s+form", r"formular", r"withdrawal\s+form", r"formular",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Widerrufsbelehrung muss ueber die Form des Widerrufs informieren und auf das Muster-Widerrufsformular hinweisen. Fuegen Sie einen entsprechenden Abschnitt mit Verweis auf das Formular hinzu.",
}, },
{ {
"id": "form_text_required", "id": "form_text_required",
@@ -72,6 +77,7 @@ WIDERRUF_CHECKLIST = [
r"(?:mittels|durch)\s+(?:einer?\s+)?(?:eindeutige|klare)\w*\s+erkl(?:ae|ä)rung", r"(?:mittels|durch)\s+(?:einer?\s+)?(?:eindeutige|klare)\w*\s+erkl(?:ae|ä)rung",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es sollte klargestellt werden, dass der Widerruf in Textform (z.B. per Brief, E-Mail oder Fax) erfolgen kann. Nennen Sie die zulaessigen Kommunikationswege ausdruecklich.",
}, },
{ {
"id": "model_form", "id": "model_form",
@@ -83,6 +89,7 @@ WIDERRUF_CHECKLIST = [
r"widerruf.*(?:beigef(?:ue|ü)gt|anlage|anhang|formular)", r"widerruf.*(?:beigef(?:ue|ü)gt|anlage|anhang|formular)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Das gesetzliche Muster-Widerrufsformular muss beigefuegt oder verlinkt sein. Fuegen Sie das Formular als Anlage bei oder verlinken Sie es direkt in der Belehrung.",
}, },
# ── L1: Folgen des Widerrufs ────────────────────────────────────── # ── L1: Folgen des Widerrufs ──────────────────────────────────────
@@ -96,6 +103,7 @@ WIDERRUF_CHECKLIST = [
r"r(?:ue|ü)ckerstattung", r"r(?:ue|ü)ckerstattung",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Folgen des Widerrufs (insbesondere die Rueckerstattungspflicht) muessen erlaeutert werden. Ergaenzen Sie einen Abschnitt 'Folgen des Widerrufs' mit Angaben zur Rueckabwicklung.",
}, },
{ {
"id": "refund_timeline", "id": "refund_timeline",
@@ -107,6 +115,7 @@ WIDERRUF_CHECKLIST = [
r"(?:unverz(?:ue|ü)glich|sp(?:ae|ä)testens).*(?:r(?:ue|ü)ck|erstatt)", r"(?:unverz(?:ue|ü)glich|sp(?:ae|ä)testens).*(?:r(?:ue|ü)ck|erstatt)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Die Rueckerstattung muss innerhalb von 14 Tagen nach Eingang des Widerrufs erfolgen. Geben Sie an, dass die Rueckzahlung spaetestens 14 Tage nach Zugang des Widerrufs erfolgt.",
}, },
{ {
"id": "return_costs", "id": "return_costs",
@@ -118,6 +127,7 @@ WIDERRUF_CHECKLIST = [
r"(?:tragen|uebernehmen|übernehmen)\s+(?:die\s+)?(?:kosten|r(?:ue|ü)cksende)", r"(?:tragen|uebernehmen|übernehmen)\s+(?:die\s+)?(?:kosten|r(?:ue|ü)cksende)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Es muss angegeben werden, wer die Kosten der Ruecksendung traegt. Stellen Sie klar, ob der Verbraucher oder der Unternehmer die Ruecksendekosten uebernimmt.",
}, },
# ── L1: Empfaenger des Widerrufs ────────────────────────────────── # ── L1: Empfaenger des Widerrufs ──────────────────────────────────
@@ -131,6 +141,7 @@ WIDERRUF_CHECKLIST = [
r"widerruf.*(?:per|via|an)", r"widerruf.*(?:per|via|an)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Name und Anschrift des Widerrufsempfaengers muessen angegeben werden. Fuegen Sie den vollstaendigen Firmennamen und die Postanschrift hinzu, an die der Widerruf zu richten ist.",
}, },
{ {
"id": "recipient_full_address", "id": "recipient_full_address",
@@ -141,6 +152,7 @@ WIDERRUF_CHECKLIST = [
r"(?:d[\-\s]?)?\d{5}\s+[a-z\u00c0-\u017e]\w+.*widerruf", r"(?:d[\-\s]?)?\d{5}\s+[a-z\u00c0-\u017e]\w+.*widerruf",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Die vollstaendige Postanschrift mit Postleitzahl und Ort fehlt beim Widerrufsempfaenger. Ergaenzen Sie Strasse, PLZ und Ort des Unternehmens in der Widerrufsbelehrung.",
}, },
# ── L1: Hinweis kein Grund erforderlich ─────────────────────────── # ── L1: Hinweis kein Grund erforderlich ───────────────────────────
@@ -153,6 +165,7 @@ WIDERRUF_CHECKLIST = [
r"(?:kein|keine).*(?:begr(?:ue|ü)ndung|grund).*(?:erforderlich|n(?:oe|ö)tig)", r"(?:kein|keine).*(?:begr(?:ue|ü)ndung|grund).*(?:erforderlich|n(?:oe|ö)tig)",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Der Verbraucher muss darauf hingewiesen werden, dass er keinen Grund fuer den Widerruf angeben muss. Ergaenzen Sie den Satz 'Der Widerruf muss nicht begruendet werden'.",
}, },
# ── L1: Online-Kuendigungsbutton ────────────────────────────────── # ── L1: Online-Kuendigungsbutton ──────────────────────────────────
@@ -166,6 +179,7 @@ WIDERRUF_CHECKLIST = [
r"k(?:ue|ü)ndigung.*(?:button|link|formular|online)", r"k(?:ue|ü)ndigung.*(?:button|link|formular|online)",
], ],
"severity": "MEDIUM", "severity": "MEDIUM",
"hint": "Seit Juli 2022 ist ein Online-Kuendigungsbutton fuer Dauerschuldverhaeltnisse Pflicht (§312k BGB). Stellen Sie auf Ihrer Website einen gut sichtbaren Kuendigungsbutton bereit.",
}, },
# ── Neue L1: Ausnahme digitale Inhalte ──────────────────────────── # ── Neue L1: Ausnahme digitale Inhalte ────────────────────────────
@@ -180,5 +194,6 @@ WIDERRUF_CHECKLIST = [
r"(?:ausnahme|ausschluss).*widerruf.*digital", r"(?:ausnahme|ausschluss).*widerruf.*digital",
], ],
"severity": "LOW", "severity": "LOW",
"hint": "Falls Sie digitale Inhalte verkaufen, muss auf den moeglichen Verlust des Widerrufsrechts nach §356 BGB hingewiesen werden. Ergaenzen Sie eine Belehrung ueber die Ausnahme fuer digitale Inhalte.",
}, },
] ]