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

18 KiB

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.

# 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)


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

# 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

# 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

# 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

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

# 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

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).

# 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:

[[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):

# 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