Files
breakpilot-compliance/zeroclaw/PLAN-compliance-agent-product.md
T
Benjamin Admin e35db90232 feat: Phase 5 — DB persistence for scan results + Phase 10 in plan
- Migration 086: compliance_agent_scans table (findings, services, corrections)
- agent_history_routes.py: POST /scans (save), GET /scans (list), GET /scans/{id}
- Scan results survive page reloads and can be reviewed later
- Phase 10 (Playwright website scanner) added to product roadmap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 15:17:51 +02:00

561 lines
18 KiB
Markdown

# 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