'use client' import { useState, useEffect } from 'react' interface PIIRule { id: string name: string description?: string pattern?: string category: string action: string active: boolean created_at: string } interface PIIMatch { rule_id: string rule_name: string category: string action: string match: string start_index: number end_index: number } interface PIITestResult { has_pii: boolean matches: PIIMatch[] should_block: boolean } interface PIIRulesTabProps { apiBase: string onUpdate?: () => void } const CATEGORIES = [ { value: 'email', label: 'E-Mail-Adressen' }, { value: 'phone', label: 'Telefonnummern' }, { value: 'iban', label: 'IBAN/Bankdaten' }, { value: 'name', label: 'Personennamen' }, { value: 'address', label: 'Adressen' }, { value: 'id_number', label: 'Ausweisnummern' }, { value: 'health', label: 'Gesundheitsdaten' }, { value: 'other', label: 'Sonstige' }, ] const ACTIONS = [ { value: 'warn', label: 'Warnung', color: 'bg-amber-100 text-amber-700' }, { value: 'mask', label: 'Maskieren', color: 'bg-orange-100 text-orange-700' }, { value: 'block', label: 'Blockieren', color: 'bg-red-100 text-red-700' }, ] export function PIIRulesTab({ apiBase, onUpdate }: PIIRulesTabProps) { const [rules, setRules] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) // Category filter const [categoryFilter, setCategoryFilter] = useState('') // Test panel const [testText, setTestText] = useState('') const [testResult, setTestResult] = useState(null) const [testing, setTesting] = useState(false) // Edit modal const [editingRule, setEditingRule] = useState(null) const [isNewRule, setIsNewRule] = useState(false) const [saving, setSaving] = useState(false) // New rule form const [newRule, setNewRule] = useState({ name: '', pattern: '', category: 'email', action: 'block', active: true, }) useEffect(() => { fetchRules() }, [categoryFilter]) const fetchRules = async () => { try { setLoading(true) const params = new URLSearchParams() if (categoryFilter) params.append('category', categoryFilter) const res = await fetch(`${apiBase}/pii-rules?${params}`) if (!res.ok) throw new Error('Fehler beim Laden') const data = await res.json() setRules(data.rules || []) } catch (err) { setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } finally { setLoading(false) } } const createRule = async () => { try { setSaving(true) const res = await fetch(`${apiBase}/pii-rules`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newRule), }) if (!res.ok) throw new Error('Fehler beim Erstellen') setNewRule({ name: '', pattern: '', category: 'email', action: 'block', active: true, }) setIsNewRule(false) fetchRules() onUpdate?.() } catch (err) { setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } finally { setSaving(false) } } const updateRule = async () => { if (!editingRule) return try { setSaving(true) const res = await fetch(`${apiBase}/pii-rules/${editingRule.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(editingRule), }) if (!res.ok) throw new Error('Fehler beim Aktualisieren') setEditingRule(null) fetchRules() onUpdate?.() } catch (err) { setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } finally { setSaving(false) } } const deleteRule = async (id: string) => { if (!confirm('Regel wirklich loeschen? Diese Aktion wird im Audit-Log protokolliert.')) return try { const res = await fetch(`${apiBase}/pii-rules/${id}`, { method: 'DELETE', }) if (!res.ok) throw new Error('Fehler beim Loeschen') fetchRules() onUpdate?.() } catch (err) { setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } } const toggleRuleStatus = async (rule: PIIRule) => { try { const res = await fetch(`${apiBase}/pii-rules/${rule.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ active: !rule.active }), }) if (!res.ok) throw new Error('Fehler beim Aendern des Status') fetchRules() onUpdate?.() } catch (err) { setError(err instanceof Error ? err.message : 'Unbekannter Fehler') } } const runTest = () => { if (!testText) return setTesting(true) const matches: PIIMatch[] = [] const activeRules = rules.filter((r) => r.active && r.pattern) for (const rule of activeRules) { try { const regex = new RegExp(rule.pattern!, 'gi') let m: RegExpExecArray | null while ((m = regex.exec(testText)) !== null) { matches.push({ rule_id: rule.id, rule_name: rule.name, category: rule.category, action: rule.action, match: m[0], start_index: m.index, end_index: m.index + m[0].length, }) } } catch { // Invalid regex — skip } } const shouldBlock = matches.some((m) => m.action === 'block') setTestResult({ has_pii: matches.length > 0, matches, should_block: shouldBlock, }) setTesting(false) } const getActionBadge = (action: string) => { const config = ACTIONS.find((a) => a.value === action) return ( {config?.label || action} ) } return (
{/* Error Display */} {error && (
{error}
)} {/* Test Panel */}

PII-Test

Testen Sie, ob ein Text personenbezogene Daten (PII) enthaelt.