# 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
