This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/lib/sdk/incidents/api.ts
BreakPilot Dev 557305db5d
Some checks failed
ci/woodpecker/push/integration Pipeline failed
ci/woodpecker/push/main Pipeline failed
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
feat: Add Academy, Whistleblower, Incidents SDK modules, pitch-deck, blog and CI/CD config
- Academy, Whistleblower, Incidents frontend pages with API proxies and types
- Vendor compliance API proxy route
- Go backend handlers and models for all new SDK modules
- Investor pitch-deck app with interactive slides
- Blog section with DSGVO, AI Act, NIS2, glossary articles
- MkDocs documentation site
- CI/CD pipelines (Woodpecker, GitHub Actions), security scanning config
- Planning and implementation documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:12:16 +01:00

846 lines
29 KiB
TypeScript

/**
* 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<T>(
url: string,
options: RequestInit = {},
timeout: number = API_TIMEOUT
): Promise<T> {
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<IncidentListResponse> {
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<IncidentListResponse>(url)
}
/**
* Einzelnen Vorfall per ID abrufen
*/
export async function fetchIncident(id: string): Promise<Incident> {
return fetchWithTimeout<Incident>(`${INCIDENTS_API_BASE}/api/v1/incidents/${id}`)
}
/**
* Neuen Vorfall erstellen
*/
export async function createIncident(request: IncidentCreateRequest): Promise<Incident> {
return fetchWithTimeout<Incident>(`${INCIDENTS_API_BASE}/api/v1/incidents`, {
method: 'POST',
body: JSON.stringify(request)
})
}
/**
* Vorfall aktualisieren
*/
export async function updateIncident(id: string, update: IncidentUpdateRequest): Promise<Incident> {
return fetchWithTimeout<Incident>(`${INCIDENTS_API_BASE}/api/v1/incidents/${id}`, {
method: 'PUT',
body: JSON.stringify(update)
})
}
/**
* Vorfall loeschen (Soft Delete)
*/
export async function deleteIncident(id: string): Promise<void> {
await fetchWithTimeout<void>(`${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<Incident> {
return fetchWithTimeout<Incident>(
`${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<Blob> {
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<AuthorityNotification>
): Promise<Incident> {
return fetchWithTimeout<Incident>(
`${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<DataSubjectNotification>
): Promise<Incident> {
return fetchWithTimeout<Incident>(
`${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<IncidentMeasure, 'id' | 'incidentId'>
): Promise<Incident> {
return fetchWithTimeout<Incident>(
`${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/measures`,
{
method: 'POST',
body: JSON.stringify(measure)
}
)
}
/**
* Massnahme aktualisieren
*/
export async function updateMeasure(
measureId: string,
update: Partial<IncidentMeasure>
): Promise<IncidentMeasure> {
return fetchWithTimeout<IncidentMeasure>(
`${INCIDENTS_API_BASE}/api/v1/measures/${measureId}`,
{
method: 'PUT',
body: JSON.stringify(update)
}
)
}
/**
* Massnahme als abgeschlossen markieren
*/
export async function completeMeasure(measureId: string): Promise<IncidentMeasure> {
return fetchWithTimeout<IncidentMeasure>(
`${INCIDENTS_API_BASE}/api/v1/measures/${measureId}/complete`,
{
method: 'POST'
}
)
}
// =============================================================================
// TIMELINE
// =============================================================================
/**
* Zeitleisteneintrag hinzufuegen
*/
export async function addTimelineEntry(
incidentId: string,
entry: Omit<TimelineEntry, 'id' | 'incidentId'>
): Promise<Incident> {
return fetchWithTimeout<Incident>(
`${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<Incident> {
return fetchWithTimeout<Incident>(
`${INCIDENTS_API_BASE}/api/v1/incidents/${incidentId}/close`,
{
method: 'POST',
body: JSON.stringify({ lessonsLearned })
}
)
}
// =============================================================================
// STATISTICS
// =============================================================================
/**
* Vorfall-Statistiken abrufen
*/
export async function fetchIncidentStatistics(): Promise<IncidentStatistics> {
return fetchWithTimeout<IncidentStatistics>(
`${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 = <K extends string>(items: { [key: string]: unknown }[], field: string): Record<K, number> => {
const result: Record<string, number> = {}
items.forEach(item => {
const key = String(item[field])
result[key] = (result[key] || 0) + 1
})
return result as Record<K, number>
}
const statusCounts = countBy<IncidentStatus>(incidents as unknown as { [key: string]: unknown }[], 'status')
const severityCounts = countBy<IncidentSeverity>(incidents as unknown as { [key: string]: unknown }[], 'severity')
const categoryCounts = countBy<IncidentCategory>(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
}
}
}