# Plan: Compliance Agent — Vom PoC zum Produkt ## Kontext Der Compliance Agent PoC funktioniert: URL scannen, Qwen klassifiziert, UCCA bewertet, Dienstleister erkannt, Korrekturvorschlaege generiert, Email gesendet. Aber: Scores sind zu niedrig, zu viele False-Positive Controls, kein Consent-Test, keine Persistenz, keine PDF-Exports. Dieser Plan macht das PoC produktreif. **Strategische Bedeutung:** Erstmalig wird das RAG (166k Controls) gegen echte Webseiten getestet. Der Consent-Test (vor/nach Cookie-Einwilligung) waere ein Alleinstellungsmerkmal das kein Wettbewerber hat. --- ## Phase 0: UCCA Score-Kalibrierung (P0, 2-3 Tage) ### Problem Der UCCA-Score fuer Opodo war LOW (20/100). Realistisch waere MEDIUM (40-50). Die Intake-Flags werden aus dem Text extrahiert, aber zu wenige werden gesetzt. ### Loesung **0.1 Intelligentere Intake-Flag-Erkennung** Aktuell: einfache Keyword-Suche (`"werbung" in text.lower()`). Neu: LLM-gestuetzte Extraktion der Intake-Flags. ```python # Statt: "marketing": "werbung" in text.lower() # Neu: Qwen extrahiert strukturiert prompt = """ Analysiere diesen Text und setze folgende Flags auf true/false: - personal_data: Werden personenbezogene Daten verarbeitet? - customer_data: Werden Kundendaten gespeichert? - marketing: Werden Daten fuer Werbung/Marketing genutzt? - profiling: Findet Profiling oder Personalisierung statt? - minor_data: Werden Daten von Minderjaehrigen verarbeitet? - biometric_data: Werden biometrische Daten verarbeitet? - location_data: Werden Standortdaten erhoben? - third_party_sharing: Werden Daten an Dritte weitergegeben? - automated_decisions: Werden automatisierte Entscheidungen getroffen? - cross_border_transfer: Findet Drittlandtransfer statt? Antworte als JSON. """ ``` **0.2 Score-Gewichtung anpassen** Die UCCA-Engine (Go, `ai-compliance-sdk/internal/ucca/`) hat die Score-Berechnung. Pruefen ob die Gewichte realistisch sind: - Personenbezogene Daten allein = 10 Punkte (zu wenig) - Marketing + Drittlandtransfer + Profiling sollte mindestens +30 geben - Zahlungsdaten + Passdaten sollte +20 geben **Dateien:** - `backend-compliance/compliance/api/agent_analyze_routes.py` — Flag-Extraktion - `ai-compliance-sdk/internal/ucca/engine.go` — Score-Berechnung (Go) - `ai-compliance-sdk/internal/ucca/rules.go` — Regel-Definitionen **Testfaelle:** - Opodo: Soll MEDIUM (40-50) ergeben - Google: Soll HIGH (60-70) ergeben - Einfacher Blog ohne Tracking: Soll MINIMAL (0-10) ergeben --- ## Phase 1: Control Relevance Filter (P0, 1 Tag) ### Bereits geplant in PLAN-control-relevance-filter.md Nur Phase 1 (regelbasiert) hier umsetzen: 1. Neues `relevance_conditions` JSONB-Feld auf `canonical_controls` 2. Top-20 generische Controls mit Keywords versehen 3. Filter-Funktion im Agent: Control nur empfehlen wenn Keywords im Text vorkommen 4. Test: C_TRANSPARENCY faellt bei Opodo weg (kein KI-Nachweis) **Datei:** `backend-compliance/compliance/services/relevance_filter.py` (~200 LOC) --- ## Phase 2: Headless Browser Consent-Test (P1, 2-3 Tage) ### Das Killer-Feature Automatischer 3-Phasen-Test: ``` Phase A: Erster Besuch (ohne Interaktion) → Welche Scripts/Cookies laden VOR dem Consent-Banner? → Finding: "Script X laedt ohne Einwilligung" Phase B: Consent ablehnen ("Nur notwendige") → Button klicken, 3 Sek warten → Welche Scripts/Cookies laden NACH Ablehnung? → Finding: "Google Analytics laedt trotz Ablehnung" = schwerer Verstoss Phase C: Consent akzeptieren ("Alle akzeptieren") → Neuer Browser-Kontext, akzeptieren klicken → Welche Scripts/Cookies laden NACH Zustimmung? → Abgleich mit Cookie-Policy: "TikTok Pixel laedt, ist aber nicht dokumentiert" ``` ### Technische Implementierung **2.1 Playwright im Backend-Container** ```dockerfile # Ergaenzung im backend-compliance Dockerfile RUN pip install playwright && playwright install chromium ``` Alternativ: eigener `consent-tester` Service (besser isoliert, ~200MB Image). **2.2 Consent Test Service** ```python # backend-compliance/compliance/services/consent_tester.py (~250 LOC) class ConsentTester: async def test(self, url: str) -> ConsentTestResult: async with async_playwright() as p: browser = await p.chromium.launch(headless=True) # Phase A: Ohne Consent pre = await self._scan_phase(browser, url, action=None) # Phase B: Ablehnen reject = await self._scan_phase(browser, url, action="reject") # Phase C: Akzeptieren accept = await self._scan_phase(browser, url, action="accept") await browser.close() return ConsentTestResult( banner_detected=pre.banner_visible, banner_type=pre.banner_provider, # Didomi, OneTrust, Cookiebot etc. scripts_before_consent=pre.scripts, cookies_before_consent=pre.cookies, violations_before_consent=self._find_violations(pre), scripts_after_reject=reject.scripts, cookies_after_reject=reject.cookies, violations_after_reject=self._find_violations(reject), scripts_after_accept=accept.scripts, cookies_after_accept=accept.cookies, undocumented_after_accept=self._find_undocumented(accept, dse_text), ) ``` **2.3 Banner-Button-Erkennung** ```python # Typische Consent-Banner Button-Patterns ACCEPT_PATTERNS = [ 'button:has-text("Alle akzeptieren")', 'button:has-text("Alles akzeptieren")', 'button:has-text("Accept all")', 'button:has-text("Alle Cookies akzeptieren")', '[class*="accept-all"]', '[data-action="accept-all"]', '#didomi-notice-agree-button', # Didomi '.cky-btn-accept', # CookieYes '#onetrust-accept-btn-handler', # OneTrust '#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll', # Cookiebot ] REJECT_PATTERNS = [ 'button:has-text("Nur notwendige")', 'button:has-text("Ablehnen")', 'button:has-text("Reject")', 'button:has-text("Nur essentielle")', '[class*="reject"]', '#didomi-notice-disagree-button', '#onetrust-reject-all-handler', '#CybotCookiebotDialogBodyButtonDecline', ] ``` **2.4 Violation-Erkennung** ```python def _find_violations(self, phase: ScanPhase) -> list[Violation]: violations = [] for script in phase.scripts: service = match_service(script) # Gegen SERVICE_REGISTRY if service and service.requires_consent: if phase.action is None: # Script laedt VOR Consent → Verstoss violations.append(Violation( severity="HIGH", service=service.name, legal_ref="§25 TDDDG", text=f"{service.name} laedt OHNE vorherige Einwilligung", )) elif phase.action == "reject": # Script laedt NACH Ablehnung → schwerer Verstoss violations.append(Violation( severity="CRITICAL", service=service.name, legal_ref="§25 TDDDG, Art. 5(3) ePrivacy", text=f"{service.name} laedt TROTZ Ablehnung — Dark Pattern", )) return violations ``` **2.5 Frontend: Consent-Test Tab** Dritter Tab im Agent: "Schnellanalyse | Website-Scan | Cookie-Test" Anzeige: ``` Cookie-Consent-Test: opodo.de ═══════════════════════════════ Banner erkannt: Ja (Didomi) Phase A: Vor Einwilligung ✗ Google Analytics — laedt ohne Einwilligung (§25 TDDDG) ✓ Didomi CMP — notwendig, OK ✗ Google Tag Manager — laedt ohne Einwilligung Phase B: Nach Ablehnung ("Nur notwendige") ✗ Google Analytics — laedt TROTZ Ablehnung (KRITISCH!) ✓ Keine neuen Tracking-Cookies gesetzt Phase C: Nach Zustimmung ("Alle akzeptieren") ✓ Google Analytics — jetzt aktiv (mit Consent OK) ✓ Didomi Consent Cookie gesetzt ✗ TikTok Pixel — nicht in Cookie-Policy dokumentiert Zusammenfassung: 2 kritische Verstoesse (Tracking ohne/trotz Ablehnung) 1 Dokumentationsluecke (TikTok nicht in Policy) ``` **2.6 Neuer Endpoint** ``` POST /api/compliance/agent/consent-test Body: { "url": "https://www.opodo.de" } Response: ConsentTestResult (3 Phasen + Violations) ``` **Dateien:** - `backend-compliance/compliance/services/consent_tester.py` — Playwright Test (~250 LOC) - `backend-compliance/compliance/api/agent_consent_routes.py` — Endpoint (~100 LOC) - `admin-compliance/app/sdk/agent/_components/ConsentTestResult.tsx` — UI (~150 LOC) - `admin-compliance/app/api/sdk/v1/agent/consent-test/route.ts` — Proxy (~35 LOC) **Aufwand:** 2-3 Tage (inkl. Playwright Setup + Button-Erkennung) --- ## Phase 3: Dienstleister-Registry erweitern (P1, 1 Tag) ### Aktuell: ~20 Services in website_scanner.py ### Ziel: 80+ Services Neue Kategorien: - **Newsletter/Email Marketing** — Mailchimp, Brevo, CleverReach, ActiveCampaign, HubSpot, Rapidmail - **Social Media Embeds** — Twitter/X, Instagram, LinkedIn, Pinterest, TikTok - **A/B Testing** — Optimizely, VWO, Google Optimize (Legacy) - **Heatmaps/Session Recording** — FullStory, Mouseflow, Crazy Egg, Lucky Orange - **Werbenetwerke** — Google Ads, Meta Ads, TikTok Ads, Criteo, Taboola, Outbrain - **Tag Manager** — Google, Tealium, Segment - **CRM** — HubSpot, Salesforce Pardot, Pipedrive - **Push Notifications** — OneSignal, Pushwoosh, Firebase - **Customer Support** — Freshdesk, Zendesk (erweitern), HelpScout - **Cloud/CDN** — AWS CloudFront, Azure CDN, Vercel, Netlify - **Error Tracking** — Sentry, Bugsnag, Datadog RUM, New Relic Jeder Eintrag: Regex, Provider, Land, EU-Adaequanz, Consent-Pflicht, Rechtsgrundlage. **Datei:** `backend-compliance/compliance/services/website_scanner.py` — SERVICE_REGISTRY erweitern Evtl. in eigene Datei auslagern: `service_registry.py` (~300 LOC, reine Daten) --- ## Phase 4: Scan beschleunigen (P2, 1 Tag) ### Problem Aktuell: Seiten sequentiell fetchen + 3-4 LLM-Calls = 3-5 Minuten. ### Loesung **4.1 Parallel Fetchen** ```python # Statt sequentiell: for url in pages: html = await fetch(url) # Parallel: htmls = await asyncio.gather(*[fetch(url) for url in pages]) ``` **4.2 Qwen-Calls reduzieren** - DSE-Extraktion: Nur wenn Datenschutzseite gefunden - Korrekturvorschlaege: Nur fuer HIGH-Severity Findings (nicht fuer alle) - Batch: Alle Korrekturen in einem LLM-Call statt einzeln **4.3 Kleineres Modell fuer Klassifizierung** - Qwen 2.5:14b statt 3.5:35b fuer einfache Klassifizierung (~5x schneller) - 3.5:35b nur fuer Korrekturvorschlaege und DSE-Extraktion **Ziel:** Scan in <60 Sekunden statt 3-5 Minuten. --- ## Phase 5: Persistenz — Ergebnisse in DB speichern (P2, 1 Tag) ### Problem Ergebnisse sind aktuell nur im Browser-Session-State und in Mailpit-Emails. Bei Seitenreload oder neuem Tab: alles weg. ### Loesung **5.1 Neue DB-Tabelle** ```sql CREATE TABLE compliance_agent_scans ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, user_id TEXT NOT NULL, url TEXT NOT NULL, mode TEXT NOT NULL, -- 'quick', 'scan', 'consent_test' analysis_mode TEXT NOT NULL, -- 'pre_launch', 'post_launch' classification TEXT, risk_level TEXT, risk_score FLOAT, escalation_level TEXT, services JSONB DEFAULT '[]', findings JSONB DEFAULT '[]', corrections JSONB DEFAULT '[]', consent_test JSONB, -- Phase 2 Ergebnisse summary_html TEXT, email_sent BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_agent_scans_tenant ON compliance_agent_scans(tenant_id); CREATE INDEX idx_agent_scans_url ON compliance_agent_scans(url); ``` **5.2 Frontend: Scan-Verlauf aus DB laden** Statt Session-basierter History → API-Call: `GET /api/compliance/agent/scans` Sortiert nach Datum, filterbar nach URL/Risiko/Typ. **Dateien:** - `backend-compliance/compliance/api/agent_scan_routes.py` — DB-Save nach jedem Scan - `backend-compliance/compliance/db/agent_scan_models.py` — SQLAlchemy Model (~40 LOC) - Migration SQL --- ## Phase 6: PDF-Export (P2, 0.5 Tage) ### Nutzen Manager wollen druckbare Reports, nicht nur Emails. ### Implementierung ReportLab oder WeasyPrint (bereits im Backend vorhanden fuer andere PDF-Exports). ```python # Neuer Endpoint GET /api/compliance/agent/scans/{id}/pdf # Generiert PDF mit: # - Deckblatt (Firmenlogo, Datum, URL) # - Executive Summary (Risiko-Ampel, Rolle) # - Findings-Tabelle # - Dienstleister-Abgleich (SOLL/IST) # - Consent-Test Ergebnisse (wenn vorhanden) # - Korrekturvorschlaege # - Anhang: Gescannte Seiten, Rechtsgrundlagen ``` **Datei:** `backend-compliance/compliance/services/agent_pdf_export.py` (~200 LOC) --- ## Phase 7: Recurring Scans / ZeroClaw (P3, 1 Tag) ### Nutzen Website aendert sich → neue Dienstleister eingebunden → automatisches Alert. ### Implementierung ZeroClaw SOP mit Cron-Trigger: ```toml [[triggers]] type = "cron" schedule = "0 6 * * 1" # Jeden Montag 06:00 ``` Der Agent: 1. Laedt alle gespeicherten URLs aus der DB 2. Scannt jede URL 3. Vergleicht mit letztem Scan-Ergebnis 4. Bei Aenderungen: Email an DSB mit Diff **Oder:** Einfacher Cron-Job im Backend (kein ZeroClaw noetig): ```python # backend-compliance/compliance/services/recurring_scan.py async def run_weekly_scans(): scans = await db.get_all_monitored_urls() for scan in scans: result = await analyze(scan.url, scan.mode) if has_changes(result, scan.last_result): await send_change_alert(scan, result) ``` --- ## Phase 8: Multi-Website Vergleich (P3, 1 Tag) ### Nutzen "Wie steht mein Unternehmen im Vergleich zu 5 Wettbewerbern?" ### Implementierung Frontend: Mehrere URLs eingeben → paralleler Scan → Vergleichstabelle: ``` | Meine Firma | Opodo | Booking | Expedia | Datenschutzerkl. | ✓ | ✓ | ✓ | ✓ | Impressum | ✓ | ✗ (404) | ✓ | ✓ | Cookie-Banner | ✓ | ✓ | ✓ | ✗ | Google Fonts lokal | ✓ | ✗ | ✓ | ✗ | Kuendigungsbutton | ✓ | ✗ | n/a | n/a | Tracking vor Consent| ✗ | ✗ | ✓ | ✗ | Risiko-Score | 15/100 | 45/100 | 20/100 | 55/100 | ``` **Endpoint:** `POST /api/compliance/agent/compare` **Body:** `{ "urls": ["url1", "url2", "url3"] }` --- ## Implementierungsreihenfolge | Woche | Phase | Was | Ergebnis | |-------|-------|-----|----------| | 1 | Phase 0 | UCCA Score-Kalibrierung | Realistische Risiko-Scores | | 1 | Phase 1 | Control Relevance Filter | Keine False Positives mehr | | 2 | Phase 2 | Consent-Test (Playwright) | Killer-Feature, vor/nach Einwilligung | | 2 | Phase 3 | Registry 80+ Services | Umfassende Dienstleister-Erkennung | | 3 | Phase 4 | Scan beschleunigen | <60 Sekunden statt 3-5 Minuten | | 3 | Phase 5 | DB-Persistenz | Scan-Verlauf, keine verlorenen Ergebnisse | | 4 | Phase 6 | PDF-Export | Druckbare Reports fuer Management | | 4 | Phase 7 | Recurring Scans | Automatische Ueberwachung | | 5 | Phase 8 | Multi-Website Vergleich | Wettbewerber-Benchmark | | 6 | Phase 9 | Authenticated Testing | Login-Bereich pruefen (§312k, Art. 17, 20) | --- ## Phase 9: Authenticated Website Testing (P3, 2 Tage) ### Konzept Ein DSB gibt seine eigenen Credentials im SDK ein. Playwright loggt sich ein und prueft den Kundenbereich auf Pflichtfunktionen: ### Pruefbare Rechte nach Login | Pruefung | Rechtsgrundlage | Methode | |----------|----------------|---------| | Kuendigungsbutton (2 Klicks) | §312k BGB | Navigation suchen, Klicks zaehlen | | Konto loeschen | Art. 17 DSGVO | "Konto loeschen" Button suchen | | Daten exportieren | Art. 20 DSGVO | "Daten herunterladen" suchen | | Einwilligungen widerrufen | Art. 7(3) DSGVO | Consent-Einstellungen suchen | | Profildaten einsehen | Art. 15 DSGVO | Profil-/Kontobereich pruefen | ### Sicherheit - Credentials werden NUR fuer die Dauer des Tests im Browser-Kontext gehalten - Kein Speichern in DB, kein Logging, kein Senden an Dritte - Nach Test: Browser-Kontext wird zerstoert, Credentials verworfen - HTTPS-only (kein HTTP-Login) ### Implementierung - Erweiterung des `consent-tester` Service um Login-Flow - Neuer Tab im Frontend: "Authentifizierter Test" - Credential-Eingabe als einmalige Formularfelder (nicht gespeichert) - Screenshots als Belege fuer den Report ### Dateien | Datei | LOC | Zweck | |-------|-----|-------| | `consent-tester/services/authenticated_scanner.py` | ~200 | Login + Kundenbereich-Checks | | `consent-tester/main.py` | +30 | Neuer /authenticated-scan Endpoint | | Frontend: AuthenticatedTestTab | ~150 | Credential-Eingabe + Ergebnis | --- ## Phase 10: Website-Scan auf Playwright umstellen (P3, 1-2 Tage) ### Problem Der Website-Scan nutzt httpx (wie curl) — bekommt nur initiales HTML. SPAs (React, Angular, Vue) die Inhalte per JavaScript nachladen werden unvollstaendig gescannt. Opodo-Stil Seiten liefern nur Shell-HTML. ### Loesung Website-Scanner auf Playwright umstellen — gleicher Headless Browser wie der Consent-Tester. Dann sieht der Scan ALLES was der Browser sieht. | Technologie | Aktuell (httpx) | Nach Phase 10 (Playwright) | |------------|-----------------|---------------------------| | Statisches HTML | ✓ | ✓ | | WordPress | ✓ | ✓ | | React/Vue SPA | ✗ (nur Shell) | ✓ (rendert JS) | | Angular SSR | ✗/✓ | ✓ | | JS-heavy (Opodo) | ✗ | ✓ | ### Implementierung - `consent-tester` Service um `/website-scan` Endpoint erweitern - Playwright navigiert zu jeder Seite, wartet auf JS, extrahiert HTML - Backend-Scanner ruft consent-tester statt httpx auf - Gleicher Output (DetectedService, ScanResult) — nur bessere Eingabedaten ## Investoren-Demo Szenario Nach Phase 2 (Woche 2) koennen wir folgende Demo zeigen: 1. **URL eingeben:** `https://www.opodo.de` 2. **Website-Scan:** 6 Seiten gescannt, Google Analytics + Fonts + GTM erkannt 3. **SOLL/IST:** 3 Dienste NICHT in DSE dokumentiert → Art. 13 Verstoss 4. **Consent-Test:** Google Analytics laedt VOR Einwilligung → §25 TDDDG Verstoss 5. **Consent ablehnen:** Analytics laedt TROTZ Ablehnung → KRITISCH 6. **Score:** MEDIUM (45/100) mit 5 Findings 7. **Korrekturvorschlag:** Einbaufertige DSE-Textbausteine per Qwen 8. **Email an DSB:** Formatierter HTML-Report mit Handlungsanweisung 9. **Vergleich:** Opodo vs. Booking.com — wer ist besser aufgestellt? Das demonstriert: - RAG funktioniert gegen echte Systeme (166k Controls) - LLM generiert juristische Textbausteine - Automatisierte Compliance-Pruefung in <60 Sekunden - Consent-Test den kein Wettbewerber hat