/** * Incident/Breach Management API Client * * API client for DSGVO Art. 33/34 Incident & Data Breach Management * Connects via Next.js proxy to the ai-compliance-sdk backend */ import { Incident, IncidentListResponse, IncidentFilters, IncidentCreateRequest, IncidentUpdateRequest, IncidentStatistics, IncidentMeasure, TimelineEntry, RiskAssessmentRequest, RiskAssessment, AuthorityNotification, DataSubjectNotification, IncidentSeverity, IncidentStatus, IncidentCategory, calculateRiskLevel, isNotificationRequired, get72hDeadline } from './types' // ============================================================================= // CONFIGURATION // ============================================================================= const INCIDENTS_API_BASE = process.env.NEXT_PUBLIC_SDK_API_URL || 'http://localhost:8093' const API_TIMEOUT = 30000 // 30 seconds // ============================================================================= // HELPER FUNCTIONS // ============================================================================= function getTenantId(): string { if (typeof window !== 'undefined') { return localStorage.getItem('bp_tenant_id') || 'default-tenant' } return 'default-tenant' } function getAuthHeaders(): HeadersInit { const headers: HeadersInit = { 'Content-Type': 'application/json', 'X-Tenant-ID': getTenantId() } if (typeof window !== 'undefined') { const token = localStorage.getItem('authToken') if (token) { headers['Authorization'] = `Bearer ${token}` } const userId = localStorage.getItem('bp_user_id') if (userId) { headers['X-User-ID'] = userId } } return headers } async function fetchWithTimeout( url: string, options: RequestInit = {}, timeout: number = API_TIMEOUT ): Promise { const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), timeout) try { const response = await fetch(url, { ...options, signal: controller.signal, headers: { ...getAuthHeaders(), ...options.headers } }) if (!response.ok) { const errorBody = await response.text() let errorMessage = `HTTP ${response.status}: ${response.statusText}` try { const errorJson = JSON.parse(errorBody) errorMessage = errorJson.error || errorJson.message || errorMessage } catch { // Keep the HTTP status message } throw new Error(errorMessage) } // Handle empty responses const contentType = response.headers.get('content-type') if (contentType && contentType.includes('application/json')) { return response.json() } return {} as T } finally { clearTimeout(timeoutId) } } // ============================================================================= // INCIDENT LIST & CRUD // ============================================================================= /** * Alle Vorfaelle abrufen mit optionalen Filtern */ export async function fetchIncidents(filters?: IncidentFilters): Promise { const params = new URLSearchParams() if (filters) { if (filters.status) { const statuses = Array.isArray(filters.status) ? filters.status : [filters.status] statuses.forEach(s => params.append('status', s)) } if (filters.severity) { const severities = Array.isArray(filters.severity) ? filters.severity : [filters.severity] severities.forEach(s => params.append('severity', s)) } if (filters.category) { const categories = Array.isArray(filters.category) ? filters.category : [filters.category] categories.forEach(c => params.append('category', c)) } if (filters.assignedTo) params.set('assignedTo', filters.assignedTo) if (filters.overdue !== undefined) params.set('overdue', String(filters.overdue)) if (filters.search) params.set('search', filters.search) if (filters.dateFrom) params.set('dateFrom', filters.dateFrom) if (filters.dateTo) params.set('dateTo', filters.dateTo) } const queryString = params.toString() const url = `${INCIDENTS_API_BASE}/api/v1/incidents${queryString ? `?${queryString}` : ''}` return fetchWithTimeout(url) } /** * Einzelnen Vorfall per ID abrufen */ export async function fetchIncident(id: string): Promise { return fetchWithTimeout(`${INCIDENTS_API_BASE}/api/v1/incidents/${id}`) } /** * Neuen Vorfall erstellen */ export async function createIncident(request: IncidentCreateRequest): Promise { return fetchWithTimeout(`${INCIDENTS_API_BASE}/api/v1/incidents`, { method: 'POST', body: JSON.stringify(request) }) } /** * Vorfall aktualisieren */ export async function updateIncident(id: string, update: IncidentUpdateRequest): Promise { return fetchWithTimeout(`${INCIDENTS_API_BASE}/api/v1/incidents/${id}`, { method: 'PUT', body: JSON.stringify(update) }) } /** * Vorfall loeschen (Soft Delete) */ export async function deleteIncident(id: string): Promise { await fetchWithTimeout(`${INCIDENTS_API_BASE}/api/v1/incidents/${id}`, { method: 'DELETE' }) } // ============================================================================= // RISK ASSESSMENT // ============================================================================= /** * Risikobewertung fuer einen Vorfall durchfuehren (Art. 33 DSGVO) */ export async function submitRiskAssessment( incidentId: string, assessment: RiskAssessmentRequest ): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/risk-assessment`, { method: 'POST', body: JSON.stringify(assessment) } ) } // ============================================================================= // AUTHORITY NOTIFICATION (Art. 33 DSGVO) // ============================================================================= /** * Meldeformular fuer die Aufsichtsbehoerde generieren */ export async function generateAuthorityForm(incidentId: string): Promise { const response = await fetch( `${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/authority-form/pdf`, { headers: getAuthHeaders() } ) if (!response.ok) { throw new Error(`PDF-Generierung fehlgeschlagen: ${response.statusText}`) } return response.blob() } /** * Meldung an die Aufsichtsbehoerde einreichen (Art. 33 DSGVO) */ export async function submitAuthorityNotification( incidentId: string, data: Partial ): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/authority-notification`, { method: 'POST', body: JSON.stringify(data) } ) } // ============================================================================= // DATA SUBJECT NOTIFICATION (Art. 34 DSGVO) // ============================================================================= /** * Betroffene Personen benachrichtigen (Art. 34 DSGVO) */ export async function sendDataSubjectNotification( incidentId: string, data: Partial ): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/data-subject-notification`, { method: 'POST', body: JSON.stringify(data) } ) } // ============================================================================= // MEASURES (Massnahmen) // ============================================================================= /** * Massnahme hinzufuegen (Sofort-, Korrektur- oder Praeventionsmassnahme) */ export async function addMeasure( incidentId: string, measure: Omit ): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/measures`, { method: 'POST', body: JSON.stringify(measure) } ) } /** * Massnahme aktualisieren */ export async function updateMeasure( measureId: string, update: Partial ): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/measures/${measureId}`, { method: 'PUT', body: JSON.stringify(update) } ) } /** * Massnahme als abgeschlossen markieren */ export async function completeMeasure(measureId: string): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/measures/${measureId}/complete`, { method: 'POST' } ) } // ============================================================================= // TIMELINE // ============================================================================= /** * Zeitleisteneintrag hinzufuegen */ export async function addTimelineEntry( incidentId: string, entry: Omit ): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/timeline`, { method: 'POST', body: JSON.stringify(entry) } ) } // ============================================================================= // CLOSE INCIDENT // ============================================================================= /** * Vorfall abschliessen mit Lessons Learned */ export async function closeIncident( incidentId: string, lessonsLearned: string ): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/close`, { method: 'POST', body: JSON.stringify({ lessonsLearned }) } ) } // ============================================================================= // STATISTICS // ============================================================================= /** * Vorfall-Statistiken abrufen */ export async function fetchIncidentStatistics(): Promise { return fetchWithTimeout( `${INCIDENTS_API_BASE}/api/v1/incidents/statistics` ) } // ============================================================================= // SDK PROXY FUNCTION (mit Fallback auf Mock-Daten) // ============================================================================= /** * Fetch Incident-Liste via SDK-Proxy mit Fallback auf Mock-Daten */ export async function fetchSDKIncidentList(): Promise<{ incidents: Incident[]; statistics: IncidentStatistics }> { try { const res = await fetch('/api/sdk/v1/incidents', { headers: getAuthHeaders() }) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } const data = await res.json() const incidents: Incident[] = data.incidents || [] // Statistiken lokal berechnen const statistics = computeStatistics(incidents) return { incidents, statistics } } catch (error) { console.warn('SDK-Backend nicht erreichbar, verwende Mock-Daten:', error) const incidents = createMockIncidents() const statistics = createMockStatistics() return { incidents, statistics } } } /** * Statistiken lokal aus Incident-Liste berechnen */ function computeStatistics(incidents: Incident[]): IncidentStatistics { const countBy = (items: { [key: string]: unknown }[], field: string): Record => { const result: Record = {} items.forEach(item => { const key = String(item[field]) result[key] = (result[key] || 0) + 1 }) return result as Record } const statusCounts = countBy(incidents as unknown as { [key: string]: unknown }[], 'status') const severityCounts = countBy(incidents as unknown as { [key: string]: unknown }[], 'severity') const categoryCounts = countBy(incidents as unknown as { [key: string]: unknown }[], 'category') const openIncidents = incidents.filter(i => i.status !== 'closed').length const notificationsPending = incidents.filter(i => i.authorityNotification !== null && i.authorityNotification.status === 'pending' && i.status !== 'closed' ).length // Durchschnittliche Reaktionszeit berechnen let totalResponseHours = 0 let respondedCount = 0 incidents.forEach(i => { if (i.riskAssessment && i.riskAssessment.assessedAt) { const detected = new Date(i.detectedAt).getTime() const assessed = new Date(i.riskAssessment.assessedAt).getTime() totalResponseHours += (assessed - detected) / (1000 * 60 * 60) respondedCount++ } }) return { totalIncidents: incidents.length, openIncidents, notificationsPending, averageResponseTimeHours: respondedCount > 0 ? Math.round(totalResponseHours / respondedCount * 10) / 10 : 0, bySeverity: { low: severityCounts['low'] || 0, medium: severityCounts['medium'] || 0, high: severityCounts['high'] || 0, critical: severityCounts['critical'] || 0 }, byCategory: { data_breach: categoryCounts['data_breach'] || 0, unauthorized_access: categoryCounts['unauthorized_access'] || 0, data_loss: categoryCounts['data_loss'] || 0, system_compromise: categoryCounts['system_compromise'] || 0, phishing: categoryCounts['phishing'] || 0, ransomware: categoryCounts['ransomware'] || 0, insider_threat: categoryCounts['insider_threat'] || 0, physical_breach: categoryCounts['physical_breach'] || 0, other: categoryCounts['other'] || 0 }, byStatus: { detected: statusCounts['detected'] || 0, assessment: statusCounts['assessment'] || 0, containment: statusCounts['containment'] || 0, notification_required: statusCounts['notification_required'] || 0, notification_sent: statusCounts['notification_sent'] || 0, remediation: statusCounts['remediation'] || 0, closed: statusCounts['closed'] || 0 } } } // ============================================================================= // MOCK DATA (Demo-Daten fuer Entwicklung und Tests) // ============================================================================= /** * Erstellt Demo-Vorfaelle fuer die Entwicklung */ export function createMockIncidents(): Incident[] { const now = new Date() return [ // 1. Gerade erkannt - noch nicht bewertet (detected/new) { id: 'inc-001', referenceNumber: 'INC-2026-000001', title: 'Unbefugter Zugriff auf Schuelerdatenbank', description: 'Ein ehemaliger Mitarbeiter hat sich mit noch aktiven Zugangsdaten in die Schuelerdatenbank eingeloggt. Der Zugriff wurde durch die Log-Analyse entdeckt.', category: 'unauthorized_access', severity: 'high', status: 'detected', detectedAt: new Date(now.getTime() - 3 * 60 * 60 * 1000).toISOString(), // 3 Stunden her detectedBy: 'Log-Analyse (automatisiert)', affectedSystems: ['Schuelerdatenbank', 'Schulverwaltungssystem'], affectedDataCategories: ['Personenbezogene Daten', 'Daten von Kindern', 'Gesundheitsdaten'], estimatedAffectedPersons: 800, riskAssessment: null, authorityNotification: null, dataSubjectNotification: null, measures: [], timeline: [ { id: 'tl-001', incidentId: 'inc-001', timestamp: new Date(now.getTime() - 3 * 60 * 60 * 1000).toISOString(), action: 'Vorfall erkannt', description: 'Automatische Log-Analyse meldet verdaechtigen Login eines deaktivierten Kontos', performedBy: 'SIEM-System' } ], assignedTo: undefined }, // 2. In Bewertung (assessment) - Risikobewertung laeuft { id: 'inc-002', referenceNumber: 'INC-2026-000002', title: 'E-Mail mit Kundendaten an falschen Empfaenger', description: 'Ein Mitarbeiter hat eine Excel-Datei mit Kundendaten (Name, Adresse, Vertragsnummer) an einen falschen E-Mail-Empfaenger gesendet. Der Empfaenger wurde kontaktiert und hat die Loeschung bestaetigt.', category: 'data_breach', severity: 'medium', status: 'assessment', detectedAt: new Date(now.getTime() - 18 * 60 * 60 * 1000).toISOString(), // 18 Stunden her detectedBy: 'Vertriebsabteilung', affectedSystems: ['E-Mail-System (Exchange)'], affectedDataCategories: ['Personenbezogene Daten', 'Kundendaten'], estimatedAffectedPersons: 150, riskAssessment: { id: 'ra-002', assessedBy: 'DSB Mueller', assessedAt: new Date(now.getTime() - 12 * 60 * 60 * 1000).toISOString(), likelihoodScore: 3, impactScore: 2, overallRisk: 'medium', notificationRequired: false, reasoning: 'Empfaenger hat Loeschung bestaetigt. Datenkategorie: allgemeine Kontaktdaten und Vertragsnummern. Geringes Risiko fuer betroffene Personen.' }, authorityNotification: { id: 'an-002', authority: 'LfD Niedersachsen', deadline72h: new Date(new Date(now.getTime() - 18 * 60 * 60 * 1000).getTime() + 72 * 60 * 60 * 1000).toISOString(), status: 'pending', formData: {} }, dataSubjectNotification: null, measures: [ { id: 'meas-001', incidentId: 'inc-002', title: 'Empfaenger kontaktiert', description: 'Falscher Empfaenger kontaktiert mit Bitte um Loeschung', type: 'immediate', status: 'completed', responsible: 'Vertriebsleitung', dueDate: new Date(now.getTime() - 16 * 60 * 60 * 1000).toISOString(), completedAt: new Date(now.getTime() - 15 * 60 * 60 * 1000).toISOString() } ], timeline: [ { id: 'tl-002', incidentId: 'inc-002', timestamp: new Date(now.getTime() - 18 * 60 * 60 * 1000).toISOString(), action: 'Vorfall gemeldet', description: 'Mitarbeiter meldet versehentlichen E-Mail-Versand', performedBy: 'M. Schmidt (Vertrieb)' }, { id: 'tl-003', incidentId: 'inc-002', timestamp: new Date(now.getTime() - 15 * 60 * 60 * 1000).toISOString(), action: 'Sofortmassnahme', description: 'Empfaenger kontaktiert und Loeschung bestaetigt', performedBy: 'Vertriebsleitung' }, { id: 'tl-004', incidentId: 'inc-002', timestamp: new Date(now.getTime() - 12 * 60 * 60 * 1000).toISOString(), action: 'Risikobewertung', description: 'Bewertung durchgefuehrt - mittleres Risiko, keine Meldepflicht', performedBy: 'DSB Mueller' } ], assignedTo: 'DSB Mueller' }, // 3. Gemeldet (notification_sent) - Ransomware-Angriff { id: 'inc-003', referenceNumber: 'INC-2026-000003', title: 'Ransomware-Angriff auf Dateiserver', description: 'Am Montagmorgen wurde ein Ransomware-Angriff auf den zentralen Dateiserver erkannt. Mehrere verschluesselte Dateien wurden identifiziert. Der Angriffsvektor war eine Phishing-E-Mail an einen Mitarbeiter.', category: 'ransomware', severity: 'critical', status: 'notification_sent', detectedAt: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(), detectedBy: 'IT-Sicherheitsteam', affectedSystems: ['Dateiserver (FS-01)', 'E-Mail-System', 'Backup-Server'], affectedDataCategories: ['Personenbezogene Daten', 'Beschaeftigtendaten', 'Kundendaten', 'Finanzdaten'], estimatedAffectedPersons: 2500, riskAssessment: { id: 'ra-003', assessedBy: 'DSB Mueller', assessedAt: new Date(now.getTime() - 4.5 * 24 * 60 * 60 * 1000).toISOString(), likelihoodScore: 5, impactScore: 5, overallRisk: 'critical', notificationRequired: true, reasoning: 'Hohes Risiko fuer Rechte und Freiheiten der betroffenen Personen durch potentiellen Zugriff auf personenbezogene Daten und Finanzdaten. Verschluesselung betrifft Verfuegbarkeit, Exfiltration nicht auszuschliessen.' }, authorityNotification: { id: 'an-003', authority: 'LfD Niedersachsen', deadline72h: new Date(new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).getTime() + 72 * 60 * 60 * 1000).toISOString(), submittedAt: new Date(now.getTime() - 4 * 24 * 60 * 60 * 1000).toISOString(), status: 'submitted', formData: { referenceNumber: 'LfD-NI-2026-04821', incidentType: 'Ransomware', affectedPersons: 2500 }, pdfUrl: '/api/sdk/v1/incidents/inc-003/authority-form.pdf' }, dataSubjectNotification: { id: 'dsn-003', notificationRequired: true, templateText: 'Sehr geehrte Damen und Herren, wir informieren Sie ueber einen Sicherheitsvorfall, bei dem moeglicherweise Ihre personenbezogenen Daten betroffen sind...', sentAt: new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000).toISOString(), recipientCount: 2500, method: 'email' }, measures: [ { id: 'meas-002', incidentId: 'inc-003', title: 'Netzwerksegmentierung', description: 'Betroffene Systeme vom Netzwerk isoliert', type: 'immediate', status: 'completed', responsible: 'IT-Sicherheitsteam', dueDate: new Date(now.getTime() - 4.8 * 24 * 60 * 60 * 1000).toISOString(), completedAt: new Date(now.getTime() - 4.9 * 24 * 60 * 60 * 1000).toISOString() }, { id: 'meas-003', incidentId: 'inc-003', title: 'Passwoerter zuruecksetzen', description: 'Alle Benutzerpasswoerter zurueckgesetzt', type: 'immediate', status: 'completed', responsible: 'IT-Administration', dueDate: new Date(now.getTime() - 4.5 * 24 * 60 * 60 * 1000).toISOString(), completedAt: new Date(now.getTime() - 4.5 * 24 * 60 * 60 * 1000).toISOString() }, { id: 'meas-004', incidentId: 'inc-003', title: 'E-Mail-Security Gateway implementieren', description: 'Implementierung eines fortgeschrittenen E-Mail-Sicherheitsgateways mit Sandboxing', type: 'preventive', status: 'in_progress', responsible: 'IT-Sicherheitsteam', dueDate: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString() }, { id: 'meas-005', incidentId: 'inc-003', title: 'Mitarbeiterschulung Phishing', description: 'Verpflichtende Schulung fuer alle Mitarbeiter zum Thema Phishing-Erkennung', type: 'preventive', status: 'planned', responsible: 'Personalwesen', dueDate: new Date(now.getTime() + 60 * 24 * 60 * 60 * 1000).toISOString() } ], timeline: [ { id: 'tl-005', incidentId: 'inc-003', timestamp: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(), action: 'Vorfall erkannt', description: 'IT-Sicherheitsteam erkennt ungewoehnliche Verschluesselungsaktivitaet', performedBy: 'IT-Sicherheitsteam' }, { id: 'tl-006', incidentId: 'inc-003', timestamp: new Date(now.getTime() - 4.9 * 24 * 60 * 60 * 1000).toISOString(), action: 'Eindaemmung gestartet', description: 'Netzwerksegmentierung und Isolation betroffener Systeme', performedBy: 'IT-Sicherheitsteam' }, { id: 'tl-007', incidentId: 'inc-003', timestamp: new Date(now.getTime() - 4.5 * 24 * 60 * 60 * 1000).toISOString(), action: 'Risikobewertung abgeschlossen', description: 'Kritisches Risiko festgestellt - Meldepflicht ausgeloest', performedBy: 'DSB Mueller' }, { id: 'tl-008', incidentId: 'inc-003', timestamp: new Date(now.getTime() - 4 * 24 * 60 * 60 * 1000).toISOString(), action: 'Behoerdenbenachrichtigung', description: 'Meldung an LfD Niedersachsen eingereicht', performedBy: 'DSB Mueller' }, { id: 'tl-009', incidentId: 'inc-003', timestamp: new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000).toISOString(), action: 'Betroffene benachrichtigt', description: '2.500 betroffene Personen per E-Mail informiert', performedBy: 'Kommunikationsabteilung' } ], assignedTo: 'DSB Mueller' }, // 4. Abgeschlossener Vorfall (closed) - Phishing { id: 'inc-004', referenceNumber: 'INC-2026-000004', title: 'Phishing-Angriff auf Personalabteilung', description: 'Gezielter Phishing-Angriff auf die Personalabteilung. Ein Mitarbeiter hat Zugangsdaten auf einer gefaelschten Login-Seite eingegeben. Das Konto wurde sofort gesperrt. Keine Datenexfiltration festgestellt.', category: 'phishing', severity: 'high', status: 'closed', detectedAt: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString(), detectedBy: 'IT-Sicherheitsteam (SIEM-Alert)', affectedSystems: ['Active Directory', 'HR-Portal'], affectedDataCategories: ['Beschaeftigtendaten', 'Personenbezogene Daten'], estimatedAffectedPersons: 0, riskAssessment: { id: 'ra-004', assessedBy: 'DSB Mueller', assessedAt: new Date(now.getTime() - 29 * 24 * 60 * 60 * 1000).toISOString(), likelihoodScore: 4, impactScore: 3, overallRisk: 'high', notificationRequired: true, reasoning: 'Zugangsdaten kompromittiert, potentieller Zugriff auf Personaldaten. Keine Exfiltration festgestellt, dennoch Meldung wegen Kompromittierung der Zugangsdaten.' }, authorityNotification: { id: 'an-004', authority: 'LfD Niedersachsen', deadline72h: new Date(new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).getTime() + 72 * 60 * 60 * 1000).toISOString(), submittedAt: new Date(now.getTime() - 29 * 24 * 60 * 60 * 1000).toISOString(), status: 'acknowledged', formData: { referenceNumber: 'LfD-NI-2026-03912', incidentType: 'Phishing', affectedPersons: 0 } }, dataSubjectNotification: { id: 'dsn-004', notificationRequired: false, templateText: '', recipientCount: 0, method: 'email' }, measures: [ { id: 'meas-006', incidentId: 'inc-004', title: 'Konto gesperrt', description: 'Kompromittiertes Benutzerkonto sofort gesperrt', type: 'immediate', status: 'completed', responsible: 'IT-Administration', dueDate: new Date(now.getTime() - 29.8 * 24 * 60 * 60 * 1000).toISOString(), completedAt: new Date(now.getTime() - 29.9 * 24 * 60 * 60 * 1000).toISOString() }, { id: 'meas-007', incidentId: 'inc-004', title: 'MFA fuer alle Mitarbeiter', description: 'Einfuehrung von Multi-Faktor-Authentifizierung fuer alle Konten', type: 'preventive', status: 'completed', responsible: 'IT-Sicherheitsteam', dueDate: new Date(now.getTime() - 10 * 24 * 60 * 60 * 1000).toISOString(), completedAt: new Date(now.getTime() - 12 * 24 * 60 * 60 * 1000).toISOString() } ], timeline: [ { id: 'tl-010', incidentId: 'inc-004', timestamp: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString(), action: 'SIEM-Alert', description: 'Verdaechtiger Login-Versuch aus unbekannter Region erkannt', performedBy: 'IT-Sicherheitsteam' }, { id: 'tl-011', incidentId: 'inc-004', timestamp: new Date(now.getTime() - 29 * 24 * 60 * 60 * 1000).toISOString(), action: 'Behoerdenbenachrichtigung', description: 'Meldung an LfD Niedersachsen', performedBy: 'DSB Mueller' }, { id: 'tl-012', incidentId: 'inc-004', timestamp: new Date(now.getTime() - 15 * 24 * 60 * 60 * 1000).toISOString(), action: 'Vorfall abgeschlossen', description: 'Alle Massnahmen umgesetzt, keine Datenexfiltration festgestellt', performedBy: 'DSB Mueller' } ], assignedTo: 'DSB Mueller', closedAt: new Date(now.getTime() - 15 * 24 * 60 * 60 * 1000).toISOString(), lessonsLearned: '1. MFA haette den Zugriff verhindert (jetzt implementiert). 2. E-Mail-Security-Gateway muss verbesserte Phishing-Erkennung erhalten. 3. Regelmaessige Phishing-Simulationen fuer alle Mitarbeiter einfuehren.' } ] } /** * Erstellt Mock-Statistiken fuer die Entwicklung */ export function createMockStatistics(): IncidentStatistics { return { totalIncidents: 4, openIncidents: 3, notificationsPending: 1, averageResponseTimeHours: 8.5, bySeverity: { low: 0, medium: 1, high: 2, critical: 1 }, byCategory: { data_breach: 1, unauthorized_access: 1, data_loss: 0, system_compromise: 0, phishing: 1, ransomware: 1, insider_threat: 0, physical_breach: 0, other: 0 }, byStatus: { detected: 1, assessment: 1, containment: 0, notification_required: 0, notification_sent: 1, remediation: 0, closed: 1 } } }