refactor(admin): split lib document generators and data catalogs into domain barrels
obligations-document, tom-document, loeschfristen-document, compliance-scope-triggers, sdk-flow/flow-data, processing-activities, loeschfristen-baseline-catalog, catalog-registry, dsfa mitigation-library + risk-catalog, vvt-baseline-catalog, vendor contract-review checklists + findings, demo-data, tom-compliance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,734 +1,12 @@
|
||||
/**
|
||||
* SDK Catalog Manager - Central Registry
|
||||
*
|
||||
* Maps all SDK catalogs to a unified interface for browsing, searching, and CRUD.
|
||||
*
|
||||
* Barrel re-export — split into domain modules under registry/.
|
||||
* Do NOT import directly from registry/ sub-files in consumers;
|
||||
* always import from this barrel to preserve the stable public path.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CatalogId,
|
||||
CatalogMeta,
|
||||
CatalogModule,
|
||||
CatalogEntry,
|
||||
CatalogStats,
|
||||
CatalogOverviewStats,
|
||||
CustomCatalogEntry,
|
||||
CustomCatalogs,
|
||||
} from './types'
|
||||
|
||||
// =============================================================================
|
||||
// CATALOG DATA IMPORTS
|
||||
// =============================================================================
|
||||
|
||||
import { RISK_CATALOG } from '../dsfa/risk-catalog'
|
||||
import { MITIGATION_LIBRARY } from '../dsfa/mitigation-library'
|
||||
import { AI_RISK_CATALOG } from '../dsfa/ai-risk-catalog'
|
||||
import { AI_MITIGATION_LIBRARY } from '../dsfa/ai-mitigation-library'
|
||||
import { PROHIBITED_AI_PRACTICES } from '../dsfa/prohibited-ai-practices'
|
||||
import { EU_BASE_FRAMEWORKS, NATIONAL_FRAMEWORKS } from '../dsfa/eu-legal-frameworks'
|
||||
import { GDPR_ENFORCEMENT_CASES } from '../dsfa/gdpr-enforcement-cases'
|
||||
import { WP248_CRITERIA, SDM_GOALS, DSFA_AUTHORITY_RESOURCES } from '../dsfa/types'
|
||||
import { VVT_BASELINE_CATALOG } from '../vvt-baseline-catalog'
|
||||
import { BASELINE_TEMPLATES } from '../loeschfristen-baseline-catalog'
|
||||
import { VENDOR_TEMPLATES, COUNTRY_RISK_PROFILES } from '../vendor-compliance/catalog/vendor-templates'
|
||||
import { LEGAL_BASIS_INFO, STANDARD_RETENTION_PERIODS } from '../vendor-compliance/catalog/legal-basis'
|
||||
|
||||
// =============================================================================
|
||||
// HELPER: Resolve localized text fields
|
||||
// =============================================================================
|
||||
|
||||
function resolveField(value: unknown): string {
|
||||
if (value === null || value === undefined) return ''
|
||||
if (typeof value === 'string') return value
|
||||
if (typeof value === 'number') return String(value)
|
||||
if (typeof value === 'object' && 'de' in (value as Record<string, unknown>)) {
|
||||
return String((value as Record<string, string>).de || '')
|
||||
}
|
||||
return String(value)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SDM_GOALS as entries array (it's a Record, not an array)
|
||||
// =============================================================================
|
||||
|
||||
const SDM_GOALS_ENTRIES = Object.entries(SDM_GOALS).map(([key, val]) => ({
|
||||
id: key,
|
||||
name: val.name,
|
||||
description: val.description,
|
||||
article: val.article,
|
||||
}))
|
||||
|
||||
// =============================================================================
|
||||
// CATALOG REGISTRY
|
||||
// =============================================================================
|
||||
|
||||
export const CATALOG_REGISTRY: Record<CatalogId, CatalogMeta> = {
|
||||
'dsfa-risks': {
|
||||
id: 'dsfa-risks',
|
||||
name: 'DSFA Risikokatalog',
|
||||
description: 'Standardrisiken fuer Datenschutz-Folgenabschaetzungen',
|
||||
module: 'dsfa',
|
||||
icon: 'ShieldAlert',
|
||||
systemCount: RISK_CATALOG.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'category',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Risiko-ID', type: 'text', required: true, placeholder: 'R-XXX-01' },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'category', label: 'Kategorie', type: 'select', required: true, options: [
|
||||
{ value: 'confidentiality', label: 'Vertraulichkeit' },
|
||||
{ value: 'integrity', label: 'Integritaet' },
|
||||
{ value: 'availability', label: 'Verfuegbarkeit' },
|
||||
{ value: 'rights_freedoms', label: 'Rechte & Freiheiten' },
|
||||
]},
|
||||
{ key: 'typicalLikelihood', label: 'Typische Eintrittswahrscheinlichkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
{ key: 'typicalImpact', label: 'Typische Auswirkung', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'category'],
|
||||
},
|
||||
|
||||
'dsfa-mitigations': {
|
||||
id: 'dsfa-mitigations',
|
||||
name: 'DSFA Massnahmenbibliothek',
|
||||
description: 'Technische und organisatorische Massnahmen fuer DSFAs',
|
||||
module: 'dsfa',
|
||||
icon: 'Shield',
|
||||
systemCount: MITIGATION_LIBRARY.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'type',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Massnahmen-ID', type: 'text', required: true, placeholder: 'M-XXX-01' },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'select', required: true, options: [
|
||||
{ value: 'technical', label: 'Technisch' },
|
||||
{ value: 'organizational', label: 'Organisatorisch' },
|
||||
{ value: 'legal', label: 'Rechtlich' },
|
||||
]},
|
||||
{ key: 'effectiveness', label: 'Wirksamkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
{ key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'type', 'legalBasis'],
|
||||
},
|
||||
|
||||
'ai-risks': {
|
||||
id: 'ai-risks',
|
||||
name: 'KI-Risikokatalog',
|
||||
description: 'Spezifische Risiken fuer KI-Systeme',
|
||||
module: 'ai_act',
|
||||
icon: 'Bot',
|
||||
systemCount: AI_RISK_CATALOG.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'category',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Risiko-ID', type: 'text', required: true, placeholder: 'R-AI-XXX-01' },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'category', label: 'Kategorie', type: 'select', required: true, options: [
|
||||
{ value: 'confidentiality', label: 'Vertraulichkeit' },
|
||||
{ value: 'integrity', label: 'Integritaet' },
|
||||
{ value: 'availability', label: 'Verfuegbarkeit' },
|
||||
{ value: 'rights_freedoms', label: 'Rechte & Freiheiten' },
|
||||
]},
|
||||
{ key: 'typicalLikelihood', label: 'Eintrittswahrscheinlichkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
{ key: 'typicalImpact', label: 'Auswirkung', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'category'],
|
||||
},
|
||||
|
||||
'ai-mitigations': {
|
||||
id: 'ai-mitigations',
|
||||
name: 'KI-Massnahmenbibliothek',
|
||||
description: 'Massnahmen fuer KI-spezifische Risiken',
|
||||
module: 'ai_act',
|
||||
icon: 'ShieldCheck',
|
||||
systemCount: AI_MITIGATION_LIBRARY.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'type',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Massnahmen-ID', type: 'text', required: true },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'select', required: true, options: [
|
||||
{ value: 'technical', label: 'Technisch' },
|
||||
{ value: 'organizational', label: 'Organisatorisch' },
|
||||
{ value: 'legal', label: 'Rechtlich' },
|
||||
]},
|
||||
{ key: 'effectiveness', label: 'Wirksamkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'type'],
|
||||
},
|
||||
|
||||
'prohibited-ai-practices': {
|
||||
id: 'prohibited-ai-practices',
|
||||
name: 'Verbotene KI-Praktiken',
|
||||
description: 'Absolut und bedingt verbotene KI-Anwendungen nach AI Act',
|
||||
module: 'ai_act',
|
||||
icon: 'Ban',
|
||||
systemCount: PROHIBITED_AI_PRACTICES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'severity',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'severity', label: 'Schwere', type: 'select', required: true, options: [
|
||||
{ value: 'absolute', label: 'Absolutes Verbot' },
|
||||
{ value: 'conditional', label: 'Bedingtes Verbot' },
|
||||
]},
|
||||
{ key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'severity', 'legalBasis'],
|
||||
},
|
||||
|
||||
'vvt-templates': {
|
||||
id: 'vvt-templates',
|
||||
name: 'VVT Baseline-Vorlagen',
|
||||
description: 'Vorlagen fuer Verarbeitungstaetigkeiten',
|
||||
module: 'vvt',
|
||||
icon: 'FileText',
|
||||
systemCount: VVT_BASELINE_CATALOG.length,
|
||||
allowCustom: true,
|
||||
idField: 'templateId',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'businessFunction',
|
||||
fields: [
|
||||
{ key: 'templateId', label: 'Vorlagen-ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'businessFunction', label: 'Geschaeftsbereich', type: 'select', required: true, options: [
|
||||
{ value: 'hr', label: 'Personal' },
|
||||
{ value: 'finance', label: 'Finanzen' },
|
||||
{ value: 'sales', label: 'Vertrieb' },
|
||||
{ value: 'marketing', label: 'Marketing' },
|
||||
{ value: 'support', label: 'Support' },
|
||||
{ value: 'it', label: 'IT' },
|
||||
{ value: 'other', label: 'Sonstige' },
|
||||
]},
|
||||
{ key: 'protectionLevel', label: 'Schutzniveau', type: 'select', required: false, options: [
|
||||
{ value: 'LOW', label: 'Niedrig' },
|
||||
{ value: 'MEDIUM', label: 'Mittel' },
|
||||
{ value: 'HIGH', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['templateId', 'name', 'description', 'businessFunction'],
|
||||
},
|
||||
|
||||
'loeschfristen-templates': {
|
||||
id: 'loeschfristen-templates',
|
||||
name: 'Loeschfristen-Vorlagen',
|
||||
description: 'Baseline-Vorlagen fuer Aufbewahrungsfristen',
|
||||
module: 'vvt',
|
||||
icon: 'Clock',
|
||||
systemCount: BASELINE_TEMPLATES.length,
|
||||
allowCustom: true,
|
||||
idField: 'templateId',
|
||||
nameField: 'dataObjectName',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'retentionDriver',
|
||||
fields: [
|
||||
{ key: 'templateId', label: 'Vorlagen-ID', type: 'text', required: true },
|
||||
{ key: 'dataObjectName', label: 'Datenobjekt', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'retentionDuration', label: 'Aufbewahrungsdauer', type: 'number', required: true, min: 0 },
|
||||
{ key: 'retentionUnit', label: 'Einheit', type: 'select', required: true, options: [
|
||||
{ value: 'days', label: 'Tage' },
|
||||
{ value: 'months', label: 'Monate' },
|
||||
{ value: 'years', label: 'Jahre' },
|
||||
]},
|
||||
{ key: 'deletionMethod', label: 'Loeschmethode', type: 'text', required: false },
|
||||
{ key: 'responsibleRole', label: 'Verantwortlich', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['templateId', 'dataObjectName', 'description', 'retentionDriver'],
|
||||
},
|
||||
|
||||
'vendor-templates': {
|
||||
id: 'vendor-templates',
|
||||
name: 'AV-Vorlagen',
|
||||
description: 'Vorlagen fuer Auftragsverarbeitungsvertraege',
|
||||
module: 'vendor',
|
||||
icon: 'Building2',
|
||||
systemCount: VENDOR_TEMPLATES.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'serviceCategory',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'serviceCategory', label: 'Dienstleistungskategorie', type: 'select', required: true, options: [
|
||||
{ value: 'HOSTING', label: 'Hosting' },
|
||||
{ value: 'CLOUD_INFRASTRUCTURE', label: 'Cloud-Infrastruktur' },
|
||||
{ value: 'ANALYTICS', label: 'Analyse' },
|
||||
{ value: 'CRM', label: 'CRM' },
|
||||
{ value: 'ERP', label: 'ERP' },
|
||||
{ value: 'HR_SOFTWARE', label: 'HR-Software' },
|
||||
{ value: 'PAYMENT', label: 'Zahlungsdienstleister' },
|
||||
{ value: 'EMAIL', label: 'E-Mail' },
|
||||
{ value: 'MARKETING', label: 'Marketing' },
|
||||
{ value: 'SUPPORT', label: 'Support' },
|
||||
{ value: 'SECURITY', label: 'Sicherheit' },
|
||||
{ value: 'COMMUNICATION', label: 'Kommunikation' },
|
||||
{ value: 'STORAGE', label: 'Speicher' },
|
||||
{ value: 'BACKUP', label: 'Backup' },
|
||||
{ value: 'CDN', label: 'CDN' },
|
||||
{ value: 'ACCOUNTING', label: 'Buchhaltung' },
|
||||
{ value: 'CONSULTING', label: 'Beratung' },
|
||||
{ value: 'OTHER', label: 'Sonstige' },
|
||||
]},
|
||||
{ key: 'suggestedRole', label: 'Vorgeschlagene Rolle', type: 'select', required: false, options: [
|
||||
{ value: 'PROCESSOR', label: 'Auftragsverarbeiter' },
|
||||
{ value: 'JOINT_CONTROLLER', label: 'Gemeinsam Verantwortlicher' },
|
||||
{ value: 'CONTROLLER', label: 'Eigenstaendiger Verantwortlicher' },
|
||||
{ value: 'SUB_PROCESSOR', label: 'Unterauftragnehmer' },
|
||||
{ value: 'THIRD_PARTY', label: 'Dritter' },
|
||||
]},
|
||||
{ key: 'suggestedDataAccess', label: 'Datenzugriff', type: 'select', required: false, options: [
|
||||
{ value: 'NONE', label: 'Kein Zugriff' },
|
||||
{ value: 'POTENTIAL', label: 'Potenzieller Zugriff' },
|
||||
{ value: 'ADMINISTRATIVE', label: 'Administrativer Zugriff' },
|
||||
{ value: 'CONTENT', label: 'Inhaltlicher Zugriff' },
|
||||
]},
|
||||
{ key: 'suggestedTransferMechanisms', label: 'Transfer-Mechanismen', type: 'multiselect', required: false, options: [
|
||||
{ value: 'ADEQUACY_DECISION', label: 'Angemessenheitsbeschluss' },
|
||||
{ value: 'SCC_CONTROLLER', label: 'SCC Controller-to-Controller' },
|
||||
{ value: 'SCC_PROCESSOR', label: 'SCC Controller-to-Processor' },
|
||||
{ value: 'BCR', label: 'Binding Corporate Rules' },
|
||||
{ value: 'DEROGATION_CONSENT', label: 'Einwilligung' },
|
||||
{ value: 'DEROGATION_CONTRACT', label: 'Vertragserfuellung' },
|
||||
{ value: 'CERTIFICATION', label: 'Zertifizierung' },
|
||||
{ value: 'CODE_OF_CONDUCT', label: 'Verhaltensregeln' },
|
||||
]},
|
||||
{ key: 'suggestedContractTypes', label: 'Vertragstypen', type: 'multiselect', required: false, options: [
|
||||
{ value: 'AVV', label: 'AVV (Auftragsverarbeitung)' },
|
||||
{ value: 'MSA', label: 'MSA (Master Service)' },
|
||||
{ value: 'SLA', label: 'SLA (Service Level)' },
|
||||
{ value: 'SCC', label: 'SCC (Standardvertragsklauseln)' },
|
||||
{ value: 'NDA', label: 'NDA (Geheimhaltung)' },
|
||||
{ value: 'TOM_ANNEX', label: 'TOM-Anlage' },
|
||||
{ value: 'CERTIFICATION', label: 'Zertifikat' },
|
||||
{ value: 'SUB_PROCESSOR_LIST', label: 'Unterauftragnehmer-Liste' },
|
||||
]},
|
||||
{ key: 'typicalDataCategories', label: 'Typische Datenkategorien', type: 'multiselect', required: false, options: [
|
||||
{ value: 'NAME', label: 'Name' },
|
||||
{ value: 'CONTACT', label: 'Kontaktdaten' },
|
||||
{ value: 'ADDRESS', label: 'Adressdaten' },
|
||||
{ value: 'DOB', label: 'Geburtsdatum' },
|
||||
{ value: 'BANK_ACCOUNT', label: 'Bankverbindung' },
|
||||
{ value: 'PAYMENT_DATA', label: 'Zahlungsdaten' },
|
||||
{ value: 'EMPLOYMENT_DATA', label: 'Beschaeftigungsdaten' },
|
||||
{ value: 'SALARY_DATA', label: 'Gehaltsdaten' },
|
||||
{ value: 'HEALTH_DATA', label: 'Gesundheitsdaten' },
|
||||
{ value: 'IP_ADDRESS', label: 'IP-Adresse' },
|
||||
{ value: 'USAGE_DATA', label: 'Nutzungsdaten' },
|
||||
{ value: 'LOCATION_DATA', label: 'Standortdaten' },
|
||||
{ value: 'COMMUNICATION_DATA', label: 'Kommunikationsdaten' },
|
||||
]},
|
||||
{ key: 'typicalCertifications', label: 'Typische Zertifizierungen', type: 'tags', required: false, placeholder: 'z.B. ISO 27001, SOC 2, C5' },
|
||||
{ key: 'commonProviders', label: 'Typische Anbieter', type: 'tags', required: false, placeholder: 'z.B. AWS, Azure, GCP' },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'description', 'serviceCategory', 'suggestedRole', 'commonProviders'],
|
||||
},
|
||||
|
||||
'country-risk-profiles': {
|
||||
id: 'country-risk-profiles',
|
||||
name: 'Laenderrisikoprofile',
|
||||
description: 'Datenschutz-Risikobewertung nach Laendern',
|
||||
module: 'vendor',
|
||||
icon: 'Globe',
|
||||
systemCount: COUNTRY_RISK_PROFILES.length,
|
||||
allowCustom: false,
|
||||
idField: 'code',
|
||||
nameField: 'name',
|
||||
categoryField: 'riskLevel',
|
||||
fields: [
|
||||
{ key: 'code', label: 'Laendercode', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Land', type: 'text', required: true },
|
||||
{ key: 'riskLevel', label: 'Risikostufe', type: 'select', required: true, options: [
|
||||
{ value: 'LOW', label: 'Niedrig' },
|
||||
{ value: 'MEDIUM', label: 'Mittel' },
|
||||
{ value: 'HIGH', label: 'Hoch' },
|
||||
{ value: 'VERY_HIGH', label: 'Sehr hoch' },
|
||||
]},
|
||||
{ key: 'isEU', label: 'EU-Mitglied', type: 'boolean', required: false },
|
||||
{ key: 'isEEA', label: 'EWR-Mitglied', type: 'boolean', required: false },
|
||||
{ key: 'hasAdequacyDecision', label: 'Angemessenheitsbeschluss', type: 'boolean', required: false },
|
||||
],
|
||||
searchableFields: ['code', 'name', 'riskLevel'],
|
||||
},
|
||||
|
||||
'legal-bases': {
|
||||
id: 'legal-bases',
|
||||
name: 'Rechtsgrundlagen',
|
||||
description: 'DSGVO Art. 6 und Art. 9 Rechtsgrundlagen',
|
||||
module: 'reference',
|
||||
icon: 'Scale',
|
||||
systemCount: LEGAL_BASIS_INFO.length,
|
||||
allowCustom: false,
|
||||
idField: 'type',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'article',
|
||||
fields: [
|
||||
{ key: 'type', label: 'Typ', type: 'text', required: true },
|
||||
{ key: 'article', label: 'Artikel', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'isSpecialCategory', label: 'Besondere Kategorie (Art. 9)', type: 'boolean', required: false },
|
||||
],
|
||||
searchableFields: ['type', 'article', 'name', 'description'],
|
||||
},
|
||||
|
||||
'retention-periods': {
|
||||
id: 'retention-periods',
|
||||
name: 'Aufbewahrungsfristen',
|
||||
description: 'Gesetzliche Standard-Aufbewahrungsfristen',
|
||||
module: 'reference',
|
||||
icon: 'Timer',
|
||||
systemCount: STANDARD_RETENTION_PERIODS.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'legalBasis', 'description'],
|
||||
},
|
||||
|
||||
'eu-legal-frameworks': {
|
||||
id: 'eu-legal-frameworks',
|
||||
name: 'EU-Rechtsrahmen',
|
||||
description: 'EU-weite Datenschutzgesetze und -verordnungen',
|
||||
module: 'reference',
|
||||
icon: 'Landmark',
|
||||
systemCount: EU_BASE_FRAMEWORKS.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'type',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'fullName', label: 'Vollstaendiger Name', type: 'text', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'fullName', 'description', 'type'],
|
||||
},
|
||||
|
||||
'national-legal-frameworks': {
|
||||
id: 'national-legal-frameworks',
|
||||
name: 'Nationale Rechtsrahmen',
|
||||
description: 'Nationale Datenschutzgesetze der EU/EWR-Staaten',
|
||||
module: 'reference',
|
||||
icon: 'Flag',
|
||||
systemCount: NATIONAL_FRAMEWORKS.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'countryCode',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'countryCode', label: 'Land', type: 'text', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'countryCode', 'description', 'type'],
|
||||
},
|
||||
|
||||
'gdpr-enforcement-cases': {
|
||||
id: 'gdpr-enforcement-cases',
|
||||
name: 'DSGVO-Bussgeldentscheidungen',
|
||||
description: 'Relevante Bussgeldentscheidungen als Referenz',
|
||||
module: 'reference',
|
||||
icon: 'Gavel',
|
||||
systemCount: GDPR_ENFORCEMENT_CASES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'company',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'country',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'company', label: 'Unternehmen', type: 'text', required: true },
|
||||
{ key: 'country', label: 'Land', type: 'text', required: true },
|
||||
{ key: 'year', label: 'Jahr', type: 'number', required: true },
|
||||
{ key: 'fineOriginal', label: 'Bussgeld (EUR)', type: 'number', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'company', 'country', 'description'],
|
||||
},
|
||||
|
||||
'wp248-criteria': {
|
||||
id: 'wp248-criteria',
|
||||
name: 'WP248 Kriterien',
|
||||
description: 'Kriterien zur DSFA-Pflichtpruefung nach WP248',
|
||||
module: 'dsfa',
|
||||
icon: 'ClipboardCheck',
|
||||
systemCount: WP248_CRITERIA.length,
|
||||
allowCustom: false,
|
||||
idField: 'code',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
fields: [
|
||||
{ key: 'code', label: 'Code', type: 'text', required: true },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'gdprRef', label: 'DSGVO-Referenz', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['code', 'title', 'description', 'gdprRef'],
|
||||
},
|
||||
|
||||
'sdm-goals': {
|
||||
id: 'sdm-goals',
|
||||
name: 'SDM Gewaehrleistungsziele',
|
||||
description: 'Standard-Datenschutzmodell Gewaehrleistungsziele',
|
||||
module: 'dsfa',
|
||||
icon: 'Target',
|
||||
systemCount: SDM_GOALS_ENTRIES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'article', label: 'DSGVO-Artikel', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'description', 'article'],
|
||||
},
|
||||
|
||||
'dsfa-authority-resources': {
|
||||
id: 'dsfa-authority-resources',
|
||||
name: 'Aufsichtsbehoerden-Ressourcen',
|
||||
description: 'DSFA-Ressourcen der deutschen Aufsichtsbehoerden',
|
||||
module: 'dsfa',
|
||||
icon: 'Building',
|
||||
systemCount: DSFA_AUTHORITY_RESOURCES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'shortName',
|
||||
descriptionField: 'name',
|
||||
categoryField: 'state',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'shortName', label: 'Kurzname', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Voller Name', type: 'text', required: true },
|
||||
{ key: 'state', label: 'Bundesland', type: 'text', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'shortName', 'name', 'state'],
|
||||
},
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SYSTEM ENTRIES MAP
|
||||
// =============================================================================
|
||||
|
||||
const SYSTEM_ENTRIES_MAP: Record<CatalogId, Record<string, unknown>[]> = {
|
||||
'dsfa-risks': RISK_CATALOG as unknown as Record<string, unknown>[],
|
||||
'dsfa-mitigations': MITIGATION_LIBRARY as unknown as Record<string, unknown>[],
|
||||
'ai-risks': AI_RISK_CATALOG as unknown as Record<string, unknown>[],
|
||||
'ai-mitigations': AI_MITIGATION_LIBRARY as unknown as Record<string, unknown>[],
|
||||
'prohibited-ai-practices': PROHIBITED_AI_PRACTICES as unknown as Record<string, unknown>[],
|
||||
'vvt-templates': VVT_BASELINE_CATALOG as unknown as Record<string, unknown>[],
|
||||
'loeschfristen-templates': BASELINE_TEMPLATES as unknown as Record<string, unknown>[],
|
||||
'vendor-templates': VENDOR_TEMPLATES as unknown as Record<string, unknown>[],
|
||||
'country-risk-profiles': COUNTRY_RISK_PROFILES as unknown as Record<string, unknown>[],
|
||||
'legal-bases': LEGAL_BASIS_INFO as unknown as Record<string, unknown>[],
|
||||
'retention-periods': STANDARD_RETENTION_PERIODS as unknown as Record<string, unknown>[],
|
||||
'eu-legal-frameworks': EU_BASE_FRAMEWORKS as unknown as Record<string, unknown>[],
|
||||
'national-legal-frameworks': NATIONAL_FRAMEWORKS as unknown as Record<string, unknown>[],
|
||||
'gdpr-enforcement-cases': GDPR_ENFORCEMENT_CASES as unknown as Record<string, unknown>[],
|
||||
'wp248-criteria': WP248_CRITERIA as unknown as Record<string, unknown>[],
|
||||
'sdm-goals': SDM_GOALS_ENTRIES as unknown as Record<string, unknown>[],
|
||||
'dsfa-authority-resources': DSFA_AUTHORITY_RESOURCES as unknown as Record<string, unknown>[],
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ENTRY CONVERTERS
|
||||
// =============================================================================
|
||||
|
||||
function systemEntryToCatalogEntry(
|
||||
catalogId: CatalogId,
|
||||
data: Record<string, unknown>,
|
||||
): CatalogEntry {
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
const idValue = resolveField(data[meta.idField])
|
||||
const nameValue = resolveField(data[meta.nameField])
|
||||
const descValue = meta.descriptionField ? resolveField(data[meta.descriptionField]) : undefined
|
||||
const catValue = meta.categoryField ? resolveField(data[meta.categoryField]) : undefined
|
||||
|
||||
return {
|
||||
id: idValue || crypto.randomUUID(),
|
||||
catalogId,
|
||||
source: 'system',
|
||||
data,
|
||||
displayName: nameValue || idValue || '(Unbenannt)',
|
||||
displayDescription: descValue,
|
||||
category: catValue,
|
||||
}
|
||||
}
|
||||
|
||||
function customEntryToCatalogEntry(
|
||||
catalogId: CatalogId,
|
||||
entry: CustomCatalogEntry,
|
||||
): CatalogEntry {
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
const nameValue = resolveField(entry.data[meta.nameField])
|
||||
const descValue = meta.descriptionField ? resolveField(entry.data[meta.descriptionField]) : undefined
|
||||
const catValue = meta.categoryField ? resolveField(entry.data[meta.categoryField]) : undefined
|
||||
|
||||
return {
|
||||
id: entry.id,
|
||||
catalogId,
|
||||
source: 'custom',
|
||||
data: entry.data,
|
||||
displayName: nameValue || '(Unbenannt)',
|
||||
displayDescription: descValue,
|
||||
category: catValue,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PUBLIC API
|
||||
// =============================================================================
|
||||
|
||||
export function getSystemEntries(catalogId: CatalogId): CatalogEntry[] {
|
||||
const raw = SYSTEM_ENTRIES_MAP[catalogId] || []
|
||||
return raw.map(data => systemEntryToCatalogEntry(catalogId, data))
|
||||
}
|
||||
|
||||
export function getAllEntries(
|
||||
catalogId: CatalogId,
|
||||
customEntries: CustomCatalogEntry[] = [],
|
||||
): CatalogEntry[] {
|
||||
const system = getSystemEntries(catalogId)
|
||||
const custom = customEntries.map(e => customEntryToCatalogEntry(catalogId, e))
|
||||
return [...system, ...custom]
|
||||
}
|
||||
|
||||
export function getCatalogsByModule(module: CatalogModule): CatalogMeta[] {
|
||||
return Object.values(CATALOG_REGISTRY).filter(c => c.module === module)
|
||||
}
|
||||
|
||||
export function getCatalogStats(
|
||||
catalogId: CatalogId,
|
||||
customEntries: CustomCatalogEntry[] = [],
|
||||
): CatalogStats {
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
return {
|
||||
catalogId,
|
||||
systemCount: meta.systemCount,
|
||||
customCount: customEntries.length,
|
||||
totalCount: meta.systemCount + customEntries.length,
|
||||
}
|
||||
}
|
||||
|
||||
export function getOverviewStats(customCatalogs: CustomCatalogs): CatalogOverviewStats {
|
||||
const modules: CatalogModule[] = ['dsfa', 'vvt', 'vendor', 'ai_act', 'reference']
|
||||
const byModule = {} as Record<CatalogModule, { catalogs: number; entries: number }>
|
||||
|
||||
let totalSystemEntries = 0
|
||||
let totalCustomEntries = 0
|
||||
|
||||
for (const mod of modules) {
|
||||
const cats = getCatalogsByModule(mod)
|
||||
let entries = 0
|
||||
for (const cat of cats) {
|
||||
const customCount = customCatalogs[cat.id]?.length ?? 0
|
||||
entries += cat.systemCount + customCount
|
||||
totalSystemEntries += cat.systemCount
|
||||
totalCustomEntries += customCount
|
||||
}
|
||||
byModule[mod] = { catalogs: cats.length, entries }
|
||||
}
|
||||
|
||||
return {
|
||||
totalCatalogs: Object.keys(CATALOG_REGISTRY).length,
|
||||
totalSystemEntries,
|
||||
totalCustomEntries,
|
||||
totalEntries: totalSystemEntries + totalCustomEntries,
|
||||
byModule,
|
||||
}
|
||||
}
|
||||
|
||||
export function searchCatalog(
|
||||
catalogId: CatalogId,
|
||||
query: string,
|
||||
customEntries: CustomCatalogEntry[] = [],
|
||||
): CatalogEntry[] {
|
||||
const allEntries = getAllEntries(catalogId, customEntries)
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
const q = query.toLowerCase().trim()
|
||||
|
||||
if (!q) return allEntries
|
||||
|
||||
return allEntries
|
||||
.map(entry => {
|
||||
let score = 0
|
||||
for (const field of meta.searchableFields) {
|
||||
const value = resolveField(entry.data[field]).toLowerCase()
|
||||
if (value.includes(q)) {
|
||||
score += value.startsWith(q) ? 10 : 5
|
||||
}
|
||||
}
|
||||
// Also search display name
|
||||
if (entry.displayName.toLowerCase().includes(q)) {
|
||||
score += 15
|
||||
}
|
||||
return { entry, score }
|
||||
})
|
||||
.filter(r => r.score > 0)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.map(r => r.entry)
|
||||
}
|
||||
export * from './registry/registry-dsfa'
|
||||
export * from './registry/registry-vendor'
|
||||
export * from './registry/registry-reference'
|
||||
export * from './registry/registry-api'
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Catalog Registry — Public API, Entry Converters & Registry Assembly
|
||||
*/
|
||||
|
||||
import type {
|
||||
CatalogId,
|
||||
CatalogMeta,
|
||||
CatalogModule,
|
||||
CatalogEntry,
|
||||
CatalogStats,
|
||||
CatalogOverviewStats,
|
||||
CustomCatalogEntry,
|
||||
CustomCatalogs,
|
||||
} from '../types'
|
||||
import { DSFA_AI_CATALOG_META, DSFA_AI_SYSTEM_ENTRIES } from './registry-dsfa'
|
||||
import { VENDOR_VVT_CATALOG_META, VENDOR_VVT_SYSTEM_ENTRIES } from './registry-vendor'
|
||||
import { REFERENCE_CATALOG_META, REFERENCE_SYSTEM_ENTRIES } from './registry-reference'
|
||||
|
||||
// =============================================================================
|
||||
// CATALOG REGISTRY (assembled from sub-registries)
|
||||
// =============================================================================
|
||||
|
||||
export const CATALOG_REGISTRY: Record<CatalogId, CatalogMeta> = {
|
||||
...DSFA_AI_CATALOG_META,
|
||||
...VENDOR_VVT_CATALOG_META,
|
||||
...REFERENCE_CATALOG_META,
|
||||
} as Record<CatalogId, CatalogMeta>
|
||||
|
||||
// =============================================================================
|
||||
// SYSTEM ENTRIES MAP (assembled from sub-registries)
|
||||
// =============================================================================
|
||||
|
||||
const SYSTEM_ENTRIES_MAP: Record<CatalogId, Record<string, unknown>[]> = {
|
||||
...DSFA_AI_SYSTEM_ENTRIES,
|
||||
...VENDOR_VVT_SYSTEM_ENTRIES,
|
||||
...REFERENCE_SYSTEM_ENTRIES,
|
||||
} as Record<CatalogId, Record<string, unknown>[]>
|
||||
|
||||
// =============================================================================
|
||||
// HELPER: Resolve localized text fields
|
||||
// =============================================================================
|
||||
|
||||
function resolveField(value: unknown): string {
|
||||
if (value === null || value === undefined) return ''
|
||||
if (typeof value === 'string') return value
|
||||
if (typeof value === 'number') return String(value)
|
||||
if (typeof value === 'object' && 'de' in (value as Record<string, unknown>)) {
|
||||
return String((value as Record<string, string>).de || '')
|
||||
}
|
||||
return String(value)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ENTRY CONVERTERS
|
||||
// =============================================================================
|
||||
|
||||
function systemEntryToCatalogEntry(
|
||||
catalogId: CatalogId,
|
||||
data: Record<string, unknown>,
|
||||
): CatalogEntry {
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
const idValue = resolveField(data[meta.idField])
|
||||
const nameValue = resolveField(data[meta.nameField])
|
||||
const descValue = meta.descriptionField ? resolveField(data[meta.descriptionField]) : undefined
|
||||
const catValue = meta.categoryField ? resolveField(data[meta.categoryField]) : undefined
|
||||
|
||||
return {
|
||||
id: idValue || crypto.randomUUID(),
|
||||
catalogId,
|
||||
source: 'system',
|
||||
data,
|
||||
displayName: nameValue || idValue || '(Unbenannt)',
|
||||
displayDescription: descValue,
|
||||
category: catValue,
|
||||
}
|
||||
}
|
||||
|
||||
function customEntryToCatalogEntry(
|
||||
catalogId: CatalogId,
|
||||
entry: CustomCatalogEntry,
|
||||
): CatalogEntry {
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
const nameValue = resolveField(entry.data[meta.nameField])
|
||||
const descValue = meta.descriptionField ? resolveField(entry.data[meta.descriptionField]) : undefined
|
||||
const catValue = meta.categoryField ? resolveField(entry.data[meta.categoryField]) : undefined
|
||||
|
||||
return {
|
||||
id: entry.id,
|
||||
catalogId,
|
||||
source: 'custom',
|
||||
data: entry.data,
|
||||
displayName: nameValue || '(Unbenannt)',
|
||||
displayDescription: descValue,
|
||||
category: catValue,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PUBLIC API
|
||||
// =============================================================================
|
||||
|
||||
export function getSystemEntries(catalogId: CatalogId): CatalogEntry[] {
|
||||
const raw = SYSTEM_ENTRIES_MAP[catalogId] || []
|
||||
return raw.map(data => systemEntryToCatalogEntry(catalogId, data))
|
||||
}
|
||||
|
||||
export function getAllEntries(
|
||||
catalogId: CatalogId,
|
||||
customEntries: CustomCatalogEntry[] = [],
|
||||
): CatalogEntry[] {
|
||||
const system = getSystemEntries(catalogId)
|
||||
const custom = customEntries.map(e => customEntryToCatalogEntry(catalogId, e))
|
||||
return [...system, ...custom]
|
||||
}
|
||||
|
||||
export function getCatalogsByModule(module: CatalogModule): CatalogMeta[] {
|
||||
return Object.values(CATALOG_REGISTRY).filter(c => c.module === module)
|
||||
}
|
||||
|
||||
export function getCatalogStats(
|
||||
catalogId: CatalogId,
|
||||
customEntries: CustomCatalogEntry[] = [],
|
||||
): CatalogStats {
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
return {
|
||||
catalogId,
|
||||
systemCount: meta.systemCount,
|
||||
customCount: customEntries.length,
|
||||
totalCount: meta.systemCount + customEntries.length,
|
||||
}
|
||||
}
|
||||
|
||||
export function getOverviewStats(customCatalogs: CustomCatalogs): CatalogOverviewStats {
|
||||
const modules: CatalogModule[] = ['dsfa', 'vvt', 'vendor', 'ai_act', 'reference']
|
||||
const byModule = {} as Record<CatalogModule, { catalogs: number; entries: number }>
|
||||
|
||||
let totalSystemEntries = 0
|
||||
let totalCustomEntries = 0
|
||||
|
||||
for (const mod of modules) {
|
||||
const cats = getCatalogsByModule(mod)
|
||||
let entries = 0
|
||||
for (const cat of cats) {
|
||||
const customCount = customCatalogs[cat.id]?.length ?? 0
|
||||
entries += cat.systemCount + customCount
|
||||
totalSystemEntries += cat.systemCount
|
||||
totalCustomEntries += customCount
|
||||
}
|
||||
byModule[mod] = { catalogs: cats.length, entries }
|
||||
}
|
||||
|
||||
return {
|
||||
totalCatalogs: Object.keys(CATALOG_REGISTRY).length,
|
||||
totalSystemEntries,
|
||||
totalCustomEntries,
|
||||
totalEntries: totalSystemEntries + totalCustomEntries,
|
||||
byModule,
|
||||
}
|
||||
}
|
||||
|
||||
export function searchCatalog(
|
||||
catalogId: CatalogId,
|
||||
query: string,
|
||||
customEntries: CustomCatalogEntry[] = [],
|
||||
): CatalogEntry[] {
|
||||
const allEntries = getAllEntries(catalogId, customEntries)
|
||||
const meta = CATALOG_REGISTRY[catalogId]
|
||||
const q = query.toLowerCase().trim()
|
||||
|
||||
if (!q) return allEntries
|
||||
|
||||
return allEntries
|
||||
.map(entry => {
|
||||
let score = 0
|
||||
for (const field of meta.searchableFields) {
|
||||
const value = resolveField(entry.data[field]).toLowerCase()
|
||||
if (value.includes(q)) {
|
||||
score += value.startsWith(q) ? 10 : 5
|
||||
}
|
||||
}
|
||||
// Also search display name
|
||||
if (entry.displayName.toLowerCase().includes(q)) {
|
||||
score += 15
|
||||
}
|
||||
return { entry, score }
|
||||
})
|
||||
.filter(r => r.score > 0)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.map(r => r.entry)
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
* Catalog Registry — DSFA & AI Act Catalogs
|
||||
*/
|
||||
|
||||
import type { CatalogId, CatalogMeta } from '../types'
|
||||
import { RISK_CATALOG } from '../../dsfa/risk-catalog'
|
||||
import { MITIGATION_LIBRARY } from '../../dsfa/mitigation-library'
|
||||
import { AI_RISK_CATALOG } from '../../dsfa/ai-risk-catalog'
|
||||
import { AI_MITIGATION_LIBRARY } from '../../dsfa/ai-mitigation-library'
|
||||
import { PROHIBITED_AI_PRACTICES } from '../../dsfa/prohibited-ai-practices'
|
||||
import { WP248_CRITERIA, SDM_GOALS, DSFA_AUTHORITY_RESOURCES } from '../../dsfa/types'
|
||||
|
||||
export const SDM_GOALS_ENTRIES = Object.entries(SDM_GOALS).map(([key, val]) => ({
|
||||
id: key,
|
||||
name: val.name,
|
||||
description: val.description,
|
||||
article: val.article,
|
||||
}))
|
||||
|
||||
export const DSFA_AI_CATALOG_META: Partial<Record<CatalogId, CatalogMeta>> = {
|
||||
'dsfa-risks': {
|
||||
id: 'dsfa-risks',
|
||||
name: 'DSFA Risikokatalog',
|
||||
description: 'Standardrisiken fuer Datenschutz-Folgenabschaetzungen',
|
||||
module: 'dsfa',
|
||||
icon: 'ShieldAlert',
|
||||
systemCount: RISK_CATALOG.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'category',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Risiko-ID', type: 'text', required: true, placeholder: 'R-XXX-01' },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'category', label: 'Kategorie', type: 'select', required: true, options: [
|
||||
{ value: 'confidentiality', label: 'Vertraulichkeit' },
|
||||
{ value: 'integrity', label: 'Integritaet' },
|
||||
{ value: 'availability', label: 'Verfuegbarkeit' },
|
||||
{ value: 'rights_freedoms', label: 'Rechte & Freiheiten' },
|
||||
]},
|
||||
{ key: 'typicalLikelihood', label: 'Typische Eintrittswahrscheinlichkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
{ key: 'typicalImpact', label: 'Typische Auswirkung', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'category'],
|
||||
},
|
||||
|
||||
'dsfa-mitigations': {
|
||||
id: 'dsfa-mitigations',
|
||||
name: 'DSFA Massnahmenbibliothek',
|
||||
description: 'Technische und organisatorische Massnahmen fuer DSFAs',
|
||||
module: 'dsfa',
|
||||
icon: 'Shield',
|
||||
systemCount: MITIGATION_LIBRARY.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'type',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Massnahmen-ID', type: 'text', required: true, placeholder: 'M-XXX-01' },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'select', required: true, options: [
|
||||
{ value: 'technical', label: 'Technisch' },
|
||||
{ value: 'organizational', label: 'Organisatorisch' },
|
||||
{ value: 'legal', label: 'Rechtlich' },
|
||||
]},
|
||||
{ key: 'effectiveness', label: 'Wirksamkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
{ key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'type', 'legalBasis'],
|
||||
},
|
||||
|
||||
'ai-risks': {
|
||||
id: 'ai-risks',
|
||||
name: 'KI-Risikokatalog',
|
||||
description: 'Spezifische Risiken fuer KI-Systeme',
|
||||
module: 'ai_act',
|
||||
icon: 'Bot',
|
||||
systemCount: AI_RISK_CATALOG.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'category',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Risiko-ID', type: 'text', required: true, placeholder: 'R-AI-XXX-01' },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'category', label: 'Kategorie', type: 'select', required: true, options: [
|
||||
{ value: 'confidentiality', label: 'Vertraulichkeit' },
|
||||
{ value: 'integrity', label: 'Integritaet' },
|
||||
{ value: 'availability', label: 'Verfuegbarkeit' },
|
||||
{ value: 'rights_freedoms', label: 'Rechte & Freiheiten' },
|
||||
]},
|
||||
{ key: 'typicalLikelihood', label: 'Eintrittswahrscheinlichkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
{ key: 'typicalImpact', label: 'Auswirkung', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'category'],
|
||||
},
|
||||
|
||||
'ai-mitigations': {
|
||||
id: 'ai-mitigations',
|
||||
name: 'KI-Massnahmenbibliothek',
|
||||
description: 'Massnahmen fuer KI-spezifische Risiken',
|
||||
module: 'ai_act',
|
||||
icon: 'ShieldCheck',
|
||||
systemCount: AI_MITIGATION_LIBRARY.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'type',
|
||||
fields: [
|
||||
{ key: 'id', label: 'Massnahmen-ID', type: 'text', required: true },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'select', required: true, options: [
|
||||
{ value: 'technical', label: 'Technisch' },
|
||||
{ value: 'organizational', label: 'Organisatorisch' },
|
||||
{ value: 'legal', label: 'Rechtlich' },
|
||||
]},
|
||||
{ key: 'effectiveness', label: 'Wirksamkeit', type: 'select', required: false, options: [
|
||||
{ value: 'low', label: 'Niedrig' },
|
||||
{ value: 'medium', label: 'Mittel' },
|
||||
{ value: 'high', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'type'],
|
||||
},
|
||||
|
||||
'prohibited-ai-practices': {
|
||||
id: 'prohibited-ai-practices',
|
||||
name: 'Verbotene KI-Praktiken',
|
||||
description: 'Absolut und bedingt verbotene KI-Anwendungen nach AI Act',
|
||||
module: 'ai_act',
|
||||
icon: 'Ban',
|
||||
systemCount: PROHIBITED_AI_PRACTICES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'severity',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'severity', label: 'Schwere', type: 'select', required: true, options: [
|
||||
{ value: 'absolute', label: 'Absolutes Verbot' },
|
||||
{ value: 'conditional', label: 'Bedingtes Verbot' },
|
||||
]},
|
||||
{ key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['id', 'title', 'description', 'severity', 'legalBasis'],
|
||||
},
|
||||
|
||||
'wp248-criteria': {
|
||||
id: 'wp248-criteria',
|
||||
name: 'WP248 Kriterien',
|
||||
description: 'Kriterien zur DSFA-Pflichtpruefung nach WP248',
|
||||
module: 'dsfa',
|
||||
icon: 'ClipboardCheck',
|
||||
systemCount: WP248_CRITERIA.length,
|
||||
allowCustom: false,
|
||||
idField: 'code',
|
||||
nameField: 'title',
|
||||
descriptionField: 'description',
|
||||
fields: [
|
||||
{ key: 'code', label: 'Code', type: 'text', required: true },
|
||||
{ key: 'title', label: 'Titel', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'gdprRef', label: 'DSGVO-Referenz', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['code', 'title', 'description', 'gdprRef'],
|
||||
},
|
||||
|
||||
'sdm-goals': {
|
||||
id: 'sdm-goals',
|
||||
name: 'SDM Gewaehrleistungsziele',
|
||||
description: 'Standard-Datenschutzmodell Gewaehrleistungsziele',
|
||||
module: 'dsfa',
|
||||
icon: 'Target',
|
||||
systemCount: SDM_GOALS_ENTRIES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'article', label: 'DSGVO-Artikel', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'description', 'article'],
|
||||
},
|
||||
|
||||
'dsfa-authority-resources': {
|
||||
id: 'dsfa-authority-resources',
|
||||
name: 'Aufsichtsbehoerden-Ressourcen',
|
||||
description: 'DSFA-Ressourcen der deutschen Aufsichtsbehoerden',
|
||||
module: 'dsfa',
|
||||
icon: 'Building',
|
||||
systemCount: DSFA_AUTHORITY_RESOURCES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'shortName',
|
||||
descriptionField: 'name',
|
||||
categoryField: 'state',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'shortName', label: 'Kurzname', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Voller Name', type: 'text', required: true },
|
||||
{ key: 'state', label: 'Bundesland', type: 'text', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'shortName', 'name', 'state'],
|
||||
},
|
||||
}
|
||||
|
||||
export const DSFA_AI_SYSTEM_ENTRIES: Partial<Record<CatalogId, Record<string, unknown>[]>> = {
|
||||
'dsfa-risks': RISK_CATALOG as unknown as Record<string, unknown>[],
|
||||
'dsfa-mitigations': MITIGATION_LIBRARY as unknown as Record<string, unknown>[],
|
||||
'ai-risks': AI_RISK_CATALOG as unknown as Record<string, unknown>[],
|
||||
'ai-mitigations': AI_MITIGATION_LIBRARY as unknown as Record<string, unknown>[],
|
||||
'prohibited-ai-practices': PROHIBITED_AI_PRACTICES as unknown as Record<string, unknown>[],
|
||||
'wp248-criteria': WP248_CRITERIA as unknown as Record<string, unknown>[],
|
||||
'sdm-goals': SDM_GOALS_ENTRIES as unknown as Record<string, unknown>[],
|
||||
'dsfa-authority-resources': DSFA_AUTHORITY_RESOURCES as unknown as Record<string, unknown>[],
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Catalog Registry — Reference Catalogs (Legal Bases, Frameworks, Enforcement)
|
||||
*/
|
||||
|
||||
import type { CatalogId, CatalogMeta } from '../types'
|
||||
import { EU_BASE_FRAMEWORKS, NATIONAL_FRAMEWORKS } from '../../dsfa/eu-legal-frameworks'
|
||||
import { GDPR_ENFORCEMENT_CASES } from '../../dsfa/gdpr-enforcement-cases'
|
||||
import { LEGAL_BASIS_INFO, STANDARD_RETENTION_PERIODS } from '../../vendor-compliance/catalog/legal-basis'
|
||||
|
||||
export const REFERENCE_CATALOG_META: Partial<Record<CatalogId, CatalogMeta>> = {
|
||||
'legal-bases': {
|
||||
id: 'legal-bases',
|
||||
name: 'Rechtsgrundlagen',
|
||||
description: 'DSGVO Art. 6 und Art. 9 Rechtsgrundlagen',
|
||||
module: 'reference',
|
||||
icon: 'Scale',
|
||||
systemCount: LEGAL_BASIS_INFO.length,
|
||||
allowCustom: false,
|
||||
idField: 'type',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'article',
|
||||
fields: [
|
||||
{ key: 'type', label: 'Typ', type: 'text', required: true },
|
||||
{ key: 'article', label: 'Artikel', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'isSpecialCategory', label: 'Besondere Kategorie (Art. 9)', type: 'boolean', required: false },
|
||||
],
|
||||
searchableFields: ['type', 'article', 'name', 'description'],
|
||||
},
|
||||
|
||||
'retention-periods': {
|
||||
id: 'retention-periods',
|
||||
name: 'Aufbewahrungsfristen',
|
||||
description: 'Gesetzliche Standard-Aufbewahrungsfristen',
|
||||
module: 'reference',
|
||||
icon: 'Timer',
|
||||
systemCount: STANDARD_RETENTION_PERIODS.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'legalBasis', 'description'],
|
||||
},
|
||||
|
||||
'eu-legal-frameworks': {
|
||||
id: 'eu-legal-frameworks',
|
||||
name: 'EU-Rechtsrahmen',
|
||||
description: 'EU-weite Datenschutzgesetze und -verordnungen',
|
||||
module: 'reference',
|
||||
icon: 'Landmark',
|
||||
systemCount: EU_BASE_FRAMEWORKS.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'type',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'fullName', label: 'Vollstaendiger Name', type: 'text', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'fullName', 'description', 'type'],
|
||||
},
|
||||
|
||||
'national-legal-frameworks': {
|
||||
id: 'national-legal-frameworks',
|
||||
name: 'Nationale Rechtsrahmen',
|
||||
description: 'Nationale Datenschutzgesetze der EU/EWR-Staaten',
|
||||
module: 'reference',
|
||||
icon: 'Flag',
|
||||
systemCount: NATIONAL_FRAMEWORKS.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'countryCode',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'countryCode', label: 'Land', type: 'text', required: true },
|
||||
{ key: 'type', label: 'Typ', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'name', 'countryCode', 'description', 'type'],
|
||||
},
|
||||
|
||||
'gdpr-enforcement-cases': {
|
||||
id: 'gdpr-enforcement-cases',
|
||||
name: 'DSGVO-Bussgeldentscheidungen',
|
||||
description: 'Relevante Bussgeldentscheidungen als Referenz',
|
||||
module: 'reference',
|
||||
icon: 'Gavel',
|
||||
systemCount: GDPR_ENFORCEMENT_CASES.length,
|
||||
allowCustom: false,
|
||||
idField: 'id',
|
||||
nameField: 'company',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'country',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'company', label: 'Unternehmen', type: 'text', required: true },
|
||||
{ key: 'country', label: 'Land', type: 'text', required: true },
|
||||
{ key: 'year', label: 'Jahr', type: 'number', required: true },
|
||||
{ key: 'fineOriginal', label: 'Bussgeld (EUR)', type: 'number', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
],
|
||||
searchableFields: ['id', 'company', 'country', 'description'],
|
||||
},
|
||||
}
|
||||
|
||||
export const REFERENCE_SYSTEM_ENTRIES: Partial<Record<CatalogId, Record<string, unknown>[]>> = {
|
||||
'legal-bases': LEGAL_BASIS_INFO as unknown as Record<string, unknown>[],
|
||||
'retention-periods': STANDARD_RETENTION_PERIODS as unknown as Record<string, unknown>[],
|
||||
'eu-legal-frameworks': EU_BASE_FRAMEWORKS as unknown as Record<string, unknown>[],
|
||||
'national-legal-frameworks': NATIONAL_FRAMEWORKS as unknown as Record<string, unknown>[],
|
||||
'gdpr-enforcement-cases': GDPR_ENFORCEMENT_CASES as unknown as Record<string, unknown>[],
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Catalog Registry — Vendor, VVT & Loeschfristen Catalogs
|
||||
*/
|
||||
|
||||
import type { CatalogId, CatalogMeta } from '../types'
|
||||
import { VVT_BASELINE_CATALOG } from '../../vvt-baseline-catalog'
|
||||
import { BASELINE_TEMPLATES } from '../../loeschfristen-baseline-catalog'
|
||||
import { VENDOR_TEMPLATES, COUNTRY_RISK_PROFILES } from '../../vendor-compliance/catalog/vendor-templates'
|
||||
|
||||
export const VENDOR_VVT_CATALOG_META: Partial<Record<CatalogId, CatalogMeta>> = {
|
||||
'vvt-templates': {
|
||||
id: 'vvt-templates',
|
||||
name: 'VVT Baseline-Vorlagen',
|
||||
description: 'Vorlagen fuer Verarbeitungstaetigkeiten',
|
||||
module: 'vvt',
|
||||
icon: 'FileText',
|
||||
systemCount: VVT_BASELINE_CATALOG.length,
|
||||
allowCustom: true,
|
||||
idField: 'templateId',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'businessFunction',
|
||||
fields: [
|
||||
{ key: 'templateId', label: 'Vorlagen-ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'businessFunction', label: 'Geschaeftsbereich', type: 'select', required: true, options: [
|
||||
{ value: 'hr', label: 'Personal' },
|
||||
{ value: 'finance', label: 'Finanzen' },
|
||||
{ value: 'sales', label: 'Vertrieb' },
|
||||
{ value: 'marketing', label: 'Marketing' },
|
||||
{ value: 'support', label: 'Support' },
|
||||
{ value: 'it', label: 'IT' },
|
||||
{ value: 'other', label: 'Sonstige' },
|
||||
]},
|
||||
{ key: 'protectionLevel', label: 'Schutzniveau', type: 'select', required: false, options: [
|
||||
{ value: 'LOW', label: 'Niedrig' },
|
||||
{ value: 'MEDIUM', label: 'Mittel' },
|
||||
{ value: 'HIGH', label: 'Hoch' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['templateId', 'name', 'description', 'businessFunction'],
|
||||
},
|
||||
|
||||
'loeschfristen-templates': {
|
||||
id: 'loeschfristen-templates',
|
||||
name: 'Loeschfristen-Vorlagen',
|
||||
description: 'Baseline-Vorlagen fuer Aufbewahrungsfristen',
|
||||
module: 'vvt',
|
||||
icon: 'Clock',
|
||||
systemCount: BASELINE_TEMPLATES.length,
|
||||
allowCustom: true,
|
||||
idField: 'templateId',
|
||||
nameField: 'dataObjectName',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'retentionDriver',
|
||||
fields: [
|
||||
{ key: 'templateId', label: 'Vorlagen-ID', type: 'text', required: true },
|
||||
{ key: 'dataObjectName', label: 'Datenobjekt', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'retentionDuration', label: 'Aufbewahrungsdauer', type: 'number', required: true, min: 0 },
|
||||
{ key: 'retentionUnit', label: 'Einheit', type: 'select', required: true, options: [
|
||||
{ value: 'days', label: 'Tage' },
|
||||
{ value: 'months', label: 'Monate' },
|
||||
{ value: 'years', label: 'Jahre' },
|
||||
]},
|
||||
{ key: 'deletionMethod', label: 'Loeschmethode', type: 'text', required: false },
|
||||
{ key: 'responsibleRole', label: 'Verantwortlich', type: 'text', required: false },
|
||||
],
|
||||
searchableFields: ['templateId', 'dataObjectName', 'description', 'retentionDriver'],
|
||||
},
|
||||
|
||||
'vendor-templates': {
|
||||
id: 'vendor-templates',
|
||||
name: 'AV-Vorlagen',
|
||||
description: 'Vorlagen fuer Auftragsverarbeitungsvertraege',
|
||||
module: 'vendor',
|
||||
icon: 'Building2',
|
||||
systemCount: VENDOR_TEMPLATES.length,
|
||||
allowCustom: true,
|
||||
idField: 'id',
|
||||
nameField: 'name',
|
||||
descriptionField: 'description',
|
||||
categoryField: 'serviceCategory',
|
||||
fields: [
|
||||
{ key: 'id', label: 'ID', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Name', type: 'text', required: true },
|
||||
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
|
||||
{ key: 'serviceCategory', label: 'Dienstleistungskategorie', type: 'select', required: true, options: [
|
||||
{ value: 'HOSTING', label: 'Hosting' },
|
||||
{ value: 'CLOUD_INFRASTRUCTURE', label: 'Cloud-Infrastruktur' },
|
||||
{ value: 'ANALYTICS', label: 'Analyse' },
|
||||
{ value: 'CRM', label: 'CRM' },
|
||||
{ value: 'ERP', label: 'ERP' },
|
||||
{ value: 'HR_SOFTWARE', label: 'HR-Software' },
|
||||
{ value: 'PAYMENT', label: 'Zahlungsdienstleister' },
|
||||
{ value: 'EMAIL', label: 'E-Mail' },
|
||||
{ value: 'MARKETING', label: 'Marketing' },
|
||||
{ value: 'SUPPORT', label: 'Support' },
|
||||
{ value: 'SECURITY', label: 'Sicherheit' },
|
||||
{ value: 'COMMUNICATION', label: 'Kommunikation' },
|
||||
{ value: 'STORAGE', label: 'Speicher' },
|
||||
{ value: 'BACKUP', label: 'Backup' },
|
||||
{ value: 'CDN', label: 'CDN' },
|
||||
{ value: 'ACCOUNTING', label: 'Buchhaltung' },
|
||||
{ value: 'CONSULTING', label: 'Beratung' },
|
||||
{ value: 'OTHER', label: 'Sonstige' },
|
||||
]},
|
||||
{ key: 'suggestedRole', label: 'Vorgeschlagene Rolle', type: 'select', required: false, options: [
|
||||
{ value: 'PROCESSOR', label: 'Auftragsverarbeiter' },
|
||||
{ value: 'JOINT_CONTROLLER', label: 'Gemeinsam Verantwortlicher' },
|
||||
{ value: 'CONTROLLER', label: 'Eigenstaendiger Verantwortlicher' },
|
||||
{ value: 'SUB_PROCESSOR', label: 'Unterauftragnehmer' },
|
||||
{ value: 'THIRD_PARTY', label: 'Dritter' },
|
||||
]},
|
||||
{ key: 'suggestedDataAccess', label: 'Datenzugriff', type: 'select', required: false, options: [
|
||||
{ value: 'NONE', label: 'Kein Zugriff' },
|
||||
{ value: 'POTENTIAL', label: 'Potenzieller Zugriff' },
|
||||
{ value: 'ADMINISTRATIVE', label: 'Administrativer Zugriff' },
|
||||
{ value: 'CONTENT', label: 'Inhaltlicher Zugriff' },
|
||||
]},
|
||||
],
|
||||
searchableFields: ['id', 'name', 'description', 'serviceCategory', 'suggestedRole', 'commonProviders'],
|
||||
},
|
||||
|
||||
'country-risk-profiles': {
|
||||
id: 'country-risk-profiles',
|
||||
name: 'Laenderrisikoprofile',
|
||||
description: 'Datenschutz-Risikobewertung nach Laendern',
|
||||
module: 'vendor',
|
||||
icon: 'Globe',
|
||||
systemCount: COUNTRY_RISK_PROFILES.length,
|
||||
allowCustom: false,
|
||||
idField: 'code',
|
||||
nameField: 'name',
|
||||
categoryField: 'riskLevel',
|
||||
fields: [
|
||||
{ key: 'code', label: 'Laendercode', type: 'text', required: true },
|
||||
{ key: 'name', label: 'Land', type: 'text', required: true },
|
||||
{ key: 'riskLevel', label: 'Risikostufe', type: 'select', required: true, options: [
|
||||
{ value: 'LOW', label: 'Niedrig' },
|
||||
{ value: 'MEDIUM', label: 'Mittel' },
|
||||
{ value: 'HIGH', label: 'Hoch' },
|
||||
{ value: 'VERY_HIGH', label: 'Sehr hoch' },
|
||||
]},
|
||||
{ key: 'isEU', label: 'EU-Mitglied', type: 'boolean', required: false },
|
||||
{ key: 'isEEA', label: 'EWR-Mitglied', type: 'boolean', required: false },
|
||||
{ key: 'hasAdequacyDecision', label: 'Angemessenheitsbeschluss', type: 'boolean', required: false },
|
||||
],
|
||||
searchableFields: ['code', 'name', 'riskLevel'],
|
||||
},
|
||||
}
|
||||
|
||||
export const VENDOR_VVT_SYSTEM_ENTRIES: Partial<Record<CatalogId, Record<string, unknown>[]>> = {
|
||||
'vvt-templates': VVT_BASELINE_CATALOG as unknown as Record<string, unknown>[],
|
||||
'loeschfristen-templates': BASELINE_TEMPLATES as unknown as Record<string, unknown>[],
|
||||
'vendor-templates': VENDOR_TEMPLATES as unknown as Record<string, unknown>[],
|
||||
'country-risk-profiles': COUNTRY_RISK_PROFILES as unknown as Record<string, unknown>[],
|
||||
}
|
||||
@@ -1,823 +1,15 @@
|
||||
/**
|
||||
* 50 Hard Trigger Rules — data table.
|
||||
*
|
||||
* This file legitimately exceeds 500 LOC because it is a pure data
|
||||
* definition with no logic. Splitting it further would hurt readability.
|
||||
* 50 Hard Trigger Rules — barrel re-export.
|
||||
* Implementation split into compliance-scope-triggers/
|
||||
*/
|
||||
import type { HardTriggerRule } from './compliance-scope-types'
|
||||
import { HARD_TRIGGER_RULES_A_E } from './compliance-scope-triggers/triggers-a-e'
|
||||
import { HARD_TRIGGER_RULES_F_M } from './compliance-scope-triggers/triggers-f-m'
|
||||
|
||||
// ============================================================================
|
||||
// 50 HARD TRIGGER RULES
|
||||
// ============================================================================
|
||||
export { HARD_TRIGGER_RULES_A_E } from './compliance-scope-triggers/triggers-a-e'
|
||||
export { HARD_TRIGGER_RULES_F_M } from './compliance-scope-triggers/triggers-f-m'
|
||||
|
||||
export const HARD_TRIGGER_RULES: HardTriggerRule[] = [
|
||||
// ========== A: Art. 9 Besondere Kategorien (9 rules) ==========
|
||||
{
|
||||
id: 'HT-A01',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'gesundheit',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung von Gesundheitsdaten',
|
||||
},
|
||||
{
|
||||
id: 'HT-A02',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'biometrie',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung biometrischer Daten zur eindeutigen Identifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-A03',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'genetik',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung genetischer Daten',
|
||||
},
|
||||
{
|
||||
id: 'HT-A04',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'politisch',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung politischer Meinungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-A05',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'religion',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung religiöser oder weltanschaulicher Überzeugungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-A06',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'gewerkschaft',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung von Gewerkschaftszugehörigkeit',
|
||||
},
|
||||
{
|
||||
id: 'HT-A07',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'sexualleben',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung von Daten zum Sexualleben oder zur sexuellen Orientierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-A08',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'strafrechtlich',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 10 DSGVO',
|
||||
description: 'Verarbeitung strafrechtlicher Verurteilungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-A09',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'ethnisch',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung der rassischen oder ethnischen Herkunft',
|
||||
},
|
||||
|
||||
// ========== B: Vulnerable Gruppen (3 rules) ==========
|
||||
{
|
||||
id: 'HT-B01',
|
||||
category: 'vulnerable',
|
||||
questionId: 'data_minors',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'DSE'],
|
||||
legalReference: 'Art. 8 DSGVO',
|
||||
description: 'Verarbeitung von Daten Minderjähriger',
|
||||
},
|
||||
{
|
||||
id: 'HT-B02',
|
||||
category: 'vulnerable',
|
||||
questionId: 'data_minors',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'DSE'],
|
||||
legalReference: 'Art. 8 + Art. 9 DSGVO',
|
||||
description: 'Verarbeitung besonderer Kategorien von Daten Minderjähriger',
|
||||
combineWithArt9: true,
|
||||
},
|
||||
{
|
||||
id: 'HT-B03',
|
||||
category: 'vulnerable',
|
||||
questionId: 'data_minors',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'AI_ACT_DOKU'],
|
||||
legalReference: 'Art. 8 DSGVO + AI Act',
|
||||
description: 'KI-gestützte Verarbeitung von Daten Minderjähriger',
|
||||
combineWithAI: true,
|
||||
},
|
||||
|
||||
// ========== C: ADM/KI (6 rules) ==========
|
||||
{
|
||||
id: 'HT-C01',
|
||||
category: 'adm',
|
||||
questionId: 'proc_adm_scoring',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 22 DSGVO',
|
||||
description: 'Automatisierte Einzelentscheidung mit Rechtswirkung oder erheblicher Beeinträchtigung',
|
||||
},
|
||||
{
|
||||
id: 'HT-C02',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'autonom',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'AI_ACT_DOKU'],
|
||||
legalReference: 'Art. 22 DSGVO + AI Act',
|
||||
description: 'Autonome KI-Systeme mit Entscheidungsbefugnis',
|
||||
},
|
||||
{
|
||||
id: 'HT-C03',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'scoring',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'Art. 22 DSGVO',
|
||||
description: 'KI-gestütztes Scoring',
|
||||
},
|
||||
{
|
||||
id: 'HT-C04',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'profiling',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 22 DSGVO',
|
||||
description: 'KI-gestütztes Profiling mit erheblicher Wirkung',
|
||||
},
|
||||
{
|
||||
id: 'HT-C05',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'generativ',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'AI_ACT_DOKU'],
|
||||
legalReference: 'AI Act',
|
||||
description: 'Generative KI-Systeme',
|
||||
},
|
||||
{
|
||||
id: 'HT-C06',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'chatbot',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'AI_ACT_DOKU'],
|
||||
legalReference: 'AI Act',
|
||||
description: 'Chatbots mit Personendatenverarbeitung',
|
||||
},
|
||||
|
||||
// ========== D: Überwachung (5 rules) ==========
|
||||
{
|
||||
id: 'HT-D01',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_video_surveillance',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSE'],
|
||||
legalReference: 'Art. 6 DSGVO',
|
||||
description: 'Videoüberwachung',
|
||||
},
|
||||
{
|
||||
id: 'HT-D02',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_employee_monitoring',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 88 DSGVO + BetrVG',
|
||||
description: 'Mitarbeiterüberwachung',
|
||||
},
|
||||
{
|
||||
id: 'HT-D03',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_tracking',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'COOKIE_BANNER', 'EINWILLIGUNGEN'],
|
||||
legalReference: 'Art. 6 DSGVO + ePrivacy',
|
||||
description: 'Online-Tracking',
|
||||
},
|
||||
{
|
||||
id: 'HT-D04',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_video_surveillance',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 35 Abs. 3 DSGVO',
|
||||
description: 'Videoüberwachung kombiniert mit Mitarbeitermonitoring',
|
||||
combineWithEmployeeMonitoring: true,
|
||||
},
|
||||
{
|
||||
id: 'HT-D05',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_video_surveillance',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 35 Abs. 3 DSGVO',
|
||||
description: 'Videoüberwachung kombiniert mit automatisierter Bewertung',
|
||||
combineWithADM: true,
|
||||
},
|
||||
|
||||
// ========== E: Drittland (5 rules) ==========
|
||||
{
|
||||
id: 'HT-E01',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_third_country',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TRANSFER_DOKU'],
|
||||
legalReference: 'Art. 44 ff. DSGVO',
|
||||
description: 'Datenübermittlung in Drittland',
|
||||
},
|
||||
{
|
||||
id: 'HT-E02',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_hosting_location',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'drittland',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU'],
|
||||
legalReference: 'Art. 44 ff. DSGVO',
|
||||
description: 'Hosting in Drittland',
|
||||
},
|
||||
{
|
||||
id: 'HT-E03',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_hosting_location',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'us_adequacy',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TRANSFER_DOKU'],
|
||||
legalReference: 'Art. 45 DSGVO',
|
||||
description: 'Hosting in USA mit Angemessenheitsbeschluss',
|
||||
},
|
||||
{
|
||||
id: 'HT-E04',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_third_country',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU', 'DSFA'],
|
||||
legalReference: 'Art. 44 ff. + Art. 9 DSGVO',
|
||||
description: 'Drittlandtransfer besonderer Kategorien',
|
||||
combineWithArt9: true,
|
||||
},
|
||||
{
|
||||
id: 'HT-E05',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_third_country',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU', 'DSFA'],
|
||||
legalReference: 'Art. 44 ff. + Art. 8 DSGVO',
|
||||
description: 'Drittlandtransfer von Daten Minderjähriger',
|
||||
combineWithMinors: true,
|
||||
},
|
||||
|
||||
// ========== F: Zertifizierung (5 rules) ==========
|
||||
{
|
||||
id: 'HT-F01',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'ISO27001',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'ISO/IEC 27001',
|
||||
description: 'Angestrebte ISO 27001 Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F02',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'ISO27701',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'VVT', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'ISO/IEC 27701',
|
||||
description: 'Angestrebte ISO 27701 Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F03',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'SOC2',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'SOC 2 Type II',
|
||||
description: 'Angestrebte SOC 2 Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F04',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'TISAX',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST', 'VENDOR_MANAGEMENT'],
|
||||
legalReference: 'TISAX',
|
||||
description: 'Angestrebte TISAX Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F05',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'BSI-Grundschutz',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'BSI IT-Grundschutz',
|
||||
description: 'Angestrebte BSI-Grundschutz Zertifizierung',
|
||||
},
|
||||
|
||||
// ========== G: Volumen/Skala (5 rules) ==========
|
||||
{
|
||||
id: 'HT-G01',
|
||||
category: 'scale',
|
||||
questionId: 'data_volume',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: '>1000000',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'LOESCHKONZEPT'],
|
||||
legalReference: 'Art. 35 Abs. 3 lit. b DSGVO',
|
||||
description: 'Umfangreiche Verarbeitung personenbezogener Daten (>1 Mio. Datensätze)',
|
||||
},
|
||||
{
|
||||
id: 'HT-G02',
|
||||
category: 'scale',
|
||||
questionId: 'data_volume',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: '100000-1000000',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'Art. 35 Abs. 3 lit. b DSGVO',
|
||||
description: 'Großvolumige Datenverarbeitung (100k-1M Datensätze)',
|
||||
},
|
||||
{
|
||||
id: 'HT-G03',
|
||||
category: 'scale',
|
||||
questionId: 'org_customer_count',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: '100000+',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSR_PROZESS'],
|
||||
legalReference: 'Art. 15-22 DSGVO',
|
||||
description: 'Großer Kundenstamm (>100k) mit hoher Betroffenenanzahl',
|
||||
},
|
||||
{
|
||||
id: 'HT-G04',
|
||||
category: 'scale',
|
||||
questionId: 'org_employee_count',
|
||||
condition: 'GREATER_THAN',
|
||||
conditionValue: 249,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'LOESCHKONZEPT', 'NOTFALLPLAN'],
|
||||
legalReference: 'Art. 37 DSGVO',
|
||||
description: 'Große Organisation (>250 Mitarbeiter) mit erhöhten Compliance-Anforderungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-G05',
|
||||
category: 'scale',
|
||||
questionId: 'org_employee_count',
|
||||
condition: 'GREATER_THAN',
|
||||
conditionValue: 999,
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'LOESCHKONZEPT'],
|
||||
legalReference: 'Art. 35 + Art. 37 DSGVO',
|
||||
description: 'Sehr große Organisation (>1000 Mitarbeiter) mit Art. 9 Daten',
|
||||
combineWithArt9: true,
|
||||
},
|
||||
|
||||
// ========== H: Produkt/Business (7 rules) ==========
|
||||
{
|
||||
id: 'HT-H01a',
|
||||
category: 'product',
|
||||
questionId: 'prod_webshop',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
excludeWhen: { questionId: 'org_business_model', value: 'B2B' },
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER', 'EINWILLIGUNGEN',
|
||||
'WIDERRUFSBELEHRUNG', 'PREISANGABEN', 'FERNABSATZ_INFO', 'STREITBEILEGUNG'],
|
||||
legalReference: 'Art. 6 DSGVO + Fernabsatzrecht + PAngV + VSBG',
|
||||
description: 'E-Commerce / Webshop (B2C) — Verbraucherschutzpflichten',
|
||||
},
|
||||
{
|
||||
id: 'HT-H01b',
|
||||
category: 'product',
|
||||
questionId: 'prod_webshop',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
requireWhen: { questionId: 'org_business_model', value: 'B2B' },
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER'],
|
||||
legalReference: 'Art. 6 DSGVO + eCommerce',
|
||||
description: 'E-Commerce / Webshop (B2B) — Basis-Pflichten',
|
||||
},
|
||||
{
|
||||
id: 'HT-H02',
|
||||
category: 'product',
|
||||
questionId: 'prod_data_broker',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'EINWILLIGUNGEN'],
|
||||
legalReference: 'Art. 35 Abs. 3 DSGVO',
|
||||
description: 'Datenhandel oder Datenmakler-Tätigkeit',
|
||||
},
|
||||
{
|
||||
id: 'HT-H03',
|
||||
category: 'product',
|
||||
questionId: 'prod_api_external',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AVV'],
|
||||
legalReference: 'Art. 28 DSGVO',
|
||||
description: 'Externe API mit Datenweitergabe',
|
||||
},
|
||||
{
|
||||
id: 'HT-H04',
|
||||
category: 'product',
|
||||
questionId: 'org_business_model',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'b2c',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['DSE', 'COOKIE_BANNER', 'EINWILLIGUNGEN'],
|
||||
legalReference: 'Art. 6 DSGVO',
|
||||
description: 'B2C-Geschäftsmodell mit Endkundenkontakt',
|
||||
},
|
||||
{
|
||||
id: 'HT-H05',
|
||||
category: 'product',
|
||||
questionId: 'org_industry',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'finance',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'Art. 6 DSGVO + Finanzaufsicht',
|
||||
description: 'Finanzbranche mit erhöhten regulatorischen Anforderungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-H06',
|
||||
category: 'product',
|
||||
questionId: 'org_industry',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'healthcare',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 DSGVO + Gesundheitsrecht',
|
||||
description: 'Gesundheitsbranche mit sensiblen Daten',
|
||||
},
|
||||
{
|
||||
id: 'HT-H07',
|
||||
category: 'product',
|
||||
questionId: 'org_industry',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'public',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSR_PROZESS'],
|
||||
legalReference: 'Art. 6 Abs. 1 lit. e DSGVO',
|
||||
description: 'Öffentlicher Sektor',
|
||||
},
|
||||
|
||||
// ========== I: Prozessreife - Gap Flags (5 rules) ==========
|
||||
{
|
||||
id: 'HT-I01',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_dsar_process',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 15-22 DSGVO',
|
||||
description: 'Fehlender Prozess für Betroffenenrechte',
|
||||
},
|
||||
{
|
||||
id: 'HT-I02',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_deletion_concept',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 17 DSGVO',
|
||||
description: 'Fehlendes Löschkonzept',
|
||||
},
|
||||
{
|
||||
id: 'HT-I03',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_incident_response',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 33 DSGVO',
|
||||
description: 'Fehlender Incident-Response-Prozess',
|
||||
},
|
||||
{
|
||||
id: 'HT-I04',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_regular_audits',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 24 DSGVO',
|
||||
description: 'Fehlende regelmäßige Audits',
|
||||
},
|
||||
{
|
||||
id: 'HT-I05',
|
||||
category: 'process_maturity',
|
||||
questionId: 'comp_training',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 39 Abs. 1 lit. b DSGVO',
|
||||
description: 'Fehlende Schulungen zum Datenschutz',
|
||||
},
|
||||
|
||||
// ========== J: IACE — AI Act Produkt-Triggers (3 rules) ==========
|
||||
{
|
||||
id: 'HT-J01',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.containsAI',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act Annex I + EU Maschinenverordnung 2023/1230',
|
||||
description: 'KI mit Sicherheitsfunktion in Maschine → AI Act High-Risk',
|
||||
combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-J02',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.containsAI',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act + EU Maschinenverordnung 2023/1230',
|
||||
description: 'Autonome KI in Maschine → AI Act + Maschinenverordnung',
|
||||
combineWithMachineBuilder: { field: 'autonomousBehavior', value: true },
|
||||
riskWeight: 8,
|
||||
},
|
||||
{
|
||||
id: 'HT-J03',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.hasSafetyFunction',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act Annex III',
|
||||
description: 'KI-Bildverarbeitung mit Sicherheitsbezug',
|
||||
combineWithMachineBuilder: { field: 'aiIntegrationType', includes: 'vision' },
|
||||
riskWeight: 8,
|
||||
},
|
||||
|
||||
// ========== K: IACE — CRA Triggers (3 rules) ==========
|
||||
{
|
||||
id: 'HT-K01',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.isNetworked',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Cyber Resilience Act (CRA)',
|
||||
description: 'Vernetztes Produkt → Cyber Resilience Act',
|
||||
riskWeight: 6,
|
||||
},
|
||||
{
|
||||
id: 'HT-K02',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.hasRemoteAccess',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'CRA + NIS2 Art. 21',
|
||||
description: 'Remote-Zugriff → CRA + NIS2 Supply Chain',
|
||||
riskWeight: 7,
|
||||
},
|
||||
{
|
||||
id: 'HT-K03',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.hasOTAUpdates',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'CRA Art. 10 - Patch Management',
|
||||
description: 'OTA-Updates → CRA Patch Management Pflicht',
|
||||
riskWeight: 7,
|
||||
},
|
||||
|
||||
// ========== L: IACE — NIS2 indirekt (2 rules) ==========
|
||||
{
|
||||
id: 'HT-L01',
|
||||
category: 'iace_nis2_indirect',
|
||||
questionId: 'machineBuilder.criticalSectorClients',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'NIS2 Art. 21 - Supply Chain',
|
||||
description: 'Lieferant an KRITIS → NIS2 Supply Chain Anforderungen',
|
||||
riskWeight: 7,
|
||||
},
|
||||
{
|
||||
id: 'HT-L02',
|
||||
category: 'iace_nis2_indirect',
|
||||
questionId: 'machineBuilder.oemClients',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'NIS2 + EU Maschinenverordnung',
|
||||
description: 'OEM-Zulieferer → Compliance-Nachweispflicht',
|
||||
riskWeight: 5,
|
||||
},
|
||||
|
||||
// ========== M: IACE — Maschinenverordnung Triggers (4 rules) ==========
|
||||
{
|
||||
id: 'HT-M01',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.containsSoftware',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230 Anhang III',
|
||||
description: 'Software als Sicherheitskomponente → Maschinenverordnung',
|
||||
combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-M02',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.ceMarkingRequired',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230',
|
||||
description: 'CE-Kennzeichnung erforderlich',
|
||||
riskWeight: 6,
|
||||
},
|
||||
{
|
||||
id: 'HT-M03',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.ceMarkingRequired',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230 Art. 10',
|
||||
description: 'CE ohne bestehende Risikobeurteilung → Dringend!',
|
||||
combineWithMachineBuilder: { field: 'hasRiskAssessment', value: false },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-M04',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.containsFirmware',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Maschinenverordnung + CRA',
|
||||
description: 'Firmware mit Remote-Update → Change Management Pflicht',
|
||||
combineWithMachineBuilder: { field: 'hasOTAUpdates', value: true },
|
||||
riskWeight: 7,
|
||||
},
|
||||
...HARD_TRIGGER_RULES_A_E,
|
||||
...HARD_TRIGGER_RULES_F_M,
|
||||
]
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* Hard Trigger Rules A–E
|
||||
* Groups: Art.9 (A), Vulnerable (B), ADM/KI (C), Ueberwachung (D), Drittland (E)
|
||||
*/
|
||||
import type { HardTriggerRule } from '../compliance-scope-types'
|
||||
|
||||
export const HARD_TRIGGER_RULES_A_E: HardTriggerRule[] = [
|
||||
// ========== A: Art. 9 Besondere Kategorien (9 rules) ==========
|
||||
{
|
||||
id: 'HT-A01',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'gesundheit',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung von Gesundheitsdaten',
|
||||
},
|
||||
{
|
||||
id: 'HT-A02',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'biometrie',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung biometrischer Daten zur eindeutigen Identifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-A03',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'genetik',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung genetischer Daten',
|
||||
},
|
||||
{
|
||||
id: 'HT-A04',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'politisch',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung politischer Meinungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-A05',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'religion',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung religiöser oder weltanschaulicher Überzeugungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-A06',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'gewerkschaft',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung von Gewerkschaftszugehörigkeit',
|
||||
},
|
||||
{
|
||||
id: 'HT-A07',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'sexualleben',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung von Daten zum Sexualleben oder zur sexuellen Orientierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-A08',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'strafrechtlich',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 10 DSGVO',
|
||||
description: 'Verarbeitung strafrechtlicher Verurteilungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-A09',
|
||||
category: 'art9',
|
||||
questionId: 'data_art9',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'ethnisch',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 Abs. 1 DSGVO',
|
||||
description: 'Verarbeitung der rassischen oder ethnischen Herkunft',
|
||||
},
|
||||
|
||||
// ========== B: Vulnerable Gruppen (3 rules) ==========
|
||||
{
|
||||
id: 'HT-B01',
|
||||
category: 'vulnerable',
|
||||
questionId: 'data_minors',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'DSE'],
|
||||
legalReference: 'Art. 8 DSGVO',
|
||||
description: 'Verarbeitung von Daten Minderjähriger',
|
||||
},
|
||||
{
|
||||
id: 'HT-B02',
|
||||
category: 'vulnerable',
|
||||
questionId: 'data_minors',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'DSE'],
|
||||
legalReference: 'Art. 8 + Art. 9 DSGVO',
|
||||
description: 'Verarbeitung besonderer Kategorien von Daten Minderjähriger',
|
||||
combineWithArt9: true,
|
||||
},
|
||||
{
|
||||
id: 'HT-B03',
|
||||
category: 'vulnerable',
|
||||
questionId: 'data_minors',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'AI_ACT_DOKU'],
|
||||
legalReference: 'Art. 8 DSGVO + AI Act',
|
||||
description: 'KI-gestützte Verarbeitung von Daten Minderjähriger',
|
||||
combineWithAI: true,
|
||||
},
|
||||
|
||||
// ========== C: ADM/KI (6 rules) ==========
|
||||
{
|
||||
id: 'HT-C01',
|
||||
category: 'adm',
|
||||
questionId: 'proc_adm_scoring',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 22 DSGVO',
|
||||
description: 'Automatisierte Einzelentscheidung mit Rechtswirkung oder erheblicher Beeinträchtigung',
|
||||
},
|
||||
{
|
||||
id: 'HT-C02',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'autonom',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'AI_ACT_DOKU'],
|
||||
legalReference: 'Art. 22 DSGVO + AI Act',
|
||||
description: 'Autonome KI-Systeme mit Entscheidungsbefugnis',
|
||||
},
|
||||
{
|
||||
id: 'HT-C03',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'scoring',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'Art. 22 DSGVO',
|
||||
description: 'KI-gestütztes Scoring',
|
||||
},
|
||||
{
|
||||
id: 'HT-C04',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'profiling',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 22 DSGVO',
|
||||
description: 'KI-gestütztes Profiling mit erheblicher Wirkung',
|
||||
},
|
||||
{
|
||||
id: 'HT-C05',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'generativ',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'AI_ACT_DOKU'],
|
||||
legalReference: 'AI Act',
|
||||
description: 'Generative KI-Systeme',
|
||||
},
|
||||
{
|
||||
id: 'HT-C06',
|
||||
category: 'adm',
|
||||
questionId: 'proc_ai_usage',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'chatbot',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'AI_ACT_DOKU'],
|
||||
legalReference: 'AI Act',
|
||||
description: 'Chatbots mit Personendatenverarbeitung',
|
||||
},
|
||||
|
||||
// ========== D: Überwachung (5 rules) ==========
|
||||
{
|
||||
id: 'HT-D01',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_video_surveillance',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSE'],
|
||||
legalReference: 'Art. 6 DSGVO',
|
||||
description: 'Videoüberwachung',
|
||||
},
|
||||
{
|
||||
id: 'HT-D02',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_employee_monitoring',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 88 DSGVO + BetrVG',
|
||||
description: 'Mitarbeiterüberwachung',
|
||||
},
|
||||
{
|
||||
id: 'HT-D03',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_tracking',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'COOKIE_BANNER', 'EINWILLIGUNGEN'],
|
||||
legalReference: 'Art. 6 DSGVO + ePrivacy',
|
||||
description: 'Online-Tracking',
|
||||
},
|
||||
{
|
||||
id: 'HT-D04',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_video_surveillance',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 35 Abs. 3 DSGVO',
|
||||
description: 'Videoüberwachung kombiniert mit Mitarbeitermonitoring',
|
||||
combineWithEmployeeMonitoring: true,
|
||||
},
|
||||
{
|
||||
id: 'HT-D05',
|
||||
category: 'surveillance',
|
||||
questionId: 'proc_video_surveillance',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 35 Abs. 3 DSGVO',
|
||||
description: 'Videoüberwachung kombiniert mit automatisierter Bewertung',
|
||||
combineWithADM: true,
|
||||
},
|
||||
|
||||
// ========== E: Drittland (5 rules) ==========
|
||||
{
|
||||
id: 'HT-E01',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_third_country',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TRANSFER_DOKU'],
|
||||
legalReference: 'Art. 44 ff. DSGVO',
|
||||
description: 'Datenübermittlung in Drittland',
|
||||
},
|
||||
{
|
||||
id: 'HT-E02',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_hosting_location',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'drittland',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU'],
|
||||
legalReference: 'Art. 44 ff. DSGVO',
|
||||
description: 'Hosting in Drittland',
|
||||
},
|
||||
{
|
||||
id: 'HT-E03',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_hosting_location',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'us_adequacy',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TRANSFER_DOKU'],
|
||||
legalReference: 'Art. 45 DSGVO',
|
||||
description: 'Hosting in USA mit Angemessenheitsbeschluss',
|
||||
},
|
||||
{
|
||||
id: 'HT-E04',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_third_country',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU', 'DSFA'],
|
||||
legalReference: 'Art. 44 ff. + Art. 9 DSGVO',
|
||||
description: 'Drittlandtransfer besonderer Kategorien',
|
||||
combineWithArt9: true,
|
||||
},
|
||||
{
|
||||
id: 'HT-E05',
|
||||
category: 'third_country',
|
||||
questionId: 'tech_third_country',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'TRANSFER_DOKU', 'DSFA'],
|
||||
legalReference: 'Art. 44 ff. + Art. 8 DSGVO',
|
||||
description: 'Drittlandtransfer von Daten Minderjähriger',
|
||||
combineWithMinors: true,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,467 @@
|
||||
/**
|
||||
* Hard Trigger Rules F–M
|
||||
* Groups: Zertifizierung (F), Volumen/Skala (G), Produkt/Business (H),
|
||||
* Prozessreife (I), IACE AI Act Produkt (J), IACE CRA (K),
|
||||
* IACE NIS2 indirekt (L), IACE Maschinenverordnung (M)
|
||||
*/
|
||||
import type { HardTriggerRule } from '../compliance-scope-types'
|
||||
|
||||
export const HARD_TRIGGER_RULES_F_M: HardTriggerRule[] = [
|
||||
// ========== F: Zertifizierung (5 rules) ==========
|
||||
{
|
||||
id: 'HT-F01',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'ISO27001',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'ISO/IEC 27001',
|
||||
description: 'Angestrebte ISO 27001 Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F02',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'ISO27701',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'VVT', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'ISO/IEC 27701',
|
||||
description: 'Angestrebte ISO 27701 Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F03',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'SOC2',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'SOC 2 Type II',
|
||||
description: 'Angestrebte SOC 2 Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F04',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'TISAX',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST', 'VENDOR_MANAGEMENT'],
|
||||
legalReference: 'TISAX',
|
||||
description: 'Angestrebte TISAX Zertifizierung',
|
||||
},
|
||||
{
|
||||
id: 'HT-F05',
|
||||
category: 'certification',
|
||||
questionId: 'org_cert_target',
|
||||
condition: 'CONTAINS',
|
||||
conditionValue: 'BSI-Grundschutz',
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AUDIT_CHECKLIST'],
|
||||
legalReference: 'BSI IT-Grundschutz',
|
||||
description: 'Angestrebte BSI-Grundschutz Zertifizierung',
|
||||
},
|
||||
|
||||
// ========== G: Volumen/Skala (5 rules) ==========
|
||||
{
|
||||
id: 'HT-G01',
|
||||
category: 'scale',
|
||||
questionId: 'data_volume',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: '>1000000',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'LOESCHKONZEPT'],
|
||||
legalReference: 'Art. 35 Abs. 3 lit. b DSGVO',
|
||||
description: 'Umfangreiche Verarbeitung personenbezogener Daten (>1 Mio. Datensätze)',
|
||||
},
|
||||
{
|
||||
id: 'HT-G02',
|
||||
category: 'scale',
|
||||
questionId: 'data_volume',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: '100000-1000000',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'Art. 35 Abs. 3 lit. b DSGVO',
|
||||
description: 'Großvolumige Datenverarbeitung (100k-1M Datensätze)',
|
||||
},
|
||||
{
|
||||
id: 'HT-G03',
|
||||
category: 'scale',
|
||||
questionId: 'org_customer_count',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: '100000+',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSR_PROZESS'],
|
||||
legalReference: 'Art. 15-22 DSGVO',
|
||||
description: 'Großer Kundenstamm (>100k) mit hoher Betroffenenanzahl',
|
||||
},
|
||||
{
|
||||
id: 'HT-G04',
|
||||
category: 'scale',
|
||||
questionId: 'org_employee_count',
|
||||
condition: 'GREATER_THAN',
|
||||
conditionValue: 249,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'LOESCHKONZEPT', 'NOTFALLPLAN'],
|
||||
legalReference: 'Art. 37 DSGVO',
|
||||
description: 'Große Organisation (>250 Mitarbeiter) mit erhöhten Compliance-Anforderungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-G05',
|
||||
category: 'scale',
|
||||
questionId: 'org_employee_count',
|
||||
condition: 'GREATER_THAN',
|
||||
conditionValue: 999,
|
||||
minimumLevel: 'L4',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'LOESCHKONZEPT'],
|
||||
legalReference: 'Art. 35 + Art. 37 DSGVO',
|
||||
description: 'Sehr große Organisation (>1000 Mitarbeiter) mit Art. 9 Daten',
|
||||
combineWithArt9: true,
|
||||
},
|
||||
|
||||
// ========== H: Produkt/Business (7 rules) ==========
|
||||
{
|
||||
id: 'HT-H01a',
|
||||
category: 'product',
|
||||
questionId: 'prod_webshop',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
excludeWhen: { questionId: 'org_business_model', value: 'B2B' },
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER', 'EINWILLIGUNGEN',
|
||||
'WIDERRUFSBELEHRUNG', 'PREISANGABEN', 'FERNABSATZ_INFO', 'STREITBEILEGUNG'],
|
||||
legalReference: 'Art. 6 DSGVO + Fernabsatzrecht + PAngV + VSBG',
|
||||
description: 'E-Commerce / Webshop (B2C) — Verbraucherschutzpflichten',
|
||||
},
|
||||
{
|
||||
id: 'HT-H01b',
|
||||
category: 'product',
|
||||
questionId: 'prod_webshop',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
requireWhen: { questionId: 'org_business_model', value: 'B2B' },
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['DSE', 'AGB', 'COOKIE_BANNER'],
|
||||
legalReference: 'Art. 6 DSGVO + eCommerce',
|
||||
description: 'E-Commerce / Webshop (B2B) — Basis-Pflichten',
|
||||
},
|
||||
{
|
||||
id: 'HT-H02',
|
||||
category: 'product',
|
||||
questionId: 'prod_data_broker',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA', 'EINWILLIGUNGEN'],
|
||||
legalReference: 'Art. 35 Abs. 3 DSGVO',
|
||||
description: 'Datenhandel oder Datenmakler-Tätigkeit',
|
||||
},
|
||||
{
|
||||
id: 'HT-H03',
|
||||
category: 'product',
|
||||
questionId: 'prod_api_external',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM', 'AVV'],
|
||||
legalReference: 'Art. 28 DSGVO',
|
||||
description: 'Externe API mit Datenweitergabe',
|
||||
},
|
||||
{
|
||||
id: 'HT-H04',
|
||||
category: 'product',
|
||||
questionId: 'org_business_model',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'b2c',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['DSE', 'COOKIE_BANNER', 'EINWILLIGUNGEN'],
|
||||
legalReference: 'Art. 6 DSGVO',
|
||||
description: 'B2C-Geschäftsmodell mit Endkundenkontakt',
|
||||
},
|
||||
{
|
||||
id: 'HT-H05',
|
||||
category: 'product',
|
||||
questionId: 'org_industry',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'finance',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'Art. 6 DSGVO + Finanzaufsicht',
|
||||
description: 'Finanzbranche mit erhöhten regulatorischen Anforderungen',
|
||||
},
|
||||
{
|
||||
id: 'HT-H06',
|
||||
category: 'product',
|
||||
questionId: 'org_industry',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'healthcare',
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: true,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSFA'],
|
||||
legalReference: 'Art. 9 DSGVO + Gesundheitsrecht',
|
||||
description: 'Gesundheitsbranche mit sensiblen Daten',
|
||||
},
|
||||
{
|
||||
id: 'HT-H07',
|
||||
category: 'product',
|
||||
questionId: 'org_industry',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: 'public',
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM', 'DSR_PROZESS'],
|
||||
legalReference: 'Art. 6 Abs. 1 lit. e DSGVO',
|
||||
description: 'Öffentlicher Sektor',
|
||||
},
|
||||
|
||||
// ========== I: Prozessreife - Gap Flags (5 rules) ==========
|
||||
{
|
||||
id: 'HT-I01',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_dsar_process',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 15-22 DSGVO',
|
||||
description: 'Fehlender Prozess für Betroffenenrechte',
|
||||
},
|
||||
{
|
||||
id: 'HT-I02',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_deletion_concept',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 17 DSGVO',
|
||||
description: 'Fehlendes Löschkonzept',
|
||||
},
|
||||
{
|
||||
id: 'HT-I03',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_incident_response',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 33 DSGVO',
|
||||
description: 'Fehlender Incident-Response-Prozess',
|
||||
},
|
||||
{
|
||||
id: 'HT-I04',
|
||||
category: 'process_maturity',
|
||||
questionId: 'proc_regular_audits',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 24 DSGVO',
|
||||
description: 'Fehlende regelmäßige Audits',
|
||||
},
|
||||
{
|
||||
id: 'HT-I05',
|
||||
category: 'process_maturity',
|
||||
questionId: 'comp_training',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: false,
|
||||
minimumLevel: 'L1',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'Art. 39 Abs. 1 lit. b DSGVO',
|
||||
description: 'Fehlende Schulungen zum Datenschutz',
|
||||
},
|
||||
|
||||
// ========== J: IACE — AI Act Produkt-Triggers (3 rules) ==========
|
||||
{
|
||||
id: 'HT-J01',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.containsAI',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act Annex I + EU Maschinenverordnung 2023/1230',
|
||||
description: 'KI mit Sicherheitsfunktion in Maschine → AI Act High-Risk',
|
||||
combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-J02',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.containsAI',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act + EU Maschinenverordnung 2023/1230',
|
||||
description: 'Autonome KI in Maschine → AI Act + Maschinenverordnung',
|
||||
combineWithMachineBuilder: { field: 'autonomousBehavior', value: true },
|
||||
riskWeight: 8,
|
||||
},
|
||||
{
|
||||
id: 'HT-J03',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.hasSafetyFunction',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act Annex III',
|
||||
description: 'KI-Bildverarbeitung mit Sicherheitsbezug',
|
||||
combineWithMachineBuilder: { field: 'aiIntegrationType', includes: 'vision' },
|
||||
riskWeight: 8,
|
||||
},
|
||||
|
||||
// ========== K: IACE — CRA Triggers (3 rules) ==========
|
||||
{
|
||||
id: 'HT-K01',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.isNetworked',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Cyber Resilience Act (CRA)',
|
||||
description: 'Vernetztes Produkt → Cyber Resilience Act',
|
||||
riskWeight: 6,
|
||||
},
|
||||
{
|
||||
id: 'HT-K02',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.hasRemoteAccess',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'CRA + NIS2 Art. 21',
|
||||
description: 'Remote-Zugriff → CRA + NIS2 Supply Chain',
|
||||
riskWeight: 7,
|
||||
},
|
||||
{
|
||||
id: 'HT-K03',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.hasOTAUpdates',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'CRA Art. 10 - Patch Management',
|
||||
description: 'OTA-Updates → CRA Patch Management Pflicht',
|
||||
riskWeight: 7,
|
||||
},
|
||||
|
||||
// ========== L: IACE — NIS2 indirekt (2 rules) ==========
|
||||
{
|
||||
id: 'HT-L01',
|
||||
category: 'iace_nis2_indirect',
|
||||
questionId: 'machineBuilder.criticalSectorClients',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'NIS2 Art. 21 - Supply Chain',
|
||||
description: 'Lieferant an KRITIS → NIS2 Supply Chain Anforderungen',
|
||||
riskWeight: 7,
|
||||
},
|
||||
{
|
||||
id: 'HT-L02',
|
||||
category: 'iace_nis2_indirect',
|
||||
questionId: 'machineBuilder.oemClients',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'NIS2 + EU Maschinenverordnung',
|
||||
description: 'OEM-Zulieferer → Compliance-Nachweispflicht',
|
||||
riskWeight: 5,
|
||||
},
|
||||
|
||||
// ========== M: IACE — Maschinenverordnung Triggers (4 rules) ==========
|
||||
{
|
||||
id: 'HT-M01',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.containsSoftware',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230 Anhang III',
|
||||
description: 'Software als Sicherheitskomponente → Maschinenverordnung',
|
||||
combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-M02',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.ceMarkingRequired',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230',
|
||||
description: 'CE-Kennzeichnung erforderlich',
|
||||
riskWeight: 6,
|
||||
},
|
||||
{
|
||||
id: 'HT-M03',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.ceMarkingRequired',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230 Art. 10',
|
||||
description: 'CE ohne bestehende Risikobeurteilung → Dringend!',
|
||||
combineWithMachineBuilder: { field: 'hasRiskAssessment', value: false },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-M04',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.containsFirmware',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Maschinenverordnung + CRA',
|
||||
description: 'Firmware mit Remote-Update → Change Management Pflicht',
|
||||
combineWithMachineBuilder: { field: 'hasOTAUpdates', value: true },
|
||||
riskWeight: 7,
|
||||
},
|
||||
]
|
||||
217
admin-compliance/lib/sdk/demo-data/demo-state.ts
Normal file
217
admin-compliance/lib/sdk/demo-data/demo-state.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* Demo State Generator
|
||||
*
|
||||
* Generates a complete demo SDKState object for seeding.
|
||||
* Barrel-split from index.ts. Do NOT import directly; use index.ts.
|
||||
*/
|
||||
|
||||
import { SDKState } from '../types'
|
||||
import { getDemoUseCases } from './use-cases'
|
||||
import { getDemoRisks } from './risks'
|
||||
import { getDemoControls } from './controls'
|
||||
import { getDemoDSFA } from './dsfa'
|
||||
import { getDemoTOMs } from './toms'
|
||||
import { getDemoProcessingActivities, getDemoRetentionPolicies } from './vvt'
|
||||
|
||||
/**
|
||||
* Generate a complete demo state object
|
||||
* This is used as seed data for the API, not as runtime data
|
||||
*/
|
||||
export function generateDemoState(tenantId: string, userId: string): Partial<SDKState> {
|
||||
const now = new Date()
|
||||
|
||||
return {
|
||||
// Metadata
|
||||
version: '1.0.0',
|
||||
projectVersion: 1,
|
||||
lastModified: now,
|
||||
|
||||
// Tenant & User
|
||||
tenantId,
|
||||
userId,
|
||||
subscription: 'PROFESSIONAL',
|
||||
|
||||
// Customer Type
|
||||
customerType: 'new',
|
||||
|
||||
// Company Profile (Demo: TechStart GmbH - SaaS-Startup aus Berlin)
|
||||
companyProfile: {
|
||||
companyName: 'TechStart GmbH',
|
||||
legalForm: 'gmbh',
|
||||
industry: ['Technologie / IT'],
|
||||
industryOther: '',
|
||||
foundedYear: 2022,
|
||||
businessModel: 'B2B_B2C',
|
||||
offerings: ['app_web', 'software_saas', 'services_consulting'],
|
||||
companySize: 'small',
|
||||
employeeCount: '10-49',
|
||||
annualRevenue: '2-10 Mio',
|
||||
headquartersCountry: 'DE',
|
||||
headquartersCity: 'Berlin',
|
||||
hasInternationalLocations: false,
|
||||
internationalCountries: [],
|
||||
targetMarkets: ['germany_only', 'dach'],
|
||||
primaryJurisdiction: 'DE',
|
||||
isDataController: true,
|
||||
isDataProcessor: true,
|
||||
dpoName: 'Max Mustermann',
|
||||
dpoEmail: 'dsb@techstart.de',
|
||||
legalContactName: null,
|
||||
legalContactEmail: null,
|
||||
isComplete: true,
|
||||
completedAt: new Date('2026-01-14'),
|
||||
},
|
||||
|
||||
// Progress - showing a realistic partially completed workflow
|
||||
currentPhase: 2,
|
||||
currentStep: 'tom',
|
||||
completedSteps: [
|
||||
'company-profile',
|
||||
'use-case-assessment',
|
||||
'screening',
|
||||
'modules',
|
||||
'requirements',
|
||||
'controls',
|
||||
'evidence',
|
||||
'audit-checklist',
|
||||
'risks',
|
||||
'ai-act',
|
||||
'obligations',
|
||||
'dsfa',
|
||||
],
|
||||
checkpoints: {
|
||||
'CP-PROF': { checkpointId: 'CP-PROF', passed: true, validatedAt: new Date('2026-01-14'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-UC': { checkpointId: 'CP-UC', passed: true, validatedAt: new Date('2026-01-15'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-SCAN': { checkpointId: 'CP-SCAN', passed: true, validatedAt: new Date('2026-01-16'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-MOD': { checkpointId: 'CP-MOD', passed: true, validatedAt: new Date('2026-01-17'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-REQ': { checkpointId: 'CP-REQ', passed: true, validatedAt: new Date('2026-01-18'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-CTRL': { checkpointId: 'CP-CTRL', passed: true, validatedAt: new Date('2026-01-19'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-EVI': { checkpointId: 'CP-EVI', passed: true, validatedAt: new Date('2026-01-20'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-CHK': { checkpointId: 'CP-CHK', passed: true, validatedAt: new Date('2026-01-21'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-RISK': { checkpointId: 'CP-RISK', passed: true, validatedAt: new Date('2026-01-22'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-AI': { checkpointId: 'CP-AI', passed: true, validatedAt: new Date('2026-01-25'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-OBL': { checkpointId: 'CP-OBL', passed: true, validatedAt: new Date('2026-01-27'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-DSFA': { checkpointId: 'CP-DSFA', passed: true, validatedAt: new Date('2026-01-30'), validatedBy: 'DSB', errors: [], warnings: [] },
|
||||
},
|
||||
|
||||
// Phase 1 Data
|
||||
useCases: getDemoUseCases(),
|
||||
activeUseCase: 'demo-uc-1',
|
||||
screening: {
|
||||
id: 'demo-scan-1',
|
||||
status: 'COMPLETED',
|
||||
startedAt: new Date('2026-01-16T09:00:00'),
|
||||
completedAt: new Date('2026-01-16T09:15:00'),
|
||||
sbom: {
|
||||
format: 'CycloneDX',
|
||||
version: '1.4',
|
||||
components: [
|
||||
{ name: 'tensorflow', version: '2.15.0', type: 'library', purl: 'pkg:pypi/tensorflow@2.15.0', licenses: ['Apache-2.0'], vulnerabilities: [] },
|
||||
{ name: 'scikit-learn', version: '1.4.0', type: 'library', purl: 'pkg:pypi/scikit-learn@1.4.0', licenses: ['BSD-3-Clause'], vulnerabilities: [] },
|
||||
{ name: 'pandas', version: '2.2.0', type: 'library', purl: 'pkg:pypi/pandas@2.2.0', licenses: ['BSD-3-Clause'], vulnerabilities: [] },
|
||||
],
|
||||
dependencies: [],
|
||||
generatedAt: new Date('2026-01-16T09:10:00'),
|
||||
},
|
||||
securityScan: {
|
||||
totalIssues: 3,
|
||||
critical: 0,
|
||||
high: 1,
|
||||
medium: 1,
|
||||
low: 1,
|
||||
issues: [
|
||||
{
|
||||
id: 'sec-issue-1', severity: 'HIGH', title: 'Outdated cryptography library',
|
||||
description: 'The cryptography library version 41.0.0 has known vulnerabilities',
|
||||
cve: 'CVE-2024-1234', cvss: 7.5, affectedComponent: 'cryptography',
|
||||
remediation: 'Upgrade to cryptography >= 42.0.0', status: 'RESOLVED',
|
||||
},
|
||||
{
|
||||
id: 'sec-issue-2', severity: 'MEDIUM', title: 'Insecure default configuration',
|
||||
description: 'Debug mode enabled in production configuration',
|
||||
cve: null, cvss: 5.3, affectedComponent: 'app-config',
|
||||
remediation: 'Set DEBUG=false in production', status: 'RESOLVED',
|
||||
},
|
||||
{
|
||||
id: 'sec-issue-3', severity: 'LOW', title: 'Missing security headers',
|
||||
description: 'X-Content-Type-Options header not set',
|
||||
cve: null, cvss: 3.1, affectedComponent: 'web-server',
|
||||
remediation: 'Add security headers middleware', status: 'RESOLVED',
|
||||
},
|
||||
],
|
||||
},
|
||||
error: null,
|
||||
},
|
||||
modules: [
|
||||
{ id: 'demo-mod-1', name: 'Kundendaten-Modul', description: 'Verarbeitung von Kundendaten für Marketing und Analyse', regulations: ['DSGVO', 'TTDSG'], criticality: 'HIGH', processesPersonalData: true, hasAIComponents: true },
|
||||
{ id: 'demo-mod-2', name: 'HR-Modul', description: 'Bewerbermanagement und Personalverwaltung', regulations: ['DSGVO', 'AGG', 'AI Act'], criticality: 'HIGH', processesPersonalData: true, hasAIComponents: true },
|
||||
{ id: 'demo-mod-3', name: 'Support-Modul', description: 'Kundenservice und Chatbot-System', regulations: ['DSGVO', 'AI Act'], criticality: 'MEDIUM', processesPersonalData: true, hasAIComponents: true },
|
||||
],
|
||||
requirements: [
|
||||
{ id: 'demo-req-1', regulation: 'DSGVO', article: 'Art. 5', title: 'Grundsätze der Verarbeitung', description: 'Einhaltung der Grundsätze für die Verarbeitung personenbezogener Daten', criticality: 'CRITICAL', applicableModules: ['demo-mod-1', 'demo-mod-2', 'demo-mod-3'], status: 'IMPLEMENTED', controls: ['demo-ctrl-1', 'demo-ctrl-2', 'demo-ctrl-9'] },
|
||||
{ id: 'demo-req-2', regulation: 'DSGVO', article: 'Art. 32', title: 'Sicherheit der Verarbeitung', description: 'Geeignete technische und organisatorische Maßnahmen', criticality: 'CRITICAL', applicableModules: ['demo-mod-1', 'demo-mod-2', 'demo-mod-3'], status: 'IMPLEMENTED', controls: ['demo-ctrl-1', 'demo-ctrl-3', 'demo-ctrl-4'] },
|
||||
{ id: 'demo-req-3', regulation: 'DSGVO', article: 'Art. 25', title: 'Datenschutz durch Technikgestaltung', description: 'Privacy by Design und Privacy by Default', criticality: 'HIGH', applicableModules: ['demo-mod-1', 'demo-mod-2'], status: 'IMPLEMENTED', controls: ['demo-ctrl-9', 'demo-ctrl-10'] },
|
||||
{ id: 'demo-req-4', regulation: 'AI Act', article: 'Art. 13', title: 'Transparenz', description: 'Transparenzanforderungen für KI-Systeme', criticality: 'HIGH', applicableModules: ['demo-mod-1', 'demo-mod-2', 'demo-mod-3'], status: 'IMPLEMENTED', controls: ['demo-ctrl-7', 'demo-ctrl-8'] },
|
||||
{ id: 'demo-req-5', regulation: 'AI Act', article: 'Art. 9', title: 'Risikomanagement', description: 'Risikomanagementsystem für Hochrisiko-KI', criticality: 'HIGH', applicableModules: ['demo-mod-2'], status: 'IMPLEMENTED', controls: ['demo-ctrl-5', 'demo-ctrl-6', 'demo-ctrl-11', 'demo-ctrl-12'] },
|
||||
],
|
||||
controls: getDemoControls(),
|
||||
evidence: [
|
||||
{ id: 'demo-evi-1', controlId: 'demo-ctrl-1', type: 'SCREENSHOT', name: 'MFA-Konfiguration Azure AD', description: 'Screenshot der MFA-Einstellungen im Azure AD Admin Portal', fileUrl: null, validFrom: new Date('2026-01-01'), validUntil: new Date('2027-01-01'), uploadedBy: 'IT-Security', uploadedAt: new Date('2026-01-10') },
|
||||
{ id: 'demo-evi-2', controlId: 'demo-ctrl-2', type: 'DOCUMENT', name: 'Berechtigungskonzept v2.1', description: 'Dokumentiertes Berechtigungskonzept mit Rollenmatrix', fileUrl: null, validFrom: new Date('2026-01-01'), validUntil: null, uploadedBy: 'IT-Security', uploadedAt: new Date('2026-01-05') },
|
||||
{ id: 'demo-evi-5', controlId: 'demo-ctrl-5', type: 'AUDIT_REPORT', name: 'Bias-Audit Q1/2026', description: 'Externer Audit-Bericht zur Fairness des KI-Modells', fileUrl: null, validFrom: new Date('2026-01-15'), validUntil: new Date('2026-04-15'), uploadedBy: 'Data Science Lead', uploadedAt: new Date('2026-01-20') },
|
||||
],
|
||||
checklist: [
|
||||
{ id: 'demo-chk-1', requirementId: 'demo-req-1', title: 'Rechtmäßigkeit der Verarbeitung geprüft', description: 'Dokumentierte Prüfung der Rechtsgrundlagen', status: 'PASSED', notes: 'Geprüft durch DSB', verifiedBy: 'DSB', verifiedAt: new Date('2026-01-20') },
|
||||
{ id: 'demo-chk-2', requirementId: 'demo-req-2', title: 'TOMs dokumentiert und umgesetzt', description: 'Technische und organisatorische Maßnahmen', status: 'PASSED', notes: 'Alle TOMs implementiert', verifiedBy: 'CISO', verifiedAt: new Date('2026-01-21') },
|
||||
],
|
||||
risks: getDemoRisks(),
|
||||
|
||||
// Phase 2 Data
|
||||
aiActClassification: {
|
||||
riskCategory: 'HIGH',
|
||||
systemType: 'Beschäftigungsbezogenes KI-System (Art. 6 Abs. 2 AI Act)',
|
||||
obligations: [
|
||||
{ id: 'demo-ai-obl-1', article: 'Art. 9', title: 'Risikomanagementsystem', description: 'Einrichtung eines KI-Risikomanagementsystems', deadline: new Date('2026-08-01'), status: 'IN_PROGRESS' },
|
||||
{ id: 'demo-ai-obl-2', article: 'Art. 10', title: 'Daten-Governance', description: 'Anforderungen an Trainingsdaten', deadline: new Date('2026-08-01'), status: 'COMPLETED' },
|
||||
{ id: 'demo-ai-obl-3', article: 'Art. 13', title: 'Transparenz', description: 'Dokumentation für Nutzer', deadline: new Date('2026-08-01'), status: 'COMPLETED' },
|
||||
],
|
||||
assessmentDate: new Date('2026-01-25'),
|
||||
assessedBy: 'Compliance Team',
|
||||
justification: 'Das System fällt unter Art. 6 Abs. 2 lit. a AI Act (Einstellung und Auswahl von Personen).',
|
||||
},
|
||||
obligations: [
|
||||
{ id: 'demo-obl-1', regulation: 'DSGVO', article: 'Art. 30', title: 'Verarbeitungsverzeichnis', description: 'Führung eines Verzeichnisses der Verarbeitungstätigkeiten', deadline: null, penalty: 'Bis zu 10 Mio. EUR oder 2% des Jahresumsatzes', status: 'COMPLETED', responsible: 'DSB' },
|
||||
{ id: 'demo-obl-2', regulation: 'DSGVO', article: 'Art. 35', title: 'Datenschutz-Folgenabschätzung', description: 'Durchführung einer DSFA für Hochrisiko-Verarbeitungen', deadline: null, penalty: 'Bis zu 10 Mio. EUR oder 2% des Jahresumsatzes', status: 'COMPLETED', responsible: 'DSB' },
|
||||
{ id: 'demo-obl-3', regulation: 'AI Act', article: 'Art. 49', title: 'CE-Kennzeichnung', description: 'CE-Kennzeichnung für Hochrisiko-KI-Systeme', deadline: new Date('2026-08-01'), penalty: 'Bis zu 35 Mio. EUR oder 7% des Jahresumsatzes', status: 'PENDING', responsible: 'Compliance' },
|
||||
],
|
||||
dsfa: getDemoDSFA(),
|
||||
toms: getDemoTOMs(),
|
||||
retentionPolicies: getDemoRetentionPolicies(),
|
||||
vvt: getDemoProcessingActivities(),
|
||||
|
||||
// Documents, Cookie Banner, etc. - partially filled
|
||||
documents: [],
|
||||
cookieBanner: null,
|
||||
consents: [],
|
||||
dsrConfig: null,
|
||||
escalationWorkflows: [],
|
||||
|
||||
// Security
|
||||
sbom: null,
|
||||
securityIssues: [],
|
||||
securityBacklog: [],
|
||||
|
||||
// UI State
|
||||
commandBarHistory: [],
|
||||
recentSearches: ['DSGVO Art. 5', 'Bias-Monitoring', 'TOM Verschlüsselung'],
|
||||
preferences: {
|
||||
language: 'de',
|
||||
theme: 'light',
|
||||
compactMode: false,
|
||||
showHints: true,
|
||||
autoSave: true,
|
||||
autoValidate: true,
|
||||
allowParallelWork: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
*
|
||||
* The seedDemoData() function writes data through the API,
|
||||
* and the data is then loaded from the database like any other data.
|
||||
*
|
||||
* generateDemoState lives in ./demo-state.ts (barrel split).
|
||||
*/
|
||||
|
||||
import { SDKState } from '../types'
|
||||
@@ -39,420 +41,9 @@ export {
|
||||
DEMO_RETENTION_POLICIES,
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a complete demo state object
|
||||
* This is used as seed data for the API, not as runtime data
|
||||
*/
|
||||
export function generateDemoState(tenantId: string, userId: string): Partial<SDKState> {
|
||||
const now = new Date()
|
||||
|
||||
return {
|
||||
// Metadata
|
||||
version: '1.0.0',
|
||||
projectVersion: 1,
|
||||
lastModified: now,
|
||||
|
||||
// Tenant & User
|
||||
tenantId,
|
||||
userId,
|
||||
subscription: 'PROFESSIONAL',
|
||||
|
||||
// Customer Type
|
||||
customerType: 'new',
|
||||
|
||||
// Company Profile (Demo: TechStart GmbH - SaaS-Startup aus Berlin)
|
||||
companyProfile: {
|
||||
companyName: 'TechStart GmbH',
|
||||
legalForm: 'gmbh',
|
||||
industry: ['Technologie / IT'],
|
||||
industryOther: '',
|
||||
foundedYear: 2022,
|
||||
businessModel: 'B2B_B2C',
|
||||
offerings: ['app_web', 'software_saas', 'services_consulting'],
|
||||
companySize: 'small',
|
||||
employeeCount: '10-49',
|
||||
annualRevenue: '2-10 Mio',
|
||||
headquartersCountry: 'DE',
|
||||
headquartersCity: 'Berlin',
|
||||
hasInternationalLocations: false,
|
||||
internationalCountries: [],
|
||||
targetMarkets: ['germany_only', 'dach'],
|
||||
primaryJurisdiction: 'DE',
|
||||
isDataController: true,
|
||||
isDataProcessor: true,
|
||||
dpoName: 'Max Mustermann',
|
||||
dpoEmail: 'dsb@techstart.de',
|
||||
legalContactName: null,
|
||||
legalContactEmail: null,
|
||||
isComplete: true,
|
||||
completedAt: new Date('2026-01-14'),
|
||||
},
|
||||
|
||||
// Progress - showing a realistic partially completed workflow
|
||||
currentPhase: 2,
|
||||
currentStep: 'tom',
|
||||
completedSteps: [
|
||||
'company-profile',
|
||||
'use-case-assessment',
|
||||
'screening',
|
||||
'modules',
|
||||
'requirements',
|
||||
'controls',
|
||||
'evidence',
|
||||
'audit-checklist',
|
||||
'risks',
|
||||
'ai-act',
|
||||
'obligations',
|
||||
'dsfa',
|
||||
],
|
||||
checkpoints: {
|
||||
'CP-PROF': { checkpointId: 'CP-PROF', passed: true, validatedAt: new Date('2026-01-14'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-UC': { checkpointId: 'CP-UC', passed: true, validatedAt: new Date('2026-01-15'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-SCAN': { checkpointId: 'CP-SCAN', passed: true, validatedAt: new Date('2026-01-16'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-MOD': { checkpointId: 'CP-MOD', passed: true, validatedAt: new Date('2026-01-17'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-REQ': { checkpointId: 'CP-REQ', passed: true, validatedAt: new Date('2026-01-18'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-CTRL': { checkpointId: 'CP-CTRL', passed: true, validatedAt: new Date('2026-01-19'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-EVI': { checkpointId: 'CP-EVI', passed: true, validatedAt: new Date('2026-01-20'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-CHK': { checkpointId: 'CP-CHK', passed: true, validatedAt: new Date('2026-01-21'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-RISK': { checkpointId: 'CP-RISK', passed: true, validatedAt: new Date('2026-01-22'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-AI': { checkpointId: 'CP-AI', passed: true, validatedAt: new Date('2026-01-25'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-OBL': { checkpointId: 'CP-OBL', passed: true, validatedAt: new Date('2026-01-27'), validatedBy: 'demo-user', errors: [], warnings: [] },
|
||||
'CP-DSFA': { checkpointId: 'CP-DSFA', passed: true, validatedAt: new Date('2026-01-30'), validatedBy: 'DSB', errors: [], warnings: [] },
|
||||
},
|
||||
|
||||
// Phase 1 Data
|
||||
useCases: getDemoUseCases(),
|
||||
activeUseCase: 'demo-uc-1',
|
||||
screening: {
|
||||
id: 'demo-scan-1',
|
||||
status: 'COMPLETED',
|
||||
startedAt: new Date('2026-01-16T09:00:00'),
|
||||
completedAt: new Date('2026-01-16T09:15:00'),
|
||||
sbom: {
|
||||
format: 'CycloneDX',
|
||||
version: '1.4',
|
||||
components: [
|
||||
{
|
||||
name: 'tensorflow',
|
||||
version: '2.15.0',
|
||||
type: 'library',
|
||||
purl: 'pkg:pypi/tensorflow@2.15.0',
|
||||
licenses: ['Apache-2.0'],
|
||||
vulnerabilities: [],
|
||||
},
|
||||
{
|
||||
name: 'scikit-learn',
|
||||
version: '1.4.0',
|
||||
type: 'library',
|
||||
purl: 'pkg:pypi/scikit-learn@1.4.0',
|
||||
licenses: ['BSD-3-Clause'],
|
||||
vulnerabilities: [],
|
||||
},
|
||||
{
|
||||
name: 'pandas',
|
||||
version: '2.2.0',
|
||||
type: 'library',
|
||||
purl: 'pkg:pypi/pandas@2.2.0',
|
||||
licenses: ['BSD-3-Clause'],
|
||||
vulnerabilities: [],
|
||||
},
|
||||
],
|
||||
dependencies: [],
|
||||
generatedAt: new Date('2026-01-16T09:10:00'),
|
||||
},
|
||||
securityScan: {
|
||||
totalIssues: 3,
|
||||
critical: 0,
|
||||
high: 1,
|
||||
medium: 1,
|
||||
low: 1,
|
||||
issues: [
|
||||
{
|
||||
id: 'sec-issue-1',
|
||||
severity: 'HIGH',
|
||||
title: 'Outdated cryptography library',
|
||||
description: 'The cryptography library version 41.0.0 has known vulnerabilities',
|
||||
cve: 'CVE-2024-1234',
|
||||
cvss: 7.5,
|
||||
affectedComponent: 'cryptography',
|
||||
remediation: 'Upgrade to cryptography >= 42.0.0',
|
||||
status: 'RESOLVED',
|
||||
},
|
||||
{
|
||||
id: 'sec-issue-2',
|
||||
severity: 'MEDIUM',
|
||||
title: 'Insecure default configuration',
|
||||
description: 'Debug mode enabled in production configuration',
|
||||
cve: null,
|
||||
cvss: 5.3,
|
||||
affectedComponent: 'app-config',
|
||||
remediation: 'Set DEBUG=false in production',
|
||||
status: 'RESOLVED',
|
||||
},
|
||||
{
|
||||
id: 'sec-issue-3',
|
||||
severity: 'LOW',
|
||||
title: 'Missing security headers',
|
||||
description: 'X-Content-Type-Options header not set',
|
||||
cve: null,
|
||||
cvss: 3.1,
|
||||
affectedComponent: 'web-server',
|
||||
remediation: 'Add security headers middleware',
|
||||
status: 'RESOLVED',
|
||||
},
|
||||
],
|
||||
},
|
||||
error: null,
|
||||
},
|
||||
modules: [
|
||||
{
|
||||
id: 'demo-mod-1',
|
||||
name: 'Kundendaten-Modul',
|
||||
description: 'Verarbeitung von Kundendaten für Marketing und Analyse',
|
||||
regulations: ['DSGVO', 'TTDSG'],
|
||||
criticality: 'HIGH',
|
||||
processesPersonalData: true,
|
||||
hasAIComponents: true,
|
||||
},
|
||||
{
|
||||
id: 'demo-mod-2',
|
||||
name: 'HR-Modul',
|
||||
description: 'Bewerbermanagement und Personalverwaltung',
|
||||
regulations: ['DSGVO', 'AGG', 'AI Act'],
|
||||
criticality: 'HIGH',
|
||||
processesPersonalData: true,
|
||||
hasAIComponents: true,
|
||||
},
|
||||
{
|
||||
id: 'demo-mod-3',
|
||||
name: 'Support-Modul',
|
||||
description: 'Kundenservice und Chatbot-System',
|
||||
regulations: ['DSGVO', 'AI Act'],
|
||||
criticality: 'MEDIUM',
|
||||
processesPersonalData: true,
|
||||
hasAIComponents: true,
|
||||
},
|
||||
],
|
||||
requirements: [
|
||||
{
|
||||
id: 'demo-req-1',
|
||||
regulation: 'DSGVO',
|
||||
article: 'Art. 5',
|
||||
title: 'Grundsätze der Verarbeitung',
|
||||
description: 'Einhaltung der Grundsätze für die Verarbeitung personenbezogener Daten',
|
||||
criticality: 'CRITICAL',
|
||||
applicableModules: ['demo-mod-1', 'demo-mod-2', 'demo-mod-3'],
|
||||
status: 'IMPLEMENTED',
|
||||
controls: ['demo-ctrl-1', 'demo-ctrl-2', 'demo-ctrl-9'],
|
||||
},
|
||||
{
|
||||
id: 'demo-req-2',
|
||||
regulation: 'DSGVO',
|
||||
article: 'Art. 32',
|
||||
title: 'Sicherheit der Verarbeitung',
|
||||
description: 'Geeignete technische und organisatorische Maßnahmen',
|
||||
criticality: 'CRITICAL',
|
||||
applicableModules: ['demo-mod-1', 'demo-mod-2', 'demo-mod-3'],
|
||||
status: 'IMPLEMENTED',
|
||||
controls: ['demo-ctrl-1', 'demo-ctrl-3', 'demo-ctrl-4'],
|
||||
},
|
||||
{
|
||||
id: 'demo-req-3',
|
||||
regulation: 'DSGVO',
|
||||
article: 'Art. 25',
|
||||
title: 'Datenschutz durch Technikgestaltung',
|
||||
description: 'Privacy by Design und Privacy by Default',
|
||||
criticality: 'HIGH',
|
||||
applicableModules: ['demo-mod-1', 'demo-mod-2'],
|
||||
status: 'IMPLEMENTED',
|
||||
controls: ['demo-ctrl-9', 'demo-ctrl-10'],
|
||||
},
|
||||
{
|
||||
id: 'demo-req-4',
|
||||
regulation: 'AI Act',
|
||||
article: 'Art. 13',
|
||||
title: 'Transparenz',
|
||||
description: 'Transparenzanforderungen für KI-Systeme',
|
||||
criticality: 'HIGH',
|
||||
applicableModules: ['demo-mod-1', 'demo-mod-2', 'demo-mod-3'],
|
||||
status: 'IMPLEMENTED',
|
||||
controls: ['demo-ctrl-7', 'demo-ctrl-8'],
|
||||
},
|
||||
{
|
||||
id: 'demo-req-5',
|
||||
regulation: 'AI Act',
|
||||
article: 'Art. 9',
|
||||
title: 'Risikomanagement',
|
||||
description: 'Risikomanagementsystem für Hochrisiko-KI',
|
||||
criticality: 'HIGH',
|
||||
applicableModules: ['demo-mod-2'],
|
||||
status: 'IMPLEMENTED',
|
||||
controls: ['demo-ctrl-5', 'demo-ctrl-6', 'demo-ctrl-11', 'demo-ctrl-12'],
|
||||
},
|
||||
],
|
||||
controls: getDemoControls(),
|
||||
evidence: [
|
||||
{
|
||||
id: 'demo-evi-1',
|
||||
controlId: 'demo-ctrl-1',
|
||||
type: 'SCREENSHOT',
|
||||
name: 'MFA-Konfiguration Azure AD',
|
||||
description: 'Screenshot der MFA-Einstellungen im Azure AD Admin Portal',
|
||||
fileUrl: null,
|
||||
validFrom: new Date('2026-01-01'),
|
||||
validUntil: new Date('2027-01-01'),
|
||||
uploadedBy: 'IT-Security',
|
||||
uploadedAt: new Date('2026-01-10'),
|
||||
},
|
||||
{
|
||||
id: 'demo-evi-2',
|
||||
controlId: 'demo-ctrl-2',
|
||||
type: 'DOCUMENT',
|
||||
name: 'Berechtigungskonzept v2.1',
|
||||
description: 'Dokumentiertes Berechtigungskonzept mit Rollenmatrix',
|
||||
fileUrl: null,
|
||||
validFrom: new Date('2026-01-01'),
|
||||
validUntil: null,
|
||||
uploadedBy: 'IT-Security',
|
||||
uploadedAt: new Date('2026-01-05'),
|
||||
},
|
||||
{
|
||||
id: 'demo-evi-5',
|
||||
controlId: 'demo-ctrl-5',
|
||||
type: 'AUDIT_REPORT',
|
||||
name: 'Bias-Audit Q1/2026',
|
||||
description: 'Externer Audit-Bericht zur Fairness des KI-Modells',
|
||||
fileUrl: null,
|
||||
validFrom: new Date('2026-01-15'),
|
||||
validUntil: new Date('2026-04-15'),
|
||||
uploadedBy: 'Data Science Lead',
|
||||
uploadedAt: new Date('2026-01-20'),
|
||||
},
|
||||
],
|
||||
checklist: [
|
||||
{
|
||||
id: 'demo-chk-1',
|
||||
requirementId: 'demo-req-1',
|
||||
title: 'Rechtmäßigkeit der Verarbeitung geprüft',
|
||||
description: 'Dokumentierte Prüfung der Rechtsgrundlagen',
|
||||
status: 'PASSED',
|
||||
notes: 'Geprüft durch DSB',
|
||||
verifiedBy: 'DSB',
|
||||
verifiedAt: new Date('2026-01-20'),
|
||||
},
|
||||
{
|
||||
id: 'demo-chk-2',
|
||||
requirementId: 'demo-req-2',
|
||||
title: 'TOMs dokumentiert und umgesetzt',
|
||||
description: 'Technische und organisatorische Maßnahmen',
|
||||
status: 'PASSED',
|
||||
notes: 'Alle TOMs implementiert',
|
||||
verifiedBy: 'CISO',
|
||||
verifiedAt: new Date('2026-01-21'),
|
||||
},
|
||||
],
|
||||
risks: getDemoRisks(),
|
||||
|
||||
// Phase 2 Data
|
||||
aiActClassification: {
|
||||
riskCategory: 'HIGH',
|
||||
systemType: 'Beschäftigungsbezogenes KI-System (Art. 6 Abs. 2 AI Act)',
|
||||
obligations: [
|
||||
{
|
||||
id: 'demo-ai-obl-1',
|
||||
article: 'Art. 9',
|
||||
title: 'Risikomanagementsystem',
|
||||
description: 'Einrichtung eines KI-Risikomanagementsystems',
|
||||
deadline: new Date('2026-08-01'),
|
||||
status: 'IN_PROGRESS',
|
||||
},
|
||||
{
|
||||
id: 'demo-ai-obl-2',
|
||||
article: 'Art. 10',
|
||||
title: 'Daten-Governance',
|
||||
description: 'Anforderungen an Trainingsdaten',
|
||||
deadline: new Date('2026-08-01'),
|
||||
status: 'COMPLETED',
|
||||
},
|
||||
{
|
||||
id: 'demo-ai-obl-3',
|
||||
article: 'Art. 13',
|
||||
title: 'Transparenz',
|
||||
description: 'Dokumentation für Nutzer',
|
||||
deadline: new Date('2026-08-01'),
|
||||
status: 'COMPLETED',
|
||||
},
|
||||
],
|
||||
assessmentDate: new Date('2026-01-25'),
|
||||
assessedBy: 'Compliance Team',
|
||||
justification: 'Das System fällt unter Art. 6 Abs. 2 lit. a AI Act (Einstellung und Auswahl von Personen).',
|
||||
},
|
||||
obligations: [
|
||||
{
|
||||
id: 'demo-obl-1',
|
||||
regulation: 'DSGVO',
|
||||
article: 'Art. 30',
|
||||
title: 'Verarbeitungsverzeichnis',
|
||||
description: 'Führung eines Verzeichnisses der Verarbeitungstätigkeiten',
|
||||
deadline: null,
|
||||
penalty: 'Bis zu 10 Mio. EUR oder 2% des Jahresumsatzes',
|
||||
status: 'COMPLETED',
|
||||
responsible: 'DSB',
|
||||
},
|
||||
{
|
||||
id: 'demo-obl-2',
|
||||
regulation: 'DSGVO',
|
||||
article: 'Art. 35',
|
||||
title: 'Datenschutz-Folgenabschätzung',
|
||||
description: 'Durchführung einer DSFA für Hochrisiko-Verarbeitungen',
|
||||
deadline: null,
|
||||
penalty: 'Bis zu 10 Mio. EUR oder 2% des Jahresumsatzes',
|
||||
status: 'COMPLETED',
|
||||
responsible: 'DSB',
|
||||
},
|
||||
{
|
||||
id: 'demo-obl-3',
|
||||
regulation: 'AI Act',
|
||||
article: 'Art. 49',
|
||||
title: 'CE-Kennzeichnung',
|
||||
description: 'CE-Kennzeichnung für Hochrisiko-KI-Systeme',
|
||||
deadline: new Date('2026-08-01'),
|
||||
penalty: 'Bis zu 35 Mio. EUR oder 7% des Jahresumsatzes',
|
||||
status: 'PENDING',
|
||||
responsible: 'Compliance',
|
||||
},
|
||||
],
|
||||
dsfa: getDemoDSFA(),
|
||||
toms: getDemoTOMs(),
|
||||
retentionPolicies: getDemoRetentionPolicies(),
|
||||
vvt: getDemoProcessingActivities(),
|
||||
|
||||
// Documents, Cookie Banner, etc. - partially filled
|
||||
documents: [],
|
||||
cookieBanner: null,
|
||||
consents: [],
|
||||
dsrConfig: null,
|
||||
escalationWorkflows: [],
|
||||
|
||||
// Security
|
||||
sbom: null,
|
||||
securityIssues: [],
|
||||
securityBacklog: [],
|
||||
|
||||
// UI State
|
||||
commandBarHistory: [],
|
||||
recentSearches: ['DSGVO Art. 5', 'Bias-Monitoring', 'TOM Verschlüsselung'],
|
||||
preferences: {
|
||||
language: 'de',
|
||||
theme: 'light',
|
||||
compactMode: false,
|
||||
showHints: true,
|
||||
autoSave: true,
|
||||
autoValidate: true,
|
||||
allowParallelWork: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
// Re-export generateDemoState from its own module
|
||||
export { generateDemoState } from './demo-state'
|
||||
import { generateDemoState } from './demo-state'
|
||||
|
||||
/**
|
||||
* Seed demo data into the database via API
|
||||
|
||||
@@ -1,694 +1,12 @@
|
||||
/**
|
||||
* DSFA Massnahmenbibliothek - Vordefinierte Massnahmen
|
||||
* DSFA Massnahmenbibliothek — Barrel
|
||||
*
|
||||
* ~50 Massnahmen gegliedert nach SDM-Gewaehrleistungszielen
|
||||
* (Vertraulichkeit, Integritaet, Verfuegbarkeit, Datenminimierung,
|
||||
* Transparenz, Nichtverkettung, Intervenierbarkeit) sowie
|
||||
* Automatisierung/KI, Rechtlich/Organisatorisch.
|
||||
*
|
||||
* Quellen: Art. 25/32 DSGVO, SDM V2.0, BSI Grundschutz,
|
||||
* Baseline-DSFA Katalog
|
||||
* Split into domain modules under mitigations/.
|
||||
* Import from this file to preserve the stable public path.
|
||||
*/
|
||||
|
||||
import type { DSFAMitigationType } from './types'
|
||||
import type { SDMGoal } from './types'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface CatalogMitigation {
|
||||
id: string
|
||||
type: DSFAMitigationType
|
||||
sdmGoals: SDMGoal[]
|
||||
title: string
|
||||
description: string
|
||||
legalBasis: string
|
||||
evidenceTypes: string[]
|
||||
addressesRiskIds: string[]
|
||||
effectiveness: 'low' | 'medium' | 'high'
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MASSNAHMENBIBLIOTHEK
|
||||
// =============================================================================
|
||||
|
||||
export const MITIGATION_LIBRARY: CatalogMitigation[] = [
|
||||
// =========================================================================
|
||||
// VERTRAULICHKEIT (Access Control & Encryption)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-ACC-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Multi-Faktor-Authentifizierung (MFA) & Conditional Access',
|
||||
description: 'Einfuehrung von MFA fuer alle Benutzerkonten mit Zugriff auf personenbezogene Daten. Conditional Access Policies beschraenken den Zugriff basierend auf Standort, Geraet und Risikobewertung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['MFA-Policy-Screenshot', 'Conditional-Access-Regeln', 'Login-Statistiken'],
|
||||
addressesRiskIds: ['R-CONF-02', 'R-CONF-06'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-ACC-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Passwort-Policy & Credential-Schutz',
|
||||
description: 'Durchsetzung starker Passwort-Richtlinien, Credential-Rotation, Einsatz eines Passwort-Managers und Monitoring auf kompromittierte Zugangsdaten (Breach Detection).',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Passwort-Policy-Dokument', 'Breach-Detection-Report'],
|
||||
addressesRiskIds: ['R-CONF-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Rollenbasierte Zugriffskontrolle (RBAC) & Least Privilege',
|
||||
description: 'Implementierung eines RBAC-Systems mit dem Prinzip der minimalen Berechtigung. Jeder Benutzer erhaelt nur die Rechte, die fuer seine Aufgabe erforderlich sind.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO, Art. 25 Abs. 2 DSGVO',
|
||||
evidenceTypes: ['Rollen-Matrix', 'Berechtigungs-Audit-Report', 'Access-Review-Protokoll'],
|
||||
addressesRiskIds: ['R-CONF-01', 'R-CONF-03', 'R-INT-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Security Configuration Management',
|
||||
description: 'Regelmaessige Ueberpruefung und Haertung der Systemkonfiguration. Automatisierte Konfigurationschecks (CIS Benchmarks) und Monitoring auf Konfigurationsaenderungen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['CIS-Benchmark-Report', 'Konfigurationsaenderungs-Log'],
|
||||
addressesRiskIds: ['R-CONF-01'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Regelmaessige Zugriffsrechte-Ueberpruefung (Access Review)',
|
||||
description: 'Quartalsweiser Review aller Zugriffsberechtigungen durch Vorgesetzte. Entzug nicht mehr benoetigter Rechte, Offboarding-Prozess bei Mitarbeiteraustritt.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['Access-Review-Protokoll', 'Offboarding-Checkliste'],
|
||||
addressesRiskIds: ['R-CONF-01', 'R-CONF-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit', 'integritaet'],
|
||||
title: 'Privileged Access Management (PAM)',
|
||||
description: 'Absicherung administrativer Zugriffe durch Just-in-Time-Elevation, Session-Recording und Break-Glass-Prozeduren fuer Notfallzugriffe.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['PAM-Policy', 'Session-Recording-Logs', 'Break-Glass-Protokolle'],
|
||||
addressesRiskIds: ['R-CONF-03', 'R-INT-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-05',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Vier-Augen-Prinzip fuer sensible Operationen',
|
||||
description: 'Fuer den Zugriff auf besonders schutzwuerdige Daten oder kritische Systemoperationen ist die Genehmigung durch eine zweite Person erforderlich.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Prozessbeschreibung', 'Genehmigungsprotokoll'],
|
||||
addressesRiskIds: ['R-CONF-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-06',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Verschluesselung at-rest und in-transit',
|
||||
description: 'Vollstaendige Verschluesselung personenbezogener Daten bei Speicherung (AES-256) und Uebertragung (TLS 1.3). Verwaltung der Schluessel ueber ein zentrales Key-Management-System.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Verschluesselungs-Policy', 'TLS-Konfigurationsreport', 'KMS-Audit'],
|
||||
addressesRiskIds: ['R-CONF-04', 'R-TRANS-01', 'R-AUTO-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-07',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'End-to-End-Verschluesselung fuer Kommunikation',
|
||||
description: 'Einsatz von End-to-End-Verschluesselung fuer sensible Kommunikation (E-Mail, Messaging), sodass auch der Betreiber keinen Zugriff auf die Inhalte hat.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['E2E-Konfiguration', 'Testbericht'],
|
||||
addressesRiskIds: ['R-CONF-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-08',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit', 'datenminimierung'],
|
||||
title: 'Log-Sanitization & PII-Filtering',
|
||||
description: 'Automatische Filterung personenbezogener Daten aus Logs, Fehlermeldungen und Debug-Ausgaben. Einsatz von Tokenisierung oder Maskierung.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO, Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Log-Policy', 'PII-Filter-Konfiguration', 'Stichproben-Audit'],
|
||||
addressesRiskIds: ['R-CONF-07'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// INTEGRITAET (Audit, Monitoring, Integrity Checks)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-INT-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Input-Validierung & Injection-Schutz',
|
||||
description: 'Konsequente Validierung aller Eingaben, Prepared Statements fuer Datenbankzugriffe, Content Security Policy und Output-Encoding zum Schutz vor Injection-Angriffen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['SAST-Report', 'Penetrationstest-Bericht', 'WAF-Regeln'],
|
||||
addressesRiskIds: ['R-INT-01'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet', 'transparenz'],
|
||||
title: 'Audit-Logging & SIEM-Integration',
|
||||
description: 'Lueckenlose Protokollierung aller sicherheitsrelevanten Ereignisse mit Weiterleitung an ein SIEM-System. Manipulation-sichere Logs mit Integritaetspruefung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['SIEM-Dashboard-Screenshot', 'Audit-Log-Beispiel', 'Alert-Regeln'],
|
||||
addressesRiskIds: ['R-INT-01', 'R-INT-04', 'R-INT-05', 'R-CONF-03', 'R-ORG-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Web Application Firewall (WAF) & API-Gateway',
|
||||
description: 'Einsatz einer WAF zum Schutz vor OWASP Top 10 Angriffen und eines API-Gateways fuer Rate-Limiting, Schema-Validierung und Anomalie-Erkennung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['WAF-Regelset', 'API-Gateway-Konfiguration', 'Blockierungs-Statistiken'],
|
||||
addressesRiskIds: ['R-INT-01'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Daten-Synchronisations-Monitoring & Integritaetspruefung',
|
||||
description: 'Automatische Ueberwachung von Synchronisationsprozessen mit Checksummen-Vergleich, Konflikterkennung und Alerting bei Inkonsistenzen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Sync-Monitoring-Dashboard', 'Checksummen-Report', 'Incident-Log'],
|
||||
addressesRiskIds: ['R-INT-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-05',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Versionierung & Change-Tracking fuer personenbezogene Daten',
|
||||
description: 'Alle Aenderungen an personenbezogenen Daten werden versioniert gespeichert (Audit-Trail). Wer hat wann was geaendert ist jederzeit nachvollziehbar.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. f DSGVO',
|
||||
evidenceTypes: ['Versionierungs-Schema', 'Change-Log-Beispiel'],
|
||||
addressesRiskIds: ['R-INT-02', 'R-INT-05'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// VERFUEGBARKEIT (Backup, Recovery, Redundancy)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-AVAIL-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Backup-Strategie mit 3-2-1-Regel',
|
||||
description: 'Implementierung einer Backup-Strategie nach der 3-2-1-Regel: 3 Kopien, 2 verschiedene Medien, 1 Offsite. Verschluesselte Backups mit regelmaessiger Integritaetspruefung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. c DSGVO',
|
||||
evidenceTypes: ['Backup-Policy', 'Backup-Monitoring-Report', 'Offsite-Nachweis'],
|
||||
addressesRiskIds: ['R-AVAIL-01', 'R-AVAIL-03', 'R-INT-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-02',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Regelmaessige Restore-Tests & Disaster Recovery Uebungen',
|
||||
description: 'Mindestens quartalsweise Durchfuehrung von Restore-Tests und jaehrliche Disaster-Recovery-Uebungen. Dokumentation der Ergebnisse und Lessons Learned.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['Restore-Test-Protokoll', 'DR-Uebungs-Dokumentation', 'RTO/RPO-Nachweis'],
|
||||
addressesRiskIds: ['R-AVAIL-01', 'R-AVAIL-03', 'R-INT-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Endpoint Protection & Anti-Ransomware',
|
||||
description: 'Einsatz von Endpoint-Detection-and-Response (EDR) Loesungen mit spezifischem Ransomware-Schutz, Verhaltensanalyse und automatischer Isolation kompromittierter Systeme.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['EDR-Dashboard', 'Threat-Detection-Statistiken', 'Incident-Response-Plan'],
|
||||
addressesRiskIds: ['R-AVAIL-01'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Redundanz & High-Availability-Architektur',
|
||||
description: 'Redundante Systemauslegung mit automatischem Failover, Load-Balancing und geo-redundanter Datenhaltung zur Sicherstellung der Verfuegbarkeit.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['HA-Architekturdiagramm', 'Failover-Testprotokoll', 'SLA-Dokumentation'],
|
||||
addressesRiskIds: ['R-AVAIL-02', 'R-AVAIL-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-05',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['verfuegbarkeit', 'intervenierbarkeit'],
|
||||
title: 'Exit-Strategie & Datenportabilitaetsplan',
|
||||
description: 'Dokumentierte Exit-Strategie fuer jeden kritischen Anbieter mit Datenexport-Verfahren, Migrationsplan und Uebergangsfristen. Regelmaessiger Export-Test.',
|
||||
legalBasis: 'Art. 28 Abs. 3 lit. g DSGVO, Art. 20 DSGVO',
|
||||
evidenceTypes: ['Exit-Plan-Dokument', 'Export-Test-Protokoll', 'Vertragliche-Regelung'],
|
||||
addressesRiskIds: ['R-AVAIL-02', 'R-AVAIL-05'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-06',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'DDoS-Schutz & Rate-Limiting',
|
||||
description: 'Einsatz von DDoS-Mitigation-Services, CDN-basiertem Schutz und anwendungsspezifischem Rate-Limiting zur Abwehr von Verfuegbarkeitsangriffen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['DDoS-Schutz-Konfiguration', 'Rate-Limit-Regeln', 'Traffic-Analyse'],
|
||||
addressesRiskIds: ['R-AVAIL-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// DATENMINIMIERUNG (Retention, Anonymization, Purpose Limitation)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-DMIN-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['datenminimierung'],
|
||||
title: 'Privacy by Design: Datenerhebung auf das Minimum beschraenken',
|
||||
description: 'Technische Massnahmen zur Beschraenkung der Datenerhebung: Pflichtfelder minimieren, optionale Felder deutlich kennzeichnen, Default-Einstellungen datenschutzfreundlich.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Formular-Review', 'Default-Settings-Dokumentation'],
|
||||
addressesRiskIds: ['R-RIGHTS-07', 'R-CONF-07'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-DMIN-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['datenminimierung', 'nichtverkettung'],
|
||||
title: 'Pseudonymisierung & Anonymisierung',
|
||||
description: 'Einsatz von Pseudonymisierungsverfahren (Token-basiert, Hash-basiert) und k-Anonymity/Differential Privacy bei der Weitergabe oder Analyse von Daten.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO, Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Pseudonymisierungs-Konzept', 'Re-Identifizierungs-Risiko-Analyse'],
|
||||
addressesRiskIds: ['R-RIGHTS-04', 'R-RIGHTS-07'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-DMIN-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['datenminimierung'],
|
||||
title: 'Automatisiertes Loeschkonzept mit Aufbewahrungsfristen',
|
||||
description: 'Implementierung automatischer Loeschroutinen basierend auf definierten Aufbewahrungsfristen. Monitoring der Loeschvorgaenge und Nachweis der Loeschung.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. e DSGVO, Art. 17 DSGVO',
|
||||
evidenceTypes: ['Loeschkonzept-Dokument', 'Loeschfrist-Uebersicht', 'Loeschprotokoll'],
|
||||
addressesRiskIds: ['R-RIGHTS-07', 'R-ORG-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-DMIN-04',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['datenminimierung'],
|
||||
title: 'Regelmaessige Ueberpruefung der Datenbestaende',
|
||||
description: 'Jaehrlicher Review aller gespeicherten personenbezogenen Daten auf Erforderlichkeit. Identifikation und Bereinigung von Altbestaenden, verwaisten Datensaetzen und redundanten Kopien.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. e DSGVO',
|
||||
evidenceTypes: ['Datenbestand-Review-Bericht', 'Bereinigungs-Protokoll'],
|
||||
addressesRiskIds: ['R-ORG-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// TRANSPARENZ (Information, Documentation, Auditability)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-TRANS-01',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Datenschutzhinweise & Privacy Notices',
|
||||
description: 'Umfassende, verstaendliche Datenschutzhinweise gemaess Art. 13/14 DSGVO an allen Erhebungsstellen. Layered-Approach fuer unterschiedliche Detailstufen.',
|
||||
legalBasis: 'Art. 13, Art. 14 DSGVO',
|
||||
evidenceTypes: ['Privacy-Notice-Review', 'Zustellungs-Nachweis', 'Usability-Test'],
|
||||
addressesRiskIds: ['R-CONF-05', 'R-RIGHTS-02', 'R-RIGHTS-03', 'R-RIGHTS-06', 'R-TRANS-03', 'R-SPEC-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Vollstaendiger Audit-Trail fuer personenbezogene Daten',
|
||||
description: 'Lueckenloser, manipulationssicherer Audit-Trail fuer alle Verarbeitungsvorgaenge personenbezogener Daten. Wer hat wann auf welche Daten zugegriffen oder sie veraendert.',
|
||||
legalBasis: 'Art. 5 Abs. 2 DSGVO (Rechenschaftspflicht)',
|
||||
evidenceTypes: ['Audit-Trail-Architektur', 'Log-Integritaets-Nachweis', 'Beispiel-Audit-Export'],
|
||||
addressesRiskIds: ['R-INT-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Erklaerbarkeit von KI-Entscheidungen (Explainability)',
|
||||
description: 'Implementierung von Erklaerungsverfahren (SHAP, LIME, Feature-Importance) fuer automatisierte Entscheidungen. Bereitstellung verstaendlicher Begruendungen fuer Betroffene.',
|
||||
legalBasis: 'Art. 22 Abs. 3 DSGVO, Art. 13 Abs. 2 lit. f DSGVO',
|
||||
evidenceTypes: ['XAI-Konzept', 'Erklaerbarkeits-Beispiel', 'Betroffenen-Information'],
|
||||
addressesRiskIds: ['R-AUTO-01', 'R-AUTO-03', 'R-RIGHTS-01'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-04',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Ueberwachungs-Folgenabschaetzung & Informationspflicht',
|
||||
description: 'Bei systematischer Ueberwachung: Gesonderte Folgenabschaetzung, klare Beschilderung/Information, Verhaeltnismaessigkeitspruefung und zeitliche Begrenzung.',
|
||||
legalBasis: 'Art. 35 Abs. 3 lit. c DSGVO, Art. 13 DSGVO',
|
||||
evidenceTypes: ['Ueberwachungs-DSFA', 'Beschilderungs-Nachweis', 'Verhaeltnismaessigkeits-Bewertung'],
|
||||
addressesRiskIds: ['R-RIGHTS-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-05',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Verzeichnis von Verarbeitungstaetigkeiten (VVT) pflegen',
|
||||
description: 'Vollstaendiges und aktuelles VVT gemaess Art. 30 DSGVO fuer alle Verarbeitungstaetigkeiten. Regelmaessige Aktualisierung bei Aenderungen.',
|
||||
legalBasis: 'Art. 30 DSGVO',
|
||||
evidenceTypes: ['VVT-Export', 'Aktualisierungs-Log'],
|
||||
addressesRiskIds: ['R-RIGHTS-06'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-06',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz', 'vertraulichkeit'],
|
||||
title: 'Transfer Impact Assessment (TIA) fuer Drittlandtransfer',
|
||||
description: 'Durchfuehrung eines Transfer Impact Assessments vor jedem Drittlandtransfer. Bewertung des Schutzniveaus im Empfaengerland und Festlegung zusaetzlicher Garantien.',
|
||||
legalBasis: 'Art. 46 DSGVO, Schrems-II-Urteil',
|
||||
evidenceTypes: ['TIA-Dokument', 'Schutzniveau-Analyse', 'Zusaetzliche-Garantien-Vereinbarung'],
|
||||
addressesRiskIds: ['R-TRANS-01', 'R-TRANS-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-07',
|
||||
type: 'legal',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Standardvertragsklauseln (SCC) & Supplementary Measures',
|
||||
description: 'Abschluss aktueller EU-Standardvertragsklauseln (2021/914) mit Auftragsverarbeitern im Drittland. Ergaenzende technische und organisatorische Massnahmen (Verschluesselung, Pseudonymisierung).',
|
||||
legalBasis: 'Art. 46 Abs. 2 lit. c DSGVO',
|
||||
evidenceTypes: ['Unterzeichnete SCC', 'Supplementary-Measures-Dokumentation'],
|
||||
addressesRiskIds: ['R-TRANS-01', 'R-TRANS-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// NICHTVERKETTUNG (Purpose Limitation, Data Separation, DLP)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-NONL-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung'],
|
||||
title: 'Zweckbindung & Consent-Management',
|
||||
description: 'Technische Durchsetzung der Zweckbindung: Daten werden nur fuer den erhobenen Zweck verwendet. Consent-Management-System protokolliert und erzwingt Einwilligungen.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. b DSGVO, Art. 6 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Consent-Management-System', 'Zweckbindungs-Matrix', 'Consent-Protokolle'],
|
||||
addressesRiskIds: ['R-CONF-05', 'R-RIGHTS-02', 'R-RIGHTS-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-NONL-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung'],
|
||||
title: 'Data Loss Prevention (DLP) & Datenklassifikation',
|
||||
description: 'Implementierung von DLP-Regeln zur Verhinderung unkontrollierter Datenweitergabe. Datenklassifikation (oeffentlich, intern, vertraulich, streng vertraulich) als Grundlage.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['DLP-Policy', 'Datenklassifikations-Schema', 'DLP-Incident-Report'],
|
||||
addressesRiskIds: ['R-RIGHTS-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-NONL-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung', 'datenminimierung'],
|
||||
title: 'Differential Privacy & k-Anonymity bei Datenanalysen',
|
||||
description: 'Einsatz von Differential Privacy oder k-Anonymity-Verfahren bei der Analyse personenbezogener Daten, um Re-Identifizierung zu verhindern.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Anonymisierungs-Konzept', 'Privacy-Budget-Berechnung', 'k-Anonymity-Nachweis'],
|
||||
addressesRiskIds: ['R-RIGHTS-04', 'R-AUTO-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-NONL-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung'],
|
||||
title: 'Mandantentrennung & Datenisolierung',
|
||||
description: 'Strikte logische oder physische Trennung personenbezogener Daten verschiedener Mandanten/Zwecke. Verhinderung unbeabsichtigter Zusammenfuehrung.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Mandantentrennungs-Konzept', 'Isolierungs-Test-Bericht'],
|
||||
addressesRiskIds: ['R-RIGHTS-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// INTERVENIERBARKEIT (Data Subject Rights, Correction, Deletion)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-INTERV-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'DSAR-Workflow (Data Subject Access Request)',
|
||||
description: 'Automatisierter Workflow fuer Betroffenenanfragen (Auskunft, Loeschung, Berichtigung, Export). Fristenmanagement (1 Monat), Identitaetspruefung und Dokumentation.',
|
||||
legalBasis: 'Art. 15-22 DSGVO, Art. 12 Abs. 3 DSGVO',
|
||||
evidenceTypes: ['DSAR-Workflow-Dokumentation', 'Bearbeitungszeiten-Statistik', 'Audit-Trail'],
|
||||
addressesRiskIds: ['R-RIGHTS-05', 'R-AVAIL-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INTERV-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'Self-Service Datenverwaltung fuer Betroffene',
|
||||
description: 'Bereitstellung eines Self-Service-Portals, ueber das Betroffene ihre Daten einsehen, korrigieren, exportieren und die Loeschung beantragen koennen.',
|
||||
legalBasis: 'Art. 15-20 DSGVO',
|
||||
evidenceTypes: ['Portal-Screenshot', 'Funktions-Testprotokoll', 'Nutzungs-Statistik'],
|
||||
addressesRiskIds: ['R-RIGHTS-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INTERV-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'Widerspruchs- und Einschraenkungsprozess',
|
||||
description: 'Definierter Prozess fuer die Bearbeitung von Widerspruechen (Art. 21) und Einschraenkungsersuchen (Art. 18). Technische Moeglichkeit zur Sperrung einzelner Datensaetze.',
|
||||
legalBasis: 'Art. 18, Art. 21 DSGVO',
|
||||
evidenceTypes: ['Prozessbeschreibung', 'Sperr-Funktionalitaets-Nachweis'],
|
||||
addressesRiskIds: ['R-RIGHTS-05'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-INTERV-04',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'Human-in-the-Loop bei automatisierten Entscheidungen',
|
||||
description: 'Sicherstellung menschlicher Ueberpruefung bei automatisierten Entscheidungen mit erheblicher Auswirkung. Eskalationsprozess und Einspruchsmoeglichkeit fuer Betroffene.',
|
||||
legalBasis: 'Art. 22 Abs. 3 DSGVO',
|
||||
evidenceTypes: ['HITL-Prozessbeschreibung', 'Eskalations-Statistik', 'Einspruchs-Protokoll'],
|
||||
addressesRiskIds: ['R-AUTO-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// AUTOMATISIERUNG / KI
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-AUTO-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung', 'transparenz'],
|
||||
title: 'Bias-Monitoring & Fairness-Tests',
|
||||
description: 'Regelmaessige Ueberpruefung von KI-Modellen auf Bias und Diskriminierung. Fairness-Metriken (Demographic Parity, Equal Opportunity) und Korrekturmassnahmen bei Abweichungen.',
|
||||
legalBasis: 'Art. 22 Abs. 3 DSGVO, AI Act Art. 10',
|
||||
evidenceTypes: ['Bias-Audit-Report', 'Fairness-Metriken-Dashboard', 'Korrektur-Dokumentation'],
|
||||
addressesRiskIds: ['R-RIGHTS-01', 'R-AUTO-01', 'R-AUTO-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AUTO-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'KI-Modell-Dokumentation & Model Cards',
|
||||
description: 'Ausfuehrliche Dokumentation aller KI-Modelle: Trainingsdaten, Architektur, Performance-Metriken, bekannte Einschraenkungen, Einsatzzweck (Model Cards).',
|
||||
legalBasis: 'Art. 13 Abs. 2 lit. f DSGVO, AI Act Art. 11',
|
||||
evidenceTypes: ['Model-Card', 'Performance-Report', 'Einsatzbereich-Dokumentation'],
|
||||
addressesRiskIds: ['R-AUTO-01', 'R-AUTO-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-AUTO-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['intervenierbarkeit', 'transparenz'],
|
||||
title: 'KI-Governance-Framework & Human Oversight Board',
|
||||
description: 'Etablierung eines KI-Governance-Frameworks mit einem Human Oversight Board, das alle KI-Systeme mit hohem Risiko ueberwacht und Interventionsmoeglichkeiten hat.',
|
||||
legalBasis: 'Art. 22 DSGVO, AI Act Art. 14',
|
||||
evidenceTypes: ['Governance-Policy', 'Oversight-Board-Protokolle', 'Interventions-Log'],
|
||||
addressesRiskIds: ['R-AUTO-01', 'R-AUTO-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AUTO-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung', 'datenminimierung'],
|
||||
title: 'Datenschutzkonformes KI-Training (Privacy-Preserving ML)',
|
||||
description: 'Einsatz von Federated Learning, Differential Privacy beim Training oder synthetischen Trainingsdaten, um personenbezogene Daten im Modell zu schuetzen.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Privacy-Preserving-ML-Konzept', 'Training-Daten-Analyse', 'Modell-Invertierbarkeiots-Test'],
|
||||
addressesRiskIds: ['R-AUTO-02', 'R-AUTO-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// ORGANISATORISCHE MASSNAHMEN
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-ORG-01',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['vertraulichkeit', 'integritaet'],
|
||||
title: 'Datenschutz-Schulungen & Awareness-Programm',
|
||||
description: 'Regelmaessige verpflichtende Datenschutz-Schulungen fuer alle Mitarbeiter. Awareness-Kampagnen zu Phishing, Social Engineering und sicherem Datenumgang.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO, Art. 39 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Schulungsplan', 'Teilnahmequoten', 'Phishing-Simulations-Ergebnis'],
|
||||
addressesRiskIds: ['R-CONF-06', 'R-ORG-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-ORG-02',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Verpflichtung auf Vertraulichkeit & Datenschutz-Policy',
|
||||
description: 'Schriftliche Verpflichtung aller Mitarbeiter und externen Dienstleister auf Vertraulichkeit und Einhaltung der Datenschutz-Policies.',
|
||||
legalBasis: 'Art. 28 Abs. 3 lit. b DSGVO, Art. 29 DSGVO',
|
||||
evidenceTypes: ['Unterzeichnete-Verpflichtungserklaerung', 'Datenschutz-Policy'],
|
||||
addressesRiskIds: ['R-ORG-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-ORG-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Datenpannen-Erkennungs- und Meldeprozess (Incident Response)',
|
||||
description: 'Definierter Incident-Response-Prozess mit klaren Eskalationswegen, 72h-Meldepflicht-Tracking, Klassifizierungsschema und Kommunikationsplan.',
|
||||
legalBasis: 'Art. 33, Art. 34 DSGVO',
|
||||
evidenceTypes: ['Incident-Response-Plan', 'Melde-Template', 'Uebungs-Protokoll'],
|
||||
addressesRiskIds: ['R-ORG-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-ORG-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz', 'verfuegbarkeit'],
|
||||
title: 'Automatisiertes Breach-Detection & Alerting',
|
||||
description: 'Automatische Erkennung von Datenpannen durch Anomalie-Detection, ungewoehnliche Zugriffsmuster und Datenexfiltrations-Erkennung mit sofortigem Alert an den Incident-Response-Team.',
|
||||
legalBasis: 'Art. 33 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Alert-Regeln', 'Detection-Dashboard', 'Reaktionszeiten-Statistik'],
|
||||
addressesRiskIds: ['R-ORG-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// RECHTLICHE MASSNAHMEN
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'M-LEGAL-01',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Angemessenheitsbeschluss oder Binding Corporate Rules (BCR)',
|
||||
description: 'Sicherstellung, dass Drittlandtransfers auf einem Angemessenheitsbeschluss oder genehmigten BCRs basieren. Laufende Ueberwachung des Schutzniveaus.',
|
||||
legalBasis: 'Art. 45, Art. 47 DSGVO',
|
||||
evidenceTypes: ['Angemessenheitsbeschluss-Referenz', 'BCR-Genehmigung'],
|
||||
addressesRiskIds: ['R-TRANS-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-LEGAL-02',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Auftragsverarbeitungsvertrag (AVV) nach Art. 28 DSGVO',
|
||||
description: 'Abschluss vollstaendiger AVVs mit allen Auftragsverarbeitern. Regelung von Zweck, Dauer, Datenkategorien, Weisungsbindung, Sub-Auftragsverarbeiter und Audit-Rechten.',
|
||||
legalBasis: 'Art. 28 Abs. 3 DSGVO',
|
||||
evidenceTypes: ['Unterzeichneter-AVV', 'Sub-Auftragsverarbeiter-Liste', 'Audit-Bericht'],
|
||||
addressesRiskIds: ['R-ORG-01', 'R-TRANS-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-LEGAL-03',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Regelmaessige Auftragsverarbeiter-Audits',
|
||||
description: 'Jaehrliche Ueberpruefung der Auftragsverarbeiter auf Einhaltung der AVV-Vorgaben. Dokumentierte Audits vor Ort oder anhand von Zertifizierungen (SOC 2, ISO 27001).',
|
||||
legalBasis: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
evidenceTypes: ['Audit-Bericht', 'Zertifizierungs-Nachweis', 'Massnahmenplan'],
|
||||
addressesRiskIds: ['R-ORG-01'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-LEGAL-04',
|
||||
type: 'legal',
|
||||
sdmGoals: ['intervenierbarkeit', 'transparenz'],
|
||||
title: 'Altersverifikation & Eltern-Einwilligung (Art. 8)',
|
||||
description: 'Implementierung einer altersgerechten Verifikation und Einholung der Eltern-Einwilligung bei Minderjaehrigen unter 16 Jahren. Kindgerechte Datenschutzinformationen.',
|
||||
legalBasis: 'Art. 8 DSGVO, EG 38 DSGVO',
|
||||
evidenceTypes: ['Altersverifikations-Konzept', 'Eltern-Einwilligungs-Formular', 'Kindgerechte-Privacy-Notice'],
|
||||
addressesRiskIds: ['R-SPEC-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
export function getMitigationsBySDMGoal(goal: SDMGoal): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.sdmGoals.includes(goal))
|
||||
}
|
||||
|
||||
export function getMitigationsByType(type: DSFAMitigationType): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.type === type)
|
||||
}
|
||||
|
||||
export function getMitigationsForRisk(riskId: string): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.addressesRiskIds.includes(riskId))
|
||||
}
|
||||
|
||||
export function getCatalogMitigationById(id: string): CatalogMitigation | undefined {
|
||||
return MITIGATION_LIBRARY.find(m => m.id === id)
|
||||
}
|
||||
|
||||
export function getMitigationsByEffectiveness(effectiveness: 'low' | 'medium' | 'high'): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.effectiveness === effectiveness)
|
||||
}
|
||||
|
||||
export const MITIGATION_TYPE_LABELS: Record<DSFAMitigationType, string> = {
|
||||
technical: 'Technisch',
|
||||
organizational: 'Organisatorisch',
|
||||
legal: 'Rechtlich',
|
||||
}
|
||||
|
||||
export const SDM_GOAL_LABELS: Record<SDMGoal, string> = {
|
||||
datenminimierung: 'Datenminimierung',
|
||||
verfuegbarkeit: 'Verfuegbarkeit',
|
||||
integritaet: 'Integritaet',
|
||||
vertraulichkeit: 'Vertraulichkeit',
|
||||
nichtverkettung: 'Nichtverkettung',
|
||||
transparenz: 'Transparenz',
|
||||
intervenierbarkeit: 'Intervenierbarkeit',
|
||||
}
|
||||
|
||||
export const EFFECTIVENESS_LABELS: Record<string, string> = {
|
||||
low: 'Gering',
|
||||
medium: 'Mittel',
|
||||
high: 'Hoch',
|
||||
}
|
||||
export type { CatalogMitigation } from './mitigations/types'
|
||||
export * from './mitigations/access-integrity'
|
||||
export * from './mitigations/availability-minimization'
|
||||
export * from './mitigations/transparency-intervention-org'
|
||||
export * from './mitigations/helpers'
|
||||
|
||||
176
admin-compliance/lib/sdk/dsfa/mitigations/access-integrity.ts
Normal file
176
admin-compliance/lib/sdk/dsfa/mitigations/access-integrity.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* DSFA Massnahmenbibliothek — Vertraulichkeit / Zugriff & Integritaet
|
||||
*/
|
||||
|
||||
import type { CatalogMitigation } from './types'
|
||||
|
||||
export const ACCESS_CONTROL_MITIGATIONS: CatalogMitigation[] = [
|
||||
{
|
||||
id: 'M-ACC-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Multi-Faktor-Authentifizierung (MFA) & Conditional Access',
|
||||
description: 'Einfuehrung von MFA fuer alle Benutzerkonten mit Zugriff auf personenbezogene Daten. Conditional Access Policies beschraenken den Zugriff basierend auf Standort, Geraet und Risikobewertung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['MFA-Policy-Screenshot', 'Conditional-Access-Regeln', 'Login-Statistiken'],
|
||||
addressesRiskIds: ['R-CONF-02', 'R-CONF-06'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-ACC-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Passwort-Policy & Credential-Schutz',
|
||||
description: 'Durchsetzung starker Passwort-Richtlinien, Credential-Rotation, Einsatz eines Passwort-Managers und Monitoring auf kompromittierte Zugangsdaten (Breach Detection).',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Passwort-Policy-Dokument', 'Breach-Detection-Report'],
|
||||
addressesRiskIds: ['R-CONF-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Rollenbasierte Zugriffskontrolle (RBAC) & Least Privilege',
|
||||
description: 'Implementierung eines RBAC-Systems mit dem Prinzip der minimalen Berechtigung. Jeder Benutzer erhaelt nur die Rechte, die fuer seine Aufgabe erforderlich sind.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO, Art. 25 Abs. 2 DSGVO',
|
||||
evidenceTypes: ['Rollen-Matrix', 'Berechtigungs-Audit-Report', 'Access-Review-Protokoll'],
|
||||
addressesRiskIds: ['R-CONF-01', 'R-CONF-03', 'R-INT-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Security Configuration Management',
|
||||
description: 'Regelmaessige Ueberpruefung und Haertung der Systemkonfiguration. Automatisierte Konfigurationschecks (CIS Benchmarks) und Monitoring auf Konfigurationsaenderungen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['CIS-Benchmark-Report', 'Konfigurationsaenderungs-Log'],
|
||||
addressesRiskIds: ['R-CONF-01'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Regelmaessige Zugriffsrechte-Ueberpruefung (Access Review)',
|
||||
description: 'Quartalsweiser Review aller Zugriffsberechtigungen durch Vorgesetzte. Entzug nicht mehr benoetigter Rechte, Offboarding-Prozess bei Mitarbeiteraustritt.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['Access-Review-Protokoll', 'Offboarding-Checkliste'],
|
||||
addressesRiskIds: ['R-CONF-01', 'R-CONF-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit', 'integritaet'],
|
||||
title: 'Privileged Access Management (PAM)',
|
||||
description: 'Absicherung administrativer Zugriffe durch Just-in-Time-Elevation, Session-Recording und Break-Glass-Prozeduren fuer Notfallzugriffe.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['PAM-Policy', 'Session-Recording-Logs', 'Break-Glass-Protokolle'],
|
||||
addressesRiskIds: ['R-CONF-03', 'R-INT-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-05',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Vier-Augen-Prinzip fuer sensible Operationen',
|
||||
description: 'Fuer den Zugriff auf besonders schutzwuerdige Daten oder kritische Systemoperationen ist die Genehmigung durch eine zweite Person erforderlich.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Prozessbeschreibung', 'Genehmigungsprotokoll'],
|
||||
addressesRiskIds: ['R-CONF-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-06',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Verschluesselung at-rest und in-transit',
|
||||
description: 'Vollstaendige Verschluesselung personenbezogener Daten bei Speicherung (AES-256) und Uebertragung (TLS 1.3). Verwaltung der Schluessel ueber ein zentrales Key-Management-System.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Verschluesselungs-Policy', 'TLS-Konfigurationsreport', 'KMS-Audit'],
|
||||
addressesRiskIds: ['R-CONF-04', 'R-TRANS-01', 'R-AUTO-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-07',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'End-to-End-Verschluesselung fuer Kommunikation',
|
||||
description: 'Einsatz von End-to-End-Verschluesselung fuer sensible Kommunikation (E-Mail, Messaging), sodass auch der Betreiber keinen Zugriff auf die Inhalte hat.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['E2E-Konfiguration', 'Testbericht'],
|
||||
addressesRiskIds: ['R-CONF-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-CONF-08',
|
||||
type: 'technical',
|
||||
sdmGoals: ['vertraulichkeit', 'datenminimierung'],
|
||||
title: 'Log-Sanitization & PII-Filtering',
|
||||
description: 'Automatische Filterung personenbezogener Daten aus Logs, Fehlermeldungen und Debug-Ausgaben. Einsatz von Tokenisierung oder Maskierung.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO, Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Log-Policy', 'PII-Filter-Konfiguration', 'Stichproben-Audit'],
|
||||
addressesRiskIds: ['R-CONF-07'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
]
|
||||
|
||||
export const INTEGRITY_MITIGATIONS: CatalogMitigation[] = [
|
||||
{
|
||||
id: 'M-INT-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Input-Validierung & Injection-Schutz',
|
||||
description: 'Konsequente Validierung aller Eingaben, Prepared Statements fuer Datenbankzugriffe, Content Security Policy und Output-Encoding zum Schutz vor Injection-Angriffen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['SAST-Report', 'Penetrationstest-Bericht', 'WAF-Regeln'],
|
||||
addressesRiskIds: ['R-INT-01'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet', 'transparenz'],
|
||||
title: 'Audit-Logging & SIEM-Integration',
|
||||
description: 'Lueckenlose Protokollierung aller sicherheitsrelevanten Ereignisse mit Weiterleitung an ein SIEM-System. Manipulation-sichere Logs mit Integritaetspruefung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['SIEM-Dashboard-Screenshot', 'Audit-Log-Beispiel', 'Alert-Regeln'],
|
||||
addressesRiskIds: ['R-INT-01', 'R-INT-04', 'R-INT-05', 'R-CONF-03', 'R-ORG-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Web Application Firewall (WAF) & API-Gateway',
|
||||
description: 'Einsatz einer WAF zum Schutz vor OWASP Top 10 Angriffen und eines API-Gateways fuer Rate-Limiting, Schema-Validierung und Anomalie-Erkennung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['WAF-Regelset', 'API-Gateway-Konfiguration', 'Blockierungs-Statistiken'],
|
||||
addressesRiskIds: ['R-INT-01'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Daten-Synchronisations-Monitoring & Integritaetspruefung',
|
||||
description: 'Automatische Ueberwachung von Synchronisationsprozessen mit Checksummen-Vergleich, Konflikterkennung und Alerting bei Inkonsistenzen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Sync-Monitoring-Dashboard', 'Checksummen-Report', 'Incident-Log'],
|
||||
addressesRiskIds: ['R-INT-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-INT-05',
|
||||
type: 'technical',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Versionierung & Change-Tracking fuer personenbezogene Daten',
|
||||
description: 'Alle Aenderungen an personenbezogenen Daten werden versioniert gespeichert (Audit-Trail). Wer hat wann was geaendert ist jederzeit nachvollziehbar.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. f DSGVO',
|
||||
evidenceTypes: ['Versionierungs-Schema', 'Change-Log-Beispiel'],
|
||||
addressesRiskIds: ['R-INT-02', 'R-INT-05'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* DSFA Massnahmenbibliothek — Verfuegbarkeit, Datenminimierung & Nichtverkettung
|
||||
*/
|
||||
|
||||
import type { CatalogMitigation } from './types'
|
||||
|
||||
export const AVAILABILITY_MITIGATIONS: CatalogMitigation[] = [
|
||||
{
|
||||
id: 'M-AVAIL-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Backup-Strategie mit 3-2-1-Regel',
|
||||
description: 'Implementierung einer Backup-Strategie nach der 3-2-1-Regel: 3 Kopien, 2 verschiedene Medien, 1 Offsite. Verschluesselte Backups mit regelmaessiger Integritaetspruefung.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. c DSGVO',
|
||||
evidenceTypes: ['Backup-Policy', 'Backup-Monitoring-Report', 'Offsite-Nachweis'],
|
||||
addressesRiskIds: ['R-AVAIL-01', 'R-AVAIL-03', 'R-INT-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-02',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Regelmaessige Restore-Tests & Disaster Recovery Uebungen',
|
||||
description: 'Mindestens quartalsweise Durchfuehrung von Restore-Tests und jaehrliche Disaster-Recovery-Uebungen. Dokumentation der Ergebnisse und Lessons Learned.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. d DSGVO',
|
||||
evidenceTypes: ['Restore-Test-Protokoll', 'DR-Uebungs-Dokumentation', 'RTO/RPO-Nachweis'],
|
||||
addressesRiskIds: ['R-AVAIL-01', 'R-AVAIL-03', 'R-INT-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Endpoint Protection & Anti-Ransomware',
|
||||
description: 'Einsatz von Endpoint-Detection-and-Response (EDR) Loesungen mit spezifischem Ransomware-Schutz, Verhaltensanalyse und automatischer Isolation kompromittierter Systeme.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['EDR-Dashboard', 'Threat-Detection-Statistiken', 'Incident-Response-Plan'],
|
||||
addressesRiskIds: ['R-AVAIL-01'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'Redundanz & High-Availability-Architektur',
|
||||
description: 'Redundante Systemauslegung mit automatischem Failover, Load-Balancing und geo-redundanter Datenhaltung zur Sicherstellung der Verfuegbarkeit.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['HA-Architekturdiagramm', 'Failover-Testprotokoll', 'SLA-Dokumentation'],
|
||||
addressesRiskIds: ['R-AVAIL-02', 'R-AVAIL-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-05',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['verfuegbarkeit', 'intervenierbarkeit'],
|
||||
title: 'Exit-Strategie & Datenportabilitaetsplan',
|
||||
description: 'Dokumentierte Exit-Strategie fuer jeden kritischen Anbieter mit Datenexport-Verfahren, Migrationsplan und Uebergangsfristen. Regelmaessiger Export-Test.',
|
||||
legalBasis: 'Art. 28 Abs. 3 lit. g DSGVO, Art. 20 DSGVO',
|
||||
evidenceTypes: ['Exit-Plan-Dokument', 'Export-Test-Protokoll', 'Vertragliche-Regelung'],
|
||||
addressesRiskIds: ['R-AVAIL-02', 'R-AVAIL-05'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-AVAIL-06',
|
||||
type: 'technical',
|
||||
sdmGoals: ['verfuegbarkeit'],
|
||||
title: 'DDoS-Schutz & Rate-Limiting',
|
||||
description: 'Einsatz von DDoS-Mitigation-Services, CDN-basiertem Schutz und anwendungsspezifischem Rate-Limiting zur Abwehr von Verfuegbarkeitsangriffen.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['DDoS-Schutz-Konfiguration', 'Rate-Limit-Regeln', 'Traffic-Analyse'],
|
||||
addressesRiskIds: ['R-AVAIL-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
]
|
||||
|
||||
export const MINIMIZATION_NONLINKAGE_MITIGATIONS: CatalogMitigation[] = [
|
||||
{
|
||||
id: 'M-DMIN-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['datenminimierung'],
|
||||
title: 'Privacy by Design: Datenerhebung auf das Minimum beschraenken',
|
||||
description: 'Technische Massnahmen zur Beschraenkung der Datenerhebung: Pflichtfelder minimieren, optionale Felder deutlich kennzeichnen, Default-Einstellungen datenschutzfreundlich.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Formular-Review', 'Default-Settings-Dokumentation'],
|
||||
addressesRiskIds: ['R-RIGHTS-07', 'R-CONF-07'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-DMIN-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['datenminimierung', 'nichtverkettung'],
|
||||
title: 'Pseudonymisierung & Anonymisierung',
|
||||
description: 'Einsatz von Pseudonymisierungsverfahren (Token-basiert, Hash-basiert) und k-Anonymity/Differential Privacy bei der Weitergabe oder Analyse von Daten.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO, Art. 32 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Pseudonymisierungs-Konzept', 'Re-Identifizierungs-Risiko-Analyse'],
|
||||
addressesRiskIds: ['R-RIGHTS-04', 'R-RIGHTS-07'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-DMIN-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['datenminimierung'],
|
||||
title: 'Automatisiertes Loeschkonzept mit Aufbewahrungsfristen',
|
||||
description: 'Implementierung automatischer Loeschroutinen basierend auf definierten Aufbewahrungsfristen. Monitoring der Loeschvorgaenge und Nachweis der Loeschung.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. e DSGVO, Art. 17 DSGVO',
|
||||
evidenceTypes: ['Loeschkonzept-Dokument', 'Loeschfrist-Uebersicht', 'Loeschprotokoll'],
|
||||
addressesRiskIds: ['R-RIGHTS-07', 'R-ORG-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-DMIN-04',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['datenminimierung'],
|
||||
title: 'Regelmaessige Ueberpruefung der Datenbestaende',
|
||||
description: 'Jaehrlicher Review aller gespeicherten personenbezogenen Daten auf Erforderlichkeit. Identifikation und Bereinigung von Altbestaenden, verwaisten Datensaetzen und redundanten Kopien.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. e DSGVO',
|
||||
evidenceTypes: ['Datenbestand-Review-Bericht', 'Bereinigungs-Protokoll'],
|
||||
addressesRiskIds: ['R-ORG-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-NONL-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung'],
|
||||
title: 'Zweckbindung & Consent-Management',
|
||||
description: 'Technische Durchsetzung der Zweckbindung: Daten werden nur fuer den erhobenen Zweck verwendet. Consent-Management-System protokolliert und erzwingt Einwilligungen.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. b DSGVO, Art. 6 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Consent-Management-System', 'Zweckbindungs-Matrix', 'Consent-Protokolle'],
|
||||
addressesRiskIds: ['R-CONF-05', 'R-RIGHTS-02', 'R-RIGHTS-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-NONL-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung'],
|
||||
title: 'Data Loss Prevention (DLP) & Datenklassifikation',
|
||||
description: 'Implementierung von DLP-Regeln zur Verhinderung unkontrollierter Datenweitergabe. Datenklassifikation (oeffentlich, intern, vertraulich, streng vertraulich) als Grundlage.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['DLP-Policy', 'Datenklassifikations-Schema', 'DLP-Incident-Report'],
|
||||
addressesRiskIds: ['R-RIGHTS-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-NONL-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung', 'datenminimierung'],
|
||||
title: 'Differential Privacy & k-Anonymity bei Datenanalysen',
|
||||
description: 'Einsatz von Differential Privacy oder k-Anonymity-Verfahren bei der Analyse personenbezogener Daten, um Re-Identifizierung zu verhindern.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Anonymisierungs-Konzept', 'Privacy-Budget-Berechnung', 'k-Anonymity-Nachweis'],
|
||||
addressesRiskIds: ['R-RIGHTS-04', 'R-AUTO-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-NONL-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung'],
|
||||
title: 'Mandantentrennung & Datenisolierung',
|
||||
description: 'Strikte logische oder physische Trennung personenbezogener Daten verschiedener Mandanten/Zwecke. Verhinderung unbeabsichtigter Zusammenfuehrung.',
|
||||
legalBasis: 'Art. 5 Abs. 1 lit. b DSGVO',
|
||||
evidenceTypes: ['Mandantentrennungs-Konzept', 'Isolierungs-Test-Bericht'],
|
||||
addressesRiskIds: ['R-RIGHTS-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
]
|
||||
61
admin-compliance/lib/sdk/dsfa/mitigations/helpers.ts
Normal file
61
admin-compliance/lib/sdk/dsfa/mitigations/helpers.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* DSFA Massnahmenbibliothek — Helpers & Assembled Library
|
||||
*/
|
||||
|
||||
import type { DSFAMitigationType, SDMGoal } from '../types'
|
||||
import type { CatalogMitigation } from './types'
|
||||
import { ACCESS_CONTROL_MITIGATIONS, INTEGRITY_MITIGATIONS } from './access-integrity'
|
||||
import { AVAILABILITY_MITIGATIONS, MINIMIZATION_NONLINKAGE_MITIGATIONS } from './availability-minimization'
|
||||
import { TRANSPARENCY_MITIGATIONS, INTERVENTION_MITIGATIONS, AUTOMATION_ORG_LEGAL_MITIGATIONS } from './transparency-intervention-org'
|
||||
|
||||
export const MITIGATION_LIBRARY: CatalogMitigation[] = [
|
||||
...ACCESS_CONTROL_MITIGATIONS,
|
||||
...INTEGRITY_MITIGATIONS,
|
||||
...AVAILABILITY_MITIGATIONS,
|
||||
...MINIMIZATION_NONLINKAGE_MITIGATIONS,
|
||||
...TRANSPARENCY_MITIGATIONS,
|
||||
...INTERVENTION_MITIGATIONS,
|
||||
...AUTOMATION_ORG_LEGAL_MITIGATIONS,
|
||||
]
|
||||
|
||||
export function getMitigationsBySDMGoal(goal: SDMGoal): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.sdmGoals.includes(goal))
|
||||
}
|
||||
|
||||
export function getMitigationsByType(type: DSFAMitigationType): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.type === type)
|
||||
}
|
||||
|
||||
export function getMitigationsForRisk(riskId: string): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.addressesRiskIds.includes(riskId))
|
||||
}
|
||||
|
||||
export function getCatalogMitigationById(id: string): CatalogMitigation | undefined {
|
||||
return MITIGATION_LIBRARY.find(m => m.id === id)
|
||||
}
|
||||
|
||||
export function getMitigationsByEffectiveness(effectiveness: 'low' | 'medium' | 'high'): CatalogMitigation[] {
|
||||
return MITIGATION_LIBRARY.filter(m => m.effectiveness === effectiveness)
|
||||
}
|
||||
|
||||
export const MITIGATION_TYPE_LABELS: Record<DSFAMitigationType, string> = {
|
||||
technical: 'Technisch',
|
||||
organizational: 'Organisatorisch',
|
||||
legal: 'Rechtlich',
|
||||
}
|
||||
|
||||
export const SDM_GOAL_LABELS: Record<SDMGoal, string> = {
|
||||
datenminimierung: 'Datenminimierung',
|
||||
verfuegbarkeit: 'Verfuegbarkeit',
|
||||
integritaet: 'Integritaet',
|
||||
vertraulichkeit: 'Vertraulichkeit',
|
||||
nichtverkettung: 'Nichtverkettung',
|
||||
transparenz: 'Transparenz',
|
||||
intervenierbarkeit: 'Intervenierbarkeit',
|
||||
}
|
||||
|
||||
export const EFFECTIVENESS_LABELS: Record<string, string> = {
|
||||
low: 'Gering',
|
||||
medium: 'Mittel',
|
||||
high: 'Hoch',
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* DSFA Massnahmenbibliothek — Transparenz, Intervenierbarkeit, Automatisierung, Org & Legal
|
||||
*/
|
||||
|
||||
import type { CatalogMitigation } from './types'
|
||||
|
||||
export const TRANSPARENCY_MITIGATIONS: CatalogMitigation[] = [
|
||||
{
|
||||
id: 'M-TRANS-01',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Datenschutzhinweise & Privacy Notices',
|
||||
description: 'Umfassende, verstaendliche Datenschutzhinweise gemaess Art. 13/14 DSGVO an allen Erhebungsstellen. Layered-Approach fuer unterschiedliche Detailstufen.',
|
||||
legalBasis: 'Art. 13, Art. 14 DSGVO',
|
||||
evidenceTypes: ['Privacy-Notice-Review', 'Zustellungs-Nachweis', 'Usability-Test'],
|
||||
addressesRiskIds: ['R-CONF-05', 'R-RIGHTS-02', 'R-RIGHTS-03', 'R-RIGHTS-06', 'R-TRANS-03', 'R-SPEC-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Vollstaendiger Audit-Trail fuer personenbezogene Daten',
|
||||
description: 'Lueckenloser, manipulationssicherer Audit-Trail fuer alle Verarbeitungsvorgaenge personenbezogener Daten. Wer hat wann auf welche Daten zugegriffen oder sie veraendert.',
|
||||
legalBasis: 'Art. 5 Abs. 2 DSGVO (Rechenschaftspflicht)',
|
||||
evidenceTypes: ['Audit-Trail-Architektur', 'Log-Integritaets-Nachweis', 'Beispiel-Audit-Export'],
|
||||
addressesRiskIds: ['R-INT-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-03',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Erklaerbarkeit von KI-Entscheidungen (Explainability)',
|
||||
description: 'Implementierung von Erklaerungsverfahren (SHAP, LIME, Feature-Importance) fuer automatisierte Entscheidungen. Bereitstellung verstaendlicher Begruendungen fuer Betroffene.',
|
||||
legalBasis: 'Art. 22 Abs. 3 DSGVO, Art. 13 Abs. 2 lit. f DSGVO',
|
||||
evidenceTypes: ['XAI-Konzept', 'Erklaerbarkeits-Beispiel', 'Betroffenen-Information'],
|
||||
addressesRiskIds: ['R-AUTO-01', 'R-AUTO-03', 'R-RIGHTS-01'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-04',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Ueberwachungs-Folgenabschaetzung & Informationspflicht',
|
||||
description: 'Bei systematischer Ueberwachung: Gesonderte Folgenabschaetzung, klare Beschilderung/Information, Verhaeltnismaessigkeitspruefung und zeitliche Begrenzung.',
|
||||
legalBasis: 'Art. 35 Abs. 3 lit. c DSGVO, Art. 13 DSGVO',
|
||||
evidenceTypes: ['Ueberwachungs-DSFA', 'Beschilderungs-Nachweis', 'Verhaeltnismaessigkeits-Bewertung'],
|
||||
addressesRiskIds: ['R-RIGHTS-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-05',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Verzeichnis von Verarbeitungstaetigkeiten (VVT) pflegen',
|
||||
description: 'Vollstaendiges und aktuelles VVT gemaess Art. 30 DSGVO fuer alle Verarbeitungstaetigkeiten. Regelmaessige Aktualisierung bei Aenderungen.',
|
||||
legalBasis: 'Art. 30 DSGVO',
|
||||
evidenceTypes: ['VVT-Export', 'Aktualisierungs-Log'],
|
||||
addressesRiskIds: ['R-RIGHTS-06'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-06',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz', 'vertraulichkeit'],
|
||||
title: 'Transfer Impact Assessment (TIA) fuer Drittlandtransfer',
|
||||
description: 'Durchfuehrung eines Transfer Impact Assessments vor jedem Drittlandtransfer. Bewertung des Schutzniveaus im Empfaengerland und Festlegung zusaetzlicher Garantien.',
|
||||
legalBasis: 'Art. 46 DSGVO, Schrems-II-Urteil',
|
||||
evidenceTypes: ['TIA-Dokument', 'Schutzniveau-Analyse', 'Zusaetzliche-Garantien-Vereinbarung'],
|
||||
addressesRiskIds: ['R-TRANS-01', 'R-TRANS-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-TRANS-07',
|
||||
type: 'legal',
|
||||
sdmGoals: ['vertraulichkeit'],
|
||||
title: 'Standardvertragsklauseln (SCC) & Supplementary Measures',
|
||||
description: 'Abschluss aktueller EU-Standardvertragsklauseln (2021/914) mit Auftragsverarbeitern im Drittland. Ergaenzende technische und organisatorische Massnahmen (Verschluesselung, Pseudonymisierung).',
|
||||
legalBasis: 'Art. 46 Abs. 2 lit. c DSGVO',
|
||||
evidenceTypes: ['Unterzeichnete SCC', 'Supplementary-Measures-Dokumentation'],
|
||||
addressesRiskIds: ['R-TRANS-01', 'R-TRANS-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
]
|
||||
|
||||
export const INTERVENTION_MITIGATIONS: CatalogMitigation[] = [
|
||||
{
|
||||
id: 'M-INTERV-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'DSAR-Workflow (Data Subject Access Request)',
|
||||
description: 'Automatisierter Workflow fuer Betroffenenanfragen (Auskunft, Loeschung, Berichtigung, Export). Fristenmanagement (1 Monat), Identitaetspruefung und Dokumentation.',
|
||||
legalBasis: 'Art. 15-22 DSGVO, Art. 12 Abs. 3 DSGVO',
|
||||
evidenceTypes: ['DSAR-Workflow-Dokumentation', 'Bearbeitungszeiten-Statistik', 'Audit-Trail'],
|
||||
addressesRiskIds: ['R-RIGHTS-05', 'R-AVAIL-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INTERV-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'Self-Service Datenverwaltung fuer Betroffene',
|
||||
description: 'Bereitstellung eines Self-Service-Portals, ueber das Betroffene ihre Daten einsehen, korrigieren, exportieren und die Loeschung beantragen koennen.',
|
||||
legalBasis: 'Art. 15-20 DSGVO',
|
||||
evidenceTypes: ['Portal-Screenshot', 'Funktions-Testprotokoll', 'Nutzungs-Statistik'],
|
||||
addressesRiskIds: ['R-RIGHTS-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-INTERV-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'Widerspruchs- und Einschraenkungsprozess',
|
||||
description: 'Definierter Prozess fuer die Bearbeitung von Widerspruechen (Art. 21) und Einschraenkungsersuchen (Art. 18). Technische Moeglichkeit zur Sperrung einzelner Datensaetze.',
|
||||
legalBasis: 'Art. 18, Art. 21 DSGVO',
|
||||
evidenceTypes: ['Prozessbeschreibung', 'Sperr-Funktionalitaets-Nachweis'],
|
||||
addressesRiskIds: ['R-RIGHTS-05'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-INTERV-04',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['intervenierbarkeit'],
|
||||
title: 'Human-in-the-Loop bei automatisierten Entscheidungen',
|
||||
description: 'Sicherstellung menschlicher Ueberpruefung bei automatisierten Entscheidungen mit erheblicher Auswirkung. Eskalationsprozess und Einspruchsmoeglichkeit fuer Betroffene.',
|
||||
legalBasis: 'Art. 22 Abs. 3 DSGVO',
|
||||
evidenceTypes: ['HITL-Prozessbeschreibung', 'Eskalations-Statistik', 'Einspruchs-Protokoll'],
|
||||
addressesRiskIds: ['R-AUTO-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
]
|
||||
|
||||
export const AUTOMATION_ORG_LEGAL_MITIGATIONS: CatalogMitigation[] = [
|
||||
{
|
||||
id: 'M-AUTO-01',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung', 'transparenz'],
|
||||
title: 'Bias-Monitoring & Fairness-Tests',
|
||||
description: 'Regelmaessige Ueberpruefung von KI-Modellen auf Bias und Diskriminierung. Fairness-Metriken (Demographic Parity, Equal Opportunity) und Korrekturmassnahmen bei Abweichungen.',
|
||||
legalBasis: 'Art. 22 Abs. 3 DSGVO, AI Act Art. 10',
|
||||
evidenceTypes: ['Bias-Audit-Report', 'Fairness-Metriken-Dashboard', 'Korrektur-Dokumentation'],
|
||||
addressesRiskIds: ['R-RIGHTS-01', 'R-AUTO-01', 'R-AUTO-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AUTO-02',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'KI-Modell-Dokumentation & Model Cards',
|
||||
description: 'Ausfuehrliche Dokumentation aller KI-Modelle: Trainingsdaten, Architektur, Performance-Metriken, bekannte Einschraenkungen, Einsatzzweck (Model Cards).',
|
||||
legalBasis: 'Art. 13 Abs. 2 lit. f DSGVO, AI Act Art. 11',
|
||||
evidenceTypes: ['Model-Card', 'Performance-Report', 'Einsatzbereich-Dokumentation'],
|
||||
addressesRiskIds: ['R-AUTO-01', 'R-AUTO-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-AUTO-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['intervenierbarkeit', 'transparenz'],
|
||||
title: 'KI-Governance-Framework & Human Oversight Board',
|
||||
description: 'Etablierung eines KI-Governance-Frameworks mit einem Human Oversight Board, das alle KI-Systeme mit hohem Risiko ueberwacht und Interventionsmoeglichkeiten hat.',
|
||||
legalBasis: 'Art. 22 DSGVO, AI Act Art. 14',
|
||||
evidenceTypes: ['Governance-Policy', 'Oversight-Board-Protokolle', 'Interventions-Log'],
|
||||
addressesRiskIds: ['R-AUTO-01', 'R-AUTO-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-AUTO-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['nichtverkettung', 'datenminimierung'],
|
||||
title: 'Datenschutzkonformes KI-Training (Privacy-Preserving ML)',
|
||||
description: 'Einsatz von Federated Learning, Differential Privacy beim Training oder synthetischen Trainingsdaten, um personenbezogene Daten im Modell zu schuetzen.',
|
||||
legalBasis: 'Art. 25 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Privacy-Preserving-ML-Konzept', 'Training-Daten-Analyse', 'Modell-Invertierbarkeiots-Test'],
|
||||
addressesRiskIds: ['R-AUTO-02', 'R-AUTO-05'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-ORG-01',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['vertraulichkeit', 'integritaet'],
|
||||
title: 'Datenschutz-Schulungen & Awareness-Programm',
|
||||
description: 'Regelmaessige verpflichtende Datenschutz-Schulungen fuer alle Mitarbeiter. Awareness-Kampagnen zu Phishing, Social Engineering und sicherem Datenumgang.',
|
||||
legalBasis: 'Art. 32 Abs. 1 lit. b DSGVO, Art. 39 Abs. 1 lit. a DSGVO',
|
||||
evidenceTypes: ['Schulungsplan', 'Teilnahmequoten', 'Phishing-Simulations-Ergebnis'],
|
||||
addressesRiskIds: ['R-CONF-06', 'R-ORG-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-ORG-02',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['integritaet'],
|
||||
title: 'Verpflichtung auf Vertraulichkeit & Datenschutz-Policy',
|
||||
description: 'Schriftliche Verpflichtung aller Mitarbeiter und externen Dienstleister auf Vertraulichkeit und Einhaltung der Datenschutz-Policies.',
|
||||
legalBasis: 'Art. 28 Abs. 3 lit. b DSGVO, Art. 29 DSGVO',
|
||||
evidenceTypes: ['Unterzeichnete-Verpflichtungserklaerung', 'Datenschutz-Policy'],
|
||||
addressesRiskIds: ['R-ORG-03'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-ORG-03',
|
||||
type: 'organizational',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Datenpannen-Erkennungs- und Meldeprozess (Incident Response)',
|
||||
description: 'Definierter Incident-Response-Prozess mit klaren Eskalationswegen, 72h-Meldepflicht-Tracking, Klassifizierungsschema und Kommunikationsplan.',
|
||||
legalBasis: 'Art. 33, Art. 34 DSGVO',
|
||||
evidenceTypes: ['Incident-Response-Plan', 'Melde-Template', 'Uebungs-Protokoll'],
|
||||
addressesRiskIds: ['R-ORG-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-ORG-04',
|
||||
type: 'technical',
|
||||
sdmGoals: ['transparenz', 'verfuegbarkeit'],
|
||||
title: 'Automatisiertes Breach-Detection & Alerting',
|
||||
description: 'Automatische Erkennung von Datenpannen durch Anomalie-Detection, ungewoehnliche Zugriffsmuster und Datenexfiltrations-Erkennung mit sofortigem Alert an den Incident-Response-Team.',
|
||||
legalBasis: 'Art. 33 Abs. 1 DSGVO',
|
||||
evidenceTypes: ['Alert-Regeln', 'Detection-Dashboard', 'Reaktionszeiten-Statistik'],
|
||||
addressesRiskIds: ['R-ORG-04'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-LEGAL-01',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Angemessenheitsbeschluss oder Binding Corporate Rules (BCR)',
|
||||
description: 'Sicherstellung, dass Drittlandtransfers auf einem Angemessenheitsbeschluss oder genehmigten BCRs basieren. Laufende Ueberwachung des Schutzniveaus.',
|
||||
legalBasis: 'Art. 45, Art. 47 DSGVO',
|
||||
evidenceTypes: ['Angemessenheitsbeschluss-Referenz', 'BCR-Genehmigung'],
|
||||
addressesRiskIds: ['R-TRANS-02'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-LEGAL-02',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Auftragsverarbeitungsvertrag (AVV) nach Art. 28 DSGVO',
|
||||
description: 'Abschluss vollstaendiger AVVs mit allen Auftragsverarbeitern. Regelung von Zweck, Dauer, Datenkategorien, Weisungsbindung, Sub-Auftragsverarbeiter und Audit-Rechten.',
|
||||
legalBasis: 'Art. 28 Abs. 3 DSGVO',
|
||||
evidenceTypes: ['Unterzeichneter-AVV', 'Sub-Auftragsverarbeiter-Liste', 'Audit-Bericht'],
|
||||
addressesRiskIds: ['R-ORG-01', 'R-TRANS-03'],
|
||||
effectiveness: 'high',
|
||||
},
|
||||
{
|
||||
id: 'M-LEGAL-03',
|
||||
type: 'legal',
|
||||
sdmGoals: ['transparenz'],
|
||||
title: 'Regelmaessige Auftragsverarbeiter-Audits',
|
||||
description: 'Jaehrliche Ueberpruefung der Auftragsverarbeiter auf Einhaltung der AVV-Vorgaben. Dokumentierte Audits vor Ort oder anhand von Zertifizierungen (SOC 2, ISO 27001).',
|
||||
legalBasis: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
evidenceTypes: ['Audit-Bericht', 'Zertifizierungs-Nachweis', 'Massnahmenplan'],
|
||||
addressesRiskIds: ['R-ORG-01'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
{
|
||||
id: 'M-LEGAL-04',
|
||||
type: 'legal',
|
||||
sdmGoals: ['intervenierbarkeit', 'transparenz'],
|
||||
title: 'Altersverifikation & Eltern-Einwilligung (Art. 8)',
|
||||
description: 'Implementierung einer altersgerechten Verifikation und Einholung der Eltern-Einwilligung bei Minderjaehrigen unter 16 Jahren. Kindgerechte Datenschutzinformationen.',
|
||||
legalBasis: 'Art. 8 DSGVO, EG 38 DSGVO',
|
||||
evidenceTypes: ['Altersverifikations-Konzept', 'Eltern-Einwilligungs-Formular', 'Kindgerechte-Privacy-Notice'],
|
||||
addressesRiskIds: ['R-SPEC-02'],
|
||||
effectiveness: 'medium',
|
||||
},
|
||||
]
|
||||
17
admin-compliance/lib/sdk/dsfa/mitigations/types.ts
Normal file
17
admin-compliance/lib/sdk/dsfa/mitigations/types.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* DSFA Massnahmenbibliothek — Types
|
||||
*/
|
||||
|
||||
import type { DSFAMitigationType, SDMGoal } from '../types'
|
||||
|
||||
export interface CatalogMitigation {
|
||||
id: string
|
||||
type: DSFAMitigationType
|
||||
sdmGoals: SDMGoal[]
|
||||
title: string
|
||||
description: string
|
||||
legalBasis: string
|
||||
evidenceTypes: string[]
|
||||
addressesRiskIds: string[]
|
||||
effectiveness: 'low' | 'medium' | 'high'
|
||||
}
|
||||
@@ -1,615 +1,11 @@
|
||||
/**
|
||||
* DSFA Risikokatalog - Vordefinierte Risikoszenarien
|
||||
* DSFA Risikokatalog — Barrel
|
||||
*
|
||||
* ~40 Risiken gegliedert nach Vertraulichkeit, Integritaet, Verfuegbarkeit,
|
||||
* Rechte & Freiheiten, Drittlandtransfer und Automatisierung.
|
||||
*
|
||||
* Quellen: EG 75 DSGVO, Art. 32 DSGVO, Art. 28/46 DSGVO, Art. 22 DSGVO,
|
||||
* Baseline-DSFA Katalog, SDM V2.0
|
||||
* Split into domain modules under risks/.
|
||||
* Import from this file to preserve the stable public path.
|
||||
*/
|
||||
|
||||
import type { DSFARiskCategory } from './types'
|
||||
import type { SDMGoal } from './types'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface CatalogRisk {
|
||||
id: string
|
||||
category: DSFARiskCategory
|
||||
sdmGoal: SDMGoal
|
||||
title: string
|
||||
description: string
|
||||
impactExamples: string[]
|
||||
typicalLikelihood: 'low' | 'medium' | 'high'
|
||||
typicalImpact: 'low' | 'medium' | 'high'
|
||||
wp248Criteria: string[]
|
||||
applicableTo: string[]
|
||||
mitigationIds: string[]
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// RISIKOKATALOG
|
||||
// =============================================================================
|
||||
|
||||
export const RISK_CATALOG: CatalogRisk[] = [
|
||||
// =========================================================================
|
||||
// VERTRAULICHKEIT (Confidentiality)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-CONF-01',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unbefugte Offenlegung durch Fehlkonfiguration',
|
||||
description: 'Personenbezogene Daten werden durch fehlerhafte Systemkonfiguration (z.B. offene APIs, fehlerhafte Zugriffsrechte, oeffentliche Cloud-Speicher) unbefugt zugaenglich.',
|
||||
impactExamples: ['Identitaetsdiebstahl', 'Reputationsschaden', 'Diskriminierung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K5'],
|
||||
applicableTo: ['cloud_storage', 'web_application', 'api_service'],
|
||||
mitigationIds: ['M-CONF-01', 'M-CONF-02', 'M-CONF-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-02',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Account Takeover / Credential Stuffing',
|
||||
description: 'Angreifer uebernehmen Benutzerkonten durch gestohlene Zugangsdaten, Brute-Force-Angriffe oder Phishing und erlangen Zugriff auf personenbezogene Daten.',
|
||||
impactExamples: ['Kontrollverlust ueber eigene Daten', 'Finanzieller Schaden', 'Missbrauch der Identitaet'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K7'],
|
||||
applicableTo: ['identity', 'web_application', 'email_service'],
|
||||
mitigationIds: ['M-ACC-01', 'M-ACC-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-03',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unbefugter Zugriff durch Support-/Administrationspersonal',
|
||||
description: 'Administratoren oder Support-Mitarbeiter greifen ohne dienstliche Notwendigkeit auf personenbezogene Daten zu (Insider-Bedrohung).',
|
||||
impactExamples: ['Verletzung der Privatsphaere', 'Datenmissbrauch', 'Vertrauensverlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K4'],
|
||||
applicableTo: ['identity', 'crm', 'cloud_storage', 'support_system'],
|
||||
mitigationIds: ['M-CONF-04', 'M-CONF-05', 'M-INT-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-04',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Datenleck durch unzureichende Verschluesselung',
|
||||
description: 'Personenbezogene Daten werden bei Uebertragung oder Speicherung nicht oder unzureichend verschluesselt und koennen abgefangen werden.',
|
||||
impactExamples: ['Man-in-the-Middle-Angriff', 'Datendiebstahl bei Speichermedien-Verlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K8'],
|
||||
applicableTo: ['cloud_storage', 'email_service', 'mobile_app', 'api_service'],
|
||||
mitigationIds: ['M-CONF-06', 'M-CONF-07'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-05',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unkontrollierte Datenweitergabe an Dritte',
|
||||
description: 'Personenbezogene Daten werden ohne Rechtsgrundlage oder ueber das vereinbarte Mass hinaus an Dritte weitergegeben (z.B. durch Tracking, Analyse-Tools, Sub-Auftragsverarbeiter).',
|
||||
impactExamples: ['Unerwuenschte Werbung', 'Profiling ohne Wissen', 'Kontrollverlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K1', 'K6'],
|
||||
applicableTo: ['web_application', 'analytics', 'marketing', 'crm'],
|
||||
mitigationIds: ['M-NONL-01', 'M-TRANS-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-06',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Social Engineering / Phishing gegen Betroffene',
|
||||
description: 'Betroffene werden durch manipulative Kommunikation dazu verleitet, personenbezogene Daten preiszugeben oder Zugriff zu gewaehren.',
|
||||
impactExamples: ['Identitaetsdiebstahl', 'Finanzieller Schaden', 'Uebernahme von Konten'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K7'],
|
||||
applicableTo: ['email_service', 'web_application', 'identity'],
|
||||
mitigationIds: ['M-ACC-01', 'M-ORG-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-07',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unbeabsichtigte Offenlegung in Logs/Debugging',
|
||||
description: 'Personenbezogene Daten gelangen in Protokolldateien, Fehlermeldungen oder Debug-Ausgaben und werden dort nicht geschuetzt.',
|
||||
impactExamples: ['Zugriff durch Unbefugte auf Logdaten', 'Langzeitspeicherung ohne Rechtsgrundlage'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K4'],
|
||||
applicableTo: ['api_service', 'web_application', 'cloud_storage'],
|
||||
mitigationIds: ['M-CONF-08', 'M-DMIN-01'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// INTEGRITAET (Integrity)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-INT-01',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Datenmanipulation durch externen Angriff',
|
||||
description: 'Personenbezogene Daten werden durch einen Cyberangriff (SQL-Injection, API-Manipulation) veraendert, ohne dass dies erkannt wird.',
|
||||
impactExamples: ['Falsche Entscheidungen auf Basis manipulierter Daten', 'Rufschaedigung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K8'],
|
||||
applicableTo: ['api_service', 'web_application', 'database'],
|
||||
mitigationIds: ['M-INT-01', 'M-INT-02', 'M-INT-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-02',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Fehlerhafte Synchronisation zwischen Systemen',
|
||||
description: 'Bei der Synchronisation personenbezogener Daten zwischen verschiedenen Systemen kommt es zu Inkonsistenzen, Duplikaten oder Datenverlust.',
|
||||
impactExamples: ['Falsche Kontaktdaten', 'Doppelte Verarbeitung', 'Falsche Auskuenfte'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K6'],
|
||||
applicableTo: ['crm', 'cloud_storage', 'erp', 'identity'],
|
||||
mitigationIds: ['M-INT-04', 'M-INT-05'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-03',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Backup-Korruption oder fehlerhafte Wiederherstellung',
|
||||
description: 'Backups personenbezogener Daten sind beschaedigt, unvollstaendig oder veraltet, sodass eine zuverlaessige Wiederherstellung nicht moeglich ist.',
|
||||
impactExamples: ['Datenverlust bei Wiederherstellung', 'Veraltete Datenbasis', 'Compliance-Verstoss'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['database', 'cloud_storage', 'erp'],
|
||||
mitigationIds: ['M-AVAIL-01', 'M-AVAIL-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-04',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Unbemerkte Aenderung von Zugriffsrechten',
|
||||
description: 'Zugriffsberechtigungen werden unbefugt oder fehlerhaft geaendert, wodurch unberechtigte Personen Zugang zu personenbezogenen Daten erhalten.',
|
||||
impactExamples: ['Privilege Escalation', 'Unbefugter Datenzugriff'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4'],
|
||||
applicableTo: ['identity', 'cloud_storage', 'api_service'],
|
||||
mitigationIds: ['M-INT-02', 'M-CONF-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-05',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Fehlende Nachvollziehbarkeit von Datenveraenderungen',
|
||||
description: 'Aenderungen an personenbezogenen Daten werden nicht protokolliert, sodass Manipulationen oder Fehler nicht erkannt oder nachvollzogen werden koennen.',
|
||||
impactExamples: ['Unmoeglich festzustellen wer/wann Daten geaendert hat', 'Audit-Versagen'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K3'],
|
||||
applicableTo: ['database', 'crm', 'erp', 'web_application'],
|
||||
mitigationIds: ['M-INT-02', 'M-TRANS-02'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// VERFUEGBARKEIT (Availability)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-AVAIL-01',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Ransomware-Angriff mit Datenverschluesselung',
|
||||
description: 'Schadsoftware verschluesselt personenbezogene Daten und macht sie unzugaenglich. Die Wiederherstellung erfordert entweder Loesegeldzahlung oder Backup-Restore.',
|
||||
impactExamples: ['Verlust des Zugangs zu eigenen Daten', 'Betriebsunterbrechung', 'Loesegeld-Erpressung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5', 'K8'],
|
||||
applicableTo: ['cloud_storage', 'database', 'erp', 'web_application'],
|
||||
mitigationIds: ['M-AVAIL-01', 'M-AVAIL-02', 'M-AVAIL-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-02',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Provider-Ausfall / Cloud-Service Nichtverfuegbarkeit',
|
||||
description: 'Der Cloud-/Hosting-Provider faellt aus, was den Zugang zu personenbezogenen Daten verhindert. Betroffene koennen ihre Rechte nicht ausueben.',
|
||||
impactExamples: ['Keine Auskunft moeglich', 'Vertragsverletzung', 'Geschaeftsunterbrechung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5', 'K9'],
|
||||
applicableTo: ['cloud_storage', 'web_application', 'api_service'],
|
||||
mitigationIds: ['M-AVAIL-04', 'M-AVAIL-05'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-03',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Datenverlust durch fehlende oder ungetestete Backups',
|
||||
description: 'Personenbezogene Daten gehen unwiederbringlich verloren, weil keine ausreichenden Backups existieren oder Restore-Prozesse nicht getestet werden.',
|
||||
impactExamples: ['Unwiderruflicher Datenverlust', 'Verlust von Beweismitteln', 'Compliance-Verstoss'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['database', 'cloud_storage', 'erp'],
|
||||
mitigationIds: ['M-AVAIL-01', 'M-AVAIL-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-04',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'DDoS-Angriff auf oeffentliche Dienste',
|
||||
description: 'Ein Distributed-Denial-of-Service-Angriff verhindert den Zugang zu Systemen, die personenbezogene Daten verarbeiten.',
|
||||
impactExamples: ['Betroffene koennen Rechte nicht ausueben', 'Geschaeftsausfall'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5', 'K9'],
|
||||
applicableTo: ['web_application', 'api_service'],
|
||||
mitigationIds: ['M-AVAIL-06', 'M-AVAIL-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-05',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Vendor Lock-in mit Kontrollverlust',
|
||||
description: 'Abhaengigkeit von einem einzelnen Anbieter erschwert oder verhindert den Zugang zu personenbezogenen Daten bei Vertragsbeendigung oder Anbieterwechsel.',
|
||||
impactExamples: ['Datenexport nicht moeglich', 'Erzwungene Weiternutzung', 'Datenverlust bei Kuendigung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K9'],
|
||||
applicableTo: ['cloud_storage', 'erp', 'crm'],
|
||||
mitigationIds: ['M-AVAIL-05', 'M-INTERV-01'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// RECHTE & FREIHEITEN (Rights & Freedoms)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-RIGHTS-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Diskriminierung durch automatisierte Verarbeitung',
|
||||
description: 'Automatisierte Entscheidungssysteme fuehren zu einer diskriminierenden Behandlung bestimmter Personengruppen aufgrund von Merkmalen wie Alter, Geschlecht, Herkunft oder Gesundheitszustand.',
|
||||
impactExamples: ['Benachteiligung bei Kreditvergabe', 'Ausschluss von Dienstleistungen', 'Ungleichbehandlung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K2', 'K7'],
|
||||
applicableTo: ['ai_ml', 'scoring', 'identity'],
|
||||
mitigationIds: ['M-AUTO-01', 'M-AUTO-02', 'M-TRANS-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Unzulaessiges Profiling ohne Einwilligung',
|
||||
description: 'Nutzerverhalten wird systematisch analysiert und zu Profilen zusammengefuehrt, ohne dass eine Rechtsgrundlage oder Einwilligung vorliegt.',
|
||||
impactExamples: ['Persoenlichkeitsprofile ohne Wissen', 'Gezielte Manipulation', 'Filterblase'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K3', 'K6'],
|
||||
applicableTo: ['analytics', 'marketing', 'web_application', 'ai_ml'],
|
||||
mitigationIds: ['M-NONL-01', 'M-NONL-02', 'M-TRANS-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-03',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Systematische Ueberwachung von Betroffenen',
|
||||
description: 'Betroffene werden systematisch ueberwacht (z.B. durch Standorttracking, E-Mail-Monitoring, Videoueberwachung), ohne angemessene Transparenz oder Rechtsgrundlage.',
|
||||
impactExamples: ['Einschuechterungseffekt (Chilling Effect)', 'Verletzung der Privatsphaere', 'Vertrauensverlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K3', 'K4', 'K7'],
|
||||
applicableTo: ['monitoring', 'hr_system', 'mobile_app'],
|
||||
mitigationIds: ['M-TRANS-01', 'M-TRANS-04', 'M-NONL-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-04',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Re-Identifizierung pseudonymisierter Daten',
|
||||
description: 'Pseudonymisierte oder anonymisierte Daten werden durch Zusammenfuehrung mit anderen Datenquellen re-identifiziert, wodurch der Schutz der Betroffenen aufgehoben wird.',
|
||||
impactExamples: ['Verlust der Anonymitaet', 'Unerwuenschte Identifizierung', 'Zweckentfremdung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K6', 'K8'],
|
||||
applicableTo: ['analytics', 'ai_ml', 'research'],
|
||||
mitigationIds: ['M-NONL-03', 'M-NONL-04', 'M-DMIN-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-05',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'intervenierbarkeit',
|
||||
title: 'Hinderung bei Ausuebung von Betroffenenrechten',
|
||||
description: 'Betroffene werden an der Ausuebung ihrer Rechte (Auskunft, Loeschung, Berichtigung, Widerspruch) gehindert — z.B. durch fehlende Prozesse, technische Huerden oder Verzoegerungen.',
|
||||
impactExamples: ['Keine Loeschung moeglich', 'Verzoegerte Auskunft', 'Bussgeld gem. Art. 83'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K9'],
|
||||
applicableTo: ['web_application', 'crm', 'identity', 'cloud_storage'],
|
||||
mitigationIds: ['M-INTERV-01', 'M-INTERV-02', 'M-INTERV-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-06',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende oder unzureichende Informationspflichten',
|
||||
description: 'Betroffene werden nicht oder unzureichend ueber die Verarbeitung ihrer Daten informiert (Verstoss gegen Art. 13/14 DSGVO).',
|
||||
impactExamples: ['Keine informierte Einwilligung moeglich', 'Vertrauensverlust', 'Bussgeld'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K9'],
|
||||
applicableTo: ['web_application', 'mobile_app', 'marketing'],
|
||||
mitigationIds: ['M-TRANS-01', 'M-TRANS-05'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-07',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'datenminimierung',
|
||||
title: 'Uebermassige Datenerhebung (Verstoss Datenminimierung)',
|
||||
description: 'Es werden mehr personenbezogene Daten erhoben als fuer den Verarbeitungszweck notwendig (Verstoss gegen Art. 5 Abs. 1 lit. c DSGVO).',
|
||||
impactExamples: ['Unnoetige Risikoexposition', 'Hoeherer Schaden bei Datenpanne'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['web_application', 'mobile_app', 'crm', 'hr_system'],
|
||||
mitigationIds: ['M-DMIN-01', 'M-DMIN-02', 'M-DMIN-03'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// DRITTLANDTRANSFER
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-TRANS-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Zugriff durch Drittland-Behoerden (FISA/CLOUD Act)',
|
||||
description: 'Behoerden eines Drittlandes (z.B. USA) greifen auf personenbezogene Daten zu, die bei einem Cloud-Provider in der EU oder im Drittland gespeichert sind.',
|
||||
impactExamples: ['Ueberwachung ohne Wissen', 'Kein Rechtsschutz', 'Schrems-II-Risiko'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K5', 'K7'],
|
||||
applicableTo: ['cloud_storage', 'email_service', 'crm', 'analytics'],
|
||||
mitigationIds: ['M-TRANS-06', 'M-TRANS-07', 'M-CONF-06'],
|
||||
},
|
||||
{
|
||||
id: 'R-TRANS-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unzureichende Schutzgarantien bei Drittlandtransfer',
|
||||
description: 'Personenbezogene Daten werden in Drittlaender uebermittelt, ohne dass angemessene Garantien (SCC, BCR, Angemessenheitsbeschluss) vorhanden sind.',
|
||||
impactExamples: ['Rechtswidriger Transfer', 'Bussgeld', 'Untersagung der Verarbeitung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5', 'K7'],
|
||||
applicableTo: ['cloud_storage', 'email_service', 'crm', 'analytics'],
|
||||
mitigationIds: ['M-TRANS-06', 'M-TRANS-07', 'M-LEGAL-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-TRANS-03',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Intransparente Sub-Auftragsverarbeiter-Kette',
|
||||
description: 'Die Kette der Sub-Auftragsverarbeiter ist nicht transparent. Betroffene und Verantwortliche wissen nicht, wo ihre Daten tatsaechlich verarbeitet werden.',
|
||||
impactExamples: ['Unkontrollierte Datenweitergabe', 'Unbekannter Verarbeitungsort'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['cloud_storage', 'crm', 'analytics'],
|
||||
mitigationIds: ['M-TRANS-01', 'M-LEGAL-02'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// AUTOMATISIERUNG / KI
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-AUTO-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'KI-Fehlentscheidung mit erheblicher Auswirkung',
|
||||
description: 'Ein KI-System trifft eine fehlerhafte automatisierte Entscheidung (z.B. Ablehnung, Sperrung, Bewertung), die erhebliche Auswirkungen auf eine betroffene Person hat.',
|
||||
impactExamples: ['Unrechtmaessige Ablehnung', 'Falsche Risikoeinstufung', 'Benachteiligung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K2', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring', 'hr_system'],
|
||||
mitigationIds: ['M-AUTO-01', 'M-AUTO-02', 'M-AUTO-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Algorithmischer Bias in Trainingsdaten',
|
||||
description: 'KI-Modelle spiegeln Vorurteile in den Trainingsdaten wider und treffen diskriminierende Entscheidungen bezueglich geschuetzter Merkmale.',
|
||||
impactExamples: ['Diskriminierung nach Geschlecht/Herkunft', 'Systematische Benachteiligung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K2', 'K7', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring'],
|
||||
mitigationIds: ['M-AUTO-01', 'M-AUTO-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-03',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende Erklaerbarkeit automatisierter Entscheidungen',
|
||||
description: 'Automatisierte Entscheidungen koennen den Betroffenen nicht erklaert werden ("Black Box"), sodass der Anspruch auf aussagekraeftige Informationen (Art. 22 Abs. 3) nicht erfuellt wird.',
|
||||
impactExamples: ['Keine Anfechtbarkeit', 'Vertrauensverlust', 'Verstoss gegen Art. 22'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K2', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring'],
|
||||
mitigationIds: ['M-AUTO-02', 'M-TRANS-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-04',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'intervenierbarkeit',
|
||||
title: 'Fehlende menschliche Aufsicht bei KI-Entscheidungen',
|
||||
description: 'Automatisierte Entscheidungen werden ohne menschliche Ueberpruefung oder Interventionsmoeglichkeit getroffen, obwohl dies erforderlich waere.',
|
||||
impactExamples: ['Keine Korrekturmoeglichkeit', 'Eskalation von Fehlern'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K2', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring', 'hr_system'],
|
||||
mitigationIds: ['M-AUTO-03', 'M-INTERV-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-05',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Datenleck durch KI-Training mit personenbezogenen Daten',
|
||||
description: 'Personenbezogene Daten, die fuer das Training von KI-Modellen verwendet werden, koennen durch das Modell reproduziert oder extrahiert werden (Model Inversion, Membership Inference).',
|
||||
impactExamples: ['Offenlegung von Trainingsdaten', 'Re-Identifizierung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K8'],
|
||||
applicableTo: ['ai_ml'],
|
||||
mitigationIds: ['M-CONF-06', 'M-NONL-03', 'M-AUTO-04'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// ORGANISATORISCHE RISIKEN
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-ORG-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende oder fehlerhafte Auftragsverarbeitungsvertraege',
|
||||
description: 'Mit Auftragsverarbeitern existieren keine oder unzureichende Vertraege gemaess Art. 28 DSGVO, sodass Pflichten und Rechte nicht geregelt sind.',
|
||||
impactExamples: ['Keine Kontrolle ueber Verarbeiter', 'Bussgeld', 'Datenmissbrauch durch Verarbeiter'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['cloud_storage', 'crm', 'analytics', 'email_service'],
|
||||
mitigationIds: ['M-LEGAL-02', 'M-LEGAL-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-ORG-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'datenminimierung',
|
||||
title: 'Fehlende Loeschprozesse / Ueberschreitung von Aufbewahrungsfristen',
|
||||
description: 'Personenbezogene Daten werden laenger als notwendig gespeichert, weil keine automatischen Loeschprozesse oder Aufbewahrungsfristen definiert sind.',
|
||||
impactExamples: ['Unnoetige Risikoexposition', 'Verstoss gegen Speicherbegrenzung', 'Bussgeld'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['database', 'cloud_storage', 'crm', 'erp', 'email_service'],
|
||||
mitigationIds: ['M-DMIN-03', 'M-DMIN-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-ORG-03',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Unzureichende Schulung/Sensibilisierung der Mitarbeiter',
|
||||
description: 'Mitarbeiter sind nicht ausreichend im Umgang mit personenbezogenen Daten geschult und verursachen durch Unkenntnis Datenpannen oder Verarbeitungsfehler.',
|
||||
impactExamples: ['Versehentliche Datenweitergabe', 'Phishing-Erfolg', 'Fehlerhafte Verarbeitung'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5', 'K7'],
|
||||
applicableTo: ['hr_system', 'email_service', 'crm', 'web_application'],
|
||||
mitigationIds: ['M-ORG-01', 'M-ORG-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-ORG-04',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende Datenpannen-Erkennung und -Meldung',
|
||||
description: 'Datenpannen werden nicht rechtzeitig erkannt oder nicht innerhalb der 72-Stunden-Frist (Art. 33 DSGVO) an die Aufsichtsbehoerde gemeldet.',
|
||||
impactExamples: ['Verspaetete Meldung', 'Bussgeld', 'Verzoegerte Benachrichtigung Betroffener'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['web_application', 'cloud_storage', 'database', 'api_service'],
|
||||
mitigationIds: ['M-ORG-03', 'M-ORG-04', 'M-INT-02'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// BESONDERE DATENKATEGORIEN
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'R-SPEC-01',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Kompromittierung besonderer Datenkategorien (Art. 9)',
|
||||
description: 'Besonders schutzwuerdige Daten (Gesundheit, Religion, Biometrie, Gewerkschaftszugehoerigkeit) werden offengelegt oder missbraucht.',
|
||||
impactExamples: ['Schwerwiegende Diskriminierung', 'Existenzielle Bedrohung', 'Soziale Ausgrenzung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K7'],
|
||||
applicableTo: ['hr_system', 'health_system', 'identity'],
|
||||
mitigationIds: ['M-CONF-06', 'M-CONF-01', 'M-CONF-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-SPEC-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'intervenierbarkeit',
|
||||
title: 'Verarbeitung von Kinderdaten ohne angemessenen Schutz',
|
||||
description: 'Daten von Minderjaehrigen werden verarbeitet, ohne die besonderen Schutzmassnahmen fuer Kinder (Art. 8, EG 38 DSGVO) zu beachten.',
|
||||
impactExamples: ['Langzeitfolgen fuer Minderjaehrige', 'Einschraenkung der Entwicklung', 'Manipulation'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K7'],
|
||||
applicableTo: ['web_application', 'mobile_app', 'education'],
|
||||
mitigationIds: ['M-LEGAL-04', 'M-DMIN-01', 'M-TRANS-01'],
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
export function getRisksByCategory(category: DSFARiskCategory): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.category === category)
|
||||
}
|
||||
|
||||
export function getRisksBySDMGoal(goal: SDMGoal): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.sdmGoal === goal)
|
||||
}
|
||||
|
||||
export function getRisksByWP248Criterion(criterionCode: string): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.wp248Criteria.includes(criterionCode))
|
||||
}
|
||||
|
||||
export function getRisksByComponent(component: string): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.applicableTo.includes(component))
|
||||
}
|
||||
|
||||
export function getCatalogRiskById(id: string): CatalogRisk | undefined {
|
||||
return RISK_CATALOG.find(r => r.id === id)
|
||||
}
|
||||
|
||||
export const RISK_CATEGORY_LABELS: Record<DSFARiskCategory, string> = {
|
||||
confidentiality: 'Vertraulichkeit',
|
||||
integrity: 'Integritaet',
|
||||
availability: 'Verfuegbarkeit',
|
||||
rights_freedoms: 'Rechte & Freiheiten',
|
||||
}
|
||||
|
||||
export const COMPONENT_FAMILY_LABELS: Record<string, string> = {
|
||||
identity: 'Identitaet & Zugang',
|
||||
cloud_storage: 'Cloud-Speicher',
|
||||
web_application: 'Web-Anwendung',
|
||||
api_service: 'API-Service',
|
||||
email_service: 'E-Mail-Dienst',
|
||||
mobile_app: 'Mobile App',
|
||||
database: 'Datenbank',
|
||||
crm: 'CRM-System',
|
||||
erp: 'ERP-System',
|
||||
analytics: 'Analyse/Tracking',
|
||||
marketing: 'Marketing',
|
||||
ai_ml: 'KI / Machine Learning',
|
||||
scoring: 'Scoring / Bewertung',
|
||||
hr_system: 'HR-System',
|
||||
health_system: 'Gesundheitssystem',
|
||||
monitoring: 'Ueberwachungssystem',
|
||||
support_system: 'Support-System',
|
||||
education: 'Bildungsplattform',
|
||||
research: 'Forschung',
|
||||
}
|
||||
export type { CatalogRisk } from './risks/types'
|
||||
export * from './risks/confidentiality-integrity'
|
||||
export * from './risks/availability-rights-auto-org'
|
||||
export * from './risks/helpers'
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* DSFA Risikokatalog — Verfuegbarkeit, Rechte & Freiheiten, Drittlandtransfer, Auto/KI, Org
|
||||
*/
|
||||
|
||||
import type { CatalogRisk } from './types'
|
||||
|
||||
export const AVAILABILITY_RISKS: CatalogRisk[] = [
|
||||
{
|
||||
id: 'R-AVAIL-01',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Ransomware-Angriff mit Datenverschluesselung',
|
||||
description: 'Schadsoftware verschluesselt personenbezogene Daten und macht sie unzugaenglich. Die Wiederherstellung erfordert entweder Loesegeldzahlung oder Backup-Restore.',
|
||||
impactExamples: ['Verlust des Zugangs zu eigenen Daten', 'Betriebsunterbrechung', 'Loesegeld-Erpressung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5', 'K8'],
|
||||
applicableTo: ['cloud_storage', 'database', 'erp', 'web_application'],
|
||||
mitigationIds: ['M-AVAIL-01', 'M-AVAIL-02', 'M-AVAIL-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-02',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Provider-Ausfall / Cloud-Service Nichtverfuegbarkeit',
|
||||
description: 'Der Cloud-/Hosting-Provider faellt aus, was den Zugang zu personenbezogenen Daten verhindert. Betroffene koennen ihre Rechte nicht ausueben.',
|
||||
impactExamples: ['Keine Auskunft moeglich', 'Vertragsverletzung', 'Geschaeftsunterbrechung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5', 'K9'],
|
||||
applicableTo: ['cloud_storage', 'web_application', 'api_service'],
|
||||
mitigationIds: ['M-AVAIL-04', 'M-AVAIL-05'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-03',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Datenverlust durch fehlende oder ungetestete Backups',
|
||||
description: 'Personenbezogene Daten gehen unwiederbringlich verloren, weil keine ausreichenden Backups existieren oder Restore-Prozesse nicht getestet werden.',
|
||||
impactExamples: ['Unwiderruflicher Datenverlust', 'Verlust von Beweismitteln', 'Compliance-Verstoss'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['database', 'cloud_storage', 'erp'],
|
||||
mitigationIds: ['M-AVAIL-01', 'M-AVAIL-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-04',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'DDoS-Angriff auf oeffentliche Dienste',
|
||||
description: 'Ein Distributed-Denial-of-Service-Angriff verhindert den Zugang zu Systemen, die personenbezogene Daten verarbeiten.',
|
||||
impactExamples: ['Betroffene koennen Rechte nicht ausueben', 'Geschaeftsausfall'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5', 'K9'],
|
||||
applicableTo: ['web_application', 'api_service'],
|
||||
mitigationIds: ['M-AVAIL-06', 'M-AVAIL-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-AVAIL-05',
|
||||
category: 'availability',
|
||||
sdmGoal: 'verfuegbarkeit',
|
||||
title: 'Vendor Lock-in mit Kontrollverlust',
|
||||
description: 'Abhaengigkeit von einem einzelnen Anbieter erschwert oder verhindert den Zugang zu personenbezogenen Daten bei Vertragsbeendigung oder Anbieterwechsel.',
|
||||
impactExamples: ['Datenexport nicht moeglich', 'Erzwungene Weiternutzung', 'Datenverlust bei Kuendigung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K9'],
|
||||
applicableTo: ['cloud_storage', 'erp', 'crm'],
|
||||
mitigationIds: ['M-AVAIL-05', 'M-INTERV-01'],
|
||||
},
|
||||
]
|
||||
|
||||
export const RIGHTS_FREEDOMS_RISKS: CatalogRisk[] = [
|
||||
{
|
||||
id: 'R-RIGHTS-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Diskriminierung durch automatisierte Verarbeitung',
|
||||
description: 'Automatisierte Entscheidungssysteme fuehren zu einer diskriminierenden Behandlung bestimmter Personengruppen aufgrund von Merkmalen wie Alter, Geschlecht, Herkunft oder Gesundheitszustand.',
|
||||
impactExamples: ['Benachteiligung bei Kreditvergabe', 'Ausschluss von Dienstleistungen', 'Ungleichbehandlung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K2', 'K7'],
|
||||
applicableTo: ['ai_ml', 'scoring', 'identity'],
|
||||
mitigationIds: ['M-AUTO-01', 'M-AUTO-02', 'M-TRANS-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Unzulaessiges Profiling ohne Einwilligung',
|
||||
description: 'Nutzerverhalten wird systematisch analysiert und zu Profilen zusammengefuehrt, ohne dass eine Rechtsgrundlage oder Einwilligung vorliegt.',
|
||||
impactExamples: ['Persoenlichkeitsprofile ohne Wissen', 'Gezielte Manipulation', 'Filterblase'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K3', 'K6'],
|
||||
applicableTo: ['analytics', 'marketing', 'web_application', 'ai_ml'],
|
||||
mitigationIds: ['M-NONL-01', 'M-NONL-02', 'M-TRANS-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-03',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Systematische Ueberwachung von Betroffenen',
|
||||
description: 'Betroffene werden systematisch ueberwacht (z.B. durch Standorttracking, E-Mail-Monitoring, Videoueberwachung), ohne angemessene Transparenz oder Rechtsgrundlage.',
|
||||
impactExamples: ['Einschuechterungseffekt (Chilling Effect)', 'Verletzung der Privatsphaere', 'Vertrauensverlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K3', 'K4', 'K7'],
|
||||
applicableTo: ['monitoring', 'hr_system', 'mobile_app'],
|
||||
mitigationIds: ['M-TRANS-01', 'M-TRANS-04', 'M-NONL-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-04',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Re-Identifizierung pseudonymisierter Daten',
|
||||
description: 'Pseudonymisierte oder anonymisierte Daten werden durch Zusammenfuehrung mit anderen Datenquellen re-identifiziert, wodurch der Schutz der Betroffenen aufgehoben wird.',
|
||||
impactExamples: ['Verlust der Anonymitaet', 'Unerwuenschte Identifizierung', 'Zweckentfremdung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K6', 'K8'],
|
||||
applicableTo: ['analytics', 'ai_ml', 'research'],
|
||||
mitigationIds: ['M-NONL-03', 'M-NONL-04', 'M-DMIN-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-05',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'intervenierbarkeit',
|
||||
title: 'Hinderung bei Ausuebung von Betroffenenrechten',
|
||||
description: 'Betroffene werden an der Ausuebung ihrer Rechte (Auskunft, Loeschung, Berichtigung, Widerspruch) gehindert — z.B. durch fehlende Prozesse, technische Huerden oder Verzoegerungen.',
|
||||
impactExamples: ['Keine Loeschung moeglich', 'Verzoegerte Auskunft', 'Bussgeld gem. Art. 83'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K9'],
|
||||
applicableTo: ['web_application', 'crm', 'identity', 'cloud_storage'],
|
||||
mitigationIds: ['M-INTERV-01', 'M-INTERV-02', 'M-INTERV-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-06',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende oder unzureichende Informationspflichten',
|
||||
description: 'Betroffene werden nicht oder unzureichend ueber die Verarbeitung ihrer Daten informiert (Verstoss gegen Art. 13/14 DSGVO).',
|
||||
impactExamples: ['Keine informierte Einwilligung moeglich', 'Vertrauensverlust', 'Bussgeld'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K9'],
|
||||
applicableTo: ['web_application', 'mobile_app', 'marketing'],
|
||||
mitigationIds: ['M-TRANS-01', 'M-TRANS-05'],
|
||||
},
|
||||
{
|
||||
id: 'R-RIGHTS-07',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'datenminimierung',
|
||||
title: 'Uebermassige Datenerhebung (Verstoss Datenminimierung)',
|
||||
description: 'Es werden mehr personenbezogene Daten erhoben als fuer den Verarbeitungszweck notwendig (Verstoss gegen Art. 5 Abs. 1 lit. c DSGVO).',
|
||||
impactExamples: ['Unnoetige Risikoexposition', 'Hoeherer Schaden bei Datenpanne'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['web_application', 'mobile_app', 'crm', 'hr_system'],
|
||||
mitigationIds: ['M-DMIN-01', 'M-DMIN-02', 'M-DMIN-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-TRANS-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Zugriff durch Drittland-Behoerden (FISA/CLOUD Act)',
|
||||
description: 'Behoerden eines Drittlandes (z.B. USA) greifen auf personenbezogene Daten zu, die bei einem Cloud-Provider in der EU oder im Drittland gespeichert sind.',
|
||||
impactExamples: ['Ueberwachung ohne Wissen', 'Kein Rechtsschutz', 'Schrems-II-Risiko'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K5', 'K7'],
|
||||
applicableTo: ['cloud_storage', 'email_service', 'crm', 'analytics'],
|
||||
mitigationIds: ['M-TRANS-06', 'M-TRANS-07', 'M-CONF-06'],
|
||||
},
|
||||
{
|
||||
id: 'R-TRANS-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unzureichende Schutzgarantien bei Drittlandtransfer',
|
||||
description: 'Personenbezogene Daten werden in Drittlaender uebermittelt, ohne dass angemessene Garantien (SCC, BCR, Angemessenheitsbeschluss) vorhanden sind.',
|
||||
impactExamples: ['Rechtswidriger Transfer', 'Bussgeld', 'Untersagung der Verarbeitung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5', 'K7'],
|
||||
applicableTo: ['cloud_storage', 'email_service', 'crm', 'analytics'],
|
||||
mitigationIds: ['M-TRANS-06', 'M-TRANS-07', 'M-LEGAL-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-TRANS-03',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Intransparente Sub-Auftragsverarbeiter-Kette',
|
||||
description: 'Die Kette der Sub-Auftragsverarbeiter ist nicht transparent. Betroffene und Verantwortliche wissen nicht, wo ihre Daten tatsaechlich verarbeitet werden.',
|
||||
impactExamples: ['Unkontrollierte Datenweitergabe', 'Unbekannter Verarbeitungsort'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['cloud_storage', 'crm', 'analytics'],
|
||||
mitigationIds: ['M-TRANS-01', 'M-LEGAL-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'KI-Fehlentscheidung mit erheblicher Auswirkung',
|
||||
description: 'Ein KI-System trifft eine fehlerhafte automatisierte Entscheidung (z.B. Ablehnung, Sperrung, Bewertung), die erhebliche Auswirkungen auf eine betroffene Person hat.',
|
||||
impactExamples: ['Unrechtmaessige Ablehnung', 'Falsche Risikoeinstufung', 'Benachteiligung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K2', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring', 'hr_system'],
|
||||
mitigationIds: ['M-AUTO-01', 'M-AUTO-02', 'M-AUTO-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'nichtverkettung',
|
||||
title: 'Algorithmischer Bias in Trainingsdaten',
|
||||
description: 'KI-Modelle spiegeln Vorurteile in den Trainingsdaten wider und treffen diskriminierende Entscheidungen bezueglich geschuetzter Merkmale.',
|
||||
impactExamples: ['Diskriminierung nach Geschlecht/Herkunft', 'Systematische Benachteiligung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K1', 'K2', 'K7', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring'],
|
||||
mitigationIds: ['M-AUTO-01', 'M-AUTO-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-03',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende Erklaerbarkeit automatisierter Entscheidungen',
|
||||
description: 'Automatisierte Entscheidungen koennen den Betroffenen nicht erklaert werden ("Black Box"), sodass der Anspruch auf aussagekraeftige Informationen (Art. 22 Abs. 3) nicht erfuellt wird.',
|
||||
impactExamples: ['Keine Anfechtbarkeit', 'Vertrauensverlust', 'Verstoss gegen Art. 22'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K2', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring'],
|
||||
mitigationIds: ['M-AUTO-02', 'M-TRANS-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-04',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'intervenierbarkeit',
|
||||
title: 'Fehlende menschliche Aufsicht bei KI-Entscheidungen',
|
||||
description: 'Automatisierte Entscheidungen werden ohne menschliche Ueberpruefung oder Interventionsmoeglichkeit getroffen, obwohl dies erforderlich waere.',
|
||||
impactExamples: ['Keine Korrekturmoeglichkeit', 'Eskalation von Fehlern'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K2', 'K8'],
|
||||
applicableTo: ['ai_ml', 'scoring', 'hr_system'],
|
||||
mitigationIds: ['M-AUTO-03', 'M-INTERV-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-ORG-01',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende oder fehlerhafte Auftragsverarbeitungsvertraege',
|
||||
description: 'Mit Auftragsverarbeitern existieren keine oder unzureichende Vertraege gemaess Art. 28 DSGVO, sodass Pflichten und Rechte nicht geregelt sind.',
|
||||
impactExamples: ['Keine Kontrolle ueber Verarbeiter', 'Bussgeld', 'Datenmissbrauch durch Verarbeiter'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['cloud_storage', 'crm', 'analytics', 'email_service'],
|
||||
mitigationIds: ['M-LEGAL-02', 'M-LEGAL-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-ORG-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'datenminimierung',
|
||||
title: 'Fehlende Loeschprozesse / Ueberschreitung von Aufbewahrungsfristen',
|
||||
description: 'Personenbezogene Daten werden laenger als notwendig gespeichert, weil keine automatischen Loeschprozesse oder Aufbewahrungsfristen definiert sind.',
|
||||
impactExamples: ['Unnoetige Risikoexposition', 'Verstoss gegen Speicherbegrenzung', 'Bussgeld'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['database', 'cloud_storage', 'crm', 'erp', 'email_service'],
|
||||
mitigationIds: ['M-DMIN-03', 'M-DMIN-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-ORG-04',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'transparenz',
|
||||
title: 'Fehlende Datenpannen-Erkennung und -Meldung',
|
||||
description: 'Datenpannen werden nicht rechtzeitig erkannt oder nicht innerhalb der 72-Stunden-Frist (Art. 33 DSGVO) an die Aufsichtsbehoerde gemeldet.',
|
||||
impactExamples: ['Verspaetete Meldung', 'Bussgeld', 'Verzoegerte Benachrichtigung Betroffener'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['web_application', 'cloud_storage', 'database', 'api_service'],
|
||||
mitigationIds: ['M-ORG-03', 'M-ORG-04', 'M-INT-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-SPEC-02',
|
||||
category: 'rights_freedoms',
|
||||
sdmGoal: 'intervenierbarkeit',
|
||||
title: 'Verarbeitung von Kinderdaten ohne angemessenen Schutz',
|
||||
description: 'Daten von Minderjaehrigen werden verarbeitet, ohne die besonderen Schutzmassnahmen fuer Kinder (Art. 8, EG 38 DSGVO) zu beachten.',
|
||||
impactExamples: ['Langzeitfolgen fuer Minderjaehrige', 'Einschraenkung der Entwicklung', 'Manipulation'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K7'],
|
||||
applicableTo: ['web_application', 'mobile_app', 'education'],
|
||||
mitigationIds: ['M-LEGAL-04', 'M-DMIN-01', 'M-TRANS-01'],
|
||||
},
|
||||
]
|
||||
206
admin-compliance/lib/sdk/dsfa/risks/confidentiality-integrity.ts
Normal file
206
admin-compliance/lib/sdk/dsfa/risks/confidentiality-integrity.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* DSFA Risikokatalog — Vertraulichkeit & Integritaet
|
||||
*/
|
||||
|
||||
import type { CatalogRisk } from './types'
|
||||
|
||||
export const CONFIDENTIALITY_RISKS: CatalogRisk[] = [
|
||||
{
|
||||
id: 'R-CONF-01',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unbefugte Offenlegung durch Fehlkonfiguration',
|
||||
description: 'Personenbezogene Daten werden durch fehlerhafte Systemkonfiguration (z.B. offene APIs, fehlerhafte Zugriffsrechte, oeffentliche Cloud-Speicher) unbefugt zugaenglich.',
|
||||
impactExamples: ['Identitaetsdiebstahl', 'Reputationsschaden', 'Diskriminierung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K5'],
|
||||
applicableTo: ['cloud_storage', 'web_application', 'api_service'],
|
||||
mitigationIds: ['M-CONF-01', 'M-CONF-02', 'M-CONF-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-02',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Account Takeover / Credential Stuffing',
|
||||
description: 'Angreifer uebernehmen Benutzerkonten durch gestohlene Zugangsdaten, Brute-Force-Angriffe oder Phishing und erlangen Zugriff auf personenbezogene Daten.',
|
||||
impactExamples: ['Kontrollverlust ueber eigene Daten', 'Finanzieller Schaden', 'Missbrauch der Identitaet'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K7'],
|
||||
applicableTo: ['identity', 'web_application', 'email_service'],
|
||||
mitigationIds: ['M-ACC-01', 'M-ACC-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-03',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unbefugter Zugriff durch Support-/Administrationspersonal',
|
||||
description: 'Administratoren oder Support-Mitarbeiter greifen ohne dienstliche Notwendigkeit auf personenbezogene Daten zu (Insider-Bedrohung).',
|
||||
impactExamples: ['Verletzung der Privatsphaere', 'Datenmissbrauch', 'Vertrauensverlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K4'],
|
||||
applicableTo: ['identity', 'crm', 'cloud_storage', 'support_system'],
|
||||
mitigationIds: ['M-CONF-04', 'M-CONF-05', 'M-INT-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-04',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Datenleck durch unzureichende Verschluesselung',
|
||||
description: 'Personenbezogene Daten werden bei Uebertragung oder Speicherung nicht oder unzureichend verschluesselt und koennen abgefangen werden.',
|
||||
impactExamples: ['Man-in-the-Middle-Angriff', 'Datendiebstahl bei Speichermedien-Verlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K8'],
|
||||
applicableTo: ['cloud_storage', 'email_service', 'mobile_app', 'api_service'],
|
||||
mitigationIds: ['M-CONF-06', 'M-CONF-07'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-05',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unkontrollierte Datenweitergabe an Dritte',
|
||||
description: 'Personenbezogene Daten werden ohne Rechtsgrundlage oder ueber das vereinbarte Mass hinaus an Dritte weitergegeben (z.B. durch Tracking, Analyse-Tools, Sub-Auftragsverarbeiter).',
|
||||
impactExamples: ['Unerwuenschte Werbung', 'Profiling ohne Wissen', 'Kontrollverlust'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K1', 'K6'],
|
||||
applicableTo: ['web_application', 'analytics', 'marketing', 'crm'],
|
||||
mitigationIds: ['M-NONL-01', 'M-TRANS-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-06',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Social Engineering / Phishing gegen Betroffene',
|
||||
description: 'Betroffene werden durch manipulative Kommunikation dazu verleitet, personenbezogene Daten preiszugeben oder Zugriff zu gewaehren.',
|
||||
impactExamples: ['Identitaetsdiebstahl', 'Finanzieller Schaden', 'Uebernahme von Konten'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K7'],
|
||||
applicableTo: ['email_service', 'web_application', 'identity'],
|
||||
mitigationIds: ['M-ACC-01', 'M-ORG-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-CONF-07',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Unbeabsichtigte Offenlegung in Logs/Debugging',
|
||||
description: 'Personenbezogene Daten gelangen in Protokolldateien, Fehlermeldungen oder Debug-Ausgaben und werden dort nicht geschuetzt.',
|
||||
impactExamples: ['Zugriff durch Unbefugte auf Logdaten', 'Langzeitspeicherung ohne Rechtsgrundlage'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K4'],
|
||||
applicableTo: ['api_service', 'web_application', 'cloud_storage'],
|
||||
mitigationIds: ['M-CONF-08', 'M-DMIN-01'],
|
||||
},
|
||||
{
|
||||
id: 'R-SPEC-01',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Kompromittierung besonderer Datenkategorien (Art. 9)',
|
||||
description: 'Besonders schutzwuerdige Daten (Gesundheit, Religion, Biometrie, Gewerkschaftszugehoerigkeit) werden offengelegt oder missbraucht.',
|
||||
impactExamples: ['Schwerwiegende Diskriminierung', 'Existenzielle Bedrohung', 'Soziale Ausgrenzung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K7'],
|
||||
applicableTo: ['hr_system', 'health_system', 'identity'],
|
||||
mitigationIds: ['M-CONF-06', 'M-CONF-01', 'M-CONF-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-AUTO-05',
|
||||
category: 'confidentiality',
|
||||
sdmGoal: 'vertraulichkeit',
|
||||
title: 'Datenleck durch KI-Training mit personenbezogenen Daten',
|
||||
description: 'Personenbezogene Daten, die fuer das Training von KI-Modellen verwendet werden, koennen durch das Modell reproduziert oder extrahiert werden (Model Inversion, Membership Inference).',
|
||||
impactExamples: ['Offenlegung von Trainingsdaten', 'Re-Identifizierung'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K8'],
|
||||
applicableTo: ['ai_ml'],
|
||||
mitigationIds: ['M-CONF-06', 'M-NONL-03', 'M-AUTO-04'],
|
||||
},
|
||||
]
|
||||
|
||||
export const INTEGRITY_RISKS: CatalogRisk[] = [
|
||||
{
|
||||
id: 'R-INT-01',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Datenmanipulation durch externen Angriff',
|
||||
description: 'Personenbezogene Daten werden durch einen Cyberangriff (SQL-Injection, API-Manipulation) veraendert, ohne dass dies erkannt wird.',
|
||||
impactExamples: ['Falsche Entscheidungen auf Basis manipulierter Daten', 'Rufschaedigung'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4', 'K8'],
|
||||
applicableTo: ['api_service', 'web_application', 'database'],
|
||||
mitigationIds: ['M-INT-01', 'M-INT-02', 'M-INT-03'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-02',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Fehlerhafte Synchronisation zwischen Systemen',
|
||||
description: 'Bei der Synchronisation personenbezogener Daten zwischen verschiedenen Systemen kommt es zu Inkonsistenzen, Duplikaten oder Datenverlust.',
|
||||
impactExamples: ['Falsche Kontaktdaten', 'Doppelte Verarbeitung', 'Falsche Auskuenfte'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K6'],
|
||||
applicableTo: ['crm', 'cloud_storage', 'erp', 'identity'],
|
||||
mitigationIds: ['M-INT-04', 'M-INT-05'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-03',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Backup-Korruption oder fehlerhafte Wiederherstellung',
|
||||
description: 'Backups personenbezogener Daten sind beschaedigt, unvollstaendig oder veraltet, sodass eine zuverlaessige Wiederherstellung nicht moeglich ist.',
|
||||
impactExamples: ['Datenverlust bei Wiederherstellung', 'Veraltete Datenbasis', 'Compliance-Verstoss'],
|
||||
typicalLikelihood: 'low',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K5'],
|
||||
applicableTo: ['database', 'cloud_storage', 'erp'],
|
||||
mitigationIds: ['M-AVAIL-01', 'M-AVAIL-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-04',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Unbemerkte Aenderung von Zugriffsrechten',
|
||||
description: 'Zugriffsberechtigungen werden unbefugt oder fehlerhaft geaendert, wodurch unberechtigte Personen Zugang zu personenbezogenen Daten erhalten.',
|
||||
impactExamples: ['Privilege Escalation', 'Unbefugter Datenzugriff'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'high',
|
||||
wp248Criteria: ['K4'],
|
||||
applicableTo: ['identity', 'cloud_storage', 'api_service'],
|
||||
mitigationIds: ['M-INT-02', 'M-CONF-04'],
|
||||
},
|
||||
{
|
||||
id: 'R-INT-05',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Fehlende Nachvollziehbarkeit von Datenveraenderungen',
|
||||
description: 'Aenderungen an personenbezogenen Daten werden nicht protokolliert, sodass Manipulationen oder Fehler nicht erkannt oder nachvollzogen werden koennen.',
|
||||
impactExamples: ['Unmoeglich festzustellen wer/wann Daten geaendert hat', 'Audit-Versagen'],
|
||||
typicalLikelihood: 'medium',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K3'],
|
||||
applicableTo: ['database', 'crm', 'erp', 'web_application'],
|
||||
mitigationIds: ['M-INT-02', 'M-TRANS-02'],
|
||||
},
|
||||
{
|
||||
id: 'R-ORG-03',
|
||||
category: 'integrity',
|
||||
sdmGoal: 'integritaet',
|
||||
title: 'Unzureichende Schulung/Sensibilisierung der Mitarbeiter',
|
||||
description: 'Mitarbeiter sind nicht ausreichend im Umgang mit personenbezogenen Daten geschult und verursachen durch Unkenntnis Datenpannen oder Verarbeitungsfehler.',
|
||||
impactExamples: ['Versehentliche Datenweitergabe', 'Phishing-Erfolg', 'Fehlerhafte Verarbeitung'],
|
||||
typicalLikelihood: 'high',
|
||||
typicalImpact: 'medium',
|
||||
wp248Criteria: ['K5', 'K7'],
|
||||
applicableTo: ['hr_system', 'email_service', 'crm', 'web_application'],
|
||||
mitigationIds: ['M-ORG-01', 'M-ORG-02'],
|
||||
},
|
||||
]
|
||||
64
admin-compliance/lib/sdk/dsfa/risks/helpers.ts
Normal file
64
admin-compliance/lib/sdk/dsfa/risks/helpers.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* DSFA Risikokatalog — Helpers & Assembled Catalog
|
||||
*/
|
||||
|
||||
import type { DSFARiskCategory, SDMGoal } from '../types'
|
||||
import type { CatalogRisk } from './types'
|
||||
import { CONFIDENTIALITY_RISKS, INTEGRITY_RISKS } from './confidentiality-integrity'
|
||||
import { AVAILABILITY_RISKS, RIGHTS_FREEDOMS_RISKS } from './availability-rights-auto-org'
|
||||
|
||||
export const RISK_CATALOG: CatalogRisk[] = [
|
||||
...CONFIDENTIALITY_RISKS,
|
||||
...INTEGRITY_RISKS,
|
||||
...AVAILABILITY_RISKS,
|
||||
...RIGHTS_FREEDOMS_RISKS,
|
||||
]
|
||||
|
||||
export function getRisksByCategory(category: DSFARiskCategory): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.category === category)
|
||||
}
|
||||
|
||||
export function getRisksBySDMGoal(goal: SDMGoal): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.sdmGoal === goal)
|
||||
}
|
||||
|
||||
export function getRisksByWP248Criterion(criterionCode: string): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.wp248Criteria.includes(criterionCode))
|
||||
}
|
||||
|
||||
export function getRisksByComponent(component: string): CatalogRisk[] {
|
||||
return RISK_CATALOG.filter(r => r.applicableTo.includes(component))
|
||||
}
|
||||
|
||||
export function getCatalogRiskById(id: string): CatalogRisk | undefined {
|
||||
return RISK_CATALOG.find(r => r.id === id)
|
||||
}
|
||||
|
||||
export const RISK_CATEGORY_LABELS: Record<DSFARiskCategory, string> = {
|
||||
confidentiality: 'Vertraulichkeit',
|
||||
integrity: 'Integritaet',
|
||||
availability: 'Verfuegbarkeit',
|
||||
rights_freedoms: 'Rechte & Freiheiten',
|
||||
}
|
||||
|
||||
export const COMPONENT_FAMILY_LABELS: Record<string, string> = {
|
||||
identity: 'Identitaet & Zugang',
|
||||
cloud_storage: 'Cloud-Speicher',
|
||||
web_application: 'Web-Anwendung',
|
||||
api_service: 'API-Service',
|
||||
email_service: 'E-Mail-Dienst',
|
||||
mobile_app: 'Mobile App',
|
||||
database: 'Datenbank',
|
||||
crm: 'CRM-System',
|
||||
erp: 'ERP-System',
|
||||
analytics: 'Analyse/Tracking',
|
||||
marketing: 'Marketing',
|
||||
ai_ml: 'KI / Machine Learning',
|
||||
scoring: 'Scoring / Bewertung',
|
||||
hr_system: 'HR-System',
|
||||
health_system: 'Gesundheitssystem',
|
||||
monitoring: 'Ueberwachungssystem',
|
||||
support_system: 'Support-System',
|
||||
education: 'Bildungsplattform',
|
||||
research: 'Forschung',
|
||||
}
|
||||
19
admin-compliance/lib/sdk/dsfa/risks/types.ts
Normal file
19
admin-compliance/lib/sdk/dsfa/risks/types.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* DSFA Risikokatalog — Types
|
||||
*/
|
||||
|
||||
import type { DSFARiskCategory, SDMGoal } from '../types'
|
||||
|
||||
export interface CatalogRisk {
|
||||
id: string
|
||||
category: DSFARiskCategory
|
||||
sdmGoal: SDMGoal
|
||||
title: string
|
||||
description: string
|
||||
impactExamples: string[]
|
||||
typicalLikelihood: 'low' | 'medium' | 'high'
|
||||
typicalImpact: 'low' | 'medium' | 'high'
|
||||
wp248Criteria: string[]
|
||||
applicableTo: string[]
|
||||
mitigationIds: string[]
|
||||
}
|
||||
@@ -1,760 +1,13 @@
|
||||
/**
|
||||
* Loeschfristen Baseline-Katalog
|
||||
* Loeschfristen Baseline-Katalog — Barrel
|
||||
*
|
||||
* 25 vordefinierte Aufbewahrungsfristen-Templates fuer gaengige
|
||||
* Datenobjekte in deutschen Unternehmen. Basierend auf AO, HGB,
|
||||
* UStG, BGB, ArbZG, AGG, BDSG, BSIG und ArbMedVV.
|
||||
*
|
||||
* Werden genutzt, um neue Loeschfrist-Policies schnell aus
|
||||
* bewaehrten Vorlagen zu erstellen.
|
||||
* Datenobjekte. Split into domain modules; re-exported here for backward compatibility.
|
||||
*/
|
||||
|
||||
import type {
|
||||
LoeschfristPolicy,
|
||||
RetentionDriverType,
|
||||
DeletionMethodType,
|
||||
StorageLocation,
|
||||
PolicyStatus,
|
||||
ReviewInterval,
|
||||
RetentionUnit,
|
||||
DeletionTriggerLevel,
|
||||
} from './loeschfristen-types'
|
||||
|
||||
import { createEmptyPolicy } from './loeschfristen-types'
|
||||
|
||||
// =============================================================================
|
||||
// BASELINE TEMPLATE INTERFACE
|
||||
// =============================================================================
|
||||
|
||||
export interface BaselineTemplate {
|
||||
templateId: string
|
||||
dataObjectName: string
|
||||
description: string
|
||||
affectedGroups: string[]
|
||||
dataCategories: string[]
|
||||
primaryPurpose: string
|
||||
deletionTrigger: DeletionTriggerLevel
|
||||
retentionDriver: RetentionDriverType | null
|
||||
retentionDriverDetail: string
|
||||
retentionDuration: number | null
|
||||
retentionUnit: RetentionUnit | null
|
||||
retentionDescription: string
|
||||
startEvent: string
|
||||
deletionMethod: DeletionMethodType
|
||||
deletionMethodDetail: string
|
||||
responsibleRole: string
|
||||
reviewInterval: ReviewInterval
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// BASELINE TEMPLATES (25 Vorlagen)
|
||||
// =============================================================================
|
||||
|
||||
export const BASELINE_TEMPLATES: BaselineTemplate[] = [
|
||||
// ==================== 1. Personalakten ====================
|
||||
{
|
||||
templateId: 'personal-akten',
|
||||
dataObjectName: 'Personalakten',
|
||||
description:
|
||||
'Vollstaendige Personalakten inkl. Arbeitsvertraege, Zeugnisse, Abmahnungen und sonstige beschaeftigungsrelevante Dokumente.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Stammdaten', 'Vertragsdaten', 'Gehaltsdaten', 'Zeugnisse'],
|
||||
primaryPurpose:
|
||||
'Dokumentation und Nachweisfuehrung des Beschaeftigungsverhaeltnisses sowie Erfuellung steuerrechtlicher Aufbewahrungspflichten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AO_147',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 147 AO fuer steuerlich relevante Unterlagen der Personalakte.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende des Beschaeftigungsverhaeltnisses',
|
||||
startEvent: 'Ende des Beschaeftigungsverhaeltnisses',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung aller digitalen Personalakten-Dokumente nach Ablauf der Aufbewahrungsfrist. Papierakten werden datenschutzkonform vernichtet.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'steuer'],
|
||||
},
|
||||
|
||||
// ==================== 2. Buchhaltungsbelege ====================
|
||||
{
|
||||
templateId: 'buchhaltungsbelege',
|
||||
dataObjectName: 'Buchhaltungsbelege',
|
||||
description:
|
||||
'Buchungsbelege, Kontoauszuege, Kassenbuecher und sonstige Belege der laufenden Buchhaltung.',
|
||||
affectedGroups: ['Kunden', 'Lieferanten'],
|
||||
dataCategories: ['Finanzdaten', 'Transaktionsdaten', 'Kontodaten'],
|
||||
primaryPurpose:
|
||||
'Ordnungsgemaesse Buchfuehrung und Erfuellung handelsrechtlicher Aufbewahrungspflichten nach HGB.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer Handelsbuecher und Buchungsbelege.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende des Geschaeftsjahres',
|
||||
startEvent: 'Ende des Geschaeftsjahres',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die Buchhaltung vor Loeschung, um sicherzustellen, dass keine laufenden Pruefungen oder Rechtsstreitigkeiten bestehen.',
|
||||
responsibleRole: 'Buchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['finanzen', 'hgb'],
|
||||
},
|
||||
|
||||
// ==================== 3. Rechnungen ====================
|
||||
{
|
||||
templateId: 'rechnungen',
|
||||
dataObjectName: 'Rechnungen',
|
||||
description:
|
||||
'Eingangs- und Ausgangsrechnungen inkl. Rechnungsanhaenge und rechnungsbegruendende Unterlagen.',
|
||||
affectedGroups: ['Kunden', 'Lieferanten'],
|
||||
dataCategories: ['Rechnungsdaten', 'Umsatzsteuerdaten', 'Adressdaten'],
|
||||
primaryPurpose:
|
||||
'Dokumentation umsatzsteuerrelevanter Vorgaenge und Erfuellung der Aufbewahrungspflicht nach UStG.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'USTG_14B',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 14b UStG fuer Rechnungen und rechnungsbegruendende Unterlagen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre ab Rechnungsdatum',
|
||||
startEvent: 'Rechnungsdatum',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung nach Ablauf der 10-Jahres-Frist. Vor Loeschung wird geprueft, ob Rechnungen in laufenden Betriebspruefungen benoetigt werden.',
|
||||
responsibleRole: 'Buchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['finanzen', 'ustg'],
|
||||
},
|
||||
|
||||
// ==================== 4. Geschaeftsbriefe ====================
|
||||
{
|
||||
templateId: 'geschaeftsbriefe',
|
||||
dataObjectName: 'Geschaeftsbriefe',
|
||||
description:
|
||||
'Empfangene und versandte Handelsbriefe, Geschaeftskorrespondenz und geschaeftsrelevante E-Mails.',
|
||||
affectedGroups: ['Kunden', 'Lieferanten'],
|
||||
dataCategories: ['Korrespondenz', 'Vertragskommunikation', 'Angebote'],
|
||||
primaryPurpose:
|
||||
'Nachweisfuehrung geschaeftlicher Kommunikation und Erfuellung der handelsrechtlichen Aufbewahrungspflicht fuer Handelsbriefe.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer empfangene und versandte Handelsbriefe (6 Jahre).',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '6 Jahre ab Eingang oder Versand des Geschaeftsbriefes',
|
||||
startEvent: 'Eingang bzw. Versand des Geschaeftsbriefes',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die Geschaeftsleitung, da Geschaeftsbriefe ggf. als Beweismittel in Rechtsstreitigkeiten dienen koennen.',
|
||||
responsibleRole: 'Geschaeftsleitung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['kommunikation', 'hgb'],
|
||||
},
|
||||
|
||||
// ==================== 5. Bewerbungsunterlagen ====================
|
||||
{
|
||||
templateId: 'bewerbungsunterlagen',
|
||||
dataObjectName: 'Bewerbungsunterlagen',
|
||||
description:
|
||||
'Eingereichte Bewerbungsunterlagen inkl. Anschreiben, Lebenslauf, Zeugnisse und Korrespondenz mit Bewerbern.',
|
||||
affectedGroups: ['Bewerber'],
|
||||
dataCategories: ['Bewerbungsdaten', 'Qualifikationen', 'Kontaktdaten'],
|
||||
primaryPurpose:
|
||||
'Durchfuehrung des Bewerbungsverfahrens und Absicherung gegen Entschaedigungsansprueche nach dem AGG.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AGG_15',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer 6 Monate nach Absage gemaess 15 Abs. 4 AGG (Frist fuer Geltendmachung von Entschaedigungsanspruechen).',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'MONTHS',
|
||||
retentionDescription: '6 Monate nach Absage oder Stellenbesetzung',
|
||||
startEvent: 'Absage oder endgueltige Stellenbesetzung',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung aller Bewerbungsunterlagen und zugehoeriger Kommunikation nach Ablauf der 6-Monats-Frist.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['hr', 'bewerbung'],
|
||||
},
|
||||
|
||||
// ==================== 6. Kundenstammdaten ====================
|
||||
{
|
||||
templateId: 'kundenstammdaten',
|
||||
dataObjectName: 'Kundenstammdaten',
|
||||
description:
|
||||
'Stammdaten von Kunden inkl. Kontaktdaten, Anschrift, Kundennummer und Kommunikationspraeferenzen.',
|
||||
affectedGroups: ['Kunden'],
|
||||
dataCategories: ['Stammdaten', 'Kontaktdaten', 'Adressdaten'],
|
||||
primaryPurpose:
|
||||
'Pflege der Kundenbeziehung, Vertragserfuellung und Absicherung gegen Verjaehrung vertraglicher Ansprueche.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB (3 Jahre).',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach letzter geschaeftlicher Interaktion',
|
||||
startEvent: 'Letzte geschaeftliche Interaktion mit dem Kunden',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch den Vertrieb vor Loeschung, um sicherzustellen, dass keine aktiven Geschaeftsbeziehungen oder offenen Forderungen bestehen.',
|
||||
responsibleRole: 'Vertrieb',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['crm', 'kunden'],
|
||||
},
|
||||
|
||||
// ==================== 7. Newsletter-Einwilligungen ====================
|
||||
{
|
||||
templateId: 'newsletter-einwilligungen',
|
||||
dataObjectName: 'Newsletter-Einwilligungen',
|
||||
description:
|
||||
'Einwilligungserklaerungen fuer den Newsletter-Versand inkl. Double-Opt-in-Nachweis und Abmeldezeitpunkt.',
|
||||
affectedGroups: ['Abonnenten'],
|
||||
dataCategories: ['Einwilligungsdaten', 'E-Mail-Adresse', 'Opt-in-Nachweis'],
|
||||
primaryPurpose:
|
||||
'Nachweis der wirksamen Einwilligung zum Newsletter-Versand gemaess Art. 7 DSGVO und Dokumentation des Widerrufs.',
|
||||
deletionTrigger: 'PURPOSE_END',
|
||||
retentionDriver: null,
|
||||
retentionDriverDetail:
|
||||
'Keine gesetzliche Aufbewahrungspflicht. Daten werden bis zum Widerruf der Einwilligung gespeichert.',
|
||||
retentionDuration: null,
|
||||
retentionUnit: null,
|
||||
retentionDescription: 'Bis zum Widerruf der Einwilligung durch den Abonnenten',
|
||||
startEvent: 'Widerruf der Einwilligung durch den Abonnenten',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der personenbezogenen Daten nach Eingang des Widerrufs. Der Einwilligungsnachweis selbst wird fuer die Dauer der Nachweispflicht aufbewahrt.',
|
||||
responsibleRole: 'Marketing',
|
||||
reviewInterval: 'SEMI_ANNUAL',
|
||||
tags: ['marketing', 'einwilligung'],
|
||||
},
|
||||
|
||||
// ==================== 8. Webserver-Logs ====================
|
||||
{
|
||||
templateId: 'webserver-logs',
|
||||
dataObjectName: 'Webserver-Logs',
|
||||
description:
|
||||
'Server-Zugriffsprotokolle inkl. IP-Adressen, Zeitstempel, aufgerufene URLs und HTTP-Statuscodes.',
|
||||
affectedGroups: ['Website-Besucher'],
|
||||
dataCategories: ['IP-Adressen', 'Zugriffszeitpunkte', 'User-Agent-Daten'],
|
||||
primaryPurpose:
|
||||
'Sicherstellung der IT-Sicherheit, Erkennung von Angriffen und Stoerungen sowie Erfuellung der Protokollierungspflicht nach BSIG.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BSIG',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung gemaess BSI-Gesetz / IT-Sicherheitsgesetz 2.0 fuer die Analyse von Sicherheitsvorfaellen.',
|
||||
retentionDuration: 7,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '7 Tage nach Zeitpunkt des Zugriffs',
|
||||
startEvent: 'Zeitpunkt des Server-Zugriffs',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Rotation und Loeschung der Logdateien nach 7 Tagen durch den Webserver (logrotate).',
|
||||
responsibleRole: 'IT-Abteilung',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['it', 'logs'],
|
||||
},
|
||||
|
||||
// ==================== 9. Videoueberwachung ====================
|
||||
{
|
||||
templateId: 'videoueberwachung',
|
||||
dataObjectName: 'Videoueberwachung',
|
||||
description:
|
||||
'Aufnahmen der Videoueberwachung in Geschaeftsraeumen, Eingangsbereichen und Parkplaetzen.',
|
||||
affectedGroups: ['Besucher', 'Mitarbeiter'],
|
||||
dataCategories: ['Videodaten', 'Bilddaten', 'Zeitstempel'],
|
||||
primaryPurpose:
|
||||
'Schutz des Eigentums und der Sicherheit von Personen sowie Aufklaerung von Vorfaellen in den ueberwachten Bereichen.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BDSG_35',
|
||||
retentionDriverDetail:
|
||||
'Unverzuegliche Loeschung nach Zweckwegfall gemaess 35 BDSG bzw. Art. 17 DSGVO. Maximale Speicherdauer 48 Stunden.',
|
||||
retentionDuration: 2,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '48 Stunden (2 Tage) nach Aufnahmezeitpunkt',
|
||||
startEvent: 'Aufnahmezeitpunkt der Videosequenz',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatisches Ueberschreiben der Aufnahmen durch das Videomanagementsystem nach Ablauf der 48-Stunden-Frist.',
|
||||
responsibleRole: 'Facility Management',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['sicherheit', 'video'],
|
||||
},
|
||||
|
||||
// ==================== 10. Gehaltsabrechnungen ====================
|
||||
{
|
||||
templateId: 'gehaltsabrechnungen',
|
||||
dataObjectName: 'Gehaltsabrechnungen',
|
||||
description:
|
||||
'Monatliche Gehaltsabrechnungen, Lohnsteuerbescheinigungen und Sozialversicherungsmeldungen.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Gehaltsdaten', 'Steuerdaten', 'Sozialversicherungsdaten'],
|
||||
primaryPurpose:
|
||||
'Dokumentation der Lohn- und Gehaltszahlungen sowie Erfuellung steuerrechtlicher und sozialversicherungsrechtlicher Aufbewahrungspflichten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AO_147',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 147 AO fuer lohnsteuerrelevante Unterlagen und Gehaltsbuchungen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende des Geschaeftsjahres',
|
||||
startEvent: 'Ende des Geschaeftsjahres der jeweiligen Abrechnung',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der digitalen Gehaltsabrechnungen nach Ablauf der Aufbewahrungsfrist. Papierbelege werden datenschutzkonform vernichtet.',
|
||||
responsibleRole: 'Lohnbuchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'steuer'],
|
||||
},
|
||||
|
||||
// ==================== 11. Vertraege ====================
|
||||
{
|
||||
templateId: 'vertraege',
|
||||
dataObjectName: 'Vertraege',
|
||||
description:
|
||||
'Geschaeftsvertraege, Rahmenvereinbarungen, Dienstleistungsvertraege und zugehoerige Anlagen und Nachtraege.',
|
||||
affectedGroups: ['Vertragspartner'],
|
||||
dataCategories: ['Vertragsdaten', 'Kontaktdaten', 'Konditionen'],
|
||||
primaryPurpose:
|
||||
'Dokumentation vertraglicher Vereinbarungen und Sicherung von Beweismitteln fuer die Dauer moeglicher Rechtsstreitigkeiten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer handelsrechtlich relevante Vertragsunterlagen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende der Vertragslaufzeit',
|
||||
startEvent: 'Ende der Vertragslaufzeit bzw. Vertragsbeendigung',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die Rechtsabteilung vor Loeschung, um sicherzustellen, dass keine laufenden oder angedrohten Rechtsstreitigkeiten bestehen.',
|
||||
responsibleRole: 'Rechtsabteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['recht', 'vertraege'],
|
||||
},
|
||||
|
||||
// ==================== 12. Zeiterfassungsdaten ====================
|
||||
{
|
||||
templateId: 'zeiterfassung',
|
||||
dataObjectName: 'Zeiterfassungsdaten',
|
||||
description:
|
||||
'Arbeitszeitaufzeichnungen inkl. Beginn, Ende, Pausen und Ueberstunden der Beschaeftigten.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Arbeitszeiten', 'Pausenzeiten', 'Ueberstunden'],
|
||||
primaryPurpose:
|
||||
'Erfuellung der gesetzlichen Aufzeichnungspflicht fuer Arbeitszeiten und Nachweis der Einhaltung des Arbeitszeitgesetzes.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'ARBZG_16',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 16 Abs. 2 ArbZG fuer Aufzeichnungen ueber die Arbeitszeit.',
|
||||
retentionDuration: 2,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '2 Jahre nach Ende des Erfassungszeitraums',
|
||||
startEvent: 'Ende des jeweiligen Erfassungszeitraums',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der Zeiterfassungsdaten nach Ablauf der 2-Jahres-Frist im Zeiterfassungssystem.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'arbzg'],
|
||||
},
|
||||
|
||||
// ==================== 13. Krankmeldungen ====================
|
||||
{
|
||||
templateId: 'krankmeldungen',
|
||||
dataObjectName: 'Krankmeldungen',
|
||||
description:
|
||||
'Arbeitsunfaehigkeitsbescheinigungen, Krankmeldungen und zugehoerige Abwesenheitsdokumentationen.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Gesundheitsdaten', 'Abwesenheitszeiten', 'AU-Bescheinigungen'],
|
||||
primaryPurpose:
|
||||
'Dokumentation von Fehlzeiten, Entgeltfortzahlung im Krankheitsfall und Absicherung gegen Verjaehrung arbeitsrechtlicher Ansprueche.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB zur Absicherung von Erstattungsanspruechen.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Ende des Beschaeftigungsverhaeltnisses',
|
||||
startEvent: 'Ende des Beschaeftigungsverhaeltnisses',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die HR-Abteilung vor Loeschung, da Krankmeldungen besondere Kategorien personenbezogener Daten (Gesundheitsdaten) enthalten.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'gesundheit'],
|
||||
},
|
||||
|
||||
// ==================== 14. Steuererklaerungen ====================
|
||||
{
|
||||
templateId: 'steuererklaerungen',
|
||||
dataObjectName: 'Steuererklaerungen',
|
||||
description:
|
||||
'Koerperschaftsteuer-, Gewerbesteuer- und Umsatzsteuererklaerungen inkl. Anlagen und Bescheide.',
|
||||
affectedGroups: ['Unternehmen'],
|
||||
dataCategories: ['Steuerdaten', 'Finanzkennzahlen', 'Bescheide'],
|
||||
primaryPurpose:
|
||||
'Erfuellung steuerrechtlicher Dokumentationspflichten und Nachweisfuehrung gegenueber den Finanzbehoerden.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AO_147',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 147 AO fuer Steuererklaerungen und zugehoerige Unterlagen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre ab dem jeweiligen Steuerjahr',
|
||||
startEvent: 'Ende des betreffenden Steuerjahres',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung nach Ablauf der 10-Jahres-Frist, sofern keine laufende Betriebspruefung oder Einspruchsverfahren vorliegen.',
|
||||
responsibleRole: 'Steuerberater/Buchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['finanzen', 'steuer'],
|
||||
},
|
||||
|
||||
// ==================== 15. Gesellschafterprotokolle ====================
|
||||
{
|
||||
templateId: 'protokolle-gesellschafter',
|
||||
dataObjectName: 'Gesellschafterprotokolle',
|
||||
description:
|
||||
'Protokolle der Gesellschafterversammlungen, Beschluesse, Abstimmungsergebnisse und notarielle Urkunden.',
|
||||
affectedGroups: ['Gesellschafter'],
|
||||
dataCategories: ['Beschlussdaten', 'Abstimmungsergebnisse', 'Protokolle'],
|
||||
primaryPurpose:
|
||||
'Dokumentation gesellschaftsrechtlicher Beschluesse und Erfuellung handelsrechtlicher Aufbewahrungspflichten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer Eroeffnungsbilanzen, Jahresabschluesse und zugehoerige Beschluesse.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre ab Beschlussdatum',
|
||||
startEvent: 'Datum des jeweiligen Gesellschafterbeschlusses',
|
||||
deletionMethod: 'PHYSICAL_DESTROY',
|
||||
deletionMethodDetail:
|
||||
'Physische Vernichtung der Papieroriginale durch zertifizierten Aktenvernichtungsdienstleister (DIN 66399, Sicherheitsstufe P-4). Digitale Kopien werden parallel geloescht.',
|
||||
responsibleRole: 'Geschaeftsleitung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['recht', 'gesellschaft'],
|
||||
},
|
||||
|
||||
// ==================== 16. CRM-Kontakthistorie ====================
|
||||
{
|
||||
templateId: 'crm-kontakthistorie',
|
||||
dataObjectName: 'CRM-Kontakthistorie',
|
||||
description:
|
||||
'Kontaktverlauf im CRM-System inkl. Anrufe, E-Mails, Termine, Notizen und Angebotsverlauf.',
|
||||
affectedGroups: ['Kunden', 'Interessenten'],
|
||||
dataCategories: ['Kommunikationsdaten', 'Interaktionshistorie', 'Angebotsdaten'],
|
||||
primaryPurpose:
|
||||
'Pflege der Kundenbeziehung und Nachverfolgung geschaeftlicher Interaktionen fuer Vertriebs- und Servicezwecke.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB zur Absicherung vertraglicher Ansprueche.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach letztem Kontakt mit dem Kunden oder Interessenten',
|
||||
startEvent: 'Letzter dokumentierter Kontakt im CRM-System',
|
||||
deletionMethod: 'ANONYMIZATION',
|
||||
deletionMethodDetail:
|
||||
'Anonymisierung der personenbezogenen Daten im CRM-System, sodass statistische Auswertungen weiterhin moeglich sind, aber kein Personenbezug mehr hergestellt werden kann.',
|
||||
responsibleRole: 'Vertrieb',
|
||||
reviewInterval: 'SEMI_ANNUAL',
|
||||
tags: ['crm', 'kunden'],
|
||||
},
|
||||
|
||||
// ==================== 17. Backup-Daten ====================
|
||||
{
|
||||
templateId: 'backup-daten',
|
||||
dataObjectName: 'Backup-Daten',
|
||||
description:
|
||||
'Vollstaendige und inkrementelle Sicherungskopien aller Systeme, Datenbanken und Dateisysteme.',
|
||||
affectedGroups: ['Alle Betroffenengruppen'],
|
||||
dataCategories: ['Systemsicherungen', 'Datenbankkopien', 'Dateisystemsicherungen'],
|
||||
primaryPurpose:
|
||||
'Sicherstellung der Datenwiederherstellung im Katastrophenfall und Gewaehrleistung der Geschaeftskontinuitaet.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BSIG',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung von Backups fuer 90 Tage gemaess BSI-Grundschutz-Empfehlungen zur Sicherstellung der Wiederherstellbarkeit.',
|
||||
retentionDuration: 90,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '90 Tage nach Erstellung des Backups',
|
||||
startEvent: 'Erstellungsdatum des jeweiligen Backups',
|
||||
deletionMethod: 'CRYPTO_ERASE',
|
||||
deletionMethodDetail:
|
||||
'Kryptographische Loeschung durch Vernichtung der Verschluesselungsschluessel, sodass die verschluesselten Backup-Daten nicht mehr entschluesselt werden koennen.',
|
||||
responsibleRole: 'IT-Abteilung',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['it', 'backup'],
|
||||
},
|
||||
|
||||
// ==================== 18. Cookie-Consent-Nachweise ====================
|
||||
{
|
||||
templateId: 'cookie-consent-logs',
|
||||
dataObjectName: 'Cookie-Consent-Nachweise',
|
||||
description:
|
||||
'Nachweise ueber Cookie-Einwilligungen der Website-Besucher inkl. Consent-ID, Zeitstempel, gesetzte Praeferenzen und IP-Adresse.',
|
||||
affectedGroups: ['Website-Besucher'],
|
||||
dataCategories: ['Consent-Daten', 'IP-Adressen', 'Zeitstempel', 'Praeferenzen'],
|
||||
primaryPurpose:
|
||||
'Nachweisfuehrung der Einwilligung in die Cookie-Nutzung gemaess Art. 7 Abs. 1 DSGVO und ePrivacy-Richtlinie.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung der Consent-Nachweise fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB zur Absicherung gegen Abmahnungen.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Zeitpunkt der Einwilligung',
|
||||
startEvent: 'Zeitpunkt der Cookie-Einwilligung (Consent-Zeitstempel)',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der Consent-Nachweise nach Ablauf der 3-Jahres-Frist durch das Consent-Management-System.',
|
||||
responsibleRole: 'Datenschutzbeauftragter',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['datenschutz', 'consent'],
|
||||
},
|
||||
|
||||
// ==================== 19. E-Mail-Archivierung ====================
|
||||
{
|
||||
templateId: 'email-archivierung',
|
||||
dataObjectName: 'E-Mail-Archivierung',
|
||||
description:
|
||||
'Archivierte geschaeftliche E-Mails inkl. Anhaenge, die als Handelsbriefe oder steuerrelevante Korrespondenz einzustufen sind.',
|
||||
affectedGroups: ['Mitarbeiter', 'Kunden', 'Lieferanten'],
|
||||
dataCategories: ['E-Mail-Korrespondenz', 'Anhaenge', 'Metadaten'],
|
||||
primaryPurpose:
|
||||
'Erfuellung der handelsrechtlichen Aufbewahrungspflicht fuer geschaeftliche Korrespondenz, die als Handelsbrief einzuordnen ist.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer empfangene und versandte Handelsbriefe (6 Jahre) bzw. buchhalterisch relevante E-Mails (10 Jahre).',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '6 Jahre nach Versand/Empfang der E-Mail',
|
||||
startEvent: 'Versand- bzw. Empfangsdatum der E-Mail',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung durch das E-Mail-Archivierungssystem nach Ablauf der konfigurierten Aufbewahrungsfrist. Vor Loeschung wird geprueft, ob die E-Mail in laufenden Verfahren benoetigt wird.',
|
||||
responsibleRole: 'IT-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['kommunikation', 'hgb'],
|
||||
},
|
||||
|
||||
// ==================== 20. Zutrittsprotokolle ====================
|
||||
{
|
||||
templateId: 'zutrittsprotokolle',
|
||||
dataObjectName: 'Zutrittsprotokolle',
|
||||
description:
|
||||
'Protokolle des Zutrittskontrollsystems inkl. Zeitstempel, Kartennummer, Zutrittsort und Zugangsentscheidung (gewaehrt/verweigert).',
|
||||
affectedGroups: ['Mitarbeiter', 'Besucher'],
|
||||
dataCategories: ['Zutrittsdaten', 'Zeitstempel', 'Kartennummern', 'Standortdaten'],
|
||||
primaryPurpose:
|
||||
'Sicherstellung der physischen Sicherheit, Nachvollziehbarkeit von Zutritten und Unterstuetzung bei der Aufklaerung von Sicherheitsvorfaellen.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BSIG',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung gemaess BSI-Grundschutz-Empfehlung fuer Zutrittsprotokolle zur Analyse von Sicherheitsvorfaellen (90 Tage).',
|
||||
retentionDuration: 90,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '90 Tage nach Zeitpunkt des Zutritts',
|
||||
startEvent: 'Zeitpunkt des protokollierten Zutrittsereignisses',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Rotation und Loeschung der Zutrittsprotokolle durch das Zutrittskontrollsystem nach Ablauf der 90-Tage-Frist.',
|
||||
responsibleRole: 'Facility Management',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['sicherheit', 'zutritt'],
|
||||
},
|
||||
|
||||
// ==================== 21. Schulungsnachweise ====================
|
||||
{
|
||||
templateId: 'schulungsnachweise',
|
||||
dataObjectName: 'Schulungsnachweise',
|
||||
description:
|
||||
'Teilnahmebestaetigungen, Zertifikate und Protokolle von Mitarbeiterschulungen (Datenschutz, Arbeitssicherheit, Compliance).',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Schulungsdaten', 'Zertifikate', 'Teilnahmelisten'],
|
||||
primaryPurpose:
|
||||
'Nachweis der Durchfuehrung gesetzlich vorgeschriebener Schulungen und Dokumentation der Mitarbeiterqualifikation.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'CUSTOM',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer 3 Jahre nach Ende des Beschaeftigungsverhaeltnisses als Nachweis der ordnungsgemaessen Schulungsdurchfuehrung.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Ende des Beschaeftigungsverhaeltnisses',
|
||||
startEvent: 'Ende des Beschaeftigungsverhaeltnisses des geschulten Mitarbeiters',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die HR-Abteilung vor Loeschung, da Schulungsnachweise als Compliance-Nachweis in Audits relevant sein koennen.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'schulung'],
|
||||
},
|
||||
|
||||
// ==================== 22. Betriebsarzt-Dokumentation ====================
|
||||
{
|
||||
templateId: 'betriebsarzt-doku',
|
||||
dataObjectName: 'Betriebsarzt-Dokumentation',
|
||||
description:
|
||||
'Ergebnisse arbeitsmedizinischer Vorsorgeuntersuchungen, Eignungsuntersuchungen und arbeitsmedizinische Empfehlungen.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Gesundheitsdaten', 'Vorsorgeuntersuchungen', 'Eignungsbefunde'],
|
||||
primaryPurpose:
|
||||
'Erfuellung der Dokumentationspflicht fuer arbeitsmedizinische Vorsorge gemaess ArbMedVV und Nachweisfuehrung gegenueber Berufsgenossenschaften.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'CUSTOM',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess ArbMedVV (Verordnung zur arbeitsmedizinischen Vorsorge) und Berufsgenossenschaftliche Grundsaetze: bis zu 40 Jahre bei Exposition gegenueber krebserzeugenden Gefahrstoffen.',
|
||||
retentionDuration: 40,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '40 Jahre nach letzter Exposition (bei Gefahrstoffen), sonst 10 Jahre nach Ende der Taetigkeit',
|
||||
startEvent: 'Ende der expositionsrelevanten Taetigkeit bzw. Ende des Beschaeftigungsverhaeltnisses',
|
||||
deletionMethod: 'PHYSICAL_DESTROY',
|
||||
deletionMethodDetail:
|
||||
'Physische Vernichtung der Papierunterlagen durch zertifizierten Aktenvernichtungsdienstleister (DIN 66399, Sicherheitsstufe P-5). Digitale Daten werden kryptographisch geloescht.',
|
||||
responsibleRole: 'Betriebsarzt / Arbeitsmedizinischer Dienst',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'gesundheit'],
|
||||
},
|
||||
|
||||
// ==================== 23. Kundenreklamationen ====================
|
||||
{
|
||||
templateId: 'kundenreklamationen',
|
||||
dataObjectName: 'Kundenreklamationen',
|
||||
description:
|
||||
'Reklamationsvorgaenge inkl. Beschwerdeinhalt, Kommunikationsverlauf, Massnahmen und Ergebnis der Reklamationsbearbeitung.',
|
||||
affectedGroups: ['Kunden'],
|
||||
dataCategories: ['Reklamationsdaten', 'Kommunikation', 'Massnahmenprotokolle'],
|
||||
primaryPurpose:
|
||||
'Dokumentation und Bearbeitung von Kundenreklamationen, Qualitaetssicherung und Absicherung gegen Gewaehrleistungsansprueche.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB (3 Jahre) zur Absicherung gegen Gewaehrleistungs- und Schadensersatzansprueche.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Abschluss des Reklamationsvorgangs',
|
||||
startEvent: 'Abschluss des Reklamationsvorgangs (letzte Massnahme)',
|
||||
deletionMethod: 'ANONYMIZATION',
|
||||
deletionMethodDetail:
|
||||
'Anonymisierung der personenbezogenen Daten nach Ablauf der Frist. Anonymisierte Reklamationsstatistiken bleiben fuer die Qualitaetssicherung erhalten.',
|
||||
responsibleRole: 'Qualitaetsmanagement',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['kunden', 'qualitaet'],
|
||||
},
|
||||
|
||||
// ==================== 24. Lieferantenbewertungen ====================
|
||||
{
|
||||
templateId: 'lieferantenbewertungen',
|
||||
dataObjectName: 'Lieferantenbewertungen',
|
||||
description:
|
||||
'Bewertungen und Auditergebnisse von Lieferanten und Auftragsverarbeitern inkl. Qualitaets-, Compliance- und Datenschutz-Bewertungen.',
|
||||
affectedGroups: ['Lieferanten', 'Auftragsverarbeiter'],
|
||||
dataCategories: ['Bewertungsdaten', 'Auditberichte', 'Vertragsdaten'],
|
||||
primaryPurpose:
|
||||
'Dokumentation der Sorgfaltspflicht bei der Auswahl und Ueberwachung von Auftragsverarbeitern gemaess Art. 28 DSGVO und Qualitaetssicherung in der Lieferkette.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung gemaess 257 HGB als handelsrelevante Unterlagen sowie zur Nachweisfuehrung der Sorgfaltspflicht bei der Auftragsverarbeitung.',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '6 Jahre nach Ende der Geschaeftsbeziehung',
|
||||
startEvent: 'Ende der Geschaeftsbeziehung mit dem Lieferanten/Auftragsverarbeiter',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch den Einkauf/Compliance-Abteilung vor Loeschung, um sicherzustellen, dass keine Nachweispflichten aus laufenden Vertraegen oder Audits bestehen.',
|
||||
responsibleRole: 'Einkauf / Compliance',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['lieferanten', 'einkauf'],
|
||||
},
|
||||
|
||||
// ==================== 25. Social-Media-Marketingdaten ====================
|
||||
{
|
||||
templateId: 'social-media-daten',
|
||||
dataObjectName: 'Social-Media-Marketingdaten',
|
||||
description:
|
||||
'Personenbezogene Daten aus Social-Media-Kampagnen inkl. Nutzerinteraktionen, Custom Audiences, Retargeting-Listen und Kampagnen-Analytics.',
|
||||
affectedGroups: ['Kunden', 'Interessenten', 'Website-Besucher'],
|
||||
dataCategories: ['Interaktionsdaten', 'Zielgruppendaten', 'Tracking-Daten', 'Profilmerkmale'],
|
||||
primaryPurpose:
|
||||
'Durchfuehrung zielgerichteter Marketing-Kampagnen auf Social-Media-Plattformen und Analyse der Kampagneneffektivitaet.',
|
||||
deletionTrigger: 'PURPOSE_END',
|
||||
retentionDriver: null,
|
||||
retentionDriverDetail:
|
||||
'Keine gesetzliche Aufbewahrungspflicht. Daten werden bis zum Widerruf der Einwilligung bzw. bis zum Ende der Kampagne gespeichert (Art. 6 Abs. 1 lit. a DSGVO).',
|
||||
retentionDuration: null,
|
||||
retentionUnit: null,
|
||||
retentionDescription: 'Bis zum Widerruf der Einwilligung oder Ende des Kampagnenzwecks',
|
||||
startEvent: 'Widerruf der Einwilligung oder Ende der Marketing-Kampagne',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der personenbezogenen Daten in den Social-Media-Werbekonten und internen Systemen nach Zweckwegfall. Custom Audiences werden bei Plattformanbietern geloescht.',
|
||||
responsibleRole: 'Marketing',
|
||||
reviewInterval: 'SEMI_ANNUAL',
|
||||
tags: ['marketing', 'social'],
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Erstellt eine vollstaendige LoeschfristPolicy aus einem BaselineTemplate.
|
||||
* Nutzt createEmptyPolicy() als Basis und ueberlagert die Template-Felder.
|
||||
*/
|
||||
export function templateToPolicy(template: BaselineTemplate): LoeschfristPolicy {
|
||||
const base = createEmptyPolicy()
|
||||
|
||||
return {
|
||||
...base,
|
||||
dataObjectName: template.dataObjectName,
|
||||
description: template.description,
|
||||
affectedGroups: [...template.affectedGroups],
|
||||
dataCategories: [...template.dataCategories],
|
||||
primaryPurpose: template.primaryPurpose,
|
||||
deletionTrigger: template.deletionTrigger,
|
||||
retentionDriver: template.retentionDriver,
|
||||
retentionDriverDetail: template.retentionDriverDetail,
|
||||
retentionDuration: template.retentionDuration,
|
||||
retentionUnit: template.retentionUnit,
|
||||
retentionDescription: template.retentionDescription,
|
||||
startEvent: template.startEvent,
|
||||
deletionMethod: template.deletionMethod,
|
||||
deletionMethodDetail: template.deletionMethodDetail,
|
||||
responsibleRole: template.responsibleRole,
|
||||
reviewInterval: template.reviewInterval,
|
||||
tags: [...template.tags],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Templates zurueck, die einen bestimmten Tag enthalten.
|
||||
*/
|
||||
export function getTemplatesByTag(tag: string): BaselineTemplate[] {
|
||||
return BASELINE_TEMPLATES.filter(t => t.tags.includes(tag))
|
||||
}
|
||||
|
||||
/**
|
||||
* Findet ein Template anhand seiner templateId.
|
||||
*/
|
||||
export function getTemplateById(templateId: string): BaselineTemplate | undefined {
|
||||
return BASELINE_TEMPLATES.find(t => t.templateId === templateId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle im Katalog verwendeten Tags als sortierte Liste zurueck.
|
||||
*/
|
||||
export function getAllTemplateTags(): string[] {
|
||||
const tags = new Set<string>()
|
||||
BASELINE_TEMPLATES.forEach(t => t.tags.forEach(tag => tags.add(tag)))
|
||||
return Array.from(tags).sort()
|
||||
}
|
||||
export * from './loeschfristen-baseline-catalog/types'
|
||||
export * from './loeschfristen-baseline-catalog/finance-legal'
|
||||
export * from './loeschfristen-baseline-catalog/hr'
|
||||
export * from './loeschfristen-baseline-catalog/it-security'
|
||||
export * from './loeschfristen-baseline-catalog/marketing-crm'
|
||||
export * from './loeschfristen-baseline-catalog/helpers'
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Loeschfristen — Finance & Legal Templates
|
||||
* Buchhaltungsbelege, Rechnungen, Geschaeftsbriefe, Steuererklaerungen,
|
||||
* Gesellschafterprotokolle, Vertraege, E-Mail-Archivierung, Lieferantenbewertungen
|
||||
*/
|
||||
|
||||
import { BaselineTemplate } from './types'
|
||||
|
||||
export const FINANCE_LEGAL_TEMPLATES: BaselineTemplate[] = [
|
||||
{
|
||||
templateId: 'buchhaltungsbelege',
|
||||
dataObjectName: 'Buchhaltungsbelege',
|
||||
description:
|
||||
'Buchungsbelege, Kontoauszuege, Kassenbuecher und sonstige Belege der laufenden Buchhaltung.',
|
||||
affectedGroups: ['Kunden', 'Lieferanten'],
|
||||
dataCategories: ['Finanzdaten', 'Transaktionsdaten', 'Kontodaten'],
|
||||
primaryPurpose:
|
||||
'Ordnungsgemaesse Buchfuehrung und Erfuellung handelsrechtlicher Aufbewahrungspflichten nach HGB.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer Handelsbuecher und Buchungsbelege.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende des Geschaeftsjahres',
|
||||
startEvent: 'Ende des Geschaeftsjahres',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die Buchhaltung vor Loeschung, um sicherzustellen, dass keine laufenden Pruefungen oder Rechtsstreitigkeiten bestehen.',
|
||||
responsibleRole: 'Buchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['finanzen', 'hgb'],
|
||||
},
|
||||
{
|
||||
templateId: 'rechnungen',
|
||||
dataObjectName: 'Rechnungen',
|
||||
description:
|
||||
'Eingangs- und Ausgangsrechnungen inkl. Rechnungsanhaenge und rechnungsbegruendende Unterlagen.',
|
||||
affectedGroups: ['Kunden', 'Lieferanten'],
|
||||
dataCategories: ['Rechnungsdaten', 'Umsatzsteuerdaten', 'Adressdaten'],
|
||||
primaryPurpose:
|
||||
'Dokumentation umsatzsteuerrelevanter Vorgaenge und Erfuellung der Aufbewahrungspflicht nach UStG.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'USTG_14B',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 14b UStG fuer Rechnungen und rechnungsbegruendende Unterlagen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre ab Rechnungsdatum',
|
||||
startEvent: 'Rechnungsdatum',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung nach Ablauf der 10-Jahres-Frist. Vor Loeschung wird geprueft, ob Rechnungen in laufenden Betriebspruefungen benoetigt werden.',
|
||||
responsibleRole: 'Buchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['finanzen', 'ustg'],
|
||||
},
|
||||
{
|
||||
templateId: 'geschaeftsbriefe',
|
||||
dataObjectName: 'Geschaeftsbriefe',
|
||||
description:
|
||||
'Empfangene und versandte Handelsbriefe, Geschaeftskorrespondenz und geschaeftsrelevante E-Mails.',
|
||||
affectedGroups: ['Kunden', 'Lieferanten'],
|
||||
dataCategories: ['Korrespondenz', 'Vertragskommunikation', 'Angebote'],
|
||||
primaryPurpose:
|
||||
'Nachweisfuehrung geschaeftlicher Kommunikation und Erfuellung der handelsrechtlichen Aufbewahrungspflicht fuer Handelsbriefe.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer empfangene und versandte Handelsbriefe (6 Jahre).',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '6 Jahre ab Eingang oder Versand des Geschaeftsbriefes',
|
||||
startEvent: 'Eingang bzw. Versand des Geschaeftsbriefes',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die Geschaeftsleitung, da Geschaeftsbriefe ggf. als Beweismittel in Rechtsstreitigkeiten dienen koennen.',
|
||||
responsibleRole: 'Geschaeftsleitung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['kommunikation', 'hgb'],
|
||||
},
|
||||
{
|
||||
templateId: 'steuererklaerungen',
|
||||
dataObjectName: 'Steuererklaerungen',
|
||||
description:
|
||||
'Koerperschaftsteuer-, Gewerbesteuer- und Umsatzsteuererklaerungen inkl. Anlagen und Bescheide.',
|
||||
affectedGroups: ['Unternehmen'],
|
||||
dataCategories: ['Steuerdaten', 'Finanzkennzahlen', 'Bescheide'],
|
||||
primaryPurpose:
|
||||
'Erfuellung steuerrechtlicher Dokumentationspflichten und Nachweisfuehrung gegenueber den Finanzbehoerden.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AO_147',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 147 AO fuer Steuererklaerungen und zugehoerige Unterlagen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre ab dem jeweiligen Steuerjahr',
|
||||
startEvent: 'Ende des betreffenden Steuerjahres',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung nach Ablauf der 10-Jahres-Frist, sofern keine laufende Betriebspruefung oder Einspruchsverfahren vorliegen.',
|
||||
responsibleRole: 'Steuerberater/Buchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['finanzen', 'steuer'],
|
||||
},
|
||||
{
|
||||
templateId: 'protokolle-gesellschafter',
|
||||
dataObjectName: 'Gesellschafterprotokolle',
|
||||
description:
|
||||
'Protokolle der Gesellschafterversammlungen, Beschluesse, Abstimmungsergebnisse und notarielle Urkunden.',
|
||||
affectedGroups: ['Gesellschafter'],
|
||||
dataCategories: ['Beschlussdaten', 'Abstimmungsergebnisse', 'Protokolle'],
|
||||
primaryPurpose:
|
||||
'Dokumentation gesellschaftsrechtlicher Beschluesse und Erfuellung handelsrechtlicher Aufbewahrungspflichten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer Eroeffnungsbilanzen, Jahresabschluesse und zugehoerige Beschluesse.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre ab Beschlussdatum',
|
||||
startEvent: 'Datum des jeweiligen Gesellschafterbeschlusses',
|
||||
deletionMethod: 'PHYSICAL_DESTROY',
|
||||
deletionMethodDetail:
|
||||
'Physische Vernichtung der Papieroriginale durch zertifizierten Aktenvernichtungsdienstleister (DIN 66399, Sicherheitsstufe P-4). Digitale Kopien werden parallel geloescht.',
|
||||
responsibleRole: 'Geschaeftsleitung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['recht', 'gesellschaft'],
|
||||
},
|
||||
{
|
||||
templateId: 'vertraege',
|
||||
dataObjectName: 'Vertraege',
|
||||
description:
|
||||
'Geschaeftsvertraege, Rahmenvereinbarungen, Dienstleistungsvertraege und zugehoerige Anlagen und Nachtraege.',
|
||||
affectedGroups: ['Vertragspartner'],
|
||||
dataCategories: ['Vertragsdaten', 'Kontaktdaten', 'Konditionen'],
|
||||
primaryPurpose:
|
||||
'Dokumentation vertraglicher Vereinbarungen und Sicherung von Beweismitteln fuer die Dauer moeglicher Rechtsstreitigkeiten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer handelsrechtlich relevante Vertragsunterlagen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende der Vertragslaufzeit',
|
||||
startEvent: 'Ende der Vertragslaufzeit bzw. Vertragsbeendigung',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die Rechtsabteilung vor Loeschung, um sicherzustellen, dass keine laufenden oder angedrohten Rechtsstreitigkeiten bestehen.',
|
||||
responsibleRole: 'Rechtsabteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['recht', 'vertraege'],
|
||||
},
|
||||
{
|
||||
templateId: 'email-archivierung',
|
||||
dataObjectName: 'E-Mail-Archivierung',
|
||||
description:
|
||||
'Archivierte geschaeftliche E-Mails inkl. Anhaenge, die als Handelsbriefe oder steuerrelevante Korrespondenz einzustufen sind.',
|
||||
affectedGroups: ['Mitarbeiter', 'Kunden', 'Lieferanten'],
|
||||
dataCategories: ['E-Mail-Korrespondenz', 'Anhaenge', 'Metadaten'],
|
||||
primaryPurpose:
|
||||
'Erfuellung der handelsrechtlichen Aufbewahrungspflicht fuer geschaeftliche Korrespondenz, die als Handelsbrief einzuordnen ist.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 257 HGB fuer empfangene und versandte Handelsbriefe (6 Jahre) bzw. buchhalterisch relevante E-Mails (10 Jahre).',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '6 Jahre nach Versand/Empfang der E-Mail',
|
||||
startEvent: 'Versand- bzw. Empfangsdatum der E-Mail',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung durch das E-Mail-Archivierungssystem nach Ablauf der konfigurierten Aufbewahrungsfrist. Vor Loeschung wird geprueft, ob die E-Mail in laufenden Verfahren benoetigt wird.',
|
||||
responsibleRole: 'IT-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['kommunikation', 'hgb'],
|
||||
},
|
||||
{
|
||||
templateId: 'lieferantenbewertungen',
|
||||
dataObjectName: 'Lieferantenbewertungen',
|
||||
description:
|
||||
'Bewertungen und Auditergebnisse von Lieferanten und Auftragsverarbeitern inkl. Qualitaets-, Compliance- und Datenschutz-Bewertungen.',
|
||||
affectedGroups: ['Lieferanten', 'Auftragsverarbeiter'],
|
||||
dataCategories: ['Bewertungsdaten', 'Auditberichte', 'Vertragsdaten'],
|
||||
primaryPurpose:
|
||||
'Dokumentation der Sorgfaltspflicht bei der Auswahl und Ueberwachung von Auftragsverarbeitern gemaess Art. 28 DSGVO und Qualitaetssicherung in der Lieferkette.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'HGB_257',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung gemaess 257 HGB als handelsrelevante Unterlagen sowie zur Nachweisfuehrung der Sorgfaltspflicht bei der Auftragsverarbeitung.',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '6 Jahre nach Ende der Geschaeftsbeziehung',
|
||||
startEvent: 'Ende der Geschaeftsbeziehung mit dem Lieferanten/Auftragsverarbeiter',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch den Einkauf/Compliance-Abteilung vor Loeschung, um sicherzustellen, dass keine Nachweispflichten aus laufenden Vertraegen oder Audits bestehen.',
|
||||
responsibleRole: 'Einkauf / Compliance',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['lieferanten', 'einkauf'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Loeschfristen Baseline-Katalog — Helper Functions
|
||||
*/
|
||||
|
||||
import type { LoeschfristPolicy } from '../loeschfristen-types'
|
||||
import { createEmptyPolicy } from '../loeschfristen-types'
|
||||
import type { BaselineTemplate } from './types'
|
||||
import { FINANCE_LEGAL_TEMPLATES } from './finance-legal'
|
||||
import { HR_TEMPLATES } from './hr'
|
||||
import { IT_SECURITY_TEMPLATES } from './it-security'
|
||||
import { MARKETING_CRM_TEMPLATES } from './marketing-crm'
|
||||
|
||||
export const BASELINE_TEMPLATES: BaselineTemplate[] = [
|
||||
...HR_TEMPLATES,
|
||||
...FINANCE_LEGAL_TEMPLATES,
|
||||
...IT_SECURITY_TEMPLATES,
|
||||
...MARKETING_CRM_TEMPLATES,
|
||||
]
|
||||
|
||||
/**
|
||||
* Erstellt eine vollstaendige LoeschfristPolicy aus einem BaselineTemplate.
|
||||
*/
|
||||
export function templateToPolicy(template: BaselineTemplate): LoeschfristPolicy {
|
||||
const base = createEmptyPolicy()
|
||||
|
||||
return {
|
||||
...base,
|
||||
dataObjectName: template.dataObjectName,
|
||||
description: template.description,
|
||||
affectedGroups: [...template.affectedGroups],
|
||||
dataCategories: [...template.dataCategories],
|
||||
primaryPurpose: template.primaryPurpose,
|
||||
deletionTrigger: template.deletionTrigger,
|
||||
retentionDriver: template.retentionDriver,
|
||||
retentionDriverDetail: template.retentionDriverDetail,
|
||||
retentionDuration: template.retentionDuration,
|
||||
retentionUnit: template.retentionUnit,
|
||||
retentionDescription: template.retentionDescription,
|
||||
startEvent: template.startEvent,
|
||||
deletionMethod: template.deletionMethod,
|
||||
deletionMethodDetail: template.deletionMethodDetail,
|
||||
responsibleRole: template.responsibleRole,
|
||||
reviewInterval: template.reviewInterval,
|
||||
tags: [...template.tags],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Templates zurueck, die einen bestimmten Tag enthalten.
|
||||
*/
|
||||
export function getTemplatesByTag(tag: string): BaselineTemplate[] {
|
||||
return BASELINE_TEMPLATES.filter(t => t.tags.includes(tag))
|
||||
}
|
||||
|
||||
/**
|
||||
* Findet ein Template anhand seiner templateId.
|
||||
*/
|
||||
export function getTemplateById(templateId: string): BaselineTemplate | undefined {
|
||||
return BASELINE_TEMPLATES.find(t => t.templateId === templateId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle im Katalog verwendeten Tags als sortierte Liste zurueck.
|
||||
*/
|
||||
export function getAllTemplateTags(): string[] {
|
||||
const tags = new Set<string>()
|
||||
BASELINE_TEMPLATES.forEach(t => t.tags.forEach(tag => tags.add(tag)))
|
||||
return Array.from(tags).sort()
|
||||
}
|
||||
178
admin-compliance/lib/sdk/loeschfristen-baseline-catalog/hr.ts
Normal file
178
admin-compliance/lib/sdk/loeschfristen-baseline-catalog/hr.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Loeschfristen — HR Templates
|
||||
* Personalakten, Bewerbungsunterlagen, Gehaltsabrechnungen, Zeiterfassung,
|
||||
* Krankmeldungen, Schulungsnachweise, Betriebsarzt-Dokumentation
|
||||
*/
|
||||
|
||||
import { BaselineTemplate } from './types'
|
||||
|
||||
export const HR_TEMPLATES: BaselineTemplate[] = [
|
||||
{
|
||||
templateId: 'personal-akten',
|
||||
dataObjectName: 'Personalakten',
|
||||
description:
|
||||
'Vollstaendige Personalakten inkl. Arbeitsvertraege, Zeugnisse, Abmahnungen und sonstige beschaeftigungsrelevante Dokumente.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Stammdaten', 'Vertragsdaten', 'Gehaltsdaten', 'Zeugnisse'],
|
||||
primaryPurpose:
|
||||
'Dokumentation und Nachweisfuehrung des Beschaeftigungsverhaeltnisses sowie Erfuellung steuerrechtlicher Aufbewahrungspflichten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AO_147',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 147 AO fuer steuerlich relevante Unterlagen der Personalakte.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende des Beschaeftigungsverhaeltnisses',
|
||||
startEvent: 'Ende des Beschaeftigungsverhaeltnisses',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung aller digitalen Personalakten-Dokumente nach Ablauf der Aufbewahrungsfrist. Papierakten werden datenschutzkonform vernichtet.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'steuer'],
|
||||
},
|
||||
{
|
||||
templateId: 'bewerbungsunterlagen',
|
||||
dataObjectName: 'Bewerbungsunterlagen',
|
||||
description:
|
||||
'Eingereichte Bewerbungsunterlagen inkl. Anschreiben, Lebenslauf, Zeugnisse und Korrespondenz mit Bewerbern.',
|
||||
affectedGroups: ['Bewerber'],
|
||||
dataCategories: ['Bewerbungsdaten', 'Qualifikationen', 'Kontaktdaten'],
|
||||
primaryPurpose:
|
||||
'Durchfuehrung des Bewerbungsverfahrens und Absicherung gegen Entschaedigungsansprueche nach dem AGG.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AGG_15',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer 6 Monate nach Absage gemaess 15 Abs. 4 AGG (Frist fuer Geltendmachung von Entschaedigungsanspruechen).',
|
||||
retentionDuration: 6,
|
||||
retentionUnit: 'MONTHS',
|
||||
retentionDescription: '6 Monate nach Absage oder Stellenbesetzung',
|
||||
startEvent: 'Absage oder endgueltige Stellenbesetzung',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung aller Bewerbungsunterlagen und zugehoeriger Kommunikation nach Ablauf der 6-Monats-Frist.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['hr', 'bewerbung'],
|
||||
},
|
||||
{
|
||||
templateId: 'gehaltsabrechnungen',
|
||||
dataObjectName: 'Gehaltsabrechnungen',
|
||||
description:
|
||||
'Monatliche Gehaltsabrechnungen, Lohnsteuerbescheinigungen und Sozialversicherungsmeldungen.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Gehaltsdaten', 'Steuerdaten', 'Sozialversicherungsdaten'],
|
||||
primaryPurpose:
|
||||
'Dokumentation der Lohn- und Gehaltszahlungen sowie Erfuellung steuerrechtlicher und sozialversicherungsrechtlicher Aufbewahrungspflichten.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'AO_147',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 147 AO fuer lohnsteuerrelevante Unterlagen und Gehaltsbuchungen.',
|
||||
retentionDuration: 10,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '10 Jahre nach Ende des Geschaeftsjahres',
|
||||
startEvent: 'Ende des Geschaeftsjahres der jeweiligen Abrechnung',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der digitalen Gehaltsabrechnungen nach Ablauf der Aufbewahrungsfrist. Papierbelege werden datenschutzkonform vernichtet.',
|
||||
responsibleRole: 'Lohnbuchhaltung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'steuer'],
|
||||
},
|
||||
{
|
||||
templateId: 'zeiterfassung',
|
||||
dataObjectName: 'Zeiterfassungsdaten',
|
||||
description:
|
||||
'Arbeitszeitaufzeichnungen inkl. Beginn, Ende, Pausen und Ueberstunden der Beschaeftigten.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Arbeitszeiten', 'Pausenzeiten', 'Ueberstunden'],
|
||||
primaryPurpose:
|
||||
'Erfuellung der gesetzlichen Aufzeichnungspflicht fuer Arbeitszeiten und Nachweis der Einhaltung des Arbeitszeitgesetzes.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'ARBZG_16',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess 16 Abs. 2 ArbZG fuer Aufzeichnungen ueber die Arbeitszeit.',
|
||||
retentionDuration: 2,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '2 Jahre nach Ende des Erfassungszeitraums',
|
||||
startEvent: 'Ende des jeweiligen Erfassungszeitraums',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der Zeiterfassungsdaten nach Ablauf der 2-Jahres-Frist im Zeiterfassungssystem.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'arbzg'],
|
||||
},
|
||||
{
|
||||
templateId: 'krankmeldungen',
|
||||
dataObjectName: 'Krankmeldungen',
|
||||
description:
|
||||
'Arbeitsunfaehigkeitsbescheinigungen, Krankmeldungen und zugehoerige Abwesenheitsdokumentationen.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Gesundheitsdaten', 'Abwesenheitszeiten', 'AU-Bescheinigungen'],
|
||||
primaryPurpose:
|
||||
'Dokumentation von Fehlzeiten, Entgeltfortzahlung im Krankheitsfall und Absicherung gegen Verjaehrung arbeitsrechtlicher Ansprueche.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB zur Absicherung von Erstattungsanspruechen.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Ende des Beschaeftigungsverhaeltnisses',
|
||||
startEvent: 'Ende des Beschaeftigungsverhaeltnisses',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die HR-Abteilung vor Loeschung, da Krankmeldungen besondere Kategorien personenbezogener Daten (Gesundheitsdaten) enthalten.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'gesundheit'],
|
||||
},
|
||||
{
|
||||
templateId: 'schulungsnachweise',
|
||||
dataObjectName: 'Schulungsnachweise',
|
||||
description:
|
||||
'Teilnahmebestaetigungen, Zertifikate und Protokolle von Mitarbeiterschulungen (Datenschutz, Arbeitssicherheit, Compliance).',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Schulungsdaten', 'Zertifikate', 'Teilnahmelisten'],
|
||||
primaryPurpose:
|
||||
'Nachweis der Durchfuehrung gesetzlich vorgeschriebener Schulungen und Dokumentation der Mitarbeiterqualifikation.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'CUSTOM',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer 3 Jahre nach Ende des Beschaeftigungsverhaeltnisses als Nachweis der ordnungsgemaessen Schulungsdurchfuehrung.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Ende des Beschaeftigungsverhaeltnisses',
|
||||
startEvent: 'Ende des Beschaeftigungsverhaeltnisses des geschulten Mitarbeiters',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch die HR-Abteilung vor Loeschung, da Schulungsnachweise als Compliance-Nachweis in Audits relevant sein koennen.',
|
||||
responsibleRole: 'HR-Abteilung',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'schulung'],
|
||||
},
|
||||
{
|
||||
templateId: 'betriebsarzt-doku',
|
||||
dataObjectName: 'Betriebsarzt-Dokumentation',
|
||||
description:
|
||||
'Ergebnisse arbeitsmedizinischer Vorsorgeuntersuchungen, Eignungsuntersuchungen und arbeitsmedizinische Empfehlungen.',
|
||||
affectedGroups: ['Mitarbeiter'],
|
||||
dataCategories: ['Gesundheitsdaten', 'Vorsorgeuntersuchungen', 'Eignungsbefunde'],
|
||||
primaryPurpose:
|
||||
'Erfuellung der Dokumentationspflicht fuer arbeitsmedizinische Vorsorge gemaess ArbMedVV und Nachweisfuehrung gegenueber Berufsgenossenschaften.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'CUSTOM',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrungspflicht gemaess ArbMedVV (Verordnung zur arbeitsmedizinischen Vorsorge) und Berufsgenossenschaftliche Grundsaetze: bis zu 40 Jahre bei Exposition gegenueber krebserzeugenden Gefahrstoffen.',
|
||||
retentionDuration: 40,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '40 Jahre nach letzter Exposition (bei Gefahrstoffen), sonst 10 Jahre nach Ende der Taetigkeit',
|
||||
startEvent: 'Ende der expositionsrelevanten Taetigkeit bzw. Ende des Beschaeftigungsverhaeltnisses',
|
||||
deletionMethod: 'PHYSICAL_DESTROY',
|
||||
deletionMethodDetail:
|
||||
'Physische Vernichtung der Papierunterlagen durch zertifizierten Aktenvernichtungsdienstleister (DIN 66399, Sicherheitsstufe P-5). Digitale Daten werden kryptographisch geloescht.',
|
||||
responsibleRole: 'Betriebsarzt / Arbeitsmedizinischer Dienst',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['hr', 'gesundheit'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Loeschfristen — IT & Security Templates
|
||||
* Webserver-Logs, Videoueberwachung, Backup-Daten, Zutrittsprotokolle
|
||||
*/
|
||||
|
||||
import { BaselineTemplate } from './types'
|
||||
|
||||
export const IT_SECURITY_TEMPLATES: BaselineTemplate[] = [
|
||||
{
|
||||
templateId: 'webserver-logs',
|
||||
dataObjectName: 'Webserver-Logs',
|
||||
description:
|
||||
'Server-Zugriffsprotokolle inkl. IP-Adressen, Zeitstempel, aufgerufene URLs und HTTP-Statuscodes.',
|
||||
affectedGroups: ['Website-Besucher'],
|
||||
dataCategories: ['IP-Adressen', 'Zugriffszeitpunkte', 'User-Agent-Daten'],
|
||||
primaryPurpose:
|
||||
'Sicherstellung der IT-Sicherheit, Erkennung von Angriffen und Stoerungen sowie Erfuellung der Protokollierungspflicht nach BSIG.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BSIG',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung gemaess BSI-Gesetz / IT-Sicherheitsgesetz 2.0 fuer die Analyse von Sicherheitsvorfaellen.',
|
||||
retentionDuration: 7,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '7 Tage nach Zeitpunkt des Zugriffs',
|
||||
startEvent: 'Zeitpunkt des Server-Zugriffs',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Rotation und Loeschung der Logdateien nach 7 Tagen durch den Webserver (logrotate).',
|
||||
responsibleRole: 'IT-Abteilung',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['it', 'logs'],
|
||||
},
|
||||
{
|
||||
templateId: 'videoueberwachung',
|
||||
dataObjectName: 'Videoueberwachung',
|
||||
description:
|
||||
'Aufnahmen der Videoueberwachung in Geschaeftsraeumen, Eingangsbereichen und Parkplaetzen.',
|
||||
affectedGroups: ['Besucher', 'Mitarbeiter'],
|
||||
dataCategories: ['Videodaten', 'Bilddaten', 'Zeitstempel'],
|
||||
primaryPurpose:
|
||||
'Schutz des Eigentums und der Sicherheit von Personen sowie Aufklaerung von Vorfaellen in den ueberwachten Bereichen.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BDSG_35',
|
||||
retentionDriverDetail:
|
||||
'Unverzuegliche Loeschung nach Zweckwegfall gemaess 35 BDSG bzw. Art. 17 DSGVO. Maximale Speicherdauer 48 Stunden.',
|
||||
retentionDuration: 2,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '48 Stunden (2 Tage) nach Aufnahmezeitpunkt',
|
||||
startEvent: 'Aufnahmezeitpunkt der Videosequenz',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatisches Ueberschreiben der Aufnahmen durch das Videomanagementsystem nach Ablauf der 48-Stunden-Frist.',
|
||||
responsibleRole: 'Facility Management',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['sicherheit', 'video'],
|
||||
},
|
||||
{
|
||||
templateId: 'backup-daten',
|
||||
dataObjectName: 'Backup-Daten',
|
||||
description:
|
||||
'Vollstaendige und inkrementelle Sicherungskopien aller Systeme, Datenbanken und Dateisysteme.',
|
||||
affectedGroups: ['Alle Betroffenengruppen'],
|
||||
dataCategories: ['Systemsicherungen', 'Datenbankkopien', 'Dateisystemsicherungen'],
|
||||
primaryPurpose:
|
||||
'Sicherstellung der Datenwiederherstellung im Katastrophenfall und Gewaehrleistung der Geschaeftskontinuitaet.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BSIG',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung von Backups fuer 90 Tage gemaess BSI-Grundschutz-Empfehlungen zur Sicherstellung der Wiederherstellbarkeit.',
|
||||
retentionDuration: 90,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '90 Tage nach Erstellung des Backups',
|
||||
startEvent: 'Erstellungsdatum des jeweiligen Backups',
|
||||
deletionMethod: 'CRYPTO_ERASE',
|
||||
deletionMethodDetail:
|
||||
'Kryptographische Loeschung durch Vernichtung der Verschluesselungsschluessel, sodass die verschluesselten Backup-Daten nicht mehr entschluesselt werden koennen.',
|
||||
responsibleRole: 'IT-Abteilung',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['it', 'backup'],
|
||||
},
|
||||
{
|
||||
templateId: 'zutrittsprotokolle',
|
||||
dataObjectName: 'Zutrittsprotokolle',
|
||||
description:
|
||||
'Protokolle des Zutrittskontrollsystems inkl. Zeitstempel, Kartennummer, Zutrittsort und Zugangsentscheidung (gewaehrt/verweigert).',
|
||||
affectedGroups: ['Mitarbeiter', 'Besucher'],
|
||||
dataCategories: ['Zutrittsdaten', 'Zeitstempel', 'Kartennummern', 'Standortdaten'],
|
||||
primaryPurpose:
|
||||
'Sicherstellung der physischen Sicherheit, Nachvollziehbarkeit von Zutritten und Unterstuetzung bei der Aufklaerung von Sicherheitsvorfaellen.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BSIG',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung gemaess BSI-Grundschutz-Empfehlung fuer Zutrittsprotokolle zur Analyse von Sicherheitsvorfaellen (90 Tage).',
|
||||
retentionDuration: 90,
|
||||
retentionUnit: 'DAYS',
|
||||
retentionDescription: '90 Tage nach Zeitpunkt des Zutritts',
|
||||
startEvent: 'Zeitpunkt des protokollierten Zutrittsereignisses',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Rotation und Loeschung der Zutrittsprotokolle durch das Zutrittskontrollsystem nach Ablauf der 90-Tage-Frist.',
|
||||
responsibleRole: 'Facility Management',
|
||||
reviewInterval: 'QUARTERLY',
|
||||
tags: ['sicherheit', 'zutritt'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Loeschfristen — Marketing & CRM Templates
|
||||
* Newsletter-Einwilligungen, Kundenstammdaten, CRM-Kontakthistorie,
|
||||
* Kundenreklamationen, Social-Media-Marketingdaten
|
||||
*/
|
||||
|
||||
import { BaselineTemplate } from './types'
|
||||
|
||||
export const MARKETING_CRM_TEMPLATES: BaselineTemplate[] = [
|
||||
{
|
||||
templateId: 'newsletter-einwilligungen',
|
||||
dataObjectName: 'Newsletter-Einwilligungen',
|
||||
description:
|
||||
'Einwilligungserklaerungen fuer den Newsletter-Versand inkl. Double-Opt-in-Nachweis und Abmeldezeitpunkt.',
|
||||
affectedGroups: ['Abonnenten'],
|
||||
dataCategories: ['Einwilligungsdaten', 'E-Mail-Adresse', 'Opt-in-Nachweis'],
|
||||
primaryPurpose:
|
||||
'Nachweis der wirksamen Einwilligung zum Newsletter-Versand gemaess Art. 7 DSGVO und Dokumentation des Widerrufs.',
|
||||
deletionTrigger: 'PURPOSE_END',
|
||||
retentionDriver: null,
|
||||
retentionDriverDetail:
|
||||
'Keine gesetzliche Aufbewahrungspflicht. Daten werden bis zum Widerruf der Einwilligung gespeichert.',
|
||||
retentionDuration: null,
|
||||
retentionUnit: null,
|
||||
retentionDescription: 'Bis zum Widerruf der Einwilligung durch den Abonnenten',
|
||||
startEvent: 'Widerruf der Einwilligung durch den Abonnenten',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der personenbezogenen Daten nach Eingang des Widerrufs. Der Einwilligungsnachweis selbst wird fuer die Dauer der Nachweispflicht aufbewahrt.',
|
||||
responsibleRole: 'Marketing',
|
||||
reviewInterval: 'SEMI_ANNUAL',
|
||||
tags: ['marketing', 'einwilligung'],
|
||||
},
|
||||
{
|
||||
templateId: 'kundenstammdaten',
|
||||
dataObjectName: 'Kundenstammdaten',
|
||||
description:
|
||||
'Stammdaten von Kunden inkl. Kontaktdaten, Anschrift, Kundennummer und Kommunikationspraeferenzen.',
|
||||
affectedGroups: ['Kunden'],
|
||||
dataCategories: ['Stammdaten', 'Kontaktdaten', 'Adressdaten'],
|
||||
primaryPurpose:
|
||||
'Pflege der Kundenbeziehung, Vertragserfuellung und Absicherung gegen Verjaehrung vertraglicher Ansprueche.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB (3 Jahre).',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach letzter geschaeftlicher Interaktion',
|
||||
startEvent: 'Letzte geschaeftliche Interaktion mit dem Kunden',
|
||||
deletionMethod: 'MANUAL_REVIEW_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Manuelle Pruefung durch den Vertrieb vor Loeschung, um sicherzustellen, dass keine aktiven Geschaeftsbeziehungen oder offenen Forderungen bestehen.',
|
||||
responsibleRole: 'Vertrieb',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['crm', 'kunden'],
|
||||
},
|
||||
{
|
||||
templateId: 'crm-kontakthistorie',
|
||||
dataObjectName: 'CRM-Kontakthistorie',
|
||||
description:
|
||||
'Kontaktverlauf im CRM-System inkl. Anrufe, E-Mails, Termine, Notizen und Angebotsverlauf.',
|
||||
affectedGroups: ['Kunden', 'Interessenten'],
|
||||
dataCategories: ['Kommunikationsdaten', 'Interaktionshistorie', 'Angebotsdaten'],
|
||||
primaryPurpose:
|
||||
'Pflege der Kundenbeziehung und Nachverfolgung geschaeftlicher Interaktionen fuer Vertriebs- und Servicezwecke.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB zur Absicherung vertraglicher Ansprueche.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach letztem Kontakt mit dem Kunden oder Interessenten',
|
||||
startEvent: 'Letzter dokumentierter Kontakt im CRM-System',
|
||||
deletionMethod: 'ANONYMIZATION',
|
||||
deletionMethodDetail:
|
||||
'Anonymisierung der personenbezogenen Daten im CRM-System, sodass statistische Auswertungen weiterhin moeglich sind, aber kein Personenbezug mehr hergestellt werden kann.',
|
||||
responsibleRole: 'Vertrieb',
|
||||
reviewInterval: 'SEMI_ANNUAL',
|
||||
tags: ['crm', 'kunden'],
|
||||
},
|
||||
{
|
||||
templateId: 'kundenreklamationen',
|
||||
dataObjectName: 'Kundenreklamationen',
|
||||
description:
|
||||
'Reklamationsvorgaenge inkl. Beschwerdeinhalt, Kommunikationsverlauf, Massnahmen und Ergebnis der Reklamationsbearbeitung.',
|
||||
affectedGroups: ['Kunden'],
|
||||
dataCategories: ['Reklamationsdaten', 'Kommunikation', 'Massnahmenprotokolle'],
|
||||
primaryPurpose:
|
||||
'Dokumentation und Bearbeitung von Kundenreklamationen, Qualitaetssicherung und Absicherung gegen Gewaehrleistungsansprueche.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB (3 Jahre) zur Absicherung gegen Gewaehrleistungs- und Schadensersatzansprueche.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Abschluss des Reklamationsvorgangs',
|
||||
startEvent: 'Abschluss des Reklamationsvorgangs (letzte Massnahme)',
|
||||
deletionMethod: 'ANONYMIZATION',
|
||||
deletionMethodDetail:
|
||||
'Anonymisierung der personenbezogenen Daten nach Ablauf der Frist. Anonymisierte Reklamationsstatistiken bleiben fuer die Qualitaetssicherung erhalten.',
|
||||
responsibleRole: 'Qualitaetsmanagement',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['kunden', 'qualitaet'],
|
||||
},
|
||||
{
|
||||
templateId: 'cookie-consent-logs',
|
||||
dataObjectName: 'Cookie-Consent-Nachweise',
|
||||
description:
|
||||
'Nachweise ueber Cookie-Einwilligungen der Website-Besucher inkl. Consent-ID, Zeitstempel, gesetzte Praeferenzen und IP-Adresse.',
|
||||
affectedGroups: ['Website-Besucher'],
|
||||
dataCategories: ['Consent-Daten', 'IP-Adressen', 'Zeitstempel', 'Praeferenzen'],
|
||||
primaryPurpose:
|
||||
'Nachweisfuehrung der Einwilligung in die Cookie-Nutzung gemaess Art. 7 Abs. 1 DSGVO und ePrivacy-Richtlinie.',
|
||||
deletionTrigger: 'RETENTION_DRIVER',
|
||||
retentionDriver: 'BGB_195',
|
||||
retentionDriverDetail:
|
||||
'Aufbewahrung der Consent-Nachweise fuer die Dauer der regelmaessigen Verjaehrungsfrist gemaess 195 BGB zur Absicherung gegen Abmahnungen.',
|
||||
retentionDuration: 3,
|
||||
retentionUnit: 'YEARS',
|
||||
retentionDescription: '3 Jahre nach Zeitpunkt der Einwilligung',
|
||||
startEvent: 'Zeitpunkt der Cookie-Einwilligung (Consent-Zeitstempel)',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der Consent-Nachweise nach Ablauf der 3-Jahres-Frist durch das Consent-Management-System.',
|
||||
responsibleRole: 'Datenschutzbeauftragter',
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: ['datenschutz', 'consent'],
|
||||
},
|
||||
{
|
||||
templateId: 'social-media-daten',
|
||||
dataObjectName: 'Social-Media-Marketingdaten',
|
||||
description:
|
||||
'Personenbezogene Daten aus Social-Media-Kampagnen inkl. Nutzerinteraktionen, Custom Audiences, Retargeting-Listen und Kampagnen-Analytics.',
|
||||
affectedGroups: ['Kunden', 'Interessenten', 'Website-Besucher'],
|
||||
dataCategories: ['Interaktionsdaten', 'Zielgruppendaten', 'Tracking-Daten', 'Profilmerkmale'],
|
||||
primaryPurpose:
|
||||
'Durchfuehrung zielgerichteter Marketing-Kampagnen auf Social-Media-Plattformen und Analyse der Kampagneneffektivitaet.',
|
||||
deletionTrigger: 'PURPOSE_END',
|
||||
retentionDriver: null,
|
||||
retentionDriverDetail:
|
||||
'Keine gesetzliche Aufbewahrungspflicht. Daten werden bis zum Widerruf der Einwilligung bzw. bis zum Ende der Kampagne gespeichert (Art. 6 Abs. 1 lit. a DSGVO).',
|
||||
retentionDuration: null,
|
||||
retentionUnit: null,
|
||||
retentionDescription: 'Bis zum Widerruf der Einwilligung oder Ende des Kampagnenzwecks',
|
||||
startEvent: 'Widerruf der Einwilligung oder Ende der Marketing-Kampagne',
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail:
|
||||
'Automatische Loeschung der personenbezogenen Daten in den Social-Media-Werbekonten und internen Systemen nach Zweckwegfall. Custom Audiences werden bei Plattformanbietern geloescht.',
|
||||
responsibleRole: 'Marketing',
|
||||
reviewInterval: 'SEMI_ANNUAL',
|
||||
tags: ['marketing', 'social'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Loeschfristen Baseline-Katalog — Shared Types
|
||||
*/
|
||||
|
||||
import type {
|
||||
RetentionDriverType,
|
||||
DeletionMethodType,
|
||||
ReviewInterval,
|
||||
RetentionUnit,
|
||||
DeletionTriggerLevel,
|
||||
} from '../loeschfristen-types'
|
||||
|
||||
export interface BaselineTemplate {
|
||||
templateId: string
|
||||
dataObjectName: string
|
||||
description: string
|
||||
affectedGroups: string[]
|
||||
dataCategories: string[]
|
||||
primaryPurpose: string
|
||||
deletionTrigger: DeletionTriggerLevel
|
||||
retentionDriver: RetentionDriverType | null
|
||||
retentionDriverDetail: string
|
||||
retentionDuration: number | null
|
||||
retentionUnit: RetentionUnit | null
|
||||
retentionDescription: string
|
||||
startEvent: string
|
||||
deletionMethod: DeletionMethodType
|
||||
deletionMethodDetail: string
|
||||
responsibleRole: string
|
||||
reviewInterval: ReviewInterval
|
||||
tags: string[]
|
||||
}
|
||||
@@ -1,879 +1,8 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Module - Loeschkonzept Document Generator
|
||||
// Generates a printable, audit-ready HTML document according to DSGVO Art. 5/17/30
|
||||
// Barrel re-export — implementation split into loeschfristen-document/
|
||||
// =============================================================================
|
||||
|
||||
import type {
|
||||
LoeschfristPolicy,
|
||||
RetentionDriverType,
|
||||
} from './loeschfristen-types'
|
||||
|
||||
import {
|
||||
RETENTION_DRIVER_META,
|
||||
DELETION_METHOD_LABELS,
|
||||
STATUS_LABELS,
|
||||
TRIGGER_LABELS,
|
||||
REVIEW_INTERVAL_LABELS,
|
||||
formatRetentionDuration,
|
||||
getEffectiveDeletionTrigger,
|
||||
getActiveLegalHolds,
|
||||
} from './loeschfristen-types'
|
||||
|
||||
import type { ComplianceCheckResult, ComplianceIssueSeverity } from './loeschfristen-compliance'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface LoeschkonzeptOrgHeader {
|
||||
organizationName: string
|
||||
industry: string
|
||||
dpoName: string
|
||||
dpoContact: string
|
||||
responsiblePerson: string
|
||||
locations: string[]
|
||||
employeeCount: string
|
||||
loeschkonzeptVersion: string
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: string
|
||||
}
|
||||
|
||||
export interface LoeschkonzeptRevision {
|
||||
version: string
|
||||
date: string
|
||||
author: string
|
||||
changes: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// DEFAULTS
|
||||
// =============================================================================
|
||||
|
||||
export function createDefaultLoeschkonzeptOrgHeader(): LoeschkonzeptOrgHeader {
|
||||
const now = new Date()
|
||||
const nextYear = new Date()
|
||||
nextYear.setFullYear(nextYear.getFullYear() + 1)
|
||||
|
||||
return {
|
||||
organizationName: '',
|
||||
industry: '',
|
||||
dpoName: '',
|
||||
dpoContact: '',
|
||||
responsiblePerson: '',
|
||||
locations: [],
|
||||
employeeCount: '',
|
||||
loeschkonzeptVersion: '1.0',
|
||||
lastReviewDate: now.toISOString().split('T')[0],
|
||||
nextReviewDate: nextYear.toISOString().split('T')[0],
|
||||
reviewInterval: 'Jaehrlich',
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SEVERITY LABELS (for Compliance Status section)
|
||||
// =============================================================================
|
||||
|
||||
const SEVERITY_LABELS_DE: Record<ComplianceIssueSeverity, string> = {
|
||||
CRITICAL: 'Kritisch',
|
||||
HIGH: 'Hoch',
|
||||
MEDIUM: 'Mittel',
|
||||
LOW: 'Niedrig',
|
||||
}
|
||||
|
||||
const SEVERITY_COLORS: Record<ComplianceIssueSeverity, string> = {
|
||||
CRITICAL: '#dc2626',
|
||||
HIGH: '#ea580c',
|
||||
MEDIUM: '#d97706',
|
||||
LOW: '#6b7280',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HTML DOCUMENT BUILDER
|
||||
// =============================================================================
|
||||
|
||||
export function buildLoeschkonzeptHtml(
|
||||
policies: LoeschfristPolicy[],
|
||||
orgHeader: LoeschkonzeptOrgHeader,
|
||||
vvtActivities: Array<{ id: string; vvt_id?: string; vvtId?: string; name?: string; activity_name?: string }>,
|
||||
complianceResult: ComplianceCheckResult | null,
|
||||
revisions: LoeschkonzeptRevision[]
|
||||
): string {
|
||||
const today = new Date().toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const activePolicies = policies.filter(p => p.status !== 'ARCHIVED')
|
||||
const orgName = orgHeader.organizationName || 'Organisation'
|
||||
|
||||
// Collect unique storage locations across all policies
|
||||
const allStorageLocations = new Set<string>()
|
||||
for (const p of activePolicies) {
|
||||
for (const loc of p.storageLocations) {
|
||||
allStorageLocations.add(loc.name || loc.type)
|
||||
}
|
||||
}
|
||||
|
||||
// Collect unique responsible roles
|
||||
const roleMap = new Map<string, string[]>()
|
||||
for (const p of activePolicies) {
|
||||
const role = p.responsibleRole || p.responsiblePerson || 'Nicht zugewiesen'
|
||||
if (!roleMap.has(role)) roleMap.set(role, [])
|
||||
roleMap.get(role)!.push(p.dataObjectName || p.policyId)
|
||||
}
|
||||
|
||||
// Collect active legal holds
|
||||
const allActiveLegalHolds: Array<{ policy: string; hold: LoeschfristPolicy['legalHolds'][0] }> = []
|
||||
for (const p of activePolicies) {
|
||||
for (const h of getActiveLegalHolds(p)) {
|
||||
allActiveLegalHolds.push({ policy: p.dataObjectName || p.policyId, hold: h })
|
||||
}
|
||||
}
|
||||
|
||||
// Build VVT cross-reference data
|
||||
const vvtRefs: Array<{ policyName: string; policyId: string; vvtId: string; vvtName: string }> = []
|
||||
for (const p of activePolicies) {
|
||||
for (const linkedId of p.linkedVVTActivityIds) {
|
||||
const activity = vvtActivities.find(a => a.id === linkedId)
|
||||
if (activity) {
|
||||
vvtRefs.push({
|
||||
policyName: p.dataObjectName || p.policyId,
|
||||
policyId: p.policyId,
|
||||
vvtId: activity.vvt_id || activity.vvtId || linkedId.substring(0, 8),
|
||||
vvtName: activity.activity_name || activity.name || 'Unbenannte Verarbeitungstaetigkeit',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build vendor cross-reference data
|
||||
const vendorRefs: Array<{ policyName: string; policyId: string; vendorId: string; duration: string }> = []
|
||||
for (const p of activePolicies) {
|
||||
if (p.linkedVendorIds && p.linkedVendorIds.length > 0) {
|
||||
for (const vendorId of p.linkedVendorIds) {
|
||||
vendorRefs.push({
|
||||
policyName: p.dataObjectName || p.policyId,
|
||||
policyId: p.policyId,
|
||||
vendorId,
|
||||
duration: formatRetentionDuration(p.retentionDuration, p.retentionUnit),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// HTML Template
|
||||
// =========================================================================
|
||||
|
||||
let html = `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Loeschkonzept — ${escHtml(orgName)}</title>
|
||||
<style>
|
||||
@page { size: A4; margin: 20mm 18mm 22mm 18mm; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
/* Cover */
|
||||
.cover {
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
page-break-after: always;
|
||||
}
|
||||
.cover h1 {
|
||||
font-size: 28pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.cover .subtitle {
|
||||
font-size: 14pt;
|
||||
color: #7c3aed;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.cover .org-info {
|
||||
background: #f5f3ff;
|
||||
border: 1px solid #ddd6fe;
|
||||
border-radius: 8px;
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
width: 400px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.cover .org-info div { margin-bottom: 6px; }
|
||||
.cover .org-info .label { font-weight: 600; color: #5b21b6; display: inline-block; min-width: 160px; }
|
||||
.cover .legal-ref {
|
||||
font-size: 9pt;
|
||||
color: #64748b;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* TOC */
|
||||
.toc {
|
||||
page-break-after: always;
|
||||
padding-top: 40px;
|
||||
}
|
||||
.toc h2 {
|
||||
font-size: 18pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #5b21b6;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.toc-entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px dotted #cbd5e1;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.toc-entry .toc-num { font-weight: 600; color: #5b21b6; min-width: 40px; }
|
||||
|
||||
/* Sections */
|
||||
.section {
|
||||
page-break-inside: avoid;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.section-header {
|
||||
font-size: 14pt;
|
||||
color: #5b21b6;
|
||||
font-weight: 700;
|
||||
margin: 30px 0 12px 0;
|
||||
border-bottom: 2px solid #ddd6fe;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.section-body { margin-bottom: 16px; }
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0 16px 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
background: #f5f3ff;
|
||||
color: #5b21b6;
|
||||
font-weight: 600;
|
||||
font-size: 8.5pt;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
tr:nth-child(even) td { background: #faf5ff; }
|
||||
|
||||
/* Detail cards */
|
||||
.policy-detail {
|
||||
page-break-inside: avoid;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.policy-detail-header {
|
||||
background: #f5f3ff;
|
||||
padding: 8px 12px;
|
||||
font-weight: 700;
|
||||
color: #5b21b6;
|
||||
border-bottom: 1px solid #ddd6fe;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.policy-detail-body { padding: 0; }
|
||||
.policy-detail-body table { margin: 0; }
|
||||
.policy-detail-body th { width: 200px; }
|
||||
|
||||
/* Badges */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 1px 8px;
|
||||
border-radius: 9999px;
|
||||
font-size: 8pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
.badge-active { background: #dcfce7; color: #166534; }
|
||||
.badge-draft { background: #f3f4f6; color: #374151; }
|
||||
.badge-review { background: #fef9c3; color: #854d0e; }
|
||||
.badge-critical { background: #fecaca; color: #991b1b; }
|
||||
.badge-high { background: #fed7aa; color: #9a3412; }
|
||||
.badge-medium { background: #fef3c7; color: #92400e; }
|
||||
.badge-low { background: #f3f4f6; color: #4b5563; }
|
||||
|
||||
/* Principles */
|
||||
.principle {
|
||||
margin-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.principle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #7c3aed;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.principle strong { color: #5b21b6; }
|
||||
|
||||
/* Score */
|
||||
.score-box {
|
||||
display: inline-block;
|
||||
padding: 4px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 18pt;
|
||||
font-weight: 700;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.score-excellent { background: #dcfce7; color: #166534; }
|
||||
.score-good { background: #dbeafe; color: #1e40af; }
|
||||
.score-needs-work { background: #fef3c7; color: #92400e; }
|
||||
.score-poor { background: #fecaca; color: #991b1b; }
|
||||
|
||||
/* Footer */
|
||||
.page-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 8px 18mm;
|
||||
font-size: 7.5pt;
|
||||
color: #94a3b8;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
/* Print */
|
||||
@media print {
|
||||
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
.no-print { display: none !important; }
|
||||
.page-break { page-break-after: always; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 0: Cover Page
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="cover">
|
||||
<h1>Loeschkonzept</h1>
|
||||
<div class="subtitle">gemaess Art. 5 Abs. 1 lit. e, Art. 17, Art. 30 DSGVO</div>
|
||||
<div class="org-info">
|
||||
<div><span class="label">Organisation:</span> ${escHtml(orgName)}</div>
|
||||
${orgHeader.industry ? `<div><span class="label">Branche:</span> ${escHtml(orgHeader.industry)}</div>` : ''}
|
||||
${orgHeader.dpoName ? `<div><span class="label">Datenschutzbeauftragter:</span> ${escHtml(orgHeader.dpoName)}</div>` : ''}
|
||||
${orgHeader.dpoContact ? `<div><span class="label">DSB-Kontakt:</span> ${escHtml(orgHeader.dpoContact)}</div>` : ''}
|
||||
${orgHeader.responsiblePerson ? `<div><span class="label">Verantwortlicher:</span> ${escHtml(orgHeader.responsiblePerson)}</div>` : ''}
|
||||
${orgHeader.employeeCount ? `<div><span class="label">Mitarbeiter:</span> ${escHtml(orgHeader.employeeCount)}</div>` : ''}
|
||||
${orgHeader.locations.length > 0 ? `<div><span class="label">Standorte:</span> ${escHtml(orgHeader.locations.join(', '))}</div>` : ''}
|
||||
</div>
|
||||
<div class="legal-ref">
|
||||
Version ${escHtml(orgHeader.loeschkonzeptVersion)} | Stand: ${today}<br/>
|
||||
Letzte Pruefung: ${formatDateDE(orgHeader.lastReviewDate)} | Naechste Pruefung: ${formatDateDE(orgHeader.nextReviewDate)}<br/>
|
||||
Pruefintervall: ${escHtml(orgHeader.reviewInterval)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Table of Contents
|
||||
// =========================================================================
|
||||
const sections = [
|
||||
'Ziel und Zweck',
|
||||
'Geltungsbereich',
|
||||
'Grundprinzipien der Datenspeicherung',
|
||||
'Loeschregeln-Uebersicht',
|
||||
'Detaillierte Loeschregeln',
|
||||
'VVT-Verknuepfung',
|
||||
'Auftragsverarbeiter mit Loeschpflichten',
|
||||
'Legal Hold Verfahren',
|
||||
'Verantwortlichkeiten',
|
||||
'Pruef- und Revisionszyklus',
|
||||
'Compliance-Status',
|
||||
'Aenderungshistorie',
|
||||
]
|
||||
|
||||
html += `
|
||||
<div class="toc">
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
${sections.map((s, i) => `<div class="toc-entry"><span><span class="toc-num">${i + 1}.</span> ${escHtml(s)}</span></div>`).join('\n ')}
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 1: Ziel und Zweck
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">1. Ziel und Zweck</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Loeschkonzept definiert die systematischen Regeln und Verfahren fuer die Loeschung
|
||||
personenbezogener Daten bei <strong>${escHtml(orgName)}</strong>. Es dient der Umsetzung
|
||||
folgender DSGVO-Anforderungen:</p>
|
||||
<table>
|
||||
<tr><th>Rechtsgrundlage</th><th>Inhalt</th></tr>
|
||||
<tr><td><strong>Art. 5 Abs. 1 lit. e DSGVO</strong></td><td>Grundsatz der Speicherbegrenzung — personenbezogene Daten duerfen nur so lange gespeichert werden, wie es fuer die Zwecke der Verarbeitung erforderlich ist.</td></tr>
|
||||
<tr><td><strong>Art. 17 DSGVO</strong></td><td>Recht auf Loeschung („Recht auf Vergessenwerden“) — Betroffene haben das Recht, die Loeschung ihrer Daten zu verlangen.</td></tr>
|
||||
<tr><td><strong>Art. 30 DSGVO</strong></td><td>Verzeichnis von Verarbeitungstaetigkeiten — vorgesehene Fristen fuer die Loeschung der verschiedenen Datenkategorien muessen dokumentiert werden.</td></tr>
|
||||
</table>
|
||||
<p>Das Loeschkonzept ist fester Bestandteil des Datenschutz-Managementsystems und wird
|
||||
regelmaessig ueberprueft und aktualisiert.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 2: Geltungsbereich
|
||||
// =========================================================================
|
||||
const storageListHtml = allStorageLocations.size > 0
|
||||
? Array.from(allStorageLocations).map(s => `<li>${escHtml(s)}</li>`).join('')
|
||||
: '<li><em>Keine Speicherorte dokumentiert</em></li>'
|
||||
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">2. Geltungsbereich</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Loeschkonzept gilt fuer alle personenbezogenen Daten, die von <strong>${escHtml(orgName)}</strong>
|
||||
verarbeitet werden. Es umfasst <strong>${activePolicies.length}</strong> Loeschregeln fuer folgende Systeme und Speicherorte:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
${storageListHtml}
|
||||
</ul>
|
||||
<p>Saemtliche Verarbeitungstaetigkeiten, die im Verzeichnis von Verarbeitungstaetigkeiten (VVT)
|
||||
erfasst sind, werden durch dieses Loeschkonzept abgedeckt.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 3: Grundprinzipien
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">3. Grundprinzipien der Datenspeicherung</div>
|
||||
<div class="section-body">
|
||||
<div class="principle"><strong>Speicherbegrenzung:</strong> Personenbezogene Daten werden nur so lange gespeichert, wie es fuer den jeweiligen Verarbeitungszweck erforderlich ist (Art. 5 Abs. 1 lit. e DSGVO).</div>
|
||||
<div class="principle"><strong>3-Level-Loeschlogik:</strong> Die Loeschung folgt einer dreistufigen Priorisierung: (1) Zweckende, (2) gesetzliche Aufbewahrungspflichten, (3) Legal Hold — jeweils mit der laengsten Frist als massgeblich.</div>
|
||||
<div class="principle"><strong>Dokumentationspflicht:</strong> Jede Loeschregel ist dokumentiert mit Rechtsgrundlage, Frist, Loeschmethode und Verantwortlichkeit.</div>
|
||||
<div class="principle"><strong>Regelmaessige Ueberpruefung:</strong> Alle Loeschregeln werden im definierten Intervall ueberprueft und bei Bedarf angepasst.</div>
|
||||
<div class="principle"><strong>Datenschutz durch Technikgestaltung:</strong> Loeschmechanismen werden moeglichst automatisiert, um menschliche Fehler zu minimieren (Art. 25 DSGVO).</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 4: Loeschregeln-Uebersicht
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">4. Loeschregeln-Uebersicht</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt eine Uebersicht aller ${activePolicies.length} aktiven Loeschregeln:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>LF-Nr.</th>
|
||||
<th>Datenobjekt</th>
|
||||
<th>Loeschtrigger</th>
|
||||
<th>Aufbewahrungsfrist</th>
|
||||
<th>Loeschmethode</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
`
|
||||
for (const p of activePolicies) {
|
||||
const trigger = TRIGGER_LABELS[getEffectiveDeletionTrigger(p)]
|
||||
const duration = formatRetentionDuration(p.retentionDuration, p.retentionUnit)
|
||||
const method = DELETION_METHOD_LABELS[p.deletionMethod]
|
||||
const statusLabel = STATUS_LABELS[p.status]
|
||||
const statusClass = p.status === 'ACTIVE' ? 'badge-active' : p.status === 'REVIEW_NEEDED' ? 'badge-review' : 'badge-draft'
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(p.policyId)}</td>
|
||||
<td>${escHtml(p.dataObjectName)}</td>
|
||||
<td>${escHtml(trigger)}</td>
|
||||
<td>${escHtml(duration)}</td>
|
||||
<td>${escHtml(method)}</td>
|
||||
<td><span class="badge ${statusClass}">${escHtml(statusLabel)}</span></td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 5: Detaillierte Loeschregeln
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">5. Detaillierte Loeschregeln</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
for (const p of activePolicies) {
|
||||
const trigger = TRIGGER_LABELS[getEffectiveDeletionTrigger(p)]
|
||||
const duration = formatRetentionDuration(p.retentionDuration, p.retentionUnit)
|
||||
const method = DELETION_METHOD_LABELS[p.deletionMethod]
|
||||
const statusLabel = STATUS_LABELS[p.status]
|
||||
const driverLabel = p.retentionDriver ? RETENTION_DRIVER_META[p.retentionDriver]?.label || p.retentionDriver : '-'
|
||||
const driverStatute = p.retentionDriver ? RETENTION_DRIVER_META[p.retentionDriver]?.statute || '' : ''
|
||||
const locations = p.storageLocations.map(l => l.name || l.type).join(', ') || '-'
|
||||
const responsible = [p.responsiblePerson, p.responsibleRole].filter(s => s.trim()).join(' / ') || '-'
|
||||
const activeHolds = getActiveLegalHolds(p)
|
||||
|
||||
html += `
|
||||
<div class="policy-detail">
|
||||
<div class="policy-detail-header">
|
||||
<span>${escHtml(p.policyId)} — ${escHtml(p.dataObjectName)}</span>
|
||||
<span class="badge ${p.status === 'ACTIVE' ? 'badge-active' : 'badge-draft'}">${escHtml(statusLabel)}</span>
|
||||
</div>
|
||||
<div class="policy-detail-body">
|
||||
<table>
|
||||
<tr><th>Beschreibung</th><td>${escHtml(p.description || '-')}</td></tr>
|
||||
<tr><th>Betroffenengruppen</th><td>${escHtml(p.affectedGroups.join(', ') || '-')}</td></tr>
|
||||
<tr><th>Datenkategorien</th><td>${escHtml(p.dataCategories.join(', ') || '-')}</td></tr>
|
||||
<tr><th>Verarbeitungszweck</th><td>${escHtml(p.primaryPurpose || '-')}</td></tr>
|
||||
<tr><th>Loeschtrigger</th><td>${escHtml(trigger)}</td></tr>
|
||||
<tr><th>Aufbewahrungstreiber</th><td>${escHtml(driverLabel)}${driverStatute ? ` (${escHtml(driverStatute)})` : ''}</td></tr>
|
||||
<tr><th>Aufbewahrungsfrist</th><td>${escHtml(duration)}</td></tr>
|
||||
<tr><th>Startereignis</th><td>${escHtml(p.startEvent || '-')}</td></tr>
|
||||
<tr><th>Loeschmethode</th><td>${escHtml(method)}</td></tr>
|
||||
<tr><th>Loeschmethode (Detail)</th><td>${escHtml(p.deletionMethodDetail || '-')}</td></tr>
|
||||
<tr><th>Speicherorte</th><td>${escHtml(locations)}</td></tr>
|
||||
<tr><th>Verantwortlich</th><td>${escHtml(responsible)}</td></tr>
|
||||
<tr><th>Pruefintervall</th><td>${escHtml(REVIEW_INTERVAL_LABELS[p.reviewInterval] || p.reviewInterval)}</td></tr>
|
||||
${activeHolds.length > 0 ? `<tr><th>Aktive Legal Holds</th><td>${activeHolds.map(h => `${escHtml(h.reason)} (seit ${formatDateDE(h.startDate)})`).join('<br/>')}</td></tr>` : ''}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 6: VVT-Verknuepfung
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">6. VVT-Verknuepfung</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt die Verknuepfung zwischen Loeschregeln und Verarbeitungstaetigkeiten
|
||||
im VVT (Art. 30 DSGVO):</p>
|
||||
`
|
||||
if (vvtRefs.length > 0) {
|
||||
html += ` <table>
|
||||
<tr><th>Loeschregel</th><th>LF-Nr.</th><th>VVT-Nr.</th><th>Verarbeitungstaetigkeit</th></tr>
|
||||
`
|
||||
for (const ref of vvtRefs) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(ref.policyName)}</td>
|
||||
<td>${escHtml(ref.policyId)}</td>
|
||||
<td>${escHtml(ref.vvtId)}</td>
|
||||
<td>${escHtml(ref.vvtName)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Noch keine VVT-Verknuepfungen dokumentiert. Verknuepfen Sie Ihre Loeschregeln
|
||||
mit den entsprechenden Verarbeitungstaetigkeiten im Editor-Tab.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 7: Auftragsverarbeiter mit Loeschpflichten
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">7. Auftragsverarbeiter mit Loeschpflichten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt Loeschregeln, die mit Auftragsverarbeitern verknuepft sind.
|
||||
Diese Verknuepfungen stellen sicher, dass auch bei extern verarbeiteten Daten die Loeschpflichten
|
||||
eingehalten werden (Art. 28 DSGVO).</p>
|
||||
`
|
||||
if (vendorRefs.length > 0) {
|
||||
html += ` <table>
|
||||
<tr><th>Loeschregel</th><th>LF-Nr.</th><th>Auftragsverarbeiter (ID)</th><th>Aufbewahrungsfrist</th></tr>
|
||||
`
|
||||
for (const ref of vendorRefs) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(ref.policyName)}</td>
|
||||
<td>${escHtml(ref.policyId)}</td>
|
||||
<td>${escHtml(ref.vendorId)}</td>
|
||||
<td>${escHtml(ref.duration)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Noch keine Auftragsverarbeiter mit Loeschregeln verknuepft. Verknuepfen Sie Ihre
|
||||
Loeschregeln mit den entsprechenden Auftragsverarbeitern im Editor-Tab.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 8: Legal Hold Verfahren
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">8. Legal Hold Verfahren</div>
|
||||
<div class="section-body">
|
||||
<p>Ein Legal Hold (Aufbewahrungspflicht aufgrund rechtlicher Verfahren) setzt die regulaere
|
||||
Loeschung aus. Betroffene Daten duerfen trotz abgelaufener Loeschfrist nicht geloescht werden,
|
||||
bis der Legal Hold aufgehoben wird.</p>
|
||||
<p><strong>Verfahrensschritte:</strong></p>
|
||||
<ol style="margin: 8px 0 8px 24px;">
|
||||
<li>Rechtsabteilung/DSB identifiziert betroffene Datenkategorien</li>
|
||||
<li>Legal Hold wird im System aktiviert (Status: Aktiv)</li>
|
||||
<li>Automatische Loeschung wird fuer betroffene Policies ausgesetzt</li>
|
||||
<li>Regelmaessige Pruefung, ob der Legal Hold noch erforderlich ist</li>
|
||||
<li>Nach Aufhebung: Regulaere Loeschfristen greifen wieder</li>
|
||||
</ol>
|
||||
`
|
||||
if (allActiveLegalHolds.length > 0) {
|
||||
html += ` <p><strong>Aktuell aktive Legal Holds (${allActiveLegalHolds.length}):</strong></p>
|
||||
<table>
|
||||
<tr><th>Datenobjekt</th><th>Grund</th><th>Rechtsgrundlage</th><th>Seit</th><th>Voraussichtlich bis</th></tr>
|
||||
`
|
||||
for (const { policy, hold } of allActiveLegalHolds) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(policy)}</td>
|
||||
<td>${escHtml(hold.reason)}</td>
|
||||
<td>${escHtml(hold.legalBasis)}</td>
|
||||
<td>${formatDateDE(hold.startDate)}</td>
|
||||
<td>${hold.expectedEndDate ? formatDateDE(hold.expectedEndDate) : 'Unbefristet'}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Derzeit sind keine aktiven Legal Holds vorhanden.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 9: Verantwortlichkeiten
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">9. Verantwortlichkeiten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Rollenmatrix zeigt, welche Organisationseinheiten fuer welche Datenobjekte
|
||||
die Loeschverantwortung tragen:</p>
|
||||
<table>
|
||||
<tr><th>Rolle / Verantwortlich</th><th>Datenobjekte</th><th>Anzahl</th></tr>
|
||||
`
|
||||
for (const [role, objects] of roleMap.entries()) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(role)}</td>
|
||||
<td>${objects.map(o => escHtml(o)).join(', ')}</td>
|
||||
<td>${objects.length}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 10: Pruef- und Revisionszyklus
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">10. Pruef- und Revisionszyklus</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Eigenschaft</th><th>Wert</th></tr>
|
||||
<tr><td>Aktuelles Pruefintervall</td><td>${escHtml(orgHeader.reviewInterval)}</td></tr>
|
||||
<tr><td>Letzte Pruefung</td><td>${formatDateDE(orgHeader.lastReviewDate)}</td></tr>
|
||||
<tr><td>Naechste Pruefung</td><td>${formatDateDE(orgHeader.nextReviewDate)}</td></tr>
|
||||
<tr><td>Aktuelle Version</td><td>${escHtml(orgHeader.loeschkonzeptVersion)}</td></tr>
|
||||
</table>
|
||||
<p style="margin-top: 8px;">Bei jeder Pruefung wird das Loeschkonzept auf folgende Punkte ueberprueft:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li>Vollstaendigkeit aller Loeschregeln (neue Verarbeitungen erfasst?)</li>
|
||||
<li>Aktualitaet der gesetzlichen Aufbewahrungsfristen</li>
|
||||
<li>Wirksamkeit der technischen Loeschmechanismen</li>
|
||||
<li>Einhaltung der definierten Loeschfristen</li>
|
||||
<li>Angemessenheit der Verantwortlichkeiten</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 11: Compliance-Status
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">11. Compliance-Status</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (complianceResult) {
|
||||
const scoreClass = complianceResult.score >= 90 ? 'score-excellent'
|
||||
: complianceResult.score >= 75 ? 'score-good'
|
||||
: complianceResult.score >= 50 ? 'score-needs-work'
|
||||
: 'score-poor'
|
||||
const scoreLabel = complianceResult.score >= 90 ? 'Ausgezeichnet'
|
||||
: complianceResult.score >= 75 ? 'Gut'
|
||||
: complianceResult.score >= 50 ? 'Verbesserungswuerdig'
|
||||
: 'Mangelhaft'
|
||||
|
||||
html += ` <p><span class="score-box ${scoreClass}">${complianceResult.score}/100</span> ${escHtml(scoreLabel)}</p>
|
||||
<table style="margin-top: 12px;">
|
||||
<tr><th>Kennzahl</th><th>Wert</th></tr>
|
||||
<tr><td>Gepruefte Policies</td><td>${complianceResult.stats.total}</td></tr>
|
||||
<tr><td>Bestanden</td><td>${complianceResult.stats.passed}</td></tr>
|
||||
<tr><td>Beanstandungen</td><td>${complianceResult.stats.failed}</td></tr>
|
||||
</table>
|
||||
`
|
||||
if (complianceResult.issues.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Befunde nach Schweregrad:</strong></p>
|
||||
<table>
|
||||
<tr><th>Schweregrad</th><th>Anzahl</th><th>Befunde</th></tr>
|
||||
`
|
||||
const severityOrder: ComplianceIssueSeverity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
for (const sev of severityOrder) {
|
||||
const count = complianceResult.stats.bySeverity[sev]
|
||||
if (count === 0) continue
|
||||
const issuesForSev = complianceResult.issues.filter(i => i.severity === sev)
|
||||
html += ` <tr>
|
||||
<td><span class="badge badge-${sev.toLowerCase()}" style="color: ${SEVERITY_COLORS[sev]}">${SEVERITY_LABELS_DE[sev]}</span></td>
|
||||
<td>${count}</td>
|
||||
<td>${issuesForSev.map(i => escHtml(i.title)).join('; ')}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p style="margin-top: 8px;"><em>Keine Beanstandungen. Alle Policies sind konform.</em></p>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <p><em>Compliance-Check wurde noch nicht ausgefuehrt. Fuehren Sie den Check im
|
||||
Export-Tab durch, um den Status in das Dokument aufzunehmen.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 12: Aenderungshistorie
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">12. Aenderungshistorie</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Version</th><th>Datum</th><th>Autor</th><th>Aenderungen</th></tr>
|
||||
`
|
||||
if (revisions.length > 0) {
|
||||
for (const rev of revisions) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(rev.version)}</td>
|
||||
<td>${formatDateDE(rev.date)}</td>
|
||||
<td>${escHtml(rev.author)}</td>
|
||||
<td>${escHtml(rev.changes)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(orgHeader.loeschkonzeptVersion)}</td>
|
||||
<td>${today}</td>
|
||||
<td>${escHtml(orgHeader.dpoName || orgHeader.responsiblePerson || '-')}</td>
|
||||
<td>Erstversion des Loeschkonzepts</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Footer
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="page-footer">
|
||||
<span>Loeschkonzept — ${escHtml(orgName)}</span>
|
||||
<span>Stand: ${today} | Version ${escHtml(orgHeader.loeschkonzeptVersion)}</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// INTERNAL HELPERS
|
||||
// =============================================================================
|
||||
|
||||
function escHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
function formatDateDE(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '-'
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
if (isNaN(date.getTime())) return '-'
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
} catch {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
export type { LoeschkonzeptOrgHeader, LoeschkonzeptRevision } from './loeschfristen-document/types-defaults'
|
||||
export { createDefaultLoeschkonzeptOrgHeader } from './loeschfristen-document/types-defaults'
|
||||
export { buildLoeschkonzeptHtml } from './loeschfristen-document/html-builder'
|
||||
|
||||
26
admin-compliance/lib/sdk/loeschfristen-document/helpers.ts
Normal file
26
admin-compliance/lib/sdk/loeschfristen-document/helpers.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Document — Internal Helpers
|
||||
// =============================================================================
|
||||
|
||||
export function escHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
export function formatDateDE(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '-'
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
if (isNaN(date.getTime())) return '-'
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
} catch {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
603
admin-compliance/lib/sdk/loeschfristen-document/html-builder.ts
Normal file
603
admin-compliance/lib/sdk/loeschfristen-document/html-builder.ts
Normal file
@@ -0,0 +1,603 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Document — HTML Document Builder
|
||||
// =============================================================================
|
||||
|
||||
import type {
|
||||
LoeschfristPolicy,
|
||||
} from '../loeschfristen-types'
|
||||
import {
|
||||
RETENTION_DRIVER_META,
|
||||
DELETION_METHOD_LABELS,
|
||||
STATUS_LABELS,
|
||||
TRIGGER_LABELS,
|
||||
REVIEW_INTERVAL_LABELS,
|
||||
formatRetentionDuration,
|
||||
getEffectiveDeletionTrigger,
|
||||
getActiveLegalHolds,
|
||||
} from '../loeschfristen-types'
|
||||
import type { ComplianceCheckResult, ComplianceIssueSeverity } from '../loeschfristen-compliance'
|
||||
import type { LoeschkonzeptOrgHeader, LoeschkonzeptRevision } from './types-defaults'
|
||||
import { SEVERITY_LABELS_DE, SEVERITY_COLORS } from './types-defaults'
|
||||
import { escHtml, formatDateDE } from './helpers'
|
||||
|
||||
type VVTActivity = { id: string; vvt_id?: string; vvtId?: string; name?: string; activity_name?: string }
|
||||
|
||||
export function buildLoeschkonzeptHtml(
|
||||
policies: LoeschfristPolicy[],
|
||||
orgHeader: LoeschkonzeptOrgHeader,
|
||||
vvtActivities: VVTActivity[],
|
||||
complianceResult: ComplianceCheckResult | null,
|
||||
revisions: LoeschkonzeptRevision[]
|
||||
): string {
|
||||
const today = new Date().toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const activePolicies = policies.filter(p => p.status !== 'ARCHIVED')
|
||||
const orgName = orgHeader.organizationName || 'Organisation'
|
||||
|
||||
// Collect unique storage locations
|
||||
const allStorageLocations = new Set<string>()
|
||||
for (const p of activePolicies) {
|
||||
for (const loc of p.storageLocations) {
|
||||
allStorageLocations.add(loc.name || loc.type)
|
||||
}
|
||||
}
|
||||
|
||||
// Collect unique responsible roles
|
||||
const roleMap = new Map<string, string[]>()
|
||||
for (const p of activePolicies) {
|
||||
const role = p.responsibleRole || p.responsiblePerson || 'Nicht zugewiesen'
|
||||
if (!roleMap.has(role)) roleMap.set(role, [])
|
||||
roleMap.get(role)!.push(p.dataObjectName || p.policyId)
|
||||
}
|
||||
|
||||
// Collect active legal holds
|
||||
const allActiveLegalHolds: Array<{ policy: string; hold: LoeschfristPolicy['legalHolds'][0] }> = []
|
||||
for (const p of activePolicies) {
|
||||
for (const h of getActiveLegalHolds(p)) {
|
||||
allActiveLegalHolds.push({ policy: p.dataObjectName || p.policyId, hold: h })
|
||||
}
|
||||
}
|
||||
|
||||
// Build VVT cross-reference data
|
||||
const vvtRefs: Array<{ policyName: string; policyId: string; vvtId: string; vvtName: string }> = []
|
||||
for (const p of activePolicies) {
|
||||
for (const linkedId of p.linkedVVTActivityIds) {
|
||||
const activity = vvtActivities.find(a => a.id === linkedId)
|
||||
if (activity) {
|
||||
vvtRefs.push({
|
||||
policyName: p.dataObjectName || p.policyId,
|
||||
policyId: p.policyId,
|
||||
vvtId: activity.vvt_id || activity.vvtId || linkedId.substring(0, 8),
|
||||
vvtName: activity.activity_name || activity.name || 'Unbenannte Verarbeitungstaetigkeit',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build vendor cross-reference data
|
||||
const vendorRefs: Array<{ policyName: string; policyId: string; vendorId: string; duration: string }> = []
|
||||
for (const p of activePolicies) {
|
||||
if (p.linkedVendorIds && p.linkedVendorIds.length > 0) {
|
||||
for (const vendorId of p.linkedVendorIds) {
|
||||
vendorRefs.push({
|
||||
policyName: p.dataObjectName || p.policyId,
|
||||
policyId: p.policyId,
|
||||
vendorId,
|
||||
duration: formatRetentionDuration(p.retentionDuration, p.retentionUnit),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let html = buildDocumentHeader(orgName)
|
||||
html += buildCoverAndTOC(orgHeader, orgName, today)
|
||||
html += buildSections1to5(orgName, orgHeader, activePolicies, allStorageLocations)
|
||||
html += buildSections6to9(activePolicies, vvtRefs, vendorRefs, allActiveLegalHolds, roleMap)
|
||||
html += buildSections10to12(orgHeader, complianceResult, revisions, today)
|
||||
html += `
|
||||
<div class="page-footer">
|
||||
<span>Loeschkonzept — ${escHtml(orgName)}</span>
|
||||
<span>Stand: ${today} | Version ${escHtml(orgHeader.loeschkonzeptVersion)}</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
function buildDocumentHeader(orgName: string): string {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Loeschkonzept — ${escHtml(orgName)}</title>
|
||||
<style>
|
||||
@page { size: A4; margin: 20mm 18mm 22mm 18mm; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-size: 10pt; line-height: 1.5; color: #1e293b; }
|
||||
.cover { min-height: 90vh; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; page-break-after: always; }
|
||||
.cover h1 { font-size: 28pt; color: #5b21b6; margin-bottom: 8px; font-weight: 700; }
|
||||
.cover .subtitle { font-size: 14pt; color: #7c3aed; margin-bottom: 40px; }
|
||||
.cover .org-info { background: #f5f3ff; border: 1px solid #ddd6fe; border-radius: 8px; padding: 24px 40px; text-align: left; width: 400px; margin-bottom: 24px; }
|
||||
.cover .org-info div { margin-bottom: 6px; }
|
||||
.cover .org-info .label { font-weight: 600; color: #5b21b6; display: inline-block; min-width: 160px; }
|
||||
.cover .legal-ref { font-size: 9pt; color: #64748b; margin-top: 20px; }
|
||||
.toc { page-break-after: always; padding-top: 40px; }
|
||||
.toc h2 { font-size: 18pt; color: #5b21b6; margin-bottom: 20px; border-bottom: 2px solid #5b21b6; padding-bottom: 8px; }
|
||||
.toc-entry { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px dotted #cbd5e1; font-size: 10pt; }
|
||||
.toc-entry .toc-num { font-weight: 600; color: #5b21b6; min-width: 40px; }
|
||||
.section { page-break-inside: avoid; margin-bottom: 24px; }
|
||||
.section-header { font-size: 14pt; color: #5b21b6; font-weight: 700; margin: 30px 0 12px 0; border-bottom: 2px solid #ddd6fe; padding-bottom: 6px; }
|
||||
.section-body { margin-bottom: 16px; }
|
||||
table { width: 100%; border-collapse: collapse; margin: 10px 0 16px 0; font-size: 9pt; }
|
||||
th, td { border: 1px solid #e2e8f0; padding: 6px 8px; text-align: left; vertical-align: top; }
|
||||
th { background: #f5f3ff; color: #5b21b6; font-weight: 600; font-size: 8.5pt; text-transform: uppercase; letter-spacing: 0.3px; }
|
||||
tr:nth-child(even) td { background: #faf5ff; }
|
||||
.policy-detail { page-break-inside: avoid; border: 1px solid #e2e8f0; border-radius: 6px; margin-bottom: 16px; overflow: hidden; }
|
||||
.policy-detail-header { background: #f5f3ff; padding: 8px 12px; font-weight: 700; color: #5b21b6; border-bottom: 1px solid #ddd6fe; display: flex; justify-content: space-between; }
|
||||
.policy-detail-body { padding: 0; }
|
||||
.policy-detail-body table { margin: 0; }
|
||||
.policy-detail-body th { width: 200px; }
|
||||
.badge { display: inline-block; padding: 1px 8px; border-radius: 9999px; font-size: 8pt; font-weight: 600; }
|
||||
.badge-active { background: #dcfce7; color: #166534; }
|
||||
.badge-draft { background: #f3f4f6; color: #374151; }
|
||||
.badge-review { background: #fef9c3; color: #854d0e; }
|
||||
.badge-critical { background: #fecaca; color: #991b1b; }
|
||||
.badge-high { background: #fed7aa; color: #9a3412; }
|
||||
.badge-medium { background: #fef3c7; color: #92400e; }
|
||||
.badge-low { background: #f3f4f6; color: #4b5563; }
|
||||
.principle { margin-bottom: 10px; padding-left: 20px; position: relative; }
|
||||
.principle::before { content: ''; position: absolute; left: 0; top: 6px; width: 10px; height: 10px; background: #7c3aed; border-radius: 50%; }
|
||||
.principle strong { color: #5b21b6; }
|
||||
.score-box { display: inline-block; padding: 4px 16px; border-radius: 8px; font-size: 18pt; font-weight: 700; margin-right: 12px; }
|
||||
.score-excellent { background: #dcfce7; color: #166534; }
|
||||
.score-good { background: #dbeafe; color: #1e40af; }
|
||||
.score-needs-work { background: #fef3c7; color: #92400e; }
|
||||
.score-poor { background: #fecaca; color: #991b1b; }
|
||||
.page-footer { position: fixed; bottom: 0; left: 0; right: 0; padding: 8px 18mm; font-size: 7.5pt; color: #94a3b8; display: flex; justify-content: space-between; border-top: 1px solid #e2e8f0; }
|
||||
@media print { body { -webkit-print-color-adjust: exact; print-color-adjust: exact; } .no-print { display: none !important; } .page-break { page-break-after: always; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
}
|
||||
|
||||
function buildCoverAndTOC(
|
||||
orgHeader: LoeschkonzeptOrgHeader,
|
||||
orgName: string,
|
||||
today: string
|
||||
): string {
|
||||
const sections = [
|
||||
'Ziel und Zweck', 'Geltungsbereich', 'Grundprinzipien der Datenspeicherung',
|
||||
'Loeschregeln-Uebersicht', 'Detaillierte Loeschregeln', 'VVT-Verknuepfung',
|
||||
'Auftragsverarbeiter mit Loeschpflichten', 'Legal Hold Verfahren',
|
||||
'Verantwortlichkeiten', 'Pruef- und Revisionszyklus', 'Compliance-Status', 'Aenderungshistorie',
|
||||
]
|
||||
|
||||
return `
|
||||
<div class="cover">
|
||||
<h1>Loeschkonzept</h1>
|
||||
<div class="subtitle">gemaess Art. 5 Abs. 1 lit. e, Art. 17, Art. 30 DSGVO</div>
|
||||
<div class="org-info">
|
||||
<div><span class="label">Organisation:</span> ${escHtml(orgName)}</div>
|
||||
${orgHeader.industry ? `<div><span class="label">Branche:</span> ${escHtml(orgHeader.industry)}</div>` : ''}
|
||||
${orgHeader.dpoName ? `<div><span class="label">Datenschutzbeauftragter:</span> ${escHtml(orgHeader.dpoName)}</div>` : ''}
|
||||
${orgHeader.dpoContact ? `<div><span class="label">DSB-Kontakt:</span> ${escHtml(orgHeader.dpoContact)}</div>` : ''}
|
||||
${orgHeader.responsiblePerson ? `<div><span class="label">Verantwortlicher:</span> ${escHtml(orgHeader.responsiblePerson)}</div>` : ''}
|
||||
${orgHeader.employeeCount ? `<div><span class="label">Mitarbeiter:</span> ${escHtml(orgHeader.employeeCount)}</div>` : ''}
|
||||
${orgHeader.locations.length > 0 ? `<div><span class="label">Standorte:</span> ${escHtml(orgHeader.locations.join(', '))}</div>` : ''}
|
||||
</div>
|
||||
<div class="legal-ref">
|
||||
Version ${escHtml(orgHeader.loeschkonzeptVersion)} | Stand: ${today}<br/>
|
||||
Letzte Pruefung: ${formatDateDE(orgHeader.lastReviewDate)} | Naechste Pruefung: ${formatDateDE(orgHeader.nextReviewDate)}<br/>
|
||||
Pruefintervall: ${escHtml(orgHeader.reviewInterval)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="toc">
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
${sections.map((s, i) => `<div class="toc-entry"><span><span class="toc-num">${i + 1}.</span> ${escHtml(s)}</span></div>`).join('\n ')}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSections1to5(
|
||||
orgName: string,
|
||||
orgHeader: LoeschkonzeptOrgHeader,
|
||||
activePolicies: LoeschfristPolicy[],
|
||||
allStorageLocations: Set<string>
|
||||
): string {
|
||||
const storageListHtml = allStorageLocations.size > 0
|
||||
? Array.from(allStorageLocations).map(s => `<li>${escHtml(s)}</li>`).join('')
|
||||
: '<li><em>Keine Speicherorte dokumentiert</em></li>'
|
||||
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">1. Ziel und Zweck</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Loeschkonzept definiert die systematischen Regeln und Verfahren fuer die Loeschung
|
||||
personenbezogener Daten bei <strong>${escHtml(orgName)}</strong>. Es dient der Umsetzung
|
||||
folgender DSGVO-Anforderungen:</p>
|
||||
<table>
|
||||
<tr><th>Rechtsgrundlage</th><th>Inhalt</th></tr>
|
||||
<tr><td><strong>Art. 5 Abs. 1 lit. e DSGVO</strong></td><td>Grundsatz der Speicherbegrenzung — personenbezogene Daten duerfen nur so lange gespeichert werden, wie es fuer die Zwecke der Verarbeitung erforderlich ist.</td></tr>
|
||||
<tr><td><strong>Art. 17 DSGVO</strong></td><td>Recht auf Loeschung („Recht auf Vergessenwerden“) — Betroffene haben das Recht, die Loeschung ihrer Daten zu verlangen.</td></tr>
|
||||
<tr><td><strong>Art. 30 DSGVO</strong></td><td>Verzeichnis von Verarbeitungstaetigkeiten — vorgesehene Fristen fuer die Loeschung der verschiedenen Datenkategorien muessen dokumentiert werden.</td></tr>
|
||||
</table>
|
||||
<p>Das Loeschkonzept ist fester Bestandteil des Datenschutz-Managementsystems und wird
|
||||
regelmaessig ueberprueft und aktualisiert.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">2. Geltungsbereich</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Loeschkonzept gilt fuer alle personenbezogenen Daten, die von <strong>${escHtml(orgName)}</strong>
|
||||
verarbeitet werden. Es umfasst <strong>${activePolicies.length}</strong> Loeschregeln fuer folgende Systeme und Speicherorte:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">${storageListHtml}</ul>
|
||||
<p>Saemtliche Verarbeitungstaetigkeiten, die im Verzeichnis von Verarbeitungstaetigkeiten (VVT)
|
||||
erfasst sind, werden durch dieses Loeschkonzept abgedeckt.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">3. Grundprinzipien der Datenspeicherung</div>
|
||||
<div class="section-body">
|
||||
<div class="principle"><strong>Speicherbegrenzung:</strong> Personenbezogene Daten werden nur so lange gespeichert, wie es fuer den jeweiligen Verarbeitungszweck erforderlich ist (Art. 5 Abs. 1 lit. e DSGVO).</div>
|
||||
<div class="principle"><strong>3-Level-Loeschlogik:</strong> Die Loeschung folgt einer dreistufigen Priorisierung: (1) Zweckende, (2) gesetzliche Aufbewahrungspflichten, (3) Legal Hold — jeweils mit der laengsten Frist als massgeblich.</div>
|
||||
<div class="principle"><strong>Dokumentationspflicht:</strong> Jede Loeschregel ist dokumentiert mit Rechtsgrundlage, Frist, Loeschmethode und Verantwortlichkeit.</div>
|
||||
<div class="principle"><strong>Regelmaessige Ueberpruefung:</strong> Alle Loeschregeln werden im definierten Intervall ueberprueft und bei Bedarf angepasst.</div>
|
||||
<div class="principle"><strong>Datenschutz durch Technikgestaltung:</strong> Loeschmechanismen werden moeglichst automatisiert, um menschliche Fehler zu minimieren (Art. 25 DSGVO).</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${buildSection4Overview(activePolicies)}
|
||||
${buildSection5Detail(activePolicies)}
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection4Overview(activePolicies: LoeschfristPolicy[]): string {
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">4. Loeschregeln-Uebersicht</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt eine Uebersicht aller ${activePolicies.length} aktiven Loeschregeln:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>LF-Nr.</th>
|
||||
<th>Datenobjekt</th>
|
||||
<th>Loeschtrigger</th>
|
||||
<th>Aufbewahrungsfrist</th>
|
||||
<th>Loeschmethode</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
`
|
||||
for (const p of activePolicies) {
|
||||
const trigger = TRIGGER_LABELS[getEffectiveDeletionTrigger(p)]
|
||||
const duration = formatRetentionDuration(p.retentionDuration, p.retentionUnit)
|
||||
const method = DELETION_METHOD_LABELS[p.deletionMethod]
|
||||
const statusLabel = STATUS_LABELS[p.status]
|
||||
const statusClass = p.status === 'ACTIVE' ? 'badge-active' : p.status === 'REVIEW_NEEDED' ? 'badge-review' : 'badge-draft'
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(p.policyId)}</td>
|
||||
<td>${escHtml(p.dataObjectName)}</td>
|
||||
<td>${escHtml(trigger)}</td>
|
||||
<td>${escHtml(duration)}</td>
|
||||
<td>${escHtml(method)}</td>
|
||||
<td><span class="badge ${statusClass}">${escHtml(statusLabel)}</span></td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection5Detail(activePolicies: LoeschfristPolicy[]): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">5. Detaillierte Loeschregeln</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
for (const p of activePolicies) {
|
||||
const trigger = TRIGGER_LABELS[getEffectiveDeletionTrigger(p)]
|
||||
const duration = formatRetentionDuration(p.retentionDuration, p.retentionUnit)
|
||||
const method = DELETION_METHOD_LABELS[p.deletionMethod]
|
||||
const statusLabel = STATUS_LABELS[p.status]
|
||||
const driverLabel = p.retentionDriver ? RETENTION_DRIVER_META[p.retentionDriver]?.label || p.retentionDriver : '-'
|
||||
const driverStatute = p.retentionDriver ? RETENTION_DRIVER_META[p.retentionDriver]?.statute || '' : ''
|
||||
const locations = p.storageLocations.map(l => l.name || l.type).join(', ') || '-'
|
||||
const responsible = [p.responsiblePerson, p.responsibleRole].filter(s => s.trim()).join(' / ') || '-'
|
||||
const activeHolds = getActiveLegalHolds(p)
|
||||
|
||||
html += `
|
||||
<div class="policy-detail">
|
||||
<div class="policy-detail-header">
|
||||
<span>${escHtml(p.policyId)} — ${escHtml(p.dataObjectName)}</span>
|
||||
<span class="badge ${p.status === 'ACTIVE' ? 'badge-active' : 'badge-draft'}">${escHtml(statusLabel)}</span>
|
||||
</div>
|
||||
<div class="policy-detail-body">
|
||||
<table>
|
||||
<tr><th>Beschreibung</th><td>${escHtml(p.description || '-')}</td></tr>
|
||||
<tr><th>Betroffenengruppen</th><td>${escHtml(p.affectedGroups.join(', ') || '-')}</td></tr>
|
||||
<tr><th>Datenkategorien</th><td>${escHtml(p.dataCategories.join(', ') || '-')}</td></tr>
|
||||
<tr><th>Verarbeitungszweck</th><td>${escHtml(p.primaryPurpose || '-')}</td></tr>
|
||||
<tr><th>Loeschtrigger</th><td>${escHtml(trigger)}</td></tr>
|
||||
<tr><th>Aufbewahrungstreiber</th><td>${escHtml(driverLabel)}${driverStatute ? ` (${escHtml(driverStatute)})` : ''}</td></tr>
|
||||
<tr><th>Aufbewahrungsfrist</th><td>${escHtml(duration)}</td></tr>
|
||||
<tr><th>Startereignis</th><td>${escHtml(p.startEvent || '-')}</td></tr>
|
||||
<tr><th>Loeschmethode</th><td>${escHtml(method)}</td></tr>
|
||||
<tr><th>Loeschmethode (Detail)</th><td>${escHtml(p.deletionMethodDetail || '-')}</td></tr>
|
||||
<tr><th>Speicherorte</th><td>${escHtml(locations)}</td></tr>
|
||||
<tr><th>Verantwortlich</th><td>${escHtml(responsible)}</td></tr>
|
||||
<tr><th>Pruefintervall</th><td>${escHtml(REVIEW_INTERVAL_LABELS[p.reviewInterval] || p.reviewInterval)}</td></tr>
|
||||
${activeHolds.length > 0 ? `<tr><th>Aktive Legal Holds</th><td>${activeHolds.map(h => `${escHtml(h.reason)} (seit ${formatDateDE(h.startDate)})`).join('<br/>')}</td></tr>` : ''}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSections6to9(
|
||||
activePolicies: LoeschfristPolicy[],
|
||||
vvtRefs: Array<{ policyName: string; policyId: string; vvtId: string; vvtName: string }>,
|
||||
vendorRefs: Array<{ policyName: string; policyId: string; vendorId: string; duration: string }>,
|
||||
allActiveLegalHolds: Array<{ policy: string; hold: LoeschfristPolicy['legalHolds'][0] }>,
|
||||
roleMap: Map<string, string[]>
|
||||
): string {
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">6. VVT-Verknuepfung</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt die Verknuepfung zwischen Loeschregeln und Verarbeitungstaetigkeiten im VVT (Art. 30 DSGVO):</p>
|
||||
`
|
||||
if (vvtRefs.length > 0) {
|
||||
html += ` <table>
|
||||
<tr><th>Loeschregel</th><th>LF-Nr.</th><th>VVT-Nr.</th><th>Verarbeitungstaetigkeit</th></tr>
|
||||
`
|
||||
for (const ref of vvtRefs) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(ref.policyName)}</td>
|
||||
<td>${escHtml(ref.policyId)}</td>
|
||||
<td>${escHtml(ref.vvtId)}</td>
|
||||
<td>${escHtml(ref.vvtName)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Noch keine VVT-Verknuepfungen dokumentiert.</em></p>
|
||||
`
|
||||
}
|
||||
html += ` </div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">7. Auftragsverarbeiter mit Loeschpflichten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt Loeschregeln, die mit Auftragsverarbeitern verknuepft sind (Art. 28 DSGVO).</p>
|
||||
`
|
||||
if (vendorRefs.length > 0) {
|
||||
html += ` <table>
|
||||
<tr><th>Loeschregel</th><th>LF-Nr.</th><th>Auftragsverarbeiter (ID)</th><th>Aufbewahrungsfrist</th></tr>
|
||||
`
|
||||
for (const ref of vendorRefs) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(ref.policyName)}</td>
|
||||
<td>${escHtml(ref.policyId)}</td>
|
||||
<td>${escHtml(ref.vendorId)}</td>
|
||||
<td>${escHtml(ref.duration)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Noch keine Auftragsverarbeiter mit Loeschregeln verknuepft.</em></p>
|
||||
`
|
||||
}
|
||||
html += ` </div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">8. Legal Hold Verfahren</div>
|
||||
<div class="section-body">
|
||||
<p>Ein Legal Hold setzt die regulaere Loeschung aus. Betroffene Daten duerfen trotz abgelaufener Loeschfrist nicht geloescht werden, bis der Legal Hold aufgehoben wird.</p>
|
||||
<p><strong>Verfahrensschritte:</strong></p>
|
||||
<ol style="margin: 8px 0 8px 24px;">
|
||||
<li>Rechtsabteilung/DSB identifiziert betroffene Datenkategorien</li>
|
||||
<li>Legal Hold wird im System aktiviert (Status: Aktiv)</li>
|
||||
<li>Automatische Loeschung wird fuer betroffene Policies ausgesetzt</li>
|
||||
<li>Regelmaessige Pruefung, ob der Legal Hold noch erforderlich ist</li>
|
||||
<li>Nach Aufhebung: Regulaere Loeschfristen greifen wieder</li>
|
||||
</ol>
|
||||
`
|
||||
if (allActiveLegalHolds.length > 0) {
|
||||
html += ` <p><strong>Aktuell aktive Legal Holds (${allActiveLegalHolds.length}):</strong></p>
|
||||
<table>
|
||||
<tr><th>Datenobjekt</th><th>Grund</th><th>Rechtsgrundlage</th><th>Seit</th><th>Voraussichtlich bis</th></tr>
|
||||
`
|
||||
for (const { policy, hold } of allActiveLegalHolds) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(policy)}</td>
|
||||
<td>${escHtml(hold.reason)}</td>
|
||||
<td>${escHtml(hold.legalBasis)}</td>
|
||||
<td>${formatDateDE(hold.startDate)}</td>
|
||||
<td>${hold.expectedEndDate ? formatDateDE(hold.expectedEndDate) : 'Unbefristet'}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Derzeit sind keine aktiven Legal Holds vorhanden.</em></p>
|
||||
`
|
||||
}
|
||||
html += ` </div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">9. Verantwortlichkeiten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Rollenmatrix zeigt, welche Organisationseinheiten fuer welche Datenobjekte die Loeschverantwortung tragen:</p>
|
||||
<table>
|
||||
<tr><th>Rolle / Verantwortlich</th><th>Datenobjekte</th><th>Anzahl</th></tr>
|
||||
`
|
||||
for (const [role, objects] of roleMap.entries()) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(role)}</td>
|
||||
<td>${objects.map(o => escHtml(o)).join(', ')}</td>
|
||||
<td>${objects.length}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSections10to12(
|
||||
orgHeader: LoeschkonzeptOrgHeader,
|
||||
complianceResult: ComplianceCheckResult | null,
|
||||
revisions: LoeschkonzeptRevision[],
|
||||
today: string
|
||||
): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">10. Pruef- und Revisionszyklus</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Eigenschaft</th><th>Wert</th></tr>
|
||||
<tr><td>Aktuelles Pruefintervall</td><td>${escHtml(orgHeader.reviewInterval)}</td></tr>
|
||||
<tr><td>Letzte Pruefung</td><td>${formatDateDE(orgHeader.lastReviewDate)}</td></tr>
|
||||
<tr><td>Naechste Pruefung</td><td>${formatDateDE(orgHeader.nextReviewDate)}</td></tr>
|
||||
<tr><td>Aktuelle Version</td><td>${escHtml(orgHeader.loeschkonzeptVersion)}</td></tr>
|
||||
</table>
|
||||
<p style="margin-top: 8px;">Bei jeder Pruefung wird das Loeschkonzept auf folgende Punkte ueberprueft:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li>Vollstaendigkeit aller Loeschregeln (neue Verarbeitungen erfasst?)</li>
|
||||
<li>Aktualitaet der gesetzlichen Aufbewahrungsfristen</li>
|
||||
<li>Wirksamkeit der technischen Loeschmechanismen</li>
|
||||
<li>Einhaltung der definierten Loeschfristen</li>
|
||||
<li>Angemessenheit der Verantwortlichkeiten</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
html += buildSection11Compliance(complianceResult)
|
||||
html += buildSection12History(revisions, orgHeader, today)
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection11Compliance(complianceResult: ComplianceCheckResult | null): string {
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">11. Compliance-Status</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (complianceResult) {
|
||||
const scoreClass = complianceResult.score >= 90 ? 'score-excellent'
|
||||
: complianceResult.score >= 75 ? 'score-good'
|
||||
: complianceResult.score >= 50 ? 'score-needs-work'
|
||||
: 'score-poor'
|
||||
const scoreLabel = complianceResult.score >= 90 ? 'Ausgezeichnet'
|
||||
: complianceResult.score >= 75 ? 'Gut'
|
||||
: complianceResult.score >= 50 ? 'Verbesserungswuerdig'
|
||||
: 'Mangelhaft'
|
||||
|
||||
html += ` <p><span class="score-box ${scoreClass}">${complianceResult.score}/100</span> ${escHtml(scoreLabel)}</p>
|
||||
<table style="margin-top: 12px;">
|
||||
<tr><th>Kennzahl</th><th>Wert</th></tr>
|
||||
<tr><td>Gepruefte Policies</td><td>${complianceResult.stats.total}</td></tr>
|
||||
<tr><td>Bestanden</td><td>${complianceResult.stats.passed}</td></tr>
|
||||
<tr><td>Beanstandungen</td><td>${complianceResult.stats.failed}</td></tr>
|
||||
</table>
|
||||
`
|
||||
if (complianceResult.issues.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Befunde nach Schweregrad:</strong></p>
|
||||
<table>
|
||||
<tr><th>Schweregrad</th><th>Anzahl</th><th>Befunde</th></tr>
|
||||
`
|
||||
const severityOrder: ComplianceIssueSeverity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
for (const sev of severityOrder) {
|
||||
const count = complianceResult.stats.bySeverity[sev]
|
||||
if (count === 0) continue
|
||||
const issuesForSev = complianceResult.issues.filter(i => i.severity === sev)
|
||||
html += ` <tr>
|
||||
<td><span class="badge badge-${sev.toLowerCase()}" style="color: ${SEVERITY_COLORS[sev]}">${SEVERITY_LABELS_DE[sev]}</span></td>
|
||||
<td>${count}</td>
|
||||
<td>${issuesForSev.map(i => escHtml(i.title)).join('; ')}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p style="margin-top: 8px;"><em>Keine Beanstandungen. Alle Policies sind konform.</em></p>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <p><em>Compliance-Check wurde noch nicht ausgefuehrt.</em></p>
|
||||
`
|
||||
}
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection12History(
|
||||
revisions: LoeschkonzeptRevision[],
|
||||
orgHeader: LoeschkonzeptOrgHeader,
|
||||
today: string
|
||||
): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">12. Aenderungshistorie</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Version</th><th>Datum</th><th>Autor</th><th>Aenderungen</th></tr>
|
||||
`
|
||||
if (revisions.length > 0) {
|
||||
for (const rev of revisions) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(rev.version)}</td>
|
||||
<td>${formatDateDE(rev.date)}</td>
|
||||
<td>${escHtml(rev.author)}</td>
|
||||
<td>${escHtml(rev.changes)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(orgHeader.loeschkonzeptVersion)}</td>
|
||||
<td>${today}</td>
|
||||
<td>${escHtml(orgHeader.dpoName || orgHeader.responsiblePerson || '-')}</td>
|
||||
<td>Erstversion des Loeschkonzepts</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Document — Types, Defaults & Severity Labels
|
||||
// =============================================================================
|
||||
|
||||
import type { ComplianceIssueSeverity } from '../loeschfristen-compliance'
|
||||
|
||||
export interface LoeschkonzeptOrgHeader {
|
||||
organizationName: string
|
||||
industry: string
|
||||
dpoName: string
|
||||
dpoContact: string
|
||||
responsiblePerson: string
|
||||
locations: string[]
|
||||
employeeCount: string
|
||||
loeschkonzeptVersion: string
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: string
|
||||
}
|
||||
|
||||
export interface LoeschkonzeptRevision {
|
||||
version: string
|
||||
date: string
|
||||
author: string
|
||||
changes: string
|
||||
}
|
||||
|
||||
export function createDefaultLoeschkonzeptOrgHeader(): LoeschkonzeptOrgHeader {
|
||||
const now = new Date()
|
||||
const nextYear = new Date()
|
||||
nextYear.setFullYear(nextYear.getFullYear() + 1)
|
||||
|
||||
return {
|
||||
organizationName: '',
|
||||
industry: '',
|
||||
dpoName: '',
|
||||
dpoContact: '',
|
||||
responsiblePerson: '',
|
||||
locations: [],
|
||||
employeeCount: '',
|
||||
loeschkonzeptVersion: '1.0',
|
||||
lastReviewDate: now.toISOString().split('T')[0],
|
||||
nextReviewDate: nextYear.toISOString().split('T')[0],
|
||||
reviewInterval: 'Jaehrlich',
|
||||
}
|
||||
}
|
||||
|
||||
export const SEVERITY_LABELS_DE: Record<ComplianceIssueSeverity, string> = {
|
||||
CRITICAL: 'Kritisch',
|
||||
HIGH: 'Hoch',
|
||||
MEDIUM: 'Mittel',
|
||||
LOW: 'Niedrig',
|
||||
}
|
||||
|
||||
export const SEVERITY_COLORS: Record<ComplianceIssueSeverity, string> = {
|
||||
CRITICAL: '#dc2626',
|
||||
HIGH: '#ea580c',
|
||||
MEDIUM: '#d97706',
|
||||
LOW: '#6b7280',
|
||||
}
|
||||
@@ -1,915 +1,8 @@
|
||||
// =============================================================================
|
||||
// Obligations Module - Pflichtenregister Document Generator
|
||||
// Generates a printable, audit-ready HTML document for the obligation register
|
||||
// Barrel re-export — implementation split into obligations-document/
|
||||
// =============================================================================
|
||||
|
||||
import type { Obligation, ObligationComplianceCheckResult, ObligationComplianceIssueSeverity } from './obligations-compliance'
|
||||
import { OBLIGATION_SEVERITY_LABELS_DE, OBLIGATION_SEVERITY_COLORS } from './obligations-compliance'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface ObligationDocumentOrgHeader {
|
||||
organizationName: string
|
||||
industry: string
|
||||
dpoName: string
|
||||
dpoContact: string
|
||||
responsiblePerson: string
|
||||
legalDepartment: string
|
||||
documentVersion: string
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: string
|
||||
}
|
||||
|
||||
export interface ObligationDocumentRevision {
|
||||
version: string
|
||||
date: string
|
||||
author: string
|
||||
changes: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// DEFAULTS
|
||||
// =============================================================================
|
||||
|
||||
export function createDefaultObligationDocumentOrgHeader(): ObligationDocumentOrgHeader {
|
||||
const now = new Date()
|
||||
const nextYear = new Date()
|
||||
nextYear.setFullYear(nextYear.getFullYear() + 1)
|
||||
|
||||
return {
|
||||
organizationName: '',
|
||||
industry: '',
|
||||
dpoName: '',
|
||||
dpoContact: '',
|
||||
responsiblePerson: '',
|
||||
legalDepartment: '',
|
||||
documentVersion: '1.0',
|
||||
lastReviewDate: now.toISOString().split('T')[0],
|
||||
nextReviewDate: nextYear.toISOString().split('T')[0],
|
||||
reviewInterval: 'Jaehrlich',
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// STATUS & PRIORITY LABELS
|
||||
// =============================================================================
|
||||
|
||||
const STATUS_LABELS_DE: Record<string, string> = {
|
||||
'pending': 'Ausstehend',
|
||||
'in-progress': 'In Bearbeitung',
|
||||
'completed': 'Abgeschlossen',
|
||||
'overdue': 'Ueberfaellig',
|
||||
}
|
||||
|
||||
const STATUS_BADGE_CLASSES: Record<string, string> = {
|
||||
'pending': 'badge-draft',
|
||||
'in-progress': 'badge-review',
|
||||
'completed': 'badge-active',
|
||||
'overdue': 'badge-critical',
|
||||
}
|
||||
|
||||
const PRIORITY_LABELS_DE: Record<string, string> = {
|
||||
critical: 'Kritisch',
|
||||
high: 'Hoch',
|
||||
medium: 'Mittel',
|
||||
low: 'Niedrig',
|
||||
}
|
||||
|
||||
const PRIORITY_BADGE_CLASSES: Record<string, string> = {
|
||||
critical: 'badge-critical',
|
||||
high: 'badge-high',
|
||||
medium: 'badge-medium',
|
||||
low: 'badge-low',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HTML DOCUMENT BUILDER
|
||||
// =============================================================================
|
||||
|
||||
export function buildObligationDocumentHtml(
|
||||
obligations: Obligation[],
|
||||
orgHeader: ObligationDocumentOrgHeader,
|
||||
complianceResult: ObligationComplianceCheckResult | null,
|
||||
revisions: ObligationDocumentRevision[]
|
||||
): string {
|
||||
const today = new Date().toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const orgName = orgHeader.organizationName || 'Organisation'
|
||||
|
||||
// Group obligations by source (regulation)
|
||||
const bySource = new Map<string, Obligation[]>()
|
||||
for (const o of obligations) {
|
||||
const src = o.source || 'Sonstig'
|
||||
if (!bySource.has(src)) bySource.set(src, [])
|
||||
bySource.get(src)!.push(o)
|
||||
}
|
||||
|
||||
// Build role map
|
||||
const roleMap = new Map<string, Obligation[]>()
|
||||
for (const o of obligations) {
|
||||
const role = o.responsible || 'Nicht zugewiesen'
|
||||
if (!roleMap.has(role)) roleMap.set(role, [])
|
||||
roleMap.get(role)!.push(o)
|
||||
}
|
||||
|
||||
// Distinct sources
|
||||
const distinctSources = Array.from(bySource.keys()).sort()
|
||||
|
||||
// =========================================================================
|
||||
// HTML Template
|
||||
// =========================================================================
|
||||
|
||||
let html = `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Pflichtenregister — ${escHtml(orgName)}</title>
|
||||
<style>
|
||||
@page { size: A4; margin: 20mm 18mm 22mm 18mm; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
/* Cover */
|
||||
.cover {
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
page-break-after: always;
|
||||
}
|
||||
.cover h1 {
|
||||
font-size: 28pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.cover .subtitle {
|
||||
font-size: 14pt;
|
||||
color: #7c3aed;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.cover .org-info {
|
||||
background: #f5f3ff;
|
||||
border: 1px solid #ddd6fe;
|
||||
border-radius: 8px;
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
width: 400px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.cover .org-info div { margin-bottom: 6px; }
|
||||
.cover .org-info .label { font-weight: 600; color: #5b21b6; display: inline-block; min-width: 160px; }
|
||||
.cover .legal-ref {
|
||||
font-size: 9pt;
|
||||
color: #64748b;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* TOC */
|
||||
.toc {
|
||||
page-break-after: always;
|
||||
padding-top: 40px;
|
||||
}
|
||||
.toc h2 {
|
||||
font-size: 18pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #5b21b6;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.toc-entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px dotted #cbd5e1;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.toc-entry .toc-num { font-weight: 600; color: #5b21b6; min-width: 40px; }
|
||||
|
||||
/* Sections */
|
||||
.section {
|
||||
page-break-inside: avoid;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.section-header {
|
||||
font-size: 14pt;
|
||||
color: #5b21b6;
|
||||
font-weight: 700;
|
||||
margin: 30px 0 12px 0;
|
||||
border-bottom: 2px solid #ddd6fe;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.section-body { margin-bottom: 16px; }
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0 16px 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
background: #f5f3ff;
|
||||
color: #5b21b6;
|
||||
font-weight: 600;
|
||||
font-size: 8.5pt;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
tr:nth-child(even) td { background: #faf5ff; }
|
||||
|
||||
/* Detail cards */
|
||||
.policy-detail {
|
||||
page-break-inside: avoid;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.policy-detail-header {
|
||||
background: #f5f3ff;
|
||||
padding: 8px 12px;
|
||||
font-weight: 700;
|
||||
color: #5b21b6;
|
||||
border-bottom: 1px solid #ddd6fe;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.policy-detail-body { padding: 0; }
|
||||
.policy-detail-body table { margin: 0; }
|
||||
.policy-detail-body th { width: 200px; }
|
||||
|
||||
/* Badges */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 1px 8px;
|
||||
border-radius: 9999px;
|
||||
font-size: 8pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
.badge-active { background: #dcfce7; color: #166534; }
|
||||
.badge-draft { background: #f3f4f6; color: #374151; }
|
||||
.badge-review { background: #fef9c3; color: #854d0e; }
|
||||
.badge-critical { background: #fecaca; color: #991b1b; }
|
||||
.badge-high { background: #fed7aa; color: #9a3412; }
|
||||
.badge-medium { background: #fef3c7; color: #92400e; }
|
||||
.badge-low { background: #f3f4f6; color: #4b5563; }
|
||||
|
||||
/* Principles */
|
||||
.principle {
|
||||
margin-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.principle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #7c3aed;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.principle strong { color: #5b21b6; }
|
||||
|
||||
/* Score */
|
||||
.score-box {
|
||||
display: inline-block;
|
||||
padding: 4px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 18pt;
|
||||
font-weight: 700;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.score-excellent { background: #dcfce7; color: #166534; }
|
||||
.score-good { background: #dbeafe; color: #1e40af; }
|
||||
.score-needs-work { background: #fef3c7; color: #92400e; }
|
||||
.score-poor { background: #fecaca; color: #991b1b; }
|
||||
|
||||
/* Footer */
|
||||
.page-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 8px 18mm;
|
||||
font-size: 7.5pt;
|
||||
color: #94a3b8;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
/* Print */
|
||||
@media print {
|
||||
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
.no-print { display: none !important; }
|
||||
.page-break { page-break-after: always; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 0: Cover Page
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="cover">
|
||||
<h1>Pflichtenregister</h1>
|
||||
<div class="subtitle">Regulatorische Pflichten — DSGVO, AI Act, NIS2 und weitere</div>
|
||||
<div class="org-info">
|
||||
<div><span class="label">Organisation:</span> ${escHtml(orgName)}</div>
|
||||
${orgHeader.industry ? `<div><span class="label">Branche:</span> ${escHtml(orgHeader.industry)}</div>` : ''}
|
||||
${orgHeader.dpoName ? `<div><span class="label">DSB:</span> ${escHtml(orgHeader.dpoName)}</div>` : ''}
|
||||
${orgHeader.dpoContact ? `<div><span class="label">DSB-Kontakt:</span> ${escHtml(orgHeader.dpoContact)}</div>` : ''}
|
||||
${orgHeader.responsiblePerson ? `<div><span class="label">Verantwortlicher:</span> ${escHtml(orgHeader.responsiblePerson)}</div>` : ''}
|
||||
${orgHeader.legalDepartment ? `<div><span class="label">Rechtsabteilung:</span> ${escHtml(orgHeader.legalDepartment)}</div>` : ''}
|
||||
</div>
|
||||
<div class="legal-ref">
|
||||
Version ${escHtml(orgHeader.documentVersion)} | Stand: ${today}<br/>
|
||||
Letzte Pruefung: ${formatDateDE(orgHeader.lastReviewDate)} | Naechste Pruefung: ${formatDateDE(orgHeader.nextReviewDate)}<br/>
|
||||
Pruefintervall: ${escHtml(orgHeader.reviewInterval)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Table of Contents
|
||||
// =========================================================================
|
||||
const sections = [
|
||||
'Ziel und Zweck',
|
||||
'Geltungsbereich',
|
||||
'Methodik',
|
||||
'Regulatorische Grundlagen',
|
||||
'Pflichtenuebersicht',
|
||||
'Detaillierte Pflichten',
|
||||
'Verantwortlichkeiten',
|
||||
'Fristen und Termine',
|
||||
'Nachweisverzeichnis',
|
||||
'Compliance-Status',
|
||||
'Aenderungshistorie',
|
||||
]
|
||||
|
||||
html += `
|
||||
<div class="toc">
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
${sections.map((s, i) => `<div class="toc-entry"><span><span class="toc-num">${i + 1}.</span> ${escHtml(s)}</span></div>`).join('\n ')}
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 1: Ziel und Zweck
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">1. Ziel und Zweck</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Pflichtenregister dokumentiert alle regulatorischen Pflichten, denen
|
||||
<strong>${escHtml(orgName)}</strong> unterliegt. Es dient der systematischen Erfassung,
|
||||
Ueberwachung und Nachverfolgung aller Compliance-Anforderungen aus den anwendbaren
|
||||
Regulierungen.</p>
|
||||
<p style="margin-top: 8px;">Das Register erfuellt folgende Zwecke:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li>Vollstaendige Erfassung aller anwendbaren regulatorischen Pflichten</li>
|
||||
<li>Zuordnung von Verantwortlichkeiten und Fristen</li>
|
||||
<li>Nachverfolgung des Umsetzungsstatus</li>
|
||||
<li>Dokumentation von Nachweisen fuer Audits</li>
|
||||
<li>Identifikation von Compliance-Luecken und Handlungsbedarf</li>
|
||||
</ul>
|
||||
<table>
|
||||
<tr><th>Rechtsrahmen</th><th>Relevanz</th></tr>
|
||||
<tr><td><strong>DSGVO (EU) 2016/679</strong></td><td>Datenschutz-Grundverordnung — Kernregulierung fuer personenbezogene Daten</td></tr>
|
||||
<tr><td><strong>AI Act (EU) 2024/1689</strong></td><td>KI-Verordnung — Anforderungen an KI-Systeme nach Risikoklasse</td></tr>
|
||||
<tr><td><strong>NIS2 (EU) 2022/2555</strong></td><td>Netzwerk- und Informationssicherheit — Cybersicherheitspflichten</td></tr>
|
||||
<tr><td><strong>BDSG</strong></td><td>Bundesdatenschutzgesetz — Nationale Ergaenzung zur DSGVO</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 2: Geltungsbereich
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">2. Geltungsbereich</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Pflichtenregister gilt fuer alle Geschaeftsprozesse und IT-Systeme von
|
||||
<strong>${escHtml(orgName)}</strong>${orgHeader.industry ? ` (Branche: ${escHtml(orgHeader.industry)})` : ''}.</p>
|
||||
<p style="margin-top: 8px;">Anwendbare Regulierungen:</p>
|
||||
<table>
|
||||
<tr><th>Regulierung</th><th>Anzahl Pflichten</th><th>Status</th></tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
const pct = obls.length > 0 ? Math.round((completed / obls.length) * 100) : 0
|
||||
html += ` <tr>
|
||||
<td>${escHtml(source)}</td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${completed}/${obls.length} abgeschlossen (${pct}%)</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
<p>Insgesamt umfasst dieses Register <strong>${obligations.length}</strong> Pflichten aus
|
||||
<strong>${distinctSources.length}</strong> Regulierungen.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 3: Methodik
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">3. Methodik</div>
|
||||
<div class="section-body">
|
||||
<p>Die Identifikation und Bewertung der Pflichten erfolgt in drei Schritten:</p>
|
||||
<div class="principle"><strong>Pflicht-Identifikation:</strong> Systematische Analyse aller anwendbaren Regulierungen und Extraktion der einzelnen Pflichten mit Artikel-Referenz, Beschreibung und Zielgruppe.</div>
|
||||
<div class="principle"><strong>Bewertung und Priorisierung:</strong> Jede Pflicht wird nach Prioritaet (kritisch, hoch, mittel, niedrig) und Dringlichkeit (Frist) bewertet. Die Bewertung basiert auf dem Risikopotenzial bei Nichterfuellung.</div>
|
||||
<div class="principle"><strong>Ueberwachung und Nachverfolgung:</strong> Regelmaessige Pruefung des Umsetzungsstatus, Aktualisierung der Fristen und Dokumentation von Nachweisen.</div>
|
||||
<p style="margin-top: 12px;">Die Pflichten werden ueber einen automatisierten Compliance-Check geprueft, der
|
||||
11 Kriterien umfasst (siehe Abschnitt 10: Compliance-Status).</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 4: Regulatorische Grundlagen
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">4. Regulatorische Grundlagen</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt die regulatorischen Grundlagen mit Artikelzahl und Umsetzungsstatus:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Regulierung</th>
|
||||
<th>Pflichten</th>
|
||||
<th>Kritisch</th>
|
||||
<th>Hoch</th>
|
||||
<th>Mittel</th>
|
||||
<th>Niedrig</th>
|
||||
<th>Abgeschlossen</th>
|
||||
</tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const critical = obls.filter(o => o.priority === 'critical').length
|
||||
const high = obls.filter(o => o.priority === 'high').length
|
||||
const medium = obls.filter(o => o.priority === 'medium').length
|
||||
const low = obls.filter(o => o.priority === 'low').length
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
|
||||
html += ` <tr>
|
||||
<td><strong>${escHtml(source)}</strong></td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${critical}</td>
|
||||
<td>${high}</td>
|
||||
<td>${medium}</td>
|
||||
<td>${low}</td>
|
||||
<td>${completed}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
// Totals row
|
||||
const totalCritical = obligations.filter(o => o.priority === 'critical').length
|
||||
const totalHigh = obligations.filter(o => o.priority === 'high').length
|
||||
const totalMedium = obligations.filter(o => o.priority === 'medium').length
|
||||
const totalLow = obligations.filter(o => o.priority === 'low').length
|
||||
const totalCompleted = obligations.filter(o => o.status === 'completed').length
|
||||
|
||||
html += ` <tr style="font-weight: 700; background: #f5f3ff;">
|
||||
<td>Gesamt</td>
|
||||
<td>${obligations.length}</td>
|
||||
<td>${totalCritical}</td>
|
||||
<td>${totalHigh}</td>
|
||||
<td>${totalMedium}</td>
|
||||
<td>${totalLow}</td>
|
||||
<td>${totalCompleted}</td>
|
||||
</tr>
|
||||
`
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 5: Pflichtenuebersicht
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">5. Pflichtenuebersicht</div>
|
||||
<div class="section-body">
|
||||
<p>Uebersicht aller ${obligations.length} Pflichten nach Regulierung und Status:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Regulierung</th>
|
||||
<th>Gesamt</th>
|
||||
<th>Ausstehend</th>
|
||||
<th>In Bearbeitung</th>
|
||||
<th>Abgeschlossen</th>
|
||||
<th>Ueberfaellig</th>
|
||||
</tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const pending = obls.filter(o => o.status === 'pending').length
|
||||
const inProgress = obls.filter(o => o.status === 'in-progress').length
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
const overdue = obls.filter(o => o.status === 'overdue').length
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(source)}</td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${pending}</td>
|
||||
<td>${inProgress}</td>
|
||||
<td>${completed}</td>
|
||||
<td>${overdue > 0 ? `<span class="badge badge-critical">${overdue}</span>` : '0'}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 6: Detaillierte Pflichten
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">6. Detaillierte Pflichten</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
// Sort by priority (critical first) then by title
|
||||
const priorityOrder: Record<string, number> = { critical: 0, high: 1, medium: 2, low: 3 }
|
||||
const sorted = [...obls].sort((a, b) => {
|
||||
const pa = priorityOrder[a.priority] ?? 2
|
||||
const pb = priorityOrder[b.priority] ?? 2
|
||||
if (pa !== pb) return pa - pb
|
||||
return a.title.localeCompare(b.title)
|
||||
})
|
||||
|
||||
html += ` <h3 style="color: #5b21b6; margin: 20px 0 10px 0; font-size: 11pt;">${escHtml(source)} <span style="font-weight: 400; font-size: 9pt; color: #64748b;">(${sorted.length} Pflichten)</span></h3>
|
||||
`
|
||||
|
||||
for (const o of sorted) {
|
||||
const statusLabel = STATUS_LABELS_DE[o.status] || o.status
|
||||
const statusBadge = STATUS_BADGE_CLASSES[o.status] || 'badge-draft'
|
||||
const priorityLabel = PRIORITY_LABELS_DE[o.priority] || o.priority
|
||||
const priorityBadge = PRIORITY_BADGE_CLASSES[o.priority] || 'badge-draft'
|
||||
const deadlineStr = o.deadline ? formatDateDE(o.deadline) : '—'
|
||||
const evidenceStr = o.evidence && o.evidence.length > 0
|
||||
? o.evidence.map(e => escHtml(e)).join(', ')
|
||||
: '<em style="color: #d97706;">Kein Nachweis</em>'
|
||||
const systemsStr = o.linked_systems && o.linked_systems.length > 0
|
||||
? o.linked_systems.map(s => escHtml(s)).join(', ')
|
||||
: '—'
|
||||
|
||||
html += `
|
||||
<div class="policy-detail">
|
||||
<div class="policy-detail-header">
|
||||
<span>${escHtml(o.title)}</span>
|
||||
<span class="badge ${statusBadge}">${escHtml(statusLabel)}</span>
|
||||
</div>
|
||||
<div class="policy-detail-body">
|
||||
<table>
|
||||
<tr><th>Rechtsquelle</th><td>${escHtml(o.source)} ${escHtml(o.source_article || '')}</td></tr>
|
||||
<tr><th>Beschreibung</th><td>${escHtml(o.description || '—')}</td></tr>
|
||||
<tr><th>Prioritaet</th><td><span class="badge ${priorityBadge}">${escHtml(priorityLabel)}</span></td></tr>
|
||||
<tr><th>Status</th><td><span class="badge ${statusBadge}">${escHtml(statusLabel)}</span></td></tr>
|
||||
<tr><th>Verantwortlich</th><td>${escHtml(o.responsible || '—')}</td></tr>
|
||||
<tr><th>Frist</th><td>${deadlineStr}</td></tr>
|
||||
<tr><th>Nachweise</th><td>${evidenceStr}</td></tr>
|
||||
<tr><th>Betroffene Systeme</th><td>${systemsStr}</td></tr>
|
||||
${o.linked_vendor_ids && o.linked_vendor_ids.length > 0 ? `<tr><th>Auftragsverarbeiter</th><td>${o.linked_vendor_ids.map(id => escHtml(id)).join(', ')}</td></tr>` : ''}
|
||||
${o.notes ? `<tr><th>Notizen</th><td>${escHtml(o.notes)}</td></tr>` : ''}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 7: Verantwortlichkeiten
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">7. Verantwortlichkeiten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Rollenmatrix zeigt, welche Personen oder Abteilungen fuer welche Pflichten
|
||||
die Umsetzungsverantwortung tragen:</p>
|
||||
<table>
|
||||
<tr><th>Verantwortlich</th><th>Pflichten</th><th>Anzahl</th><th>Davon offen</th></tr>
|
||||
`
|
||||
for (const [role, obls] of roleMap.entries()) {
|
||||
const openCount = obls.filter(o => o.status !== 'completed').length
|
||||
const titles = obls.slice(0, 5).map(o => escHtml(o.title))
|
||||
const suffix = obls.length > 5 ? `, ... (+${obls.length - 5})` : ''
|
||||
html += ` <tr>
|
||||
<td>${escHtml(role)}</td>
|
||||
<td>${titles.join('; ')}${suffix}</td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${openCount}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 8: Fristen und Termine
|
||||
// =========================================================================
|
||||
const now = new Date()
|
||||
const withDeadline = obligations
|
||||
.filter(o => o.deadline && o.status !== 'completed')
|
||||
.sort((a, b) => new Date(a.deadline!).getTime() - new Date(b.deadline!).getTime())
|
||||
|
||||
const overdue = withDeadline.filter(o => new Date(o.deadline!) < now)
|
||||
const upcoming = withDeadline.filter(o => new Date(o.deadline!) >= now)
|
||||
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">8. Fristen und Termine</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (overdue.length > 0) {
|
||||
html += ` <h4 style="color: #dc2626; margin-bottom: 8px;">Ueberfaellige Pflichten (${overdue.length})</h4>
|
||||
<table>
|
||||
<tr><th>Pflicht</th><th>Regulierung</th><th>Frist</th><th>Tage ueberfaellig</th><th>Prioritaet</th></tr>
|
||||
`
|
||||
for (const o of overdue) {
|
||||
const days = daysBetween(new Date(o.deadline!), now)
|
||||
html += ` <tr>
|
||||
<td>${escHtml(o.title)}</td>
|
||||
<td>${escHtml(o.source)}</td>
|
||||
<td>${formatDateDE(o.deadline)}</td>
|
||||
<td><span class="badge badge-critical">${days} Tage</span></td>
|
||||
<td><span class="badge ${PRIORITY_BADGE_CLASSES[o.priority] || 'badge-draft'}">${escHtml(PRIORITY_LABELS_DE[o.priority] || o.priority)}</span></td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
}
|
||||
|
||||
if (upcoming.length > 0) {
|
||||
html += ` <h4 style="color: #5b21b6; margin: 16px 0 8px 0;">Anstehende Fristen (${upcoming.length})</h4>
|
||||
<table>
|
||||
<tr><th>Pflicht</th><th>Regulierung</th><th>Frist</th><th>Verbleibend</th><th>Verantwortlich</th></tr>
|
||||
`
|
||||
for (const o of upcoming.slice(0, 20)) {
|
||||
const days = daysBetween(now, new Date(o.deadline!))
|
||||
html += ` <tr>
|
||||
<td>${escHtml(o.title)}</td>
|
||||
<td>${escHtml(o.source)}</td>
|
||||
<td>${formatDateDE(o.deadline)}</td>
|
||||
<td>${days} Tage</td>
|
||||
<td>${escHtml(o.responsible || '—')}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
if (upcoming.length > 20) {
|
||||
html += ` <tr><td colspan="5" style="text-align: center; color: #64748b;">... und ${upcoming.length - 20} weitere</td></tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
}
|
||||
|
||||
if (withDeadline.length === 0) {
|
||||
html += ` <p><em>Keine offenen Pflichten mit Fristen vorhanden.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 9: Nachweisverzeichnis
|
||||
// =========================================================================
|
||||
const withEvidence = obligations.filter(o => o.evidence && o.evidence.length > 0)
|
||||
const withoutEvidence = obligations.filter(o => !o.evidence || o.evidence.length === 0)
|
||||
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">9. Nachweisverzeichnis</div>
|
||||
<div class="section-body">
|
||||
<p>${withEvidence.length} von ${obligations.length} Pflichten haben Nachweise hinterlegt.</p>
|
||||
`
|
||||
if (withEvidence.length > 0) {
|
||||
html += ` <table>
|
||||
<tr><th>Pflicht</th><th>Regulierung</th><th>Nachweise</th><th>Status</th></tr>
|
||||
`
|
||||
for (const o of withEvidence) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(o.title)}</td>
|
||||
<td>${escHtml(o.source)}</td>
|
||||
<td>${o.evidence!.map(e => escHtml(e)).join(', ')}</td>
|
||||
<td><span class="badge ${STATUS_BADGE_CLASSES[o.status] || 'badge-draft'}">${escHtml(STATUS_LABELS_DE[o.status] || o.status)}</span></td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
}
|
||||
|
||||
if (withoutEvidence.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Pflichten ohne Nachweise (${withoutEvidence.length}):</strong></p>
|
||||
<ul style="margin: 4px 0 8px 24px; font-size: 9pt; color: #d97706;">
|
||||
`
|
||||
for (const o of withoutEvidence.slice(0, 15)) {
|
||||
html += ` <li>${escHtml(o.title)} (${escHtml(o.source)})</li>
|
||||
`
|
||||
}
|
||||
if (withoutEvidence.length > 15) {
|
||||
html += ` <li>... und ${withoutEvidence.length - 15} weitere</li>
|
||||
`
|
||||
}
|
||||
html += ` </ul>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 10: Compliance-Status
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">10. Compliance-Status</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (complianceResult) {
|
||||
const scoreClass = complianceResult.score >= 90 ? 'score-excellent'
|
||||
: complianceResult.score >= 75 ? 'score-good'
|
||||
: complianceResult.score >= 50 ? 'score-needs-work'
|
||||
: 'score-poor'
|
||||
const scoreLabel = complianceResult.score >= 90 ? 'Ausgezeichnet'
|
||||
: complianceResult.score >= 75 ? 'Gut'
|
||||
: complianceResult.score >= 50 ? 'Verbesserungswuerdig'
|
||||
: 'Mangelhaft'
|
||||
|
||||
html += ` <p><span class="score-box ${scoreClass}">${complianceResult.score}/100</span> ${escHtml(scoreLabel)}</p>
|
||||
<table style="margin-top: 12px;">
|
||||
<tr><th>Kennzahl</th><th>Wert</th></tr>
|
||||
<tr><td>Geprueft am</td><td>${formatDateDE(complianceResult.checkedAt)}</td></tr>
|
||||
<tr><td>Befunde gesamt</td><td>${complianceResult.summary.total}</td></tr>
|
||||
<tr><td>Kritisch</td><td>${complianceResult.summary.critical}</td></tr>
|
||||
<tr><td>Hoch</td><td>${complianceResult.summary.high}</td></tr>
|
||||
<tr><td>Mittel</td><td>${complianceResult.summary.medium}</td></tr>
|
||||
<tr><td>Niedrig</td><td>${complianceResult.summary.low}</td></tr>
|
||||
</table>
|
||||
`
|
||||
if (complianceResult.issues.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Befunde nach Schweregrad:</strong></p>
|
||||
<table>
|
||||
<tr><th>Schweregrad</th><th>Befund</th><th>Betroffene Pflichten</th><th>Empfehlung</th></tr>
|
||||
`
|
||||
const severityOrder: ObligationComplianceIssueSeverity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
for (const sev of severityOrder) {
|
||||
const issuesForSev = complianceResult.issues.filter(i => i.severity === sev)
|
||||
for (const issue of issuesForSev) {
|
||||
html += ` <tr>
|
||||
<td><span class="badge badge-${sev.toLowerCase()}" style="color: ${OBLIGATION_SEVERITY_COLORS[sev]}">${OBLIGATION_SEVERITY_LABELS_DE[sev]}</span></td>
|
||||
<td>${escHtml(issue.message)}</td>
|
||||
<td>${issue.affectedObligations.length > 0 ? issue.affectedObligations.length + ' Pflicht(en)' : '—'}</td>
|
||||
<td>${escHtml(issue.recommendation)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p style="margin-top: 8px;"><em>Keine Beanstandungen. Alle Pflichten sind konform.</em></p>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <p><em>Compliance-Check wurde noch nicht ausgefuehrt. Fuehren Sie den Check im
|
||||
Pflichtenregister-Tab durch, um den Status in das Dokument aufzunehmen.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 11: Aenderungshistorie
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">11. Aenderungshistorie</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Version</th><th>Datum</th><th>Autor</th><th>Aenderungen</th></tr>
|
||||
`
|
||||
if (revisions.length > 0) {
|
||||
for (const rev of revisions) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(rev.version)}</td>
|
||||
<td>${formatDateDE(rev.date)}</td>
|
||||
<td>${escHtml(rev.author)}</td>
|
||||
<td>${escHtml(rev.changes)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(orgHeader.documentVersion)}</td>
|
||||
<td>${today}</td>
|
||||
<td>${escHtml(orgHeader.dpoName || orgHeader.responsiblePerson || '—')}</td>
|
||||
<td>Erstversion des Pflichtenregisters</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Footer
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="page-footer">
|
||||
<span>Pflichtenregister — ${escHtml(orgName)}</span>
|
||||
<span>Stand: ${today} | Version ${escHtml(orgHeader.documentVersion)}</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// INTERNAL HELPERS
|
||||
// =============================================================================
|
||||
|
||||
function escHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
function formatDateDE(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '—'
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
if (isNaN(date.getTime())) return '—'
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
} catch {
|
||||
return '—'
|
||||
}
|
||||
}
|
||||
|
||||
function daysBetween(earlier: Date, later: Date): number {
|
||||
const diffMs = later.getTime() - earlier.getTime()
|
||||
return Math.floor(diffMs / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
export type { ObligationDocumentOrgHeader, ObligationDocumentRevision } from './obligations-document/types-defaults'
|
||||
export { createDefaultObligationDocumentOrgHeader } from './obligations-document/types-defaults'
|
||||
export { buildObligationDocumentHtml } from './obligations-document/html-builder'
|
||||
|
||||
31
admin-compliance/lib/sdk/obligations-document/helpers.ts
Normal file
31
admin-compliance/lib/sdk/obligations-document/helpers.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// =============================================================================
|
||||
// Obligations Document — Internal Helpers
|
||||
// =============================================================================
|
||||
|
||||
export function escHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
export function formatDateDE(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '—'
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
if (isNaN(date.getTime())) return '—'
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
} catch {
|
||||
return '—'
|
||||
}
|
||||
}
|
||||
|
||||
export function daysBetween(earlier: Date, later: Date): number {
|
||||
const diffMs = later.getTime() - earlier.getTime()
|
||||
return Math.floor(diffMs / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
620
admin-compliance/lib/sdk/obligations-document/html-builder.ts
Normal file
620
admin-compliance/lib/sdk/obligations-document/html-builder.ts
Normal file
@@ -0,0 +1,620 @@
|
||||
// =============================================================================
|
||||
// Obligations Document — HTML Document Builder
|
||||
// =============================================================================
|
||||
|
||||
import type { Obligation, ObligationComplianceCheckResult, ObligationComplianceIssueSeverity } from '../obligations-compliance'
|
||||
import { OBLIGATION_SEVERITY_LABELS_DE, OBLIGATION_SEVERITY_COLORS } from '../obligations-compliance'
|
||||
import type { ObligationDocumentOrgHeader, ObligationDocumentRevision } from './types-defaults'
|
||||
import {
|
||||
STATUS_LABELS_DE,
|
||||
STATUS_BADGE_CLASSES,
|
||||
PRIORITY_LABELS_DE,
|
||||
PRIORITY_BADGE_CLASSES,
|
||||
} from './types-defaults'
|
||||
import { escHtml, formatDateDE, daysBetween } from './helpers'
|
||||
import { getDocumentStyles } from './html-styles'
|
||||
|
||||
export function buildObligationDocumentHtml(
|
||||
obligations: Obligation[],
|
||||
orgHeader: ObligationDocumentOrgHeader,
|
||||
complianceResult: ObligationComplianceCheckResult | null,
|
||||
revisions: ObligationDocumentRevision[]
|
||||
): string {
|
||||
const today = new Date().toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const orgName = orgHeader.organizationName || 'Organisation'
|
||||
|
||||
// Group obligations by source (regulation)
|
||||
const bySource = new Map<string, Obligation[]>()
|
||||
for (const o of obligations) {
|
||||
const src = o.source || 'Sonstig'
|
||||
if (!bySource.has(src)) bySource.set(src, [])
|
||||
bySource.get(src)!.push(o)
|
||||
}
|
||||
|
||||
// Build role map
|
||||
const roleMap = new Map<string, Obligation[]>()
|
||||
for (const o of obligations) {
|
||||
const role = o.responsible || 'Nicht zugewiesen'
|
||||
if (!roleMap.has(role)) roleMap.set(role, [])
|
||||
roleMap.get(role)!.push(o)
|
||||
}
|
||||
|
||||
const distinctSources = Array.from(bySource.keys()).sort()
|
||||
|
||||
let html = getDocumentStyles(`Pflichtenregister — ${escHtml(orgName)}`)
|
||||
|
||||
html += buildCoverPage(orgHeader, orgName, today)
|
||||
html += buildTOC()
|
||||
html += buildSection1(orgName)
|
||||
html += buildSection2(orgName, orgHeader, bySource, obligations, distinctSources)
|
||||
html += buildSection3()
|
||||
html += buildSection4(bySource, obligations)
|
||||
html += buildSection5(bySource)
|
||||
html += buildSection6(bySource)
|
||||
html += buildSection7(roleMap)
|
||||
html += buildSection8(obligations, today)
|
||||
html += buildSection9(obligations)
|
||||
html += buildSection10(complianceResult)
|
||||
html += buildSection11(revisions, orgHeader, today)
|
||||
html += buildFooter(orgName, today, orgHeader.documentVersion)
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
function buildCoverPage(
|
||||
orgHeader: ObligationDocumentOrgHeader,
|
||||
orgName: string,
|
||||
today: string
|
||||
): string {
|
||||
return `
|
||||
<div class="cover">
|
||||
<h1>Pflichtenregister</h1>
|
||||
<div class="subtitle">Regulatorische Pflichten — DSGVO, AI Act, NIS2 und weitere</div>
|
||||
<div class="org-info">
|
||||
<div><span class="label">Organisation:</span> ${escHtml(orgName)}</div>
|
||||
${orgHeader.industry ? `<div><span class="label">Branche:</span> ${escHtml(orgHeader.industry)}</div>` : ''}
|
||||
${orgHeader.dpoName ? `<div><span class="label">DSB:</span> ${escHtml(orgHeader.dpoName)}</div>` : ''}
|
||||
${orgHeader.dpoContact ? `<div><span class="label">DSB-Kontakt:</span> ${escHtml(orgHeader.dpoContact)}</div>` : ''}
|
||||
${orgHeader.responsiblePerson ? `<div><span class="label">Verantwortlicher:</span> ${escHtml(orgHeader.responsiblePerson)}</div>` : ''}
|
||||
${orgHeader.legalDepartment ? `<div><span class="label">Rechtsabteilung:</span> ${escHtml(orgHeader.legalDepartment)}</div>` : ''}
|
||||
</div>
|
||||
<div class="legal-ref">
|
||||
Version ${escHtml(orgHeader.documentVersion)} | Stand: ${today}<br/>
|
||||
Letzte Pruefung: ${formatDateDE(orgHeader.lastReviewDate)} | Naechste Pruefung: ${formatDateDE(orgHeader.nextReviewDate)}<br/>
|
||||
Pruefintervall: ${escHtml(orgHeader.reviewInterval)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildTOC(): string {
|
||||
const sections = [
|
||||
'Ziel und Zweck',
|
||||
'Geltungsbereich',
|
||||
'Methodik',
|
||||
'Regulatorische Grundlagen',
|
||||
'Pflichtenuebersicht',
|
||||
'Detaillierte Pflichten',
|
||||
'Verantwortlichkeiten',
|
||||
'Fristen und Termine',
|
||||
'Nachweisverzeichnis',
|
||||
'Compliance-Status',
|
||||
'Aenderungshistorie',
|
||||
]
|
||||
|
||||
return `
|
||||
<div class="toc">
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
${sections.map((s, i) => `<div class="toc-entry"><span><span class="toc-num">${i + 1}.</span> ${escHtml(s)}</span></div>`).join('\n ')}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection1(orgName: string): string {
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">1. Ziel und Zweck</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Pflichtenregister dokumentiert alle regulatorischen Pflichten, denen
|
||||
<strong>${escHtml(orgName)}</strong> unterliegt. Es dient der systematischen Erfassung,
|
||||
Ueberwachung und Nachverfolgung aller Compliance-Anforderungen aus den anwendbaren
|
||||
Regulierungen.</p>
|
||||
<p style="margin-top: 8px;">Das Register erfuellt folgende Zwecke:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li>Vollstaendige Erfassung aller anwendbaren regulatorischen Pflichten</li>
|
||||
<li>Zuordnung von Verantwortlichkeiten und Fristen</li>
|
||||
<li>Nachverfolgung des Umsetzungsstatus</li>
|
||||
<li>Dokumentation von Nachweisen fuer Audits</li>
|
||||
<li>Identifikation von Compliance-Luecken und Handlungsbedarf</li>
|
||||
</ul>
|
||||
<table>
|
||||
<tr><th>Rechtsrahmen</th><th>Relevanz</th></tr>
|
||||
<tr><td><strong>DSGVO (EU) 2016/679</strong></td><td>Datenschutz-Grundverordnung — Kernregulierung fuer personenbezogene Daten</td></tr>
|
||||
<tr><td><strong>AI Act (EU) 2024/1689</strong></td><td>KI-Verordnung — Anforderungen an KI-Systeme nach Risikoklasse</td></tr>
|
||||
<tr><td><strong>NIS2 (EU) 2022/2555</strong></td><td>Netzwerk- und Informationssicherheit — Cybersicherheitspflichten</td></tr>
|
||||
<tr><td><strong>BDSG</strong></td><td>Bundesdatenschutzgesetz — Nationale Ergaenzung zur DSGVO</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection2(
|
||||
orgName: string,
|
||||
orgHeader: ObligationDocumentOrgHeader,
|
||||
bySource: Map<string, Obligation[]>,
|
||||
obligations: Obligation[],
|
||||
distinctSources: string[]
|
||||
): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">2. Geltungsbereich</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Pflichtenregister gilt fuer alle Geschaeftsprozesse und IT-Systeme von
|
||||
<strong>${escHtml(orgName)}</strong>${orgHeader.industry ? ` (Branche: ${escHtml(orgHeader.industry)})` : ''}.</p>
|
||||
<p style="margin-top: 8px;">Anwendbare Regulierungen:</p>
|
||||
<table>
|
||||
<tr><th>Regulierung</th><th>Anzahl Pflichten</th><th>Status</th></tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
const pct = obls.length > 0 ? Math.round((completed / obls.length) * 100) : 0
|
||||
html += ` <tr>
|
||||
<td>${escHtml(source)}</td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${completed}/${obls.length} abgeschlossen (${pct}%)</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
<p>Insgesamt umfasst dieses Register <strong>${obligations.length}</strong> Pflichten aus
|
||||
<strong>${distinctSources.length}</strong> Regulierungen.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection3(): string {
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">3. Methodik</div>
|
||||
<div class="section-body">
|
||||
<p>Die Identifikation und Bewertung der Pflichten erfolgt in drei Schritten:</p>
|
||||
<div class="principle"><strong>Pflicht-Identifikation:</strong> Systematische Analyse aller anwendbaren Regulierungen und Extraktion der einzelnen Pflichten mit Artikel-Referenz, Beschreibung und Zielgruppe.</div>
|
||||
<div class="principle"><strong>Bewertung und Priorisierung:</strong> Jede Pflicht wird nach Prioritaet (kritisch, hoch, mittel, niedrig) und Dringlichkeit (Frist) bewertet. Die Bewertung basiert auf dem Risikopotenzial bei Nichterfuellung.</div>
|
||||
<div class="principle"><strong>Ueberwachung und Nachverfolgung:</strong> Regelmaessige Pruefung des Umsetzungsstatus, Aktualisierung der Fristen und Dokumentation von Nachweisen.</div>
|
||||
<p style="margin-top: 12px;">Die Pflichten werden ueber einen automatisierten Compliance-Check geprueft, der
|
||||
11 Kriterien umfasst (siehe Abschnitt 10: Compliance-Status).</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection4(bySource: Map<string, Obligation[]>, obligations: Obligation[]): string {
|
||||
const totalCritical = obligations.filter(o => o.priority === 'critical').length
|
||||
const totalHigh = obligations.filter(o => o.priority === 'high').length
|
||||
const totalMedium = obligations.filter(o => o.priority === 'medium').length
|
||||
const totalLow = obligations.filter(o => o.priority === 'low').length
|
||||
const totalCompleted = obligations.filter(o => o.status === 'completed').length
|
||||
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">4. Regulatorische Grundlagen</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt die regulatorischen Grundlagen mit Artikelzahl und Umsetzungsstatus:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Regulierung</th>
|
||||
<th>Pflichten</th>
|
||||
<th>Kritisch</th>
|
||||
<th>Hoch</th>
|
||||
<th>Mittel</th>
|
||||
<th>Niedrig</th>
|
||||
<th>Abgeschlossen</th>
|
||||
</tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const critical = obls.filter(o => o.priority === 'critical').length
|
||||
const high = obls.filter(o => o.priority === 'high').length
|
||||
const medium = obls.filter(o => o.priority === 'medium').length
|
||||
const low = obls.filter(o => o.priority === 'low').length
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
|
||||
html += ` <tr>
|
||||
<td><strong>${escHtml(source)}</strong></td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${critical}</td>
|
||||
<td>${high}</td>
|
||||
<td>${medium}</td>
|
||||
<td>${low}</td>
|
||||
<td>${completed}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` <tr style="font-weight: 700; background: #f5f3ff;">
|
||||
<td>Gesamt</td>
|
||||
<td>${obligations.length}</td>
|
||||
<td>${totalCritical}</td>
|
||||
<td>${totalHigh}</td>
|
||||
<td>${totalMedium}</td>
|
||||
<td>${totalLow}</td>
|
||||
<td>${totalCompleted}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection5(bySource: Map<string, Obligation[]>): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">5. Pflichtenuebersicht</div>
|
||||
<div class="section-body">
|
||||
<p>Uebersicht aller Pflichten nach Regulierung und Status:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Regulierung</th>
|
||||
<th>Gesamt</th>
|
||||
<th>Ausstehend</th>
|
||||
<th>In Bearbeitung</th>
|
||||
<th>Abgeschlossen</th>
|
||||
<th>Ueberfaellig</th>
|
||||
</tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const pending = obls.filter(o => o.status === 'pending').length
|
||||
const inProgress = obls.filter(o => o.status === 'in-progress').length
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
const overdue = obls.filter(o => o.status === 'overdue').length
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(source)}</td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${pending}</td>
|
||||
<td>${inProgress}</td>
|
||||
<td>${completed}</td>
|
||||
<td>${overdue > 0 ? `<span class="badge badge-critical">${overdue}</span>` : '0'}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection6(bySource: Map<string, Obligation[]>): string {
|
||||
const priorityOrder: Record<string, number> = { critical: 0, high: 1, medium: 2, low: 3 }
|
||||
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">6. Detaillierte Pflichten</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const sorted = [...obls].sort((a, b) => {
|
||||
const pa = priorityOrder[a.priority] ?? 2
|
||||
const pb = priorityOrder[b.priority] ?? 2
|
||||
if (pa !== pb) return pa - pb
|
||||
return a.title.localeCompare(b.title)
|
||||
})
|
||||
|
||||
html += ` <h3 style="color: #5b21b6; margin: 20px 0 10px 0; font-size: 11pt;">${escHtml(source)} <span style="font-weight: 400; font-size: 9pt; color: #64748b;">(${sorted.length} Pflichten)</span></h3>
|
||||
`
|
||||
|
||||
for (const o of sorted) {
|
||||
const statusLabel = STATUS_LABELS_DE[o.status] || o.status
|
||||
const statusBadge = STATUS_BADGE_CLASSES[o.status] || 'badge-draft'
|
||||
const priorityLabel = PRIORITY_LABELS_DE[o.priority] || o.priority
|
||||
const priorityBadge = PRIORITY_BADGE_CLASSES[o.priority] || 'badge-draft'
|
||||
const deadlineStr = o.deadline ? formatDateDE(o.deadline) : '—'
|
||||
const evidenceStr = o.evidence && o.evidence.length > 0
|
||||
? o.evidence.map(e => escHtml(e)).join(', ')
|
||||
: '<em style="color: #d97706;">Kein Nachweis</em>'
|
||||
const systemsStr = o.linked_systems && o.linked_systems.length > 0
|
||||
? o.linked_systems.map(s => escHtml(s)).join(', ')
|
||||
: '—'
|
||||
|
||||
html += `
|
||||
<div class="policy-detail">
|
||||
<div class="policy-detail-header">
|
||||
<span>${escHtml(o.title)}</span>
|
||||
<span class="badge ${statusBadge}">${escHtml(statusLabel)}</span>
|
||||
</div>
|
||||
<div class="policy-detail-body">
|
||||
<table>
|
||||
<tr><th>Rechtsquelle</th><td>${escHtml(o.source)} ${escHtml(o.source_article || '')}</td></tr>
|
||||
<tr><th>Beschreibung</th><td>${escHtml(o.description || '—')}</td></tr>
|
||||
<tr><th>Prioritaet</th><td><span class="badge ${priorityBadge}">${escHtml(priorityLabel)}</span></td></tr>
|
||||
<tr><th>Status</th><td><span class="badge ${statusBadge}">${escHtml(statusLabel)}</span></td></tr>
|
||||
<tr><th>Verantwortlich</th><td>${escHtml(o.responsible || '—')}</td></tr>
|
||||
<tr><th>Frist</th><td>${deadlineStr}</td></tr>
|
||||
<tr><th>Nachweise</th><td>${evidenceStr}</td></tr>
|
||||
<tr><th>Betroffene Systeme</th><td>${systemsStr}</td></tr>
|
||||
${o.linked_vendor_ids && o.linked_vendor_ids.length > 0 ? `<tr><th>Auftragsverarbeiter</th><td>${o.linked_vendor_ids.map(id => escHtml(id)).join(', ')}</td></tr>` : ''}
|
||||
${o.notes ? `<tr><th>Notizen</th><td>${escHtml(o.notes)}</td></tr>` : ''}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection7(roleMap: Map<string, Obligation[]>): string {
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">7. Verantwortlichkeiten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Rollenmatrix zeigt, welche Personen oder Abteilungen fuer welche Pflichten
|
||||
die Umsetzungsverantwortung tragen:</p>
|
||||
<table>
|
||||
<tr><th>Verantwortlich</th><th>Pflichten</th><th>Anzahl</th><th>Davon offen</th></tr>
|
||||
`
|
||||
for (const [role, obls] of roleMap.entries()) {
|
||||
const openCount = obls.filter(o => o.status !== 'completed').length
|
||||
const titles = obls.slice(0, 5).map(o => escHtml(o.title))
|
||||
const suffix = obls.length > 5 ? `, ... (+${obls.length - 5})` : ''
|
||||
html += ` <tr>
|
||||
<td>${escHtml(role)}</td>
|
||||
<td>${titles.join('; ')}${suffix}</td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${openCount}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection8(obligations: Obligation[], _today: string): string {
|
||||
const now = new Date()
|
||||
const withDeadline = obligations
|
||||
.filter(o => o.deadline && o.status !== 'completed')
|
||||
.sort((a, b) => new Date(a.deadline!).getTime() - new Date(b.deadline!).getTime())
|
||||
|
||||
const overdue = withDeadline.filter(o => new Date(o.deadline!) < now)
|
||||
const upcoming = withDeadline.filter(o => new Date(o.deadline!) >= now)
|
||||
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">8. Fristen und Termine</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (overdue.length > 0) {
|
||||
html += ` <h4 style="color: #dc2626; margin-bottom: 8px;">Ueberfaellige Pflichten (${overdue.length})</h4>
|
||||
<table>
|
||||
<tr><th>Pflicht</th><th>Regulierung</th><th>Frist</th><th>Tage ueberfaellig</th><th>Prioritaet</th></tr>
|
||||
`
|
||||
for (const o of overdue) {
|
||||
const days = daysBetween(new Date(o.deadline!), now)
|
||||
html += ` <tr>
|
||||
<td>${escHtml(o.title)}</td>
|
||||
<td>${escHtml(o.source)}</td>
|
||||
<td>${formatDateDE(o.deadline)}</td>
|
||||
<td><span class="badge badge-critical">${days} Tage</span></td>
|
||||
<td><span class="badge ${PRIORITY_BADGE_CLASSES[o.priority] || 'badge-draft'}">${escHtml(PRIORITY_LABELS_DE[o.priority] || o.priority)}</span></td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
}
|
||||
|
||||
if (upcoming.length > 0) {
|
||||
html += ` <h4 style="color: #5b21b6; margin: 16px 0 8px 0;">Anstehende Fristen (${upcoming.length})</h4>
|
||||
<table>
|
||||
<tr><th>Pflicht</th><th>Regulierung</th><th>Frist</th><th>Verbleibend</th><th>Verantwortlich</th></tr>
|
||||
`
|
||||
for (const o of upcoming.slice(0, 20)) {
|
||||
const days = daysBetween(now, new Date(o.deadline!))
|
||||
html += ` <tr>
|
||||
<td>${escHtml(o.title)}</td>
|
||||
<td>${escHtml(o.source)}</td>
|
||||
<td>${formatDateDE(o.deadline)}</td>
|
||||
<td>${days} Tage</td>
|
||||
<td>${escHtml(o.responsible || '—')}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
if (upcoming.length > 20) {
|
||||
html += ` <tr><td colspan="5" style="text-align: center; color: #64748b;">... und ${upcoming.length - 20} weitere</td></tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
}
|
||||
|
||||
if (withDeadline.length === 0) {
|
||||
html += ` <p><em>Keine offenen Pflichten mit Fristen vorhanden.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection9(obligations: Obligation[]): string {
|
||||
const withEvidence = obligations.filter(o => o.evidence && o.evidence.length > 0)
|
||||
const withoutEvidence = obligations.filter(o => !o.evidence || o.evidence.length === 0)
|
||||
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">9. Nachweisverzeichnis</div>
|
||||
<div class="section-body">
|
||||
<p>${withEvidence.length} von ${obligations.length} Pflichten haben Nachweise hinterlegt.</p>
|
||||
`
|
||||
if (withEvidence.length > 0) {
|
||||
html += ` <table>
|
||||
<tr><th>Pflicht</th><th>Regulierung</th><th>Nachweise</th><th>Status</th></tr>
|
||||
`
|
||||
for (const o of withEvidence) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(o.title)}</td>
|
||||
<td>${escHtml(o.source)}</td>
|
||||
<td>${o.evidence!.map(e => escHtml(e)).join(', ')}</td>
|
||||
<td><span class="badge ${STATUS_BADGE_CLASSES[o.status] || 'badge-draft'}">${escHtml(STATUS_LABELS_DE[o.status] || o.status)}</span></td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
}
|
||||
|
||||
if (withoutEvidence.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Pflichten ohne Nachweise (${withoutEvidence.length}):</strong></p>
|
||||
<ul style="margin: 4px 0 8px 24px; font-size: 9pt; color: #d97706;">
|
||||
`
|
||||
for (const o of withoutEvidence.slice(0, 15)) {
|
||||
html += ` <li>${escHtml(o.title)} (${escHtml(o.source)})</li>
|
||||
`
|
||||
}
|
||||
if (withoutEvidence.length > 15) {
|
||||
html += ` <li>... und ${withoutEvidence.length - 15} weitere</li>
|
||||
`
|
||||
}
|
||||
html += ` </ul>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection10(complianceResult: ObligationComplianceCheckResult | null): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">10. Compliance-Status</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (complianceResult) {
|
||||
const scoreClass = complianceResult.score >= 90 ? 'score-excellent'
|
||||
: complianceResult.score >= 75 ? 'score-good'
|
||||
: complianceResult.score >= 50 ? 'score-needs-work'
|
||||
: 'score-poor'
|
||||
const scoreLabel = complianceResult.score >= 90 ? 'Ausgezeichnet'
|
||||
: complianceResult.score >= 75 ? 'Gut'
|
||||
: complianceResult.score >= 50 ? 'Verbesserungswuerdig'
|
||||
: 'Mangelhaft'
|
||||
|
||||
html += ` <p><span class="score-box ${scoreClass}">${complianceResult.score}/100</span> ${escHtml(scoreLabel)}</p>
|
||||
<table style="margin-top: 12px;">
|
||||
<tr><th>Kennzahl</th><th>Wert</th></tr>
|
||||
<tr><td>Geprueft am</td><td>${formatDateDE(complianceResult.checkedAt)}</td></tr>
|
||||
<tr><td>Befunde gesamt</td><td>${complianceResult.summary.total}</td></tr>
|
||||
<tr><td>Kritisch</td><td>${complianceResult.summary.critical}</td></tr>
|
||||
<tr><td>Hoch</td><td>${complianceResult.summary.high}</td></tr>
|
||||
<tr><td>Mittel</td><td>${complianceResult.summary.medium}</td></tr>
|
||||
<tr><td>Niedrig</td><td>${complianceResult.summary.low}</td></tr>
|
||||
</table>
|
||||
`
|
||||
if (complianceResult.issues.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Befunde nach Schweregrad:</strong></p>
|
||||
<table>
|
||||
<tr><th>Schweregrad</th><th>Befund</th><th>Betroffene Pflichten</th><th>Empfehlung</th></tr>
|
||||
`
|
||||
const severityOrder: ObligationComplianceIssueSeverity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
for (const sev of severityOrder) {
|
||||
const issuesForSev = complianceResult.issues.filter(i => i.severity === sev)
|
||||
for (const issue of issuesForSev) {
|
||||
html += ` <tr>
|
||||
<td><span class="badge badge-${sev.toLowerCase()}" style="color: ${OBLIGATION_SEVERITY_COLORS[sev]}">${OBLIGATION_SEVERITY_LABELS_DE[sev]}</span></td>
|
||||
<td>${escHtml(issue.message)}</td>
|
||||
<td>${issue.affectedObligations.length > 0 ? issue.affectedObligations.length + ' Pflicht(en)' : '—'}</td>
|
||||
<td>${escHtml(issue.recommendation)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p style="margin-top: 8px;"><em>Keine Beanstandungen. Alle Pflichten sind konform.</em></p>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <p><em>Compliance-Check wurde noch nicht ausgefuehrt. Fuehren Sie den Check im
|
||||
Pflichtenregister-Tab durch, um den Status in das Dokument aufzunehmen.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection11(
|
||||
revisions: ObligationDocumentRevision[],
|
||||
orgHeader: ObligationDocumentOrgHeader,
|
||||
today: string
|
||||
): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">11. Aenderungshistorie</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Version</th><th>Datum</th><th>Autor</th><th>Aenderungen</th></tr>
|
||||
`
|
||||
if (revisions.length > 0) {
|
||||
for (const rev of revisions) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(rev.version)}</td>
|
||||
<td>${formatDateDE(rev.date)}</td>
|
||||
<td>${escHtml(rev.author)}</td>
|
||||
<td>${escHtml(rev.changes)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(orgHeader.documentVersion)}</td>
|
||||
<td>${today}</td>
|
||||
<td>${escHtml(orgHeader.dpoName || orgHeader.responsiblePerson || '—')}</td>
|
||||
<td>Erstversion des Pflichtenregisters</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildFooter(orgName: string, today: string, version: string): string {
|
||||
return `
|
||||
<div class="page-footer">
|
||||
<span>Pflichtenregister — ${escHtml(orgName)}</span>
|
||||
<span>Stand: ${today} | Version ${escHtml(version)}</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// =============================================================================
|
||||
// Obligations Document — HTML Sections 1–5 (cover, TOC, purpose, scope, method, sources, overview)
|
||||
// =============================================================================
|
||||
|
||||
import type { Obligation } from '../obligations-compliance'
|
||||
import type { ObligationDocumentOrgHeader } from './types-defaults'
|
||||
import { escHtml, formatDateDE } from './helpers'
|
||||
|
||||
export function buildCoverPage(
|
||||
orgHeader: ObligationDocumentOrgHeader,
|
||||
orgName: string,
|
||||
today: string
|
||||
): string {
|
||||
return `
|
||||
<div class="cover">
|
||||
<h1>Pflichtenregister</h1>
|
||||
<div class="subtitle">Regulatorische Pflichten — DSGVO, AI Act, NIS2 und weitere</div>
|
||||
<div class="org-info">
|
||||
<div><span class="label">Organisation:</span> ${escHtml(orgName)}</div>
|
||||
${orgHeader.industry ? `<div><span class="label">Branche:</span> ${escHtml(orgHeader.industry)}</div>` : ''}
|
||||
${orgHeader.dpoName ? `<div><span class="label">DSB:</span> ${escHtml(orgHeader.dpoName)}</div>` : ''}
|
||||
${orgHeader.dpoContact ? `<div><span class="label">DSB-Kontakt:</span> ${escHtml(orgHeader.dpoContact)}</div>` : ''}
|
||||
${orgHeader.responsiblePerson ? `<div><span class="label">Verantwortlicher:</span> ${escHtml(orgHeader.responsiblePerson)}</div>` : ''}
|
||||
${orgHeader.legalDepartment ? `<div><span class="label">Rechtsabteilung:</span> ${escHtml(orgHeader.legalDepartment)}</div>` : ''}
|
||||
</div>
|
||||
<div class="legal-ref">
|
||||
Version ${escHtml(orgHeader.documentVersion)} | Stand: ${today}<br/>
|
||||
Letzte Pruefung: ${formatDateDE(orgHeader.lastReviewDate)} | Naechste Pruefung: ${formatDateDE(orgHeader.nextReviewDate)}<br/>
|
||||
Pruefintervall: ${escHtml(orgHeader.reviewInterval)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export function buildTOC(): string {
|
||||
const sections = [
|
||||
'Ziel und Zweck', 'Geltungsbereich', 'Methodik', 'Regulatorische Grundlagen',
|
||||
'Pflichtenuebersicht', 'Detaillierte Pflichten', 'Verantwortlichkeiten',
|
||||
'Fristen und Termine', 'Nachweisverzeichnis', 'Compliance-Status', 'Aenderungshistorie',
|
||||
]
|
||||
|
||||
return `
|
||||
<div class="toc">
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
${sections.map((s, i) => `<div class="toc-entry"><span><span class="toc-num">${i + 1}.</span> ${escHtml(s)}</span></div>`).join('\n ')}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export function buildSection1(orgName: string): string {
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">1. Ziel und Zweck</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Pflichtenregister dokumentiert alle regulatorischen Pflichten, denen
|
||||
<strong>${escHtml(orgName)}</strong> unterliegt. Es dient der systematischen Erfassung,
|
||||
Ueberwachung und Nachverfolgung aller Compliance-Anforderungen aus den anwendbaren
|
||||
Regulierungen.</p>
|
||||
<p style="margin-top: 8px;">Das Register erfuellt folgende Zwecke:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li>Vollstaendige Erfassung aller anwendbaren regulatorischen Pflichten</li>
|
||||
<li>Zuordnung von Verantwortlichkeiten und Fristen</li>
|
||||
<li>Nachverfolgung des Umsetzungsstatus</li>
|
||||
<li>Dokumentation von Nachweisen fuer Audits</li>
|
||||
<li>Identifikation von Compliance-Luecken und Handlungsbedarf</li>
|
||||
</ul>
|
||||
<table>
|
||||
<tr><th>Rechtsrahmen</th><th>Relevanz</th></tr>
|
||||
<tr><td><strong>DSGVO (EU) 2016/679</strong></td><td>Datenschutz-Grundverordnung — Kernregulierung fuer personenbezogene Daten</td></tr>
|
||||
<tr><td><strong>AI Act (EU) 2024/1689</strong></td><td>KI-Verordnung — Anforderungen an KI-Systeme nach Risikoklasse</td></tr>
|
||||
<tr><td><strong>NIS2 (EU) 2022/2555</strong></td><td>Netzwerk- und Informationssicherheit — Cybersicherheitspflichten</td></tr>
|
||||
<tr><td><strong>BDSG</strong></td><td>Bundesdatenschutzgesetz — Nationale Ergaenzung zur DSGVO</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export function buildSection2(
|
||||
orgName: string,
|
||||
orgHeader: ObligationDocumentOrgHeader,
|
||||
bySource: Map<string, Obligation[]>,
|
||||
obligations: Obligation[],
|
||||
distinctSources: string[]
|
||||
): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">2. Geltungsbereich</div>
|
||||
<div class="section-body">
|
||||
<p>Dieses Pflichtenregister gilt fuer alle Geschaeftsprozesse und IT-Systeme von
|
||||
<strong>${escHtml(orgName)}</strong>${orgHeader.industry ? ` (Branche: ${escHtml(orgHeader.industry)})` : ''}.</p>
|
||||
<p style="margin-top: 8px;">Anwendbare Regulierungen:</p>
|
||||
<table>
|
||||
<tr><th>Regulierung</th><th>Anzahl Pflichten</th><th>Status</th></tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
const pct = obls.length > 0 ? Math.round((completed / obls.length) * 100) : 0
|
||||
html += ` <tr>
|
||||
<td>${escHtml(source)}</td>
|
||||
<td>${obls.length}</td>
|
||||
<td>${completed}/${obls.length} abgeschlossen (${pct}%)</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
<p>Insgesamt umfasst dieses Register <strong>${obligations.length}</strong> Pflichten aus
|
||||
<strong>${distinctSources.length}</strong> Regulierungen.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
export function buildSection3(): string {
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">3. Methodik</div>
|
||||
<div class="section-body">
|
||||
<p>Die Identifikation und Bewertung der Pflichten erfolgt in drei Schritten:</p>
|
||||
<div class="principle"><strong>Pflicht-Identifikation:</strong> Systematische Analyse aller anwendbaren Regulierungen und Extraktion der einzelnen Pflichten mit Artikel-Referenz, Beschreibung und Zielgruppe.</div>
|
||||
<div class="principle"><strong>Bewertung und Priorisierung:</strong> Jede Pflicht wird nach Prioritaet (kritisch, hoch, mittel, niedrig) und Dringlichkeit (Frist) bewertet. Die Bewertung basiert auf dem Risikopotenzial bei Nichterfuellung.</div>
|
||||
<div class="principle"><strong>Ueberwachung und Nachverfolgung:</strong> Regelmaessige Pruefung des Umsetzungsstatus, Aktualisierung der Fristen und Dokumentation von Nachweisen.</div>
|
||||
<p style="margin-top: 12px;">Die Pflichten werden ueber einen automatisierten Compliance-Check geprueft, der
|
||||
11 Kriterien umfasst (siehe Abschnitt 10: Compliance-Status).</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export function buildSection4(bySource: Map<string, Obligation[]>, obligations: Obligation[]): string {
|
||||
const totalCritical = obligations.filter(o => o.priority === 'critical').length
|
||||
const totalHigh = obligations.filter(o => o.priority === 'high').length
|
||||
const totalMedium = obligations.filter(o => o.priority === 'medium').length
|
||||
const totalLow = obligations.filter(o => o.priority === 'low').length
|
||||
const totalCompleted = obligations.filter(o => o.status === 'completed').length
|
||||
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">4. Regulatorische Grundlagen</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt die regulatorischen Grundlagen mit Artikelzahl und Umsetzungsstatus:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Regulierung</th><th>Pflichten</th><th>Kritisch</th>
|
||||
<th>Hoch</th><th>Mittel</th><th>Niedrig</th><th>Abgeschlossen</th>
|
||||
</tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const critical = obls.filter(o => o.priority === 'critical').length
|
||||
const high = obls.filter(o => o.priority === 'high').length
|
||||
const medium = obls.filter(o => o.priority === 'medium').length
|
||||
const low = obls.filter(o => o.priority === 'low').length
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
|
||||
html += ` <tr>
|
||||
<td><strong>${escHtml(source)}</strong></td>
|
||||
<td>${obls.length}</td><td>${critical}</td><td>${high}</td>
|
||||
<td>${medium}</td><td>${low}</td><td>${completed}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` <tr style="font-weight: 700; background: #f5f3ff;">
|
||||
<td>Gesamt</td>
|
||||
<td>${obligations.length}</td><td>${totalCritical}</td><td>${totalHigh}</td>
|
||||
<td>${totalMedium}</td><td>${totalLow}</td><td>${totalCompleted}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
export function buildSection5(bySource: Map<string, Obligation[]>): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">5. Pflichtenuebersicht</div>
|
||||
<div class="section-body">
|
||||
<p>Uebersicht aller Pflichten nach Regulierung und Status:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Regulierung</th><th>Gesamt</th><th>Ausstehend</th>
|
||||
<th>In Bearbeitung</th><th>Abgeschlossen</th><th>Ueberfaellig</th>
|
||||
</tr>
|
||||
`
|
||||
for (const [source, obls] of bySource.entries()) {
|
||||
const pending = obls.filter(o => o.status === 'pending').length
|
||||
const inProgress = obls.filter(o => o.status === 'in-progress').length
|
||||
const completed = obls.filter(o => o.status === 'completed').length
|
||||
const overdue = obls.filter(o => o.status === 'overdue').length
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(source)}</td><td>${obls.length}</td><td>${pending}</td>
|
||||
<td>${inProgress}</td><td>${completed}</td>
|
||||
<td>${overdue > 0 ? `<span class="badge badge-critical">${overdue}</span>` : '0'}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
211
admin-compliance/lib/sdk/obligations-document/html-styles.ts
Normal file
211
admin-compliance/lib/sdk/obligations-document/html-styles.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
// =============================================================================
|
||||
// Obligations Document — Shared HTML/CSS Styles
|
||||
// =============================================================================
|
||||
|
||||
export function getDocumentStyles(title: string): string {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>${title}</title>
|
||||
<style>
|
||||
@page { size: A4; margin: 20mm 18mm 22mm 18mm; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
/* Cover */
|
||||
.cover {
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
page-break-after: always;
|
||||
}
|
||||
.cover h1 {
|
||||
font-size: 28pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.cover .subtitle {
|
||||
font-size: 14pt;
|
||||
color: #7c3aed;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.cover .org-info {
|
||||
background: #f5f3ff;
|
||||
border: 1px solid #ddd6fe;
|
||||
border-radius: 8px;
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
width: 400px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.cover .org-info div { margin-bottom: 6px; }
|
||||
.cover .org-info .label { font-weight: 600; color: #5b21b6; display: inline-block; min-width: 160px; }
|
||||
.cover .legal-ref {
|
||||
font-size: 9pt;
|
||||
color: #64748b;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* TOC */
|
||||
.toc {
|
||||
page-break-after: always;
|
||||
padding-top: 40px;
|
||||
}
|
||||
.toc h2 {
|
||||
font-size: 18pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #5b21b6;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.toc-entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px dotted #cbd5e1;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.toc-entry .toc-num { font-weight: 600; color: #5b21b6; min-width: 40px; }
|
||||
|
||||
/* Sections */
|
||||
.section {
|
||||
page-break-inside: avoid;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.section-header {
|
||||
font-size: 14pt;
|
||||
color: #5b21b6;
|
||||
font-weight: 700;
|
||||
margin: 30px 0 12px 0;
|
||||
border-bottom: 2px solid #ddd6fe;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.section-body { margin-bottom: 16px; }
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0 16px 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
background: #f5f3ff;
|
||||
color: #5b21b6;
|
||||
font-weight: 600;
|
||||
font-size: 8.5pt;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
tr:nth-child(even) td { background: #faf5ff; }
|
||||
|
||||
/* Detail cards */
|
||||
.policy-detail {
|
||||
page-break-inside: avoid;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.policy-detail-header {
|
||||
background: #f5f3ff;
|
||||
padding: 8px 12px;
|
||||
font-weight: 700;
|
||||
color: #5b21b6;
|
||||
border-bottom: 1px solid #ddd6fe;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.policy-detail-body { padding: 0; }
|
||||
.policy-detail-body table { margin: 0; }
|
||||
.policy-detail-body th { width: 200px; }
|
||||
|
||||
/* Badges */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 1px 8px;
|
||||
border-radius: 9999px;
|
||||
font-size: 8pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
.badge-active { background: #dcfce7; color: #166534; }
|
||||
.badge-draft { background: #f3f4f6; color: #374151; }
|
||||
.badge-review { background: #fef9c3; color: #854d0e; }
|
||||
.badge-critical { background: #fecaca; color: #991b1b; }
|
||||
.badge-high { background: #fed7aa; color: #9a3412; }
|
||||
.badge-medium { background: #fef3c7; color: #92400e; }
|
||||
.badge-low { background: #f3f4f6; color: #4b5563; }
|
||||
|
||||
/* Principles */
|
||||
.principle {
|
||||
margin-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.principle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #7c3aed;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.principle strong { color: #5b21b6; }
|
||||
|
||||
/* Score */
|
||||
.score-box {
|
||||
display: inline-block;
|
||||
padding: 4px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 18pt;
|
||||
font-weight: 700;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.score-excellent { background: #dcfce7; color: #166534; }
|
||||
.score-good { background: #dbeafe; color: #1e40af; }
|
||||
.score-needs-work { background: #fef3c7; color: #92400e; }
|
||||
.score-poor { background: #fecaca; color: #991b1b; }
|
||||
|
||||
/* Footer */
|
||||
.page-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 8px 18mm;
|
||||
font-size: 7.5pt;
|
||||
color: #94a3b8;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
/* Print */
|
||||
@media print {
|
||||
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
.no-print { display: none !important; }
|
||||
.page-break { page-break-after: always; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// =============================================================================
|
||||
// Obligations Document — Types, Defaults & Status/Priority Labels
|
||||
// =============================================================================
|
||||
|
||||
export interface ObligationDocumentOrgHeader {
|
||||
organizationName: string
|
||||
industry: string
|
||||
dpoName: string
|
||||
dpoContact: string
|
||||
responsiblePerson: string
|
||||
legalDepartment: string
|
||||
documentVersion: string
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: string
|
||||
}
|
||||
|
||||
export interface ObligationDocumentRevision {
|
||||
version: string
|
||||
date: string
|
||||
author: string
|
||||
changes: string
|
||||
}
|
||||
|
||||
export function createDefaultObligationDocumentOrgHeader(): ObligationDocumentOrgHeader {
|
||||
const now = new Date()
|
||||
const nextYear = new Date()
|
||||
nextYear.setFullYear(nextYear.getFullYear() + 1)
|
||||
|
||||
return {
|
||||
organizationName: '',
|
||||
industry: '',
|
||||
dpoName: '',
|
||||
dpoContact: '',
|
||||
responsiblePerson: '',
|
||||
legalDepartment: '',
|
||||
documentVersion: '1.0',
|
||||
lastReviewDate: now.toISOString().split('T')[0],
|
||||
nextReviewDate: nextYear.toISOString().split('T')[0],
|
||||
reviewInterval: 'Jaehrlich',
|
||||
}
|
||||
}
|
||||
|
||||
export const STATUS_LABELS_DE: Record<string, string> = {
|
||||
'pending': 'Ausstehend',
|
||||
'in-progress': 'In Bearbeitung',
|
||||
'completed': 'Abgeschlossen',
|
||||
'overdue': 'Ueberfaellig',
|
||||
}
|
||||
|
||||
export const STATUS_BADGE_CLASSES: Record<string, string> = {
|
||||
'pending': 'badge-draft',
|
||||
'in-progress': 'badge-review',
|
||||
'completed': 'badge-active',
|
||||
'overdue': 'badge-critical',
|
||||
}
|
||||
|
||||
export const PRIORITY_LABELS_DE: Record<string, string> = {
|
||||
critical: 'Kritisch',
|
||||
high: 'Hoch',
|
||||
medium: 'Mittel',
|
||||
low: 'Niedrig',
|
||||
}
|
||||
|
||||
export const PRIORITY_BADGE_CLASSES: Record<string, string> = {
|
||||
critical: 'badge-critical',
|
||||
high: 'badge-high',
|
||||
medium: 'badge-medium',
|
||||
low: 'badge-low',
|
||||
}
|
||||
328
admin-compliance/lib/sdk/tom-compliance-checks.ts
Normal file
328
admin-compliance/lib/sdk/tom-compliance-checks.ts
Normal file
@@ -0,0 +1,328 @@
|
||||
// =============================================================================
|
||||
// TOM Compliance Checks — Per-TOM and Aggregate checks
|
||||
//
|
||||
// Barrel-split from tom-compliance.ts. Do NOT import directly; use tom-compliance.ts.
|
||||
// =============================================================================
|
||||
|
||||
import type {
|
||||
TOMGeneratorState,
|
||||
DerivedTOM,
|
||||
RiskProfile,
|
||||
DataProfile,
|
||||
ControlCategory,
|
||||
} from './tom-generator/types'
|
||||
|
||||
import { getControlById } from './tom-generator/controls/loader'
|
||||
import { SDM_CATEGORY_MAPPING } from './tom-generator/types'
|
||||
import type { TOMComplianceIssue, TOMComplianceIssueType, TOMComplianceIssueSeverity } from './tom-compliance'
|
||||
|
||||
// =============================================================================
|
||||
// HELPERS
|
||||
// =============================================================================
|
||||
|
||||
let issueCounter = 0
|
||||
|
||||
export function resetIssueCounter(): void {
|
||||
issueCounter = 0
|
||||
}
|
||||
|
||||
export function createIssue(
|
||||
controlId: string,
|
||||
controlName: string,
|
||||
type: TOMComplianceIssueType,
|
||||
severity: TOMComplianceIssueSeverity,
|
||||
title: string,
|
||||
description: string,
|
||||
recommendation: string
|
||||
): TOMComplianceIssue {
|
||||
issueCounter++
|
||||
const id = `TCI-${Date.now()}-${String(issueCounter).padStart(4, '0')}`
|
||||
return { id, controlId, controlName, type, severity, title, description, recommendation }
|
||||
}
|
||||
|
||||
function daysBetween(date: Date, now: Date): number {
|
||||
const diffMs = now.getTime() - date.getTime()
|
||||
return Math.floor(diffMs / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PER-TOM CHECKS (1-3, 11)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Check 1: MISSING_RESPONSIBLE (MEDIUM)
|
||||
*/
|
||||
export function checkMissingResponsible(tom: DerivedTOM): TOMComplianceIssue | null {
|
||||
if (tom.applicability !== 'REQUIRED') return null
|
||||
|
||||
if (!tom.responsiblePerson && !tom.responsibleDepartment) {
|
||||
return createIssue(
|
||||
tom.controlId, tom.name, 'MISSING_RESPONSIBLE', 'MEDIUM',
|
||||
'Keine verantwortliche Person/Abteilung',
|
||||
`Die TOM "${tom.name}" ist als REQUIRED eingestuft, hat aber weder eine verantwortliche Person noch eine verantwortliche Abteilung zugewiesen. Ohne klare Verantwortlichkeit kann die Massnahme nicht zuverlaessig umgesetzt und gepflegt werden.`,
|
||||
'Weisen Sie eine verantwortliche Person oder Abteilung zu, die fuer die Umsetzung und regelmaessige Pruefung dieser Massnahme zustaendig ist.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 2: OVERDUE_REVIEW (MEDIUM)
|
||||
*/
|
||||
export function checkOverdueReview(tom: DerivedTOM): TOMComplianceIssue | null {
|
||||
if (!tom.reviewDate) return null
|
||||
|
||||
const reviewDate = new Date(tom.reviewDate)
|
||||
const now = new Date()
|
||||
|
||||
if (reviewDate < now) {
|
||||
const overdueDays = daysBetween(reviewDate, now)
|
||||
return createIssue(
|
||||
tom.controlId, tom.name, 'OVERDUE_REVIEW', 'MEDIUM',
|
||||
'Ueberfaellige Pruefung',
|
||||
`Die TOM "${tom.name}" haette am ${reviewDate.toLocaleDateString('de-DE')} geprueft werden muessen. Die Pruefung ist ${overdueDays} Tag(e) ueberfaellig. Gemaess Art. 32 Abs. 1 lit. d DSGVO ist eine regelmaessige Ueberpruefung der Wirksamkeit von TOMs erforderlich.`,
|
||||
'Fuehren Sie umgehend eine Wirksamkeitspruefung dieser Massnahme durch und aktualisieren Sie das naechste Pruefungsdatum.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 3: MISSING_EVIDENCE (HIGH)
|
||||
*/
|
||||
export function checkMissingEvidence(tom: DerivedTOM): TOMComplianceIssue | null {
|
||||
if (tom.implementationStatus !== 'IMPLEMENTED') return null
|
||||
if (tom.linkedEvidence.length > 0) return null
|
||||
|
||||
const control = getControlById(tom.controlId)
|
||||
if (!control || control.evidenceRequirements.length === 0) return null
|
||||
|
||||
return createIssue(
|
||||
tom.controlId, tom.name, 'MISSING_EVIDENCE', 'HIGH',
|
||||
'Kein Nachweis hinterlegt',
|
||||
`Die TOM "${tom.name}" ist als IMPLEMENTED markiert, hat aber keine verknuepften Nachweisdokumente. Der Control erfordert ${control.evidenceRequirements.length} Nachweis(e): ${control.evidenceRequirements.join(', ')}. Ohne Nachweise ist die Umsetzung nicht auditfaehig.`,
|
||||
'Laden Sie die erforderlichen Nachweisdokumente hoch und verknuepfen Sie sie mit dieser Massnahme.'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 11: STALE_NOT_IMPLEMENTED (LOW)
|
||||
*/
|
||||
export function checkStaleNotImplemented(tom: DerivedTOM, state: TOMGeneratorState): TOMComplianceIssue | null {
|
||||
if (tom.applicability !== 'REQUIRED') return null
|
||||
if (tom.implementationStatus !== 'NOT_IMPLEMENTED') return null
|
||||
if (tom.implementationDate !== null) return null
|
||||
|
||||
const referenceDate = state.createdAt ? new Date(state.createdAt) : (state.updatedAt ? new Date(state.updatedAt) : null)
|
||||
if (!referenceDate) return null
|
||||
|
||||
const ageInDays = daysBetween(referenceDate, new Date())
|
||||
if (ageInDays <= 90) return null
|
||||
|
||||
return createIssue(
|
||||
tom.controlId, tom.name, 'STALE_NOT_IMPLEMENTED', 'LOW',
|
||||
'Langfristig nicht umgesetzte Pflichtmassnahme',
|
||||
`Die TOM "${tom.name}" ist als REQUIRED eingestuft, aber seit ${ageInDays} Tagen nicht umgesetzt. Pflichtmassnahmen, die laenger als 90 Tage nicht implementiert werden, deuten auf organisatorische Blockaden oder unzureichende Priorisierung hin.`,
|
||||
'Pruefen Sie, ob die Massnahme weiterhin erforderlich ist, und erstellen Sie einen konkreten Umsetzungsplan mit Verantwortlichkeiten und Fristen.'
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// AGGREGATE CHECKS (4-10)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Check 4: INCOMPLETE_CATEGORY (HIGH)
|
||||
*/
|
||||
export function checkIncompleteCategory(toms: DerivedTOM[]): TOMComplianceIssue[] {
|
||||
const issues: TOMComplianceIssue[] = []
|
||||
const categoryMap = new Map<ControlCategory, DerivedTOM[]>()
|
||||
|
||||
for (const tom of toms) {
|
||||
const control = getControlById(tom.controlId)
|
||||
if (!control) continue
|
||||
const category = control.category
|
||||
if (!categoryMap.has(category)) categoryMap.set(category, [])
|
||||
categoryMap.get(category)!.push(tom)
|
||||
}
|
||||
|
||||
for (const [category, categoryToms] of Array.from(categoryMap.entries())) {
|
||||
const requiredToms = categoryToms.filter((t: DerivedTOM) => t.applicability === 'REQUIRED')
|
||||
if (requiredToms.length === 0) continue
|
||||
|
||||
const allNotImplemented = requiredToms.every((t: DerivedTOM) => t.implementationStatus === 'NOT_IMPLEMENTED')
|
||||
if (allNotImplemented) {
|
||||
issues.push(createIssue(
|
||||
category, category, 'INCOMPLETE_CATEGORY', 'HIGH',
|
||||
`Kategorie "${category}" vollstaendig ohne Umsetzung`,
|
||||
`Alle ${requiredToms.length} Pflichtmassnahme(n) in der Kategorie "${category}" sind nicht umgesetzt. Eine vollstaendig unabgedeckte Kategorie stellt eine erhebliche Luecke im TOM-Konzept dar.`,
|
||||
`Setzen Sie mindestens die wichtigsten Massnahmen in der Kategorie "${category}" um, um eine Grundabdeckung sicherzustellen.`
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 5: NO_ENCRYPTION_MEASURES (CRITICAL)
|
||||
*/
|
||||
export function checkNoEncryption(toms: DerivedTOM[]): TOMComplianceIssue | null {
|
||||
const hasImplementedEncryption = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return control?.category === 'ENCRYPTION' && tom.implementationStatus === 'IMPLEMENTED'
|
||||
})
|
||||
|
||||
if (!hasImplementedEncryption) {
|
||||
return createIssue(
|
||||
'ENCRYPTION', 'Verschluesselung', 'NO_ENCRYPTION_MEASURES', 'CRITICAL',
|
||||
'Keine Verschluesselungsmassnahmen umgesetzt',
|
||||
'Es ist keine einzige Verschluesselungsmassnahme als IMPLEMENTED markiert. Art. 32 Abs. 1 lit. a DSGVO nennt Verschluesselung explizit als geeignete technische Massnahme. Ohne Verschluesselung sind personenbezogene Daten bei Zugriff oder Verlust ungeschuetzt.',
|
||||
'Implementieren Sie umgehend Verschluesselungsmassnahmen fuer Daten im Ruhezustand (Encryption at Rest) und waehrend der Uebertragung (Encryption in Transit).'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 6: NO_PSEUDONYMIZATION (MEDIUM)
|
||||
*/
|
||||
export function checkNoPseudonymization(toms: DerivedTOM[], dataProfile: DataProfile | null): TOMComplianceIssue | null {
|
||||
if (!dataProfile || !dataProfile.hasSpecialCategories) return null
|
||||
|
||||
const hasImplementedPseudonymization = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return control?.category === 'PSEUDONYMIZATION' && tom.implementationStatus === 'IMPLEMENTED'
|
||||
})
|
||||
|
||||
if (!hasImplementedPseudonymization) {
|
||||
return createIssue(
|
||||
'PSEUDONYMIZATION', 'Pseudonymisierung', 'NO_PSEUDONYMIZATION', 'MEDIUM',
|
||||
'Keine Pseudonymisierung bei besonderen Datenkategorien',
|
||||
'Das Datenprofil enthaelt besondere Kategorien personenbezogener Daten (Art. 9 DSGVO), aber keine Pseudonymisierungsmassnahme ist umgesetzt. Art. 32 Abs. 1 lit. a DSGVO empfiehlt Pseudonymisierung ausdruecklich als Schutzmassnahme.',
|
||||
'Implementieren Sie Pseudonymisierungsmassnahmen fuer die Verarbeitung besonderer Datenkategorien, um das Risiko fuer betroffene Personen zu minimieren.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 7: MISSING_AVAILABILITY (HIGH)
|
||||
*/
|
||||
export function checkMissingAvailability(toms: DerivedTOM[], state: TOMGeneratorState): TOMComplianceIssue | null {
|
||||
const hasAvailabilityOrRecovery = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return (
|
||||
(control?.category === 'AVAILABILITY' || control?.category === 'RECOVERY') &&
|
||||
tom.implementationStatus === 'IMPLEMENTED'
|
||||
)
|
||||
})
|
||||
|
||||
const hasDRPlan = state.securityProfile?.hasDRPlan ?? false
|
||||
|
||||
if (!hasAvailabilityOrRecovery && !hasDRPlan) {
|
||||
return createIssue(
|
||||
'AVAILABILITY', 'Verfuegbarkeit / Wiederherstellbarkeit', 'MISSING_AVAILABILITY', 'HIGH',
|
||||
'Keine Verfuegbarkeits- oder Wiederherstellungsmassnahmen',
|
||||
'Weder Verfuegbarkeits- noch Wiederherstellungsmassnahmen sind umgesetzt, und es existiert kein Disaster-Recovery-Plan im Security-Profil. Art. 32 Abs. 1 lit. b und c DSGVO verlangen die Faehigkeit zur raschen Wiederherstellung der Verfuegbarkeit personenbezogener Daten.',
|
||||
'Implementieren Sie Backup-Konzepte, Redundanzloesungen und einen Disaster-Recovery-Plan, um die Verfuegbarkeit und Wiederherstellbarkeit sicherzustellen.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 8: NO_REVIEW_PROCESS (MEDIUM)
|
||||
*/
|
||||
export function checkNoReviewProcess(toms: DerivedTOM[]): TOMComplianceIssue | null {
|
||||
const hasImplementedReview = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return control?.category === 'REVIEW' && tom.implementationStatus === 'IMPLEMENTED'
|
||||
})
|
||||
|
||||
if (!hasImplementedReview) {
|
||||
return createIssue(
|
||||
'REVIEW', 'Ueberpruefung & Bewertung', 'NO_REVIEW_PROCESS', 'MEDIUM',
|
||||
'Kein Verfahren zur regelmaessigen Ueberpruefung',
|
||||
'Es ist keine Ueberpruefungsmassnahme als IMPLEMENTED markiert. Art. 32 Abs. 1 lit. d DSGVO verlangt ein Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung der Wirksamkeit der technischen und organisatorischen Massnahmen.',
|
||||
'Implementieren Sie einen regelmaessigen Review-Prozess (z.B. quartalsweise TOM-Audits, jaehrliche Wirksamkeitspruefung) und dokumentieren Sie die Ergebnisse.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 9: UNCOVERED_SDM_GOAL (HIGH)
|
||||
*/
|
||||
export function checkUncoveredSDMGoal(toms: DerivedTOM[]): TOMComplianceIssue[] {
|
||||
const issues: TOMComplianceIssue[] = []
|
||||
|
||||
const sdmGoals = [
|
||||
'Verfuegbarkeit', 'Integritaet', 'Vertraulichkeit',
|
||||
'Nichtverkettung', 'Intervenierbarkeit', 'Transparenz', 'Datenminimierung',
|
||||
] as const
|
||||
|
||||
const goalToCategoriesMap = new Map<string, ControlCategory[]>()
|
||||
for (const goal of sdmGoals) goalToCategoriesMap.set(goal, [])
|
||||
|
||||
for (const [category, goals] of Object.entries(SDM_CATEGORY_MAPPING)) {
|
||||
for (const goal of goals) {
|
||||
const existing = goalToCategoriesMap.get(goal)
|
||||
if (existing) existing.push(category as ControlCategory)
|
||||
}
|
||||
}
|
||||
|
||||
const implementedCategories = new Set<ControlCategory>()
|
||||
for (const tom of toms) {
|
||||
if (tom.implementationStatus !== 'IMPLEMENTED') continue
|
||||
const control = getControlById(tom.controlId)
|
||||
if (control) implementedCategories.add(control.category)
|
||||
}
|
||||
|
||||
for (const goal of sdmGoals) {
|
||||
const coveringCategories = goalToCategoriesMap.get(goal) ?? []
|
||||
const hasCoverage = coveringCategories.some((cat) => implementedCategories.has(cat))
|
||||
|
||||
if (!hasCoverage) {
|
||||
issues.push(createIssue(
|
||||
`SDM-${goal}`, goal, 'UNCOVERED_SDM_GOAL', 'HIGH',
|
||||
`SDM-Gewaehrleistungsziel "${goal}" nicht abgedeckt`,
|
||||
`Das Gewaehrleistungsziel "${goal}" des Standard-Datenschutzmodells (SDM) ist durch keine umgesetzte Massnahme abgedeckt. Zugehoerige Kategorien (${coveringCategories.join(', ')}) haben keine IMPLEMENTED Controls. Das SDM ist die anerkannte Methodik zur Umsetzung der DSGVO-Anforderungen.`,
|
||||
`Setzen Sie mindestens eine Massnahme aus den Kategorien ${coveringCategories.join(', ')} um, um das SDM-Ziel "${goal}" abzudecken.`
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 10: HIGH_RISK_WITHOUT_MEASURES (CRITICAL)
|
||||
*/
|
||||
export function checkHighRiskWithoutMeasures(toms: DerivedTOM[], riskProfile: RiskProfile | null): TOMComplianceIssue | null {
|
||||
if (!riskProfile || riskProfile.protectionLevel !== 'VERY_HIGH') return null
|
||||
|
||||
const requiredToms = toms.filter((t) => t.applicability === 'REQUIRED')
|
||||
if (requiredToms.length === 0) return null
|
||||
|
||||
const implementedCount = requiredToms.filter((t) => t.implementationStatus === 'IMPLEMENTED').length
|
||||
const implementationRate = implementedCount / requiredToms.length
|
||||
|
||||
if (implementationRate < 0.5) {
|
||||
const percentage = Math.round(implementationRate * 100)
|
||||
return createIssue(
|
||||
'RISK-PROFILE', 'Risikoprofil VERY_HIGH', 'HIGH_RISK_WITHOUT_MEASURES', 'CRITICAL',
|
||||
'Sehr hoher Schutzbedarf bei niedriger Umsetzungsrate',
|
||||
`Der Schutzbedarf ist als VERY_HIGH eingestuft, aber nur ${implementedCount} von ${requiredToms.length} Pflichtmassnahmen (${percentage}%) sind umgesetzt. Bei sehr hohem Schutzbedarf muessen mindestens 50% der Pflichtmassnahmen implementiert sein, um ein angemessenes Schutzniveau gemaess Art. 32 DSGVO zu gewaehrleisten.`,
|
||||
'Priorisieren Sie die Umsetzung der verbleibenden Pflichtmassnahmen. Beginnen Sie mit CRITICAL- und HIGH-Priority Controls. Erwaeegen Sie einen Umsetzungsplan mit klaren Meilensteinen.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -2,19 +2,26 @@
|
||||
// TOM Module - Compliance Check Engine
|
||||
// Prueft Technische und Organisatorische Massnahmen auf Vollstaendigkeit,
|
||||
// Konsistenz und DSGVO-Konformitaet (Art. 32 DSGVO)
|
||||
//
|
||||
// Check functions live in tom-compliance-checks.ts (barrel split).
|
||||
// =============================================================================
|
||||
|
||||
import type {
|
||||
TOMGeneratorState,
|
||||
DerivedTOM,
|
||||
RiskProfile,
|
||||
DataProfile,
|
||||
ControlCategory,
|
||||
ImplementationStatus,
|
||||
} from './tom-generator/types'
|
||||
import type { TOMGeneratorState } from './tom-generator/types'
|
||||
|
||||
import { getControlById, getControlsByCategory, getAllCategories } from './tom-generator/controls/loader'
|
||||
import { SDM_CATEGORY_MAPPING } from './tom-generator/types'
|
||||
import {
|
||||
resetIssueCounter,
|
||||
checkMissingResponsible,
|
||||
checkOverdueReview,
|
||||
checkMissingEvidence,
|
||||
checkStaleNotImplemented,
|
||||
checkIncompleteCategory,
|
||||
checkNoEncryption,
|
||||
checkNoPseudonymization,
|
||||
checkMissingAvailability,
|
||||
checkNoReviewProcess,
|
||||
checkUncoveredSDMGoal,
|
||||
checkHighRiskWithoutMeasures,
|
||||
} from './tom-compliance-checks'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
@@ -75,386 +82,6 @@ export const TOM_SEVERITY_COLORS: Record<TOMComplianceIssueSeverity, string> = {
|
||||
LOW: '#6b7280',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPERS
|
||||
// =============================================================================
|
||||
|
||||
let issueCounter = 0
|
||||
|
||||
function createIssueId(): string {
|
||||
issueCounter++
|
||||
return `TCI-${Date.now()}-${String(issueCounter).padStart(4, '0')}`
|
||||
}
|
||||
|
||||
function createIssue(
|
||||
controlId: string,
|
||||
controlName: string,
|
||||
type: TOMComplianceIssueType,
|
||||
severity: TOMComplianceIssueSeverity,
|
||||
title: string,
|
||||
description: string,
|
||||
recommendation: string
|
||||
): TOMComplianceIssue {
|
||||
return { id: createIssueId(), controlId, controlName, type, severity, title, description, recommendation }
|
||||
}
|
||||
|
||||
function daysBetween(date: Date, now: Date): number {
|
||||
const diffMs = now.getTime() - date.getTime()
|
||||
return Math.floor(diffMs / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PER-TOM CHECKS (1-3, 11)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Check 1: MISSING_RESPONSIBLE (MEDIUM)
|
||||
* REQUIRED TOM without responsiblePerson AND responsibleDepartment.
|
||||
*/
|
||||
function checkMissingResponsible(tom: DerivedTOM): TOMComplianceIssue | null {
|
||||
if (tom.applicability !== 'REQUIRED') return null
|
||||
|
||||
if (!tom.responsiblePerson && !tom.responsibleDepartment) {
|
||||
return createIssue(
|
||||
tom.controlId,
|
||||
tom.name,
|
||||
'MISSING_RESPONSIBLE',
|
||||
'MEDIUM',
|
||||
'Keine verantwortliche Person/Abteilung',
|
||||
`Die TOM "${tom.name}" ist als REQUIRED eingestuft, hat aber weder eine verantwortliche Person noch eine verantwortliche Abteilung zugewiesen. Ohne klare Verantwortlichkeit kann die Massnahme nicht zuverlaessig umgesetzt und gepflegt werden.`,
|
||||
'Weisen Sie eine verantwortliche Person oder Abteilung zu, die fuer die Umsetzung und regelmaessige Pruefung dieser Massnahme zustaendig ist.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 2: OVERDUE_REVIEW (MEDIUM)
|
||||
* TOM with reviewDate in the past.
|
||||
*/
|
||||
function checkOverdueReview(tom: DerivedTOM): TOMComplianceIssue | null {
|
||||
if (!tom.reviewDate) return null
|
||||
|
||||
const reviewDate = new Date(tom.reviewDate)
|
||||
const now = new Date()
|
||||
|
||||
if (reviewDate < now) {
|
||||
const overdueDays = daysBetween(reviewDate, now)
|
||||
return createIssue(
|
||||
tom.controlId,
|
||||
tom.name,
|
||||
'OVERDUE_REVIEW',
|
||||
'MEDIUM',
|
||||
'Ueberfaellige Pruefung',
|
||||
`Die TOM "${tom.name}" haette am ${reviewDate.toLocaleDateString('de-DE')} geprueft werden muessen. Die Pruefung ist ${overdueDays} Tag(e) ueberfaellig. Gemaess Art. 32 Abs. 1 lit. d DSGVO ist eine regelmaessige Ueberpruefung der Wirksamkeit von TOMs erforderlich.`,
|
||||
'Fuehren Sie umgehend eine Wirksamkeitspruefung dieser Massnahme durch und aktualisieren Sie das naechste Pruefungsdatum.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 3: MISSING_EVIDENCE (HIGH)
|
||||
* IMPLEMENTED TOM where linkedEvidence is empty but the control has evidenceRequirements.
|
||||
*/
|
||||
function checkMissingEvidence(tom: DerivedTOM): TOMComplianceIssue | null {
|
||||
if (tom.implementationStatus !== 'IMPLEMENTED') return null
|
||||
if (tom.linkedEvidence.length > 0) return null
|
||||
|
||||
const control = getControlById(tom.controlId)
|
||||
if (!control || control.evidenceRequirements.length === 0) return null
|
||||
|
||||
return createIssue(
|
||||
tom.controlId,
|
||||
tom.name,
|
||||
'MISSING_EVIDENCE',
|
||||
'HIGH',
|
||||
'Kein Nachweis hinterlegt',
|
||||
`Die TOM "${tom.name}" ist als IMPLEMENTED markiert, hat aber keine verknuepften Nachweisdokumente. Der Control erfordert ${control.evidenceRequirements.length} Nachweis(e): ${control.evidenceRequirements.join(', ')}. Ohne Nachweise ist die Umsetzung nicht auditfaehig.`,
|
||||
'Laden Sie die erforderlichen Nachweisdokumente hoch und verknuepfen Sie sie mit dieser Massnahme.'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 11: STALE_NOT_IMPLEMENTED (LOW)
|
||||
* REQUIRED TOM that has been NOT_IMPLEMENTED for >90 days.
|
||||
* Uses implementationDate === null and state.createdAt / state.updatedAt as reference.
|
||||
*/
|
||||
function checkStaleNotImplemented(tom: DerivedTOM, state: TOMGeneratorState): TOMComplianceIssue | null {
|
||||
if (tom.applicability !== 'REQUIRED') return null
|
||||
if (tom.implementationStatus !== 'NOT_IMPLEMENTED') return null
|
||||
if (tom.implementationDate !== null) return null
|
||||
|
||||
const referenceDate = state.createdAt ? new Date(state.createdAt) : (state.updatedAt ? new Date(state.updatedAt) : null)
|
||||
if (!referenceDate) return null
|
||||
|
||||
const ageInDays = daysBetween(referenceDate, new Date())
|
||||
if (ageInDays <= 90) return null
|
||||
|
||||
return createIssue(
|
||||
tom.controlId,
|
||||
tom.name,
|
||||
'STALE_NOT_IMPLEMENTED',
|
||||
'LOW',
|
||||
'Langfristig nicht umgesetzte Pflichtmassnahme',
|
||||
`Die TOM "${tom.name}" ist als REQUIRED eingestuft, aber seit ${ageInDays} Tagen nicht umgesetzt. Pflichtmassnahmen, die laenger als 90 Tage nicht implementiert werden, deuten auf organisatorische Blockaden oder unzureichende Priorisierung hin.`,
|
||||
'Pruefen Sie, ob die Massnahme weiterhin erforderlich ist, und erstellen Sie einen konkreten Umsetzungsplan mit Verantwortlichkeiten und Fristen.'
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// AGGREGATE CHECKS (4-10)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Check 4: INCOMPLETE_CATEGORY (HIGH)
|
||||
* Category where ALL applicable (REQUIRED) controls are NOT_IMPLEMENTED.
|
||||
*/
|
||||
function checkIncompleteCategory(toms: DerivedTOM[]): TOMComplianceIssue[] {
|
||||
const issues: TOMComplianceIssue[] = []
|
||||
|
||||
// Group applicable TOMs by category
|
||||
const categoryMap = new Map<ControlCategory, DerivedTOM[]>()
|
||||
|
||||
for (const tom of toms) {
|
||||
const control = getControlById(tom.controlId)
|
||||
if (!control) continue
|
||||
|
||||
const category = control.category
|
||||
if (!categoryMap.has(category)) {
|
||||
categoryMap.set(category, [])
|
||||
}
|
||||
categoryMap.get(category)!.push(tom)
|
||||
}
|
||||
|
||||
for (const [category, categoryToms] of Array.from(categoryMap.entries())) {
|
||||
// Only check categories that have at least one REQUIRED control
|
||||
const requiredToms = categoryToms.filter((t: DerivedTOM) => t.applicability === 'REQUIRED')
|
||||
if (requiredToms.length === 0) continue
|
||||
|
||||
const allNotImplemented = requiredToms.every((t: DerivedTOM) => t.implementationStatus === 'NOT_IMPLEMENTED')
|
||||
if (allNotImplemented) {
|
||||
issues.push(
|
||||
createIssue(
|
||||
category,
|
||||
category,
|
||||
'INCOMPLETE_CATEGORY',
|
||||
'HIGH',
|
||||
`Kategorie "${category}" vollstaendig ohne Umsetzung`,
|
||||
`Alle ${requiredToms.length} Pflichtmassnahme(n) in der Kategorie "${category}" sind nicht umgesetzt. Eine vollstaendig unabgedeckte Kategorie stellt eine erhebliche Luecke im TOM-Konzept dar.`,
|
||||
`Setzen Sie mindestens die wichtigsten Massnahmen in der Kategorie "${category}" um, um eine Grundabdeckung sicherzustellen.`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 5: NO_ENCRYPTION_MEASURES (CRITICAL)
|
||||
* No ENCRYPTION control with status IMPLEMENTED.
|
||||
*/
|
||||
function checkNoEncryption(toms: DerivedTOM[]): TOMComplianceIssue | null {
|
||||
const hasImplementedEncryption = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return control?.category === 'ENCRYPTION' && tom.implementationStatus === 'IMPLEMENTED'
|
||||
})
|
||||
|
||||
if (!hasImplementedEncryption) {
|
||||
return createIssue(
|
||||
'ENCRYPTION',
|
||||
'Verschluesselung',
|
||||
'NO_ENCRYPTION_MEASURES',
|
||||
'CRITICAL',
|
||||
'Keine Verschluesselungsmassnahmen umgesetzt',
|
||||
'Es ist keine einzige Verschluesselungsmassnahme als IMPLEMENTED markiert. Art. 32 Abs. 1 lit. a DSGVO nennt Verschluesselung explizit als geeignete technische Massnahme. Ohne Verschluesselung sind personenbezogene Daten bei Zugriff oder Verlust ungeschuetzt.',
|
||||
'Implementieren Sie umgehend Verschluesselungsmassnahmen fuer Daten im Ruhezustand (Encryption at Rest) und waehrend der Uebertragung (Encryption in Transit).'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 6: NO_PSEUDONYMIZATION (MEDIUM)
|
||||
* DataProfile has special categories (Art. 9) but no PSEUDONYMIZATION control implemented.
|
||||
*/
|
||||
function checkNoPseudonymization(toms: DerivedTOM[], dataProfile: DataProfile | null): TOMComplianceIssue | null {
|
||||
if (!dataProfile || !dataProfile.hasSpecialCategories) return null
|
||||
|
||||
const hasImplementedPseudonymization = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return control?.category === 'PSEUDONYMIZATION' && tom.implementationStatus === 'IMPLEMENTED'
|
||||
})
|
||||
|
||||
if (!hasImplementedPseudonymization) {
|
||||
return createIssue(
|
||||
'PSEUDONYMIZATION',
|
||||
'Pseudonymisierung',
|
||||
'NO_PSEUDONYMIZATION',
|
||||
'MEDIUM',
|
||||
'Keine Pseudonymisierung bei besonderen Datenkategorien',
|
||||
'Das Datenprofil enthaelt besondere Kategorien personenbezogener Daten (Art. 9 DSGVO), aber keine Pseudonymisierungsmassnahme ist umgesetzt. Art. 32 Abs. 1 lit. a DSGVO empfiehlt Pseudonymisierung ausdruecklich als Schutzmassnahme.',
|
||||
'Implementieren Sie Pseudonymisierungsmassnahmen fuer die Verarbeitung besonderer Datenkategorien, um das Risiko fuer betroffene Personen zu minimieren.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 7: MISSING_AVAILABILITY (HIGH)
|
||||
* No AVAILABILITY or RECOVERY control implemented AND no DR plan in securityProfile.
|
||||
*/
|
||||
function checkMissingAvailability(toms: DerivedTOM[], state: TOMGeneratorState): TOMComplianceIssue | null {
|
||||
const hasAvailabilityOrRecovery = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return (
|
||||
(control?.category === 'AVAILABILITY' || control?.category === 'RECOVERY') &&
|
||||
tom.implementationStatus === 'IMPLEMENTED'
|
||||
)
|
||||
})
|
||||
|
||||
const hasDRPlan = state.securityProfile?.hasDRPlan ?? false
|
||||
|
||||
if (!hasAvailabilityOrRecovery && !hasDRPlan) {
|
||||
return createIssue(
|
||||
'AVAILABILITY',
|
||||
'Verfuegbarkeit / Wiederherstellbarkeit',
|
||||
'MISSING_AVAILABILITY',
|
||||
'HIGH',
|
||||
'Keine Verfuegbarkeits- oder Wiederherstellungsmassnahmen',
|
||||
'Weder Verfuegbarkeits- noch Wiederherstellungsmassnahmen sind umgesetzt, und es existiert kein Disaster-Recovery-Plan im Security-Profil. Art. 32 Abs. 1 lit. b und c DSGVO verlangen die Faehigkeit zur raschen Wiederherstellung der Verfuegbarkeit personenbezogener Daten.',
|
||||
'Implementieren Sie Backup-Konzepte, Redundanzloesungen und einen Disaster-Recovery-Plan, um die Verfuegbarkeit und Wiederherstellbarkeit sicherzustellen.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 8: NO_REVIEW_PROCESS (MEDIUM)
|
||||
* No REVIEW control implemented (Art. 32 Abs. 1 lit. d requires periodic review).
|
||||
*/
|
||||
function checkNoReviewProcess(toms: DerivedTOM[]): TOMComplianceIssue | null {
|
||||
const hasImplementedReview = toms.some((tom) => {
|
||||
const control = getControlById(tom.controlId)
|
||||
return control?.category === 'REVIEW' && tom.implementationStatus === 'IMPLEMENTED'
|
||||
})
|
||||
|
||||
if (!hasImplementedReview) {
|
||||
return createIssue(
|
||||
'REVIEW',
|
||||
'Ueberpruefung & Bewertung',
|
||||
'NO_REVIEW_PROCESS',
|
||||
'MEDIUM',
|
||||
'Kein Verfahren zur regelmaessigen Ueberpruefung',
|
||||
'Es ist keine Ueberpruefungsmassnahme als IMPLEMENTED markiert. Art. 32 Abs. 1 lit. d DSGVO verlangt ein Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung der Wirksamkeit der technischen und organisatorischen Massnahmen.',
|
||||
'Implementieren Sie einen regelmaessigen Review-Prozess (z.B. quartalsweise TOM-Audits, jaehrliche Wirksamkeitspruefung) und dokumentieren Sie die Ergebnisse.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 9: UNCOVERED_SDM_GOAL (HIGH)
|
||||
* SDM goal with 0% coverage — no implemented control maps to it via SDM_CATEGORY_MAPPING.
|
||||
*/
|
||||
function checkUncoveredSDMGoal(toms: DerivedTOM[]): TOMComplianceIssue[] {
|
||||
const issues: TOMComplianceIssue[] = []
|
||||
|
||||
// Build reverse mapping: SDM goal -> ControlCategories that cover it
|
||||
const sdmGoals = [
|
||||
'Verfuegbarkeit',
|
||||
'Integritaet',
|
||||
'Vertraulichkeit',
|
||||
'Nichtverkettung',
|
||||
'Intervenierbarkeit',
|
||||
'Transparenz',
|
||||
'Datenminimierung',
|
||||
] as const
|
||||
|
||||
const goalToCategoriesMap = new Map<string, ControlCategory[]>()
|
||||
for (const goal of sdmGoals) {
|
||||
goalToCategoriesMap.set(goal, [])
|
||||
}
|
||||
|
||||
// Build reverse lookup from SDM_CATEGORY_MAPPING
|
||||
for (const [category, goals] of Object.entries(SDM_CATEGORY_MAPPING)) {
|
||||
for (const goal of goals) {
|
||||
const existing = goalToCategoriesMap.get(goal)
|
||||
if (existing) {
|
||||
existing.push(category as ControlCategory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect implemented categories
|
||||
const implementedCategories = new Set<ControlCategory>()
|
||||
for (const tom of toms) {
|
||||
if (tom.implementationStatus !== 'IMPLEMENTED') continue
|
||||
const control = getControlById(tom.controlId)
|
||||
if (control) {
|
||||
implementedCategories.add(control.category)
|
||||
}
|
||||
}
|
||||
|
||||
// Check each SDM goal
|
||||
for (const goal of sdmGoals) {
|
||||
const coveringCategories = goalToCategoriesMap.get(goal) ?? []
|
||||
const hasCoverage = coveringCategories.some((cat) => implementedCategories.has(cat))
|
||||
|
||||
if (!hasCoverage) {
|
||||
issues.push(
|
||||
createIssue(
|
||||
`SDM-${goal}`,
|
||||
goal,
|
||||
'UNCOVERED_SDM_GOAL',
|
||||
'HIGH',
|
||||
`SDM-Gewaehrleistungsziel "${goal}" nicht abgedeckt`,
|
||||
`Das Gewaehrleistungsziel "${goal}" des Standard-Datenschutzmodells (SDM) ist durch keine umgesetzte Massnahme abgedeckt. Zugehoerige Kategorien (${coveringCategories.join(', ')}) haben keine IMPLEMENTED Controls. Das SDM ist die anerkannte Methodik zur Umsetzung der DSGVO-Anforderungen.`,
|
||||
`Setzen Sie mindestens eine Massnahme aus den Kategorien ${coveringCategories.join(', ')} um, um das SDM-Ziel "${goal}" abzudecken.`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 10: HIGH_RISK_WITHOUT_MEASURES (CRITICAL)
|
||||
* Protection level VERY_HIGH but < 50% of REQUIRED controls implemented.
|
||||
*/
|
||||
function checkHighRiskWithoutMeasures(toms: DerivedTOM[], riskProfile: RiskProfile | null): TOMComplianceIssue | null {
|
||||
if (!riskProfile || riskProfile.protectionLevel !== 'VERY_HIGH') return null
|
||||
|
||||
const requiredToms = toms.filter((t) => t.applicability === 'REQUIRED')
|
||||
if (requiredToms.length === 0) return null
|
||||
|
||||
const implementedCount = requiredToms.filter((t) => t.implementationStatus === 'IMPLEMENTED').length
|
||||
const implementationRate = implementedCount / requiredToms.length
|
||||
|
||||
if (implementationRate < 0.5) {
|
||||
const percentage = Math.round(implementationRate * 100)
|
||||
return createIssue(
|
||||
'RISK-PROFILE',
|
||||
'Risikoprofil VERY_HIGH',
|
||||
'HIGH_RISK_WITHOUT_MEASURES',
|
||||
'CRITICAL',
|
||||
'Sehr hoher Schutzbedarf bei niedriger Umsetzungsrate',
|
||||
`Der Schutzbedarf ist als VERY_HIGH eingestuft, aber nur ${implementedCount} von ${requiredToms.length} Pflichtmassnahmen (${percentage}%) sind umgesetzt. Bei sehr hohem Schutzbedarf muessen mindestens 50% der Pflichtmassnahmen implementiert sein, um ein angemessenes Schutzniveau gemaess Art. 32 DSGVO zu gewaehrleisten.`,
|
||||
'Priorisieren Sie die Umsetzung der verbleibenden Pflichtmassnahmen. Beginnen Sie mit CRITICAL- und HIGH-Priority Controls. Erwaeegen Sie einen Umsetzungsplan mit klaren Meilensteinen.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MAIN COMPLIANCE CHECK
|
||||
// =============================================================================
|
||||
@@ -467,7 +94,7 @@ function checkHighRiskWithoutMeasures(toms: DerivedTOM[], riskProfile: RiskProfi
|
||||
*/
|
||||
export function runTOMComplianceCheck(state: TOMGeneratorState): TOMComplianceCheckResult {
|
||||
// Reset counter for deterministic IDs within a single check run
|
||||
issueCounter = 0
|
||||
resetIssueCounter()
|
||||
|
||||
const issues: TOMComplianceIssue[] = []
|
||||
|
||||
@@ -513,10 +140,7 @@ export function runTOMComplianceCheck(state: TOMGeneratorState): TOMComplianceCh
|
||||
|
||||
// Calculate score
|
||||
const bySeverity: Record<TOMComplianceIssueSeverity, number> = {
|
||||
LOW: 0,
|
||||
MEDIUM: 0,
|
||||
HIGH: 0,
|
||||
CRITICAL: 0,
|
||||
LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0,
|
||||
}
|
||||
|
||||
for (const issue of issues) {
|
||||
|
||||
@@ -1,906 +1,8 @@
|
||||
// =============================================================================
|
||||
// TOM Module - TOM-Dokumentation Document Generator
|
||||
// Generates a printable, audit-ready HTML document according to DSGVO Art. 32
|
||||
// Barrel re-export — implementation split into tom-document/
|
||||
// =============================================================================
|
||||
|
||||
import type {
|
||||
TOMGeneratorState,
|
||||
DerivedTOM,
|
||||
CompanyProfile,
|
||||
RiskProfile,
|
||||
ControlCategory,
|
||||
} from './tom-generator/types'
|
||||
|
||||
import { SDM_CATEGORY_MAPPING } from './tom-generator/types'
|
||||
|
||||
import {
|
||||
getControlById,
|
||||
getControlsByCategory,
|
||||
getAllCategories,
|
||||
getCategoryMetadata,
|
||||
} from './tom-generator/controls/loader'
|
||||
|
||||
import type { TOMComplianceCheckResult, TOMComplianceIssueSeverity } from './tom-compliance'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface TOMDocumentOrgHeader {
|
||||
organizationName: string
|
||||
industry: string
|
||||
dpoName: string
|
||||
dpoContact: string
|
||||
responsiblePerson: string
|
||||
itSecurityContact: string
|
||||
locations: string[]
|
||||
employeeCount: string
|
||||
documentVersion: string
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: string
|
||||
}
|
||||
|
||||
export interface TOMDocumentRevision {
|
||||
version: string
|
||||
date: string
|
||||
author: string
|
||||
changes: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// DEFAULTS
|
||||
// =============================================================================
|
||||
|
||||
export function createDefaultTOMDocumentOrgHeader(): TOMDocumentOrgHeader {
|
||||
const now = new Date()
|
||||
const nextYear = new Date()
|
||||
nextYear.setFullYear(nextYear.getFullYear() + 1)
|
||||
|
||||
return {
|
||||
organizationName: '',
|
||||
industry: '',
|
||||
dpoName: '',
|
||||
dpoContact: '',
|
||||
responsiblePerson: '',
|
||||
itSecurityContact: '',
|
||||
locations: [],
|
||||
employeeCount: '',
|
||||
documentVersion: '1.0',
|
||||
lastReviewDate: now.toISOString().split('T')[0],
|
||||
nextReviewDate: nextYear.toISOString().split('T')[0],
|
||||
reviewInterval: 'Jaehrlich',
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SEVERITY LABELS (for Compliance Status section)
|
||||
// =============================================================================
|
||||
|
||||
const SEVERITY_LABELS_DE: Record<TOMComplianceIssueSeverity, string> = {
|
||||
CRITICAL: 'Kritisch',
|
||||
HIGH: 'Hoch',
|
||||
MEDIUM: 'Mittel',
|
||||
LOW: 'Niedrig',
|
||||
}
|
||||
|
||||
const SEVERITY_COLORS: Record<TOMComplianceIssueSeverity, string> = {
|
||||
CRITICAL: '#dc2626',
|
||||
HIGH: '#ea580c',
|
||||
MEDIUM: '#d97706',
|
||||
LOW: '#6b7280',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CATEGORY LABELS (German)
|
||||
// =============================================================================
|
||||
|
||||
const CATEGORY_LABELS_DE: Record<ControlCategory, string> = {
|
||||
ACCESS_CONTROL: 'Zutrittskontrolle',
|
||||
ADMISSION_CONTROL: 'Zugangskontrolle',
|
||||
ACCESS_AUTHORIZATION: 'Zugriffskontrolle',
|
||||
TRANSFER_CONTROL: 'Weitergabekontrolle',
|
||||
INPUT_CONTROL: 'Eingabekontrolle',
|
||||
ORDER_CONTROL: 'Auftragskontrolle',
|
||||
AVAILABILITY: 'Verfuegbarkeit',
|
||||
SEPARATION: 'Trennbarkeit',
|
||||
ENCRYPTION: 'Verschluesselung',
|
||||
PSEUDONYMIZATION: 'Pseudonymisierung',
|
||||
RESILIENCE: 'Belastbarkeit',
|
||||
RECOVERY: 'Wiederherstellbarkeit',
|
||||
REVIEW: 'Ueberpruefung & Bewertung',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// STATUS & APPLICABILITY LABELS
|
||||
// =============================================================================
|
||||
|
||||
const STATUS_LABELS_DE: Record<string, string> = {
|
||||
IMPLEMENTED: 'Umgesetzt',
|
||||
PARTIAL: 'Teilweise umgesetzt',
|
||||
NOT_IMPLEMENTED: 'Nicht umgesetzt',
|
||||
}
|
||||
|
||||
const STATUS_BADGE_CLASSES: Record<string, string> = {
|
||||
IMPLEMENTED: 'badge-active',
|
||||
PARTIAL: 'badge-review',
|
||||
NOT_IMPLEMENTED: 'badge-critical',
|
||||
}
|
||||
|
||||
const APPLICABILITY_LABELS_DE: Record<string, string> = {
|
||||
REQUIRED: 'Erforderlich',
|
||||
RECOMMENDED: 'Empfohlen',
|
||||
OPTIONAL: 'Optional',
|
||||
NOT_APPLICABLE: 'Nicht anwendbar',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HTML DOCUMENT BUILDER
|
||||
// =============================================================================
|
||||
|
||||
export function buildTOMDocumentHtml(
|
||||
derivedTOMs: DerivedTOM[],
|
||||
orgHeader: TOMDocumentOrgHeader,
|
||||
companyProfile: CompanyProfile | null,
|
||||
riskProfile: RiskProfile | null,
|
||||
complianceResult: TOMComplianceCheckResult | null,
|
||||
revisions: TOMDocumentRevision[]
|
||||
): string {
|
||||
const today = new Date().toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const orgName = orgHeader.organizationName || 'Organisation'
|
||||
|
||||
// Filter out NOT_APPLICABLE TOMs for display
|
||||
const applicableTOMs = derivedTOMs.filter(t => t.applicability !== 'NOT_APPLICABLE')
|
||||
|
||||
// Group TOMs by category via control library lookup
|
||||
const tomsByCategory = new Map<ControlCategory, DerivedTOM[]>()
|
||||
for (const tom of applicableTOMs) {
|
||||
const control = getControlById(tom.controlId)
|
||||
const cat = control?.category || 'REVIEW'
|
||||
if (!tomsByCategory.has(cat)) tomsByCategory.set(cat, [])
|
||||
tomsByCategory.get(cat)!.push(tom)
|
||||
}
|
||||
|
||||
// Build role map: role/department → list of control codes
|
||||
const roleMap = new Map<string, string[]>()
|
||||
for (const tom of applicableTOMs) {
|
||||
const role = tom.responsiblePerson || tom.responsibleDepartment || 'Nicht zugewiesen'
|
||||
if (!roleMap.has(role)) roleMap.set(role, [])
|
||||
const control = getControlById(tom.controlId)
|
||||
roleMap.get(role)!.push(control?.code || tom.controlId)
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// HTML Template
|
||||
// =========================================================================
|
||||
|
||||
let html = `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>TOM-Dokumentation — ${escHtml(orgName)}</title>
|
||||
<style>
|
||||
@page { size: A4; margin: 20mm 18mm 22mm 18mm; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
/* Cover */
|
||||
.cover {
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
page-break-after: always;
|
||||
}
|
||||
.cover h1 {
|
||||
font-size: 28pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.cover .subtitle {
|
||||
font-size: 14pt;
|
||||
color: #7c3aed;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.cover .org-info {
|
||||
background: #f5f3ff;
|
||||
border: 1px solid #ddd6fe;
|
||||
border-radius: 8px;
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
width: 400px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.cover .org-info div { margin-bottom: 6px; }
|
||||
.cover .org-info .label { font-weight: 600; color: #5b21b6; display: inline-block; min-width: 160px; }
|
||||
.cover .legal-ref {
|
||||
font-size: 9pt;
|
||||
color: #64748b;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* TOC */
|
||||
.toc {
|
||||
page-break-after: always;
|
||||
padding-top: 40px;
|
||||
}
|
||||
.toc h2 {
|
||||
font-size: 18pt;
|
||||
color: #5b21b6;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid #5b21b6;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.toc-entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px dotted #cbd5e1;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.toc-entry .toc-num { font-weight: 600; color: #5b21b6; min-width: 40px; }
|
||||
|
||||
/* Sections */
|
||||
.section {
|
||||
page-break-inside: avoid;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.section-header {
|
||||
font-size: 14pt;
|
||||
color: #5b21b6;
|
||||
font-weight: 700;
|
||||
margin: 30px 0 12px 0;
|
||||
border-bottom: 2px solid #ddd6fe;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.section-body { margin-bottom: 16px; }
|
||||
|
||||
/* Tables */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0 16px 0;
|
||||
font-size: 9pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
background: #f5f3ff;
|
||||
color: #5b21b6;
|
||||
font-weight: 600;
|
||||
font-size: 8.5pt;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
tr:nth-child(even) td { background: #faf5ff; }
|
||||
|
||||
/* Detail cards */
|
||||
.policy-detail {
|
||||
page-break-inside: avoid;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.policy-detail-header {
|
||||
background: #f5f3ff;
|
||||
padding: 8px 12px;
|
||||
font-weight: 700;
|
||||
color: #5b21b6;
|
||||
border-bottom: 1px solid #ddd6fe;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.policy-detail-body { padding: 0; }
|
||||
.policy-detail-body table { margin: 0; }
|
||||
.policy-detail-body th { width: 200px; }
|
||||
|
||||
/* Badges */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 1px 8px;
|
||||
border-radius: 9999px;
|
||||
font-size: 8pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
.badge-active { background: #dcfce7; color: #166534; }
|
||||
.badge-draft { background: #f3f4f6; color: #374151; }
|
||||
.badge-review { background: #fef9c3; color: #854d0e; }
|
||||
.badge-critical { background: #fecaca; color: #991b1b; }
|
||||
.badge-high { background: #fed7aa; color: #9a3412; }
|
||||
.badge-medium { background: #fef3c7; color: #92400e; }
|
||||
.badge-low { background: #f3f4f6; color: #4b5563; }
|
||||
|
||||
/* Principles */
|
||||
.principle {
|
||||
margin-bottom: 10px;
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.principle::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #7c3aed;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.principle strong { color: #5b21b6; }
|
||||
|
||||
/* Score */
|
||||
.score-box {
|
||||
display: inline-block;
|
||||
padding: 4px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 18pt;
|
||||
font-weight: 700;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.score-excellent { background: #dcfce7; color: #166534; }
|
||||
.score-good { background: #dbeafe; color: #1e40af; }
|
||||
.score-needs-work { background: #fef3c7; color: #92400e; }
|
||||
.score-poor { background: #fecaca; color: #991b1b; }
|
||||
|
||||
/* Footer */
|
||||
.page-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 8px 18mm;
|
||||
font-size: 7.5pt;
|
||||
color: #94a3b8;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
/* Print */
|
||||
@media print {
|
||||
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
.no-print { display: none !important; }
|
||||
.page-break { page-break-after: always; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 0: Cover Page
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="cover">
|
||||
<h1>TOM-Dokumentation</h1>
|
||||
<div class="subtitle">Technische und Organisatorische Massnahmen gemaess Art. 32 DSGVO</div>
|
||||
<div class="org-info">
|
||||
<div><span class="label">Organisation:</span> ${escHtml(orgName)}</div>
|
||||
${orgHeader.industry ? `<div><span class="label">Branche:</span> ${escHtml(orgHeader.industry)}</div>` : ''}
|
||||
${orgHeader.dpoName ? `<div><span class="label">DSB:</span> ${escHtml(orgHeader.dpoName)}</div>` : ''}
|
||||
${orgHeader.dpoContact ? `<div><span class="label">DSB-Kontakt:</span> ${escHtml(orgHeader.dpoContact)}</div>` : ''}
|
||||
${orgHeader.responsiblePerson ? `<div><span class="label">Verantwortlicher:</span> ${escHtml(orgHeader.responsiblePerson)}</div>` : ''}
|
||||
${orgHeader.itSecurityContact ? `<div><span class="label">IT-Sicherheit:</span> ${escHtml(orgHeader.itSecurityContact)}</div>` : ''}
|
||||
${orgHeader.employeeCount ? `<div><span class="label">Mitarbeiter:</span> ${escHtml(orgHeader.employeeCount)}</div>` : ''}
|
||||
${orgHeader.locations.length > 0 ? `<div><span class="label">Standorte:</span> ${escHtml(orgHeader.locations.join(', '))}</div>` : ''}
|
||||
</div>
|
||||
<div class="legal-ref">
|
||||
Version ${escHtml(orgHeader.documentVersion)} | Stand: ${today}<br/>
|
||||
Letzte Pruefung: ${formatDateDE(orgHeader.lastReviewDate)} | Naechste Pruefung: ${formatDateDE(orgHeader.nextReviewDate)}<br/>
|
||||
Pruefintervall: ${escHtml(orgHeader.reviewInterval)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Table of Contents
|
||||
// =========================================================================
|
||||
const sections = [
|
||||
'Ziel und Zweck',
|
||||
'Geltungsbereich',
|
||||
'Grundprinzipien Art. 32',
|
||||
'Schutzbedarf und Risikoanalyse',
|
||||
'Massnahmen-Uebersicht',
|
||||
'Detaillierte Massnahmen',
|
||||
'SDM Gewaehrleistungsziele',
|
||||
'Verantwortlichkeiten',
|
||||
'Pruef- und Revisionszyklus',
|
||||
'Compliance-Status',
|
||||
'Aenderungshistorie',
|
||||
]
|
||||
|
||||
html += `
|
||||
<div class="toc">
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
${sections.map((s, i) => `<div class="toc-entry"><span><span class="toc-num">${i + 1}.</span> ${escHtml(s)}</span></div>`).join('\n ')}
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 1: Ziel und Zweck
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">1. Ziel und Zweck</div>
|
||||
<div class="section-body">
|
||||
<p>Diese TOM-Dokumentation beschreibt die technischen und organisatorischen Massnahmen
|
||||
zum Schutz personenbezogener Daten bei <strong>${escHtml(orgName)}</strong>. Sie dient
|
||||
der Umsetzung folgender DSGVO-Anforderungen:</p>
|
||||
<table>
|
||||
<tr><th>Rechtsgrundlage</th><th>Inhalt</th></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. a DSGVO</strong></td><td>Pseudonymisierung und Verschluesselung personenbezogener Daten</td></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. b DSGVO</strong></td><td>Faehigkeit, die Vertraulichkeit, Integritaet, Verfuegbarkeit und Belastbarkeit der Systeme und Dienste im Zusammenhang mit der Verarbeitung auf Dauer sicherzustellen</td></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. c DSGVO</strong></td><td>Faehigkeit, die Verfuegbarkeit der personenbezogenen Daten und den Zugang zu ihnen bei einem physischen oder technischen Zwischenfall rasch wiederherzustellen</td></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. d DSGVO</strong></td><td>Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung der Wirksamkeit der technischen und organisatorischen Massnahmen</td></tr>
|
||||
</table>
|
||||
<p>Die TOM-Dokumentation ist fester Bestandteil des Datenschutz-Managementsystems und wird
|
||||
regelmaessig ueberprueft und aktualisiert.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 2: Geltungsbereich
|
||||
// =========================================================================
|
||||
const industryInfo = companyProfile?.industry || orgHeader.industry || ''
|
||||
const hostingInfo = companyProfile ? `Unternehmen: ${escHtml(companyProfile.name || orgName)}, Groesse: ${escHtml(companyProfile.size || '-')}` : ''
|
||||
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">2. Geltungsbereich</div>
|
||||
<div class="section-body">
|
||||
<p>Diese TOM-Dokumentation gilt fuer alle IT-Systeme, Anwendungen und Verarbeitungsprozesse
|
||||
von <strong>${escHtml(orgName)}</strong>${industryInfo ? ` (Branche: ${escHtml(industryInfo)})` : ''}.</p>
|
||||
${hostingInfo ? `<p>${hostingInfo}</p>` : ''}
|
||||
${orgHeader.locations.length > 0 ? `<p>Standorte: ${escHtml(orgHeader.locations.join(', '))}</p>` : ''}
|
||||
<p>Die dokumentierten Massnahmen stammen aus zwei Quellen:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li><strong>Embedded Library (TOM-xxx):</strong> Integrierte Kontrollbibliothek mit spezifischen Massnahmen fuer Art. 32 DSGVO</li>
|
||||
<li><strong>Canonical Control Library (CP-CLIB):</strong> Uebergreifende Kontrollbibliothek mit framework-uebergreifenden Massnahmen</li>
|
||||
</ul>
|
||||
<p>Insgesamt umfasst dieses Dokument <strong>${applicableTOMs.length}</strong> anwendbare Massnahmen
|
||||
in <strong>${tomsByCategory.size}</strong> Kategorien.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 3: Grundprinzipien Art. 32
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">3. Grundprinzipien Art. 32</div>
|
||||
<div class="section-body">
|
||||
<div class="principle"><strong>Vertraulichkeit:</strong> Schutz personenbezogener Daten vor unbefugter Kenntnisnahme durch Zutrittskontrolle, Zugangskontrolle, Zugriffskontrolle und Verschluesselung (Art. 32 Abs. 1 lit. b DSGVO).</div>
|
||||
<div class="principle"><strong>Integritaet:</strong> Sicherstellung, dass personenbezogene Daten nicht unbefugt oder unbeabsichtigt veraendert werden koennen, durch Eingabekontrolle, Weitergabekontrolle und Protokollierung (Art. 32 Abs. 1 lit. b DSGVO).</div>
|
||||
<div class="principle"><strong>Verfuegbarkeit und Belastbarkeit:</strong> Gewaehrleistung, dass Systeme und Dienste bei Lastspitzen und Stoerungen zuverlaessig funktionieren, durch Backup, Redundanz und Disaster Recovery (Art. 32 Abs. 1 lit. b DSGVO).</div>
|
||||
<div class="principle"><strong>Rasche Wiederherstellbarkeit:</strong> Faehigkeit, nach einem physischen oder technischen Zwischenfall Daten und Systeme schnell wiederherzustellen, durch getestete Recovery-Prozesse (Art. 32 Abs. 1 lit. c DSGVO).</div>
|
||||
<div class="principle"><strong>Regelmaessige Wirksamkeitspruefung:</strong> Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung der Wirksamkeit aller technischen und organisatorischen Massnahmen (Art. 32 Abs. 1 lit. d DSGVO).</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 4: Schutzbedarf und Risikoanalyse
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">4. Schutzbedarf und Risikoanalyse</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (riskProfile) {
|
||||
html += ` <p>Die folgende Schutzbedarfsanalyse bildet die Grundlage fuer die Auswahl und Priorisierung
|
||||
der technischen und organisatorischen Massnahmen:</p>
|
||||
<table>
|
||||
<tr><th>Kriterium</th><th>Bewertung</th></tr>
|
||||
<tr><td>Vertraulichkeit</td><td>${riskProfile.ciaAssessment.confidentiality}/5</td></tr>
|
||||
<tr><td>Integritaet</td><td>${riskProfile.ciaAssessment.integrity}/5</td></tr>
|
||||
<tr><td>Verfuegbarkeit</td><td>${riskProfile.ciaAssessment.availability}/5</td></tr>
|
||||
<tr><td>Schutzniveau</td><td><strong>${escHtml(riskProfile.protectionLevel)}</strong></td></tr>
|
||||
<tr><td>DSFA-Pflicht</td><td>${riskProfile.dsfaRequired ? 'Ja' : 'Nein'}</td></tr>
|
||||
${riskProfile.specialRisks.length > 0 ? `<tr><td>Spezialrisiken</td><td>${escHtml(riskProfile.specialRisks.join(', '))}</td></tr>` : ''}
|
||||
${riskProfile.regulatoryRequirements.length > 0 ? `<tr><td>Regulatorische Anforderungen</td><td>${escHtml(riskProfile.regulatoryRequirements.join(', '))}</td></tr>` : ''}
|
||||
</table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Die Schutzbedarfsanalyse wurde noch nicht durchgefuehrt. Fuehren Sie den
|
||||
Risiko-Wizard im TOM-Generator durch, um den Schutzbedarf zu ermitteln.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 5: Massnahmen-Uebersicht
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">5. Massnahmen-Uebersicht</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt eine Uebersicht aller ${applicableTOMs.length} anwendbaren Massnahmen
|
||||
nach Kategorie:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Kategorie</th>
|
||||
<th>Gesamt</th>
|
||||
<th>Umgesetzt</th>
|
||||
<th>Teilweise</th>
|
||||
<th>Offen</th>
|
||||
</tr>
|
||||
`
|
||||
const allCategories = getAllCategories()
|
||||
for (const cat of allCategories) {
|
||||
const tomsInCat = tomsByCategory.get(cat)
|
||||
if (!tomsInCat || tomsInCat.length === 0) continue
|
||||
|
||||
const implemented = tomsInCat.filter(t => t.implementationStatus === 'IMPLEMENTED').length
|
||||
const partial = tomsInCat.filter(t => t.implementationStatus === 'PARTIAL').length
|
||||
const notImpl = tomsInCat.filter(t => t.implementationStatus === 'NOT_IMPLEMENTED').length
|
||||
const catLabel = CATEGORY_LABELS_DE[cat] || cat
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(catLabel)}</td>
|
||||
<td>${tomsInCat.length}</td>
|
||||
<td>${implemented}</td>
|
||||
<td>${partial}</td>
|
||||
<td>${notImpl}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 6: Detaillierte Massnahmen
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">6. Detaillierte Massnahmen</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
|
||||
for (const cat of allCategories) {
|
||||
const tomsInCat = tomsByCategory.get(cat)
|
||||
if (!tomsInCat || tomsInCat.length === 0) continue
|
||||
|
||||
const catLabel = CATEGORY_LABELS_DE[cat] || cat
|
||||
const catMeta = getCategoryMetadata(cat)
|
||||
const gdprRef = catMeta?.gdprReference || ''
|
||||
|
||||
html += ` <h3 style="color: #5b21b6; margin: 20px 0 10px 0; font-size: 11pt;">${escHtml(catLabel)}${gdprRef ? ` <span style="font-weight: 400; font-size: 9pt; color: #64748b;">(${escHtml(gdprRef)})</span>` : ''}</h3>
|
||||
`
|
||||
|
||||
// Sort TOMs by control code
|
||||
const sortedTOMs = [...tomsInCat].sort((a, b) => {
|
||||
const codeA = getControlById(a.controlId)?.code || a.controlId
|
||||
const codeB = getControlById(b.controlId)?.code || b.controlId
|
||||
return codeA.localeCompare(codeB)
|
||||
})
|
||||
|
||||
for (const tom of sortedTOMs) {
|
||||
const control = getControlById(tom.controlId)
|
||||
const code = control?.code || tom.controlId
|
||||
const nameDE = control?.name?.de || tom.name
|
||||
const descDE = control?.description?.de || tom.description
|
||||
const typeLabel = control?.type === 'TECHNICAL' ? 'Technisch' : control?.type === 'ORGANIZATIONAL' ? 'Organisatorisch' : '-'
|
||||
const statusLabel = STATUS_LABELS_DE[tom.implementationStatus] || tom.implementationStatus
|
||||
const statusBadge = STATUS_BADGE_CLASSES[tom.implementationStatus] || 'badge-draft'
|
||||
const applicabilityLabel = APPLICABILITY_LABELS_DE[tom.applicability] || tom.applicability
|
||||
const responsible = [tom.responsiblePerson, tom.responsibleDepartment].filter(s => s && s.trim()).join(' / ') || '-'
|
||||
const implDate = tom.implementationDate ? formatDateDE(typeof tom.implementationDate === 'string' ? tom.implementationDate : tom.implementationDate.toISOString()) : '-'
|
||||
const reviewDate = tom.reviewDate ? formatDateDE(typeof tom.reviewDate === 'string' ? tom.reviewDate : tom.reviewDate.toISOString()) : '-'
|
||||
|
||||
// Evidence
|
||||
const evidenceInfo = tom.linkedEvidence.length > 0
|
||||
? tom.linkedEvidence.join(', ')
|
||||
: tom.evidenceGaps.length > 0
|
||||
? `<em style="color: #d97706;">Fehlend: ${escHtml(tom.evidenceGaps.join(', '))}</em>`
|
||||
: '-'
|
||||
|
||||
// Framework mappings
|
||||
let mappingsHtml = '-'
|
||||
if (control?.mappings && control.mappings.length > 0) {
|
||||
mappingsHtml = control.mappings.map(m => `${escHtml(m.framework)}: ${escHtml(m.reference)}`).join('<br/>')
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="policy-detail">
|
||||
<div class="policy-detail-header">
|
||||
<span>${escHtml(code)} — ${escHtml(nameDE)}</span>
|
||||
<span class="badge ${statusBadge}">${escHtml(statusLabel)}</span>
|
||||
</div>
|
||||
<div class="policy-detail-body">
|
||||
<table>
|
||||
<tr><th>Beschreibung</th><td>${escHtml(descDE)}</td></tr>
|
||||
<tr><th>Massnahmentyp</th><td>${escHtml(typeLabel)}</td></tr>
|
||||
<tr><th>Anwendbarkeit</th><td>${escHtml(applicabilityLabel)}${tom.applicabilityReason ? ` — ${escHtml(tom.applicabilityReason)}` : ''}</td></tr>
|
||||
<tr><th>Umsetzungsstatus</th><td><span class="badge ${statusBadge}">${escHtml(statusLabel)}</span></td></tr>
|
||||
<tr><th>Verantwortlich</th><td>${escHtml(responsible)}</td></tr>
|
||||
<tr><th>Umsetzungsdatum</th><td>${implDate}</td></tr>
|
||||
<tr><th>Naechste Pruefung</th><td>${reviewDate}</td></tr>
|
||||
<tr><th>Evidence</th><td>${evidenceInfo}</td></tr>
|
||||
<tr><th>Framework-Mappings</th><td>${mappingsHtml}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 7: SDM Gewaehrleistungsziele
|
||||
// =========================================================================
|
||||
const sdmGoals: Array<{ goal: string; categories: ControlCategory[] }> = []
|
||||
const allSDMGoals = [
|
||||
'Verfuegbarkeit',
|
||||
'Integritaet',
|
||||
'Vertraulichkeit',
|
||||
'Nichtverkettung',
|
||||
'Intervenierbarkeit',
|
||||
'Transparenz',
|
||||
'Datenminimierung',
|
||||
] as const
|
||||
|
||||
for (const goal of allSDMGoals) {
|
||||
const cats: ControlCategory[] = []
|
||||
for (const [cat, goals] of Object.entries(SDM_CATEGORY_MAPPING)) {
|
||||
if (goals.includes(goal)) {
|
||||
cats.push(cat as ControlCategory)
|
||||
}
|
||||
}
|
||||
sdmGoals.push({ goal, categories: cats })
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">7. SDM Gewaehrleistungsziele</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt die Abdeckung der sieben Gewaehrleistungsziele des
|
||||
Standard-Datenschutzmodells (SDM) durch die implementierten Massnahmen:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gewaehrleistungsziel</th>
|
||||
<th>Abgedeckt</th>
|
||||
<th>Gesamt</th>
|
||||
<th>Abdeckung (%)</th>
|
||||
</tr>
|
||||
`
|
||||
for (const { goal, categories } of sdmGoals) {
|
||||
let totalInGoal = 0
|
||||
let implementedInGoal = 0
|
||||
for (const cat of categories) {
|
||||
const tomsInCat = tomsByCategory.get(cat) || []
|
||||
totalInGoal += tomsInCat.length
|
||||
implementedInGoal += tomsInCat.filter(t => t.implementationStatus === 'IMPLEMENTED').length
|
||||
}
|
||||
const percentage = totalInGoal > 0 ? Math.round((implementedInGoal / totalInGoal) * 100) : 0
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(goal)}</td>
|
||||
<td>${implementedInGoal}</td>
|
||||
<td>${totalInGoal}</td>
|
||||
<td>${percentage}%</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 8: Verantwortlichkeiten
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">8. Verantwortlichkeiten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Rollenmatrix zeigt, welche Personen oder Abteilungen fuer welche Massnahmen
|
||||
die Umsetzungsverantwortung tragen:</p>
|
||||
<table>
|
||||
<tr><th>Rolle / Verantwortlich</th><th>Massnahmen</th><th>Anzahl</th></tr>
|
||||
`
|
||||
for (const [role, controls] of roleMap.entries()) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(role)}</td>
|
||||
<td>${controls.map(c => escHtml(c)).join(', ')}</td>
|
||||
<td>${controls.length}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 9: Pruef- und Revisionszyklus
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">9. Pruef- und Revisionszyklus</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Eigenschaft</th><th>Wert</th></tr>
|
||||
<tr><td>Aktuelles Pruefintervall</td><td>${escHtml(orgHeader.reviewInterval)}</td></tr>
|
||||
<tr><td>Letzte Pruefung</td><td>${formatDateDE(orgHeader.lastReviewDate)}</td></tr>
|
||||
<tr><td>Naechste Pruefung</td><td>${formatDateDE(orgHeader.nextReviewDate)}</td></tr>
|
||||
<tr><td>Aktuelle Version</td><td>${escHtml(orgHeader.documentVersion)}</td></tr>
|
||||
</table>
|
||||
<p style="margin-top: 8px;">Bei jeder Pruefung wird die TOM-Dokumentation auf folgende Punkte ueberprueft:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li>Vollstaendigkeit aller Massnahmen (neue Systeme oder Verarbeitungen erfasst?)</li>
|
||||
<li>Aktualitaet des Umsetzungsstatus (Aenderungen seit letzter Pruefung?)</li>
|
||||
<li>Wirksamkeit der technischen Massnahmen (Penetration-Tests, Audit-Ergebnisse)</li>
|
||||
<li>Angemessenheit der organisatorischen Massnahmen (Schulungen, Richtlinien aktuell?)</li>
|
||||
<li>Abdeckung aller SDM-Gewaehrleistungsziele</li>
|
||||
<li>Zuordnung von Verantwortlichkeiten zu allen Massnahmen</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 10: Compliance-Status
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">10. Compliance-Status</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (complianceResult) {
|
||||
const scoreClass = complianceResult.score >= 90 ? 'score-excellent'
|
||||
: complianceResult.score >= 75 ? 'score-good'
|
||||
: complianceResult.score >= 50 ? 'score-needs-work'
|
||||
: 'score-poor'
|
||||
const scoreLabel = complianceResult.score >= 90 ? 'Ausgezeichnet'
|
||||
: complianceResult.score >= 75 ? 'Gut'
|
||||
: complianceResult.score >= 50 ? 'Verbesserungswuerdig'
|
||||
: 'Mangelhaft'
|
||||
|
||||
html += ` <p><span class="score-box ${scoreClass}">${complianceResult.score}/100</span> ${escHtml(scoreLabel)}</p>
|
||||
<table style="margin-top: 12px;">
|
||||
<tr><th>Kennzahl</th><th>Wert</th></tr>
|
||||
<tr><td>Gepruefte Massnahmen</td><td>${complianceResult.stats.total}</td></tr>
|
||||
<tr><td>Bestanden</td><td>${complianceResult.stats.passed}</td></tr>
|
||||
<tr><td>Beanstandungen</td><td>${complianceResult.stats.failed}</td></tr>
|
||||
</table>
|
||||
`
|
||||
if (complianceResult.issues.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Befunde nach Schweregrad:</strong></p>
|
||||
<table>
|
||||
<tr><th>Schweregrad</th><th>Anzahl</th><th>Befunde</th></tr>
|
||||
`
|
||||
const severityOrder: TOMComplianceIssueSeverity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
for (const sev of severityOrder) {
|
||||
const count = complianceResult.stats.bySeverity[sev]
|
||||
if (count === 0) continue
|
||||
const issuesForSev = complianceResult.issues.filter(i => i.severity === sev)
|
||||
html += ` <tr>
|
||||
<td><span class="badge badge-${sev.toLowerCase()}" style="color: ${SEVERITY_COLORS[sev]}">${SEVERITY_LABELS_DE[sev]}</span></td>
|
||||
<td>${count}</td>
|
||||
<td>${issuesForSev.map(i => escHtml(i.title)).join('; ')}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p style="margin-top: 8px;"><em>Keine Beanstandungen. Alle Massnahmen sind konform.</em></p>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <p><em>Compliance-Check wurde noch nicht ausgefuehrt. Fuehren Sie den Check im
|
||||
Export-Tab durch, um den Status in das Dokument aufzunehmen.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Section 11: Aenderungshistorie
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="section">
|
||||
<div class="section-header">11. Aenderungshistorie</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Version</th><th>Datum</th><th>Autor</th><th>Aenderungen</th></tr>
|
||||
`
|
||||
if (revisions.length > 0) {
|
||||
for (const rev of revisions) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(rev.version)}</td>
|
||||
<td>${formatDateDE(rev.date)}</td>
|
||||
<td>${escHtml(rev.author)}</td>
|
||||
<td>${escHtml(rev.changes)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(orgHeader.documentVersion)}</td>
|
||||
<td>${today}</td>
|
||||
<td>${escHtml(orgHeader.dpoName || orgHeader.responsiblePerson || '-')}</td>
|
||||
<td>Erstversion der TOM-Dokumentation</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
// =========================================================================
|
||||
// Footer
|
||||
// =========================================================================
|
||||
html += `
|
||||
<div class="page-footer">
|
||||
<span>TOM-Dokumentation — ${escHtml(orgName)}</span>
|
||||
<span>Stand: ${today} | Version ${escHtml(orgHeader.documentVersion)}</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// INTERNAL HELPERS
|
||||
// =============================================================================
|
||||
|
||||
function escHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
function formatDateDE(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '-'
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
if (isNaN(date.getTime())) return '-'
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
} catch {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
export type { TOMDocumentOrgHeader, TOMDocumentRevision } from './tom-document/types-defaults'
|
||||
export { createDefaultTOMDocumentOrgHeader } from './tom-document/types-defaults'
|
||||
export { buildTOMDocumentHtml } from './tom-document/html-builder'
|
||||
|
||||
26
admin-compliance/lib/sdk/tom-document/helpers.ts
Normal file
26
admin-compliance/lib/sdk/tom-document/helpers.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// =============================================================================
|
||||
// TOM Document — Internal Helpers
|
||||
// =============================================================================
|
||||
|
||||
export function escHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
export function formatDateDE(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '-'
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
if (isNaN(date.getTime())) return '-'
|
||||
return date.toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
} catch {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
82
admin-compliance/lib/sdk/tom-document/html-builder.ts
Normal file
82
admin-compliance/lib/sdk/tom-document/html-builder.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
// =============================================================================
|
||||
// TOM Document — HTML Document Builder (main entry point)
|
||||
// =============================================================================
|
||||
|
||||
import type {
|
||||
TOMGeneratorState,
|
||||
DerivedTOM,
|
||||
CompanyProfile,
|
||||
RiskProfile,
|
||||
ControlCategory,
|
||||
} from '../tom-generator/types'
|
||||
import { SDM_CATEGORY_MAPPING } from '../tom-generator/types'
|
||||
import {
|
||||
getControlById,
|
||||
getAllCategories,
|
||||
} from '../tom-generator/controls/loader'
|
||||
import type { TOMComplianceCheckResult } from '../tom-compliance'
|
||||
import type { TOMDocumentOrgHeader, TOMDocumentRevision } from './types-defaults'
|
||||
import { CATEGORY_LABELS_DE } from './types-defaults'
|
||||
import { escHtml, formatDateDE } from './helpers'
|
||||
import { buildDocumentStyles } from './html-styles'
|
||||
import { buildSections1to6 } from './html-sections-1-6'
|
||||
import { buildSections7to11 } from './html-sections-7-11'
|
||||
|
||||
export function buildTOMDocumentHtml(
|
||||
derivedTOMs: DerivedTOM[],
|
||||
orgHeader: TOMDocumentOrgHeader,
|
||||
companyProfile: CompanyProfile | null,
|
||||
riskProfile: RiskProfile | null,
|
||||
complianceResult: TOMComplianceCheckResult | null,
|
||||
revisions: TOMDocumentRevision[]
|
||||
): string {
|
||||
const today = new Date().toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const orgName = orgHeader.organizationName || 'Organisation'
|
||||
const applicableTOMs = derivedTOMs.filter(t => t.applicability !== 'NOT_APPLICABLE')
|
||||
|
||||
// Group TOMs by category
|
||||
const tomsByCategory = new Map<ControlCategory, DerivedTOM[]>()
|
||||
for (const tom of applicableTOMs) {
|
||||
const control = getControlById(tom.controlId)
|
||||
const cat = control?.category || 'REVIEW'
|
||||
if (!tomsByCategory.has(cat)) tomsByCategory.set(cat, [])
|
||||
tomsByCategory.get(cat)!.push(tom)
|
||||
}
|
||||
|
||||
// Build role map
|
||||
const roleMap = new Map<string, string[]>()
|
||||
for (const tom of applicableTOMs) {
|
||||
const role = tom.responsiblePerson || tom.responsibleDepartment || 'Nicht zugewiesen'
|
||||
if (!roleMap.has(role)) roleMap.set(role, [])
|
||||
const control = getControlById(tom.controlId)
|
||||
roleMap.get(role)!.push(control?.code || tom.controlId)
|
||||
}
|
||||
|
||||
let html = buildDocumentStyles(escHtml(orgName))
|
||||
|
||||
html += buildSections1to6(
|
||||
orgName, orgHeader, companyProfile, riskProfile,
|
||||
applicableTOMs, tomsByCategory, today
|
||||
)
|
||||
|
||||
html += buildSections7to11(
|
||||
orgName, orgHeader, tomsByCategory, roleMap,
|
||||
complianceResult, revisions, today
|
||||
)
|
||||
|
||||
html += `
|
||||
<div class="page-footer">
|
||||
<span>TOM-Dokumentation — ${escHtml(orgName)}</span>
|
||||
<span>Stand: ${today} | Version ${escHtml(orgHeader.documentVersion)}</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
return html
|
||||
}
|
||||
306
admin-compliance/lib/sdk/tom-document/html-sections-1-6.ts
Normal file
306
admin-compliance/lib/sdk/tom-document/html-sections-1-6.ts
Normal file
@@ -0,0 +1,306 @@
|
||||
// =============================================================================
|
||||
// TOM Document — HTML Sections 1–6
|
||||
// =============================================================================
|
||||
|
||||
import type { DerivedTOM, CompanyProfile, RiskProfile, ControlCategory } from '../tom-generator/types'
|
||||
import { getControlById, getAllCategories, getCategoryMetadata } from '../tom-generator/controls/loader'
|
||||
import type { TOMDocumentOrgHeader } from './types-defaults'
|
||||
import {
|
||||
CATEGORY_LABELS_DE,
|
||||
STATUS_LABELS_DE,
|
||||
STATUS_BADGE_CLASSES,
|
||||
APPLICABILITY_LABELS_DE,
|
||||
} from './types-defaults'
|
||||
import { escHtml, formatDateDE } from './helpers'
|
||||
|
||||
export function buildSections1to6(
|
||||
orgName: string,
|
||||
orgHeader: TOMDocumentOrgHeader,
|
||||
companyProfile: CompanyProfile | null,
|
||||
riskProfile: RiskProfile | null,
|
||||
applicableTOMs: DerivedTOM[],
|
||||
tomsByCategory: Map<ControlCategory, DerivedTOM[]>,
|
||||
today: string
|
||||
): string {
|
||||
let html = ''
|
||||
html += buildCoverAndTOC(orgName, orgHeader, applicableTOMs, tomsByCategory, today)
|
||||
html += buildSection1(orgName)
|
||||
html += buildSection2(orgName, orgHeader, companyProfile, applicableTOMs, tomsByCategory)
|
||||
html += buildSection3()
|
||||
html += buildSection4(riskProfile)
|
||||
html += buildSection5(tomsByCategory)
|
||||
html += buildSection6(tomsByCategory)
|
||||
return html
|
||||
}
|
||||
|
||||
function buildCoverAndTOC(
|
||||
orgName: string,
|
||||
orgHeader: TOMDocumentOrgHeader,
|
||||
applicableTOMs: DerivedTOM[],
|
||||
tomsByCategory: Map<ControlCategory, DerivedTOM[]>,
|
||||
today: string
|
||||
): string {
|
||||
const sections = [
|
||||
'Ziel und Zweck',
|
||||
'Geltungsbereich',
|
||||
'Grundprinzipien Art. 32',
|
||||
'Schutzbedarf und Risikoanalyse',
|
||||
'Massnahmen-Uebersicht',
|
||||
'Detaillierte Massnahmen',
|
||||
'SDM Gewaehrleistungsziele',
|
||||
'Verantwortlichkeiten',
|
||||
'Pruef- und Revisionszyklus',
|
||||
'Compliance-Status',
|
||||
'Aenderungshistorie',
|
||||
]
|
||||
|
||||
return `
|
||||
<div class="cover">
|
||||
<h1>TOM-Dokumentation</h1>
|
||||
<div class="subtitle">Technische und Organisatorische Massnahmen gemaess Art. 32 DSGVO</div>
|
||||
<div class="org-info">
|
||||
<div><span class="label">Organisation:</span> ${escHtml(orgName)}</div>
|
||||
${orgHeader.industry ? `<div><span class="label">Branche:</span> ${escHtml(orgHeader.industry)}</div>` : ''}
|
||||
${orgHeader.dpoName ? `<div><span class="label">DSB:</span> ${escHtml(orgHeader.dpoName)}</div>` : ''}
|
||||
${orgHeader.dpoContact ? `<div><span class="label">DSB-Kontakt:</span> ${escHtml(orgHeader.dpoContact)}</div>` : ''}
|
||||
${orgHeader.responsiblePerson ? `<div><span class="label">Verantwortlicher:</span> ${escHtml(orgHeader.responsiblePerson)}</div>` : ''}
|
||||
${orgHeader.itSecurityContact ? `<div><span class="label">IT-Sicherheit:</span> ${escHtml(orgHeader.itSecurityContact)}</div>` : ''}
|
||||
${orgHeader.employeeCount ? `<div><span class="label">Mitarbeiter:</span> ${escHtml(orgHeader.employeeCount)}</div>` : ''}
|
||||
${orgHeader.locations.length > 0 ? `<div><span class="label">Standorte:</span> ${escHtml(orgHeader.locations.join(', '))}</div>` : ''}
|
||||
</div>
|
||||
<div class="legal-ref">
|
||||
Version ${escHtml(orgHeader.documentVersion)} | Stand: ${today}<br/>
|
||||
Letzte Pruefung: ${formatDateDE(orgHeader.lastReviewDate)} | Naechste Pruefung: ${formatDateDE(orgHeader.nextReviewDate)}<br/>
|
||||
Pruefintervall: ${escHtml(orgHeader.reviewInterval)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toc">
|
||||
<h2>Inhaltsverzeichnis</h2>
|
||||
${sections.map((s, i) => `<div class="toc-entry"><span><span class="toc-num">${i + 1}.</span> ${escHtml(s)}</span></div>`).join('\n ')}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection1(orgName: string): string {
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">1. Ziel und Zweck</div>
|
||||
<div class="section-body">
|
||||
<p>Diese TOM-Dokumentation beschreibt die technischen und organisatorischen Massnahmen
|
||||
zum Schutz personenbezogener Daten bei <strong>${escHtml(orgName)}</strong>. Sie dient
|
||||
der Umsetzung folgender DSGVO-Anforderungen:</p>
|
||||
<table>
|
||||
<tr><th>Rechtsgrundlage</th><th>Inhalt</th></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. a DSGVO</strong></td><td>Pseudonymisierung und Verschluesselung personenbezogener Daten</td></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. b DSGVO</strong></td><td>Faehigkeit, die Vertraulichkeit, Integritaet, Verfuegbarkeit und Belastbarkeit der Systeme und Dienste im Zusammenhang mit der Verarbeitung auf Dauer sicherzustellen</td></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. c DSGVO</strong></td><td>Faehigkeit, die Verfuegbarkeit der personenbezogenen Daten und den Zugang zu ihnen bei einem physischen oder technischen Zwischenfall rasch wiederherzustellen</td></tr>
|
||||
<tr><td><strong>Art. 32 Abs. 1 lit. d DSGVO</strong></td><td>Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung der Wirksamkeit der technischen und organisatorischen Massnahmen</td></tr>
|
||||
</table>
|
||||
<p>Die TOM-Dokumentation ist fester Bestandteil des Datenschutz-Managementsystems und wird
|
||||
regelmaessig ueberprueft und aktualisiert.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection2(
|
||||
orgName: string,
|
||||
orgHeader: TOMDocumentOrgHeader,
|
||||
companyProfile: CompanyProfile | null,
|
||||
applicableTOMs: DerivedTOM[],
|
||||
tomsByCategory: Map<ControlCategory, DerivedTOM[]>
|
||||
): string {
|
||||
const industryInfo = companyProfile?.industry || orgHeader.industry || ''
|
||||
const hostingInfo = companyProfile
|
||||
? `Unternehmen: ${escHtml(companyProfile.name || orgName)}, Groesse: ${escHtml(companyProfile.size || '-')}`
|
||||
: ''
|
||||
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">2. Geltungsbereich</div>
|
||||
<div class="section-body">
|
||||
<p>Diese TOM-Dokumentation gilt fuer alle IT-Systeme, Anwendungen und Verarbeitungsprozesse
|
||||
von <strong>${escHtml(orgName)}</strong>${industryInfo ? ` (Branche: ${escHtml(industryInfo)})` : ''}.</p>
|
||||
${hostingInfo ? `<p>${hostingInfo}</p>` : ''}
|
||||
${orgHeader.locations.length > 0 ? `<p>Standorte: ${escHtml(orgHeader.locations.join(', '))}</p>` : ''}
|
||||
<p>Die dokumentierten Massnahmen stammen aus zwei Quellen:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li><strong>Embedded Library (TOM-xxx):</strong> Integrierte Kontrollbibliothek mit spezifischen Massnahmen fuer Art. 32 DSGVO</li>
|
||||
<li><strong>Canonical Control Library (CP-CLIB):</strong> Uebergreifende Kontrollbibliothek mit framework-uebergreifenden Massnahmen</li>
|
||||
</ul>
|
||||
<p>Insgesamt umfasst dieses Dokument <strong>${applicableTOMs.length}</strong> anwendbare Massnahmen
|
||||
in <strong>${tomsByCategory.size}</strong> Kategorien.</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection3(): string {
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">3. Grundprinzipien Art. 32</div>
|
||||
<div class="section-body">
|
||||
<div class="principle"><strong>Vertraulichkeit:</strong> Schutz personenbezogener Daten vor unbefugter Kenntnisnahme durch Zutrittskontrolle, Zugangskontrolle, Zugriffskontrolle und Verschluesselung (Art. 32 Abs. 1 lit. b DSGVO).</div>
|
||||
<div class="principle"><strong>Integritaet:</strong> Sicherstellung, dass personenbezogene Daten nicht unbefugt oder unbeabsichtigt veraendert werden koennen, durch Eingabekontrolle, Weitergabekontrolle und Protokollierung (Art. 32 Abs. 1 lit. b DSGVO).</div>
|
||||
<div class="principle"><strong>Verfuegbarkeit und Belastbarkeit:</strong> Gewaehrleistung, dass Systeme und Dienste bei Lastspitzen und Stoerungen zuverlaessig funktionieren, durch Backup, Redundanz und Disaster Recovery (Art. 32 Abs. 1 lit. b DSGVO).</div>
|
||||
<div class="principle"><strong>Rasche Wiederherstellbarkeit:</strong> Faehigkeit, nach einem physischen oder technischen Zwischenfall Daten und Systeme schnell wiederherzustellen, durch getestete Recovery-Prozesse (Art. 32 Abs. 1 lit. c DSGVO).</div>
|
||||
<div class="principle"><strong>Regelmaessige Wirksamkeitspruefung:</strong> Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung der Wirksamkeit aller technischen und organisatorischen Massnahmen (Art. 32 Abs. 1 lit. d DSGVO).</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection4(riskProfile: RiskProfile | null): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">4. Schutzbedarf und Risikoanalyse</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (riskProfile) {
|
||||
html += ` <p>Die folgende Schutzbedarfsanalyse bildet die Grundlage fuer die Auswahl und Priorisierung
|
||||
der technischen und organisatorischen Massnahmen:</p>
|
||||
<table>
|
||||
<tr><th>Kriterium</th><th>Bewertung</th></tr>
|
||||
<tr><td>Vertraulichkeit</td><td>${riskProfile.ciaAssessment.confidentiality}/5</td></tr>
|
||||
<tr><td>Integritaet</td><td>${riskProfile.ciaAssessment.integrity}/5</td></tr>
|
||||
<tr><td>Verfuegbarkeit</td><td>${riskProfile.ciaAssessment.availability}/5</td></tr>
|
||||
<tr><td>Schutzniveau</td><td><strong>${escHtml(riskProfile.protectionLevel)}</strong></td></tr>
|
||||
<tr><td>DSFA-Pflicht</td><td>${riskProfile.dsfaRequired ? 'Ja' : 'Nein'}</td></tr>
|
||||
${riskProfile.specialRisks.length > 0 ? `<tr><td>Spezialrisiken</td><td>${escHtml(riskProfile.specialRisks.join(', '))}</td></tr>` : ''}
|
||||
${riskProfile.regulatoryRequirements.length > 0 ? `<tr><td>Regulatorische Anforderungen</td><td>${escHtml(riskProfile.regulatoryRequirements.join(', '))}</td></tr>` : ''}
|
||||
</table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p><em>Die Schutzbedarfsanalyse wurde noch nicht durchgefuehrt. Fuehren Sie den
|
||||
Risiko-Wizard im TOM-Generator durch, um den Schutzbedarf zu ermitteln.</em></p>
|
||||
`
|
||||
}
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection5(tomsByCategory: Map<ControlCategory, DerivedTOM[]>): string {
|
||||
const allCategories = getAllCategories()
|
||||
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">5. Massnahmen-Uebersicht</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt eine Uebersicht aller anwendbaren Massnahmen nach Kategorie:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Kategorie</th>
|
||||
<th>Gesamt</th>
|
||||
<th>Umgesetzt</th>
|
||||
<th>Teilweise</th>
|
||||
<th>Offen</th>
|
||||
</tr>
|
||||
`
|
||||
for (const cat of allCategories) {
|
||||
const tomsInCat = tomsByCategory.get(cat)
|
||||
if (!tomsInCat || tomsInCat.length === 0) continue
|
||||
|
||||
const implemented = tomsInCat.filter(t => t.implementationStatus === 'IMPLEMENTED').length
|
||||
const partial = tomsInCat.filter(t => t.implementationStatus === 'PARTIAL').length
|
||||
const notImpl = tomsInCat.filter(t => t.implementationStatus === 'NOT_IMPLEMENTED').length
|
||||
const catLabel = CATEGORY_LABELS_DE[cat] || cat
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(catLabel)}</td>
|
||||
<td>${tomsInCat.length}</td>
|
||||
<td>${implemented}</td>
|
||||
<td>${partial}</td>
|
||||
<td>${notImpl}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection6(tomsByCategory: Map<ControlCategory, DerivedTOM[]>): string {
|
||||
const allCategories = getAllCategories()
|
||||
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">6. Detaillierte Massnahmen</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
|
||||
for (const cat of allCategories) {
|
||||
const tomsInCat = tomsByCategory.get(cat)
|
||||
if (!tomsInCat || tomsInCat.length === 0) continue
|
||||
|
||||
const catLabel = CATEGORY_LABELS_DE[cat] || cat
|
||||
const catMeta = getCategoryMetadata(cat)
|
||||
const gdprRef = catMeta?.gdprReference || ''
|
||||
|
||||
html += ` <h3 style="color: #5b21b6; margin: 20px 0 10px 0; font-size: 11pt;">${escHtml(catLabel)}${gdprRef ? ` <span style="font-weight: 400; font-size: 9pt; color: #64748b;">(${escHtml(gdprRef)})</span>` : ''}</h3>
|
||||
`
|
||||
|
||||
const sortedTOMs = [...tomsInCat].sort((a, b) => {
|
||||
const codeA = getControlById(a.controlId)?.code || a.controlId
|
||||
const codeB = getControlById(b.controlId)?.code || b.controlId
|
||||
return codeA.localeCompare(codeB)
|
||||
})
|
||||
|
||||
for (const tom of sortedTOMs) {
|
||||
const control = getControlById(tom.controlId)
|
||||
const code = control?.code || tom.controlId
|
||||
const nameDE = control?.name?.de || tom.name
|
||||
const descDE = control?.description?.de || tom.description
|
||||
const typeLabel = control?.type === 'TECHNICAL' ? 'Technisch' : control?.type === 'ORGANIZATIONAL' ? 'Organisatorisch' : '-'
|
||||
const statusLabel = STATUS_LABELS_DE[tom.implementationStatus] || tom.implementationStatus
|
||||
const statusBadge = STATUS_BADGE_CLASSES[tom.implementationStatus] || 'badge-draft'
|
||||
const applicabilityLabel = APPLICABILITY_LABELS_DE[tom.applicability] || tom.applicability
|
||||
const responsible = [tom.responsiblePerson, tom.responsibleDepartment].filter(s => s && s.trim()).join(' / ') || '-'
|
||||
const implDate = tom.implementationDate ? formatDateDE(typeof tom.implementationDate === 'string' ? tom.implementationDate : tom.implementationDate.toISOString()) : '-'
|
||||
const reviewDate = tom.reviewDate ? formatDateDE(typeof tom.reviewDate === 'string' ? tom.reviewDate : tom.reviewDate.toISOString()) : '-'
|
||||
|
||||
const evidenceInfo = tom.linkedEvidence.length > 0
|
||||
? tom.linkedEvidence.join(', ')
|
||||
: tom.evidenceGaps.length > 0
|
||||
? `<em style="color: #d97706;">Fehlend: ${escHtml(tom.evidenceGaps.join(', '))}</em>`
|
||||
: '-'
|
||||
|
||||
let mappingsHtml = '-'
|
||||
if (control?.mappings && control.mappings.length > 0) {
|
||||
mappingsHtml = control.mappings.map(m => `${escHtml(m.framework)}: ${escHtml(m.reference)}`).join('<br/>')
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="policy-detail">
|
||||
<div class="policy-detail-header">
|
||||
<span>${escHtml(code)} — ${escHtml(nameDE)}</span>
|
||||
<span class="badge ${statusBadge}">${escHtml(statusLabel)}</span>
|
||||
</div>
|
||||
<div class="policy-detail-body">
|
||||
<table>
|
||||
<tr><th>Beschreibung</th><td>${escHtml(descDE)}</td></tr>
|
||||
<tr><th>Massnahmentyp</th><td>${escHtml(typeLabel)}</td></tr>
|
||||
<tr><th>Anwendbarkeit</th><td>${escHtml(applicabilityLabel)}${tom.applicabilityReason ? ` — ${escHtml(tom.applicabilityReason)}` : ''}</td></tr>
|
||||
<tr><th>Umsetzungsstatus</th><td><span class="badge ${statusBadge}">${escHtml(statusLabel)}</span></td></tr>
|
||||
<tr><th>Verantwortlich</th><td>${escHtml(responsible)}</td></tr>
|
||||
<tr><th>Umsetzungsdatum</th><td>${implDate}</td></tr>
|
||||
<tr><th>Naechste Pruefung</th><td>${reviewDate}</td></tr>
|
||||
<tr><th>Evidence</th><td>${evidenceInfo}</td></tr>
|
||||
<tr><th>Framework-Mappings</th><td>${mappingsHtml}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
241
admin-compliance/lib/sdk/tom-document/html-sections-7-11.ts
Normal file
241
admin-compliance/lib/sdk/tom-document/html-sections-7-11.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
// =============================================================================
|
||||
// TOM Document — HTML Sections 7–11
|
||||
// =============================================================================
|
||||
|
||||
import type { DerivedTOM, ControlCategory } from '../tom-generator/types'
|
||||
import { SDM_CATEGORY_MAPPING } from '../tom-generator/types'
|
||||
import type { TOMComplianceCheckResult, TOMComplianceIssueSeverity } from '../tom-compliance'
|
||||
import type { TOMDocumentOrgHeader, TOMDocumentRevision } from './types-defaults'
|
||||
import { SEVERITY_LABELS_DE, SEVERITY_COLORS } from './types-defaults'
|
||||
import { escHtml, formatDateDE } from './helpers'
|
||||
|
||||
export function buildSections7to11(
|
||||
orgName: string,
|
||||
orgHeader: TOMDocumentOrgHeader,
|
||||
tomsByCategory: Map<ControlCategory, DerivedTOM[]>,
|
||||
roleMap: Map<string, string[]>,
|
||||
complianceResult: TOMComplianceCheckResult | null,
|
||||
revisions: TOMDocumentRevision[],
|
||||
today: string
|
||||
): string {
|
||||
let html = ''
|
||||
html += buildSection7(tomsByCategory)
|
||||
html += buildSection8(roleMap)
|
||||
html += buildSection9(orgHeader)
|
||||
html += buildSection10(complianceResult)
|
||||
html += buildSection11(revisions, orgHeader, today)
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection7(tomsByCategory: Map<ControlCategory, DerivedTOM[]>): string {
|
||||
const allSDMGoals = [
|
||||
'Verfuegbarkeit',
|
||||
'Integritaet',
|
||||
'Vertraulichkeit',
|
||||
'Nichtverkettung',
|
||||
'Intervenierbarkeit',
|
||||
'Transparenz',
|
||||
'Datenminimierung',
|
||||
] as const
|
||||
|
||||
const sdmGoals: Array<{ goal: string; categories: ControlCategory[] }> = []
|
||||
for (const goal of allSDMGoals) {
|
||||
const cats: ControlCategory[] = []
|
||||
for (const [cat, goals] of Object.entries(SDM_CATEGORY_MAPPING)) {
|
||||
if (goals.includes(goal)) {
|
||||
cats.push(cat as ControlCategory)
|
||||
}
|
||||
}
|
||||
sdmGoals.push({ goal, categories: cats })
|
||||
}
|
||||
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">7. SDM Gewaehrleistungsziele</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Tabelle zeigt die Abdeckung der sieben Gewaehrleistungsziele des
|
||||
Standard-Datenschutzmodells (SDM) durch die implementierten Massnahmen:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Gewaehrleistungsziel</th>
|
||||
<th>Abgedeckt</th>
|
||||
<th>Gesamt</th>
|
||||
<th>Abdeckung (%)</th>
|
||||
</tr>
|
||||
`
|
||||
|
||||
for (const { goal, categories } of sdmGoals) {
|
||||
let totalInGoal = 0
|
||||
let implementedInGoal = 0
|
||||
for (const cat of categories) {
|
||||
const tomsInCat = tomsByCategory.get(cat) || []
|
||||
totalInGoal += tomsInCat.length
|
||||
implementedInGoal += tomsInCat.filter(t => t.implementationStatus === 'IMPLEMENTED').length
|
||||
}
|
||||
const percentage = totalInGoal > 0 ? Math.round((implementedInGoal / totalInGoal) * 100) : 0
|
||||
|
||||
html += ` <tr>
|
||||
<td>${escHtml(goal)}</td>
|
||||
<td>${implementedInGoal}</td>
|
||||
<td>${totalInGoal}</td>
|
||||
<td>${percentage}%</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection8(roleMap: Map<string, string[]>): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">8. Verantwortlichkeiten</div>
|
||||
<div class="section-body">
|
||||
<p>Die folgende Rollenmatrix zeigt, welche Personen oder Abteilungen fuer welche Massnahmen
|
||||
die Umsetzungsverantwortung tragen:</p>
|
||||
<table>
|
||||
<tr><th>Rolle / Verantwortlich</th><th>Massnahmen</th><th>Anzahl</th></tr>
|
||||
`
|
||||
for (const [role, controls] of roleMap.entries()) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(role)}</td>
|
||||
<td>${controls.map(c => escHtml(c)).join(', ')}</td>
|
||||
<td>${controls.length}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection9(orgHeader: TOMDocumentOrgHeader): string {
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-header">9. Pruef- und Revisionszyklus</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Eigenschaft</th><th>Wert</th></tr>
|
||||
<tr><td>Aktuelles Pruefintervall</td><td>${escHtml(orgHeader.reviewInterval)}</td></tr>
|
||||
<tr><td>Letzte Pruefung</td><td>${formatDateDE(orgHeader.lastReviewDate)}</td></tr>
|
||||
<tr><td>Naechste Pruefung</td><td>${formatDateDE(orgHeader.nextReviewDate)}</td></tr>
|
||||
<tr><td>Aktuelle Version</td><td>${escHtml(orgHeader.documentVersion)}</td></tr>
|
||||
</table>
|
||||
<p style="margin-top: 8px;">Bei jeder Pruefung wird die TOM-Dokumentation auf folgende Punkte ueberprueft:</p>
|
||||
<ul style="margin: 8px 0 8px 24px;">
|
||||
<li>Vollstaendigkeit aller Massnahmen (neue Systeme oder Verarbeitungen erfasst?)</li>
|
||||
<li>Aktualitaet des Umsetzungsstatus (Aenderungen seit letzter Pruefung?)</li>
|
||||
<li>Wirksamkeit der technischen Massnahmen (Penetration-Tests, Audit-Ergebnisse)</li>
|
||||
<li>Angemessenheit der organisatorischen Massnahmen (Schulungen, Richtlinien aktuell?)</li>
|
||||
<li>Abdeckung aller SDM-Gewaehrleistungsziele</li>
|
||||
<li>Zuordnung von Verantwortlichkeiten zu allen Massnahmen</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function buildSection10(complianceResult: TOMComplianceCheckResult | null): string {
|
||||
let html = `
|
||||
<div class="section page-break">
|
||||
<div class="section-header">10. Compliance-Status</div>
|
||||
<div class="section-body">
|
||||
`
|
||||
if (complianceResult) {
|
||||
const scoreClass = complianceResult.score >= 90 ? 'score-excellent'
|
||||
: complianceResult.score >= 75 ? 'score-good'
|
||||
: complianceResult.score >= 50 ? 'score-needs-work'
|
||||
: 'score-poor'
|
||||
const scoreLabel = complianceResult.score >= 90 ? 'Ausgezeichnet'
|
||||
: complianceResult.score >= 75 ? 'Gut'
|
||||
: complianceResult.score >= 50 ? 'Verbesserungswuerdig'
|
||||
: 'Mangelhaft'
|
||||
|
||||
html += ` <p><span class="score-box ${scoreClass}">${complianceResult.score}/100</span> ${escHtml(scoreLabel)}</p>
|
||||
<table style="margin-top: 12px;">
|
||||
<tr><th>Kennzahl</th><th>Wert</th></tr>
|
||||
<tr><td>Gepruefte Massnahmen</td><td>${complianceResult.stats.total}</td></tr>
|
||||
<tr><td>Bestanden</td><td>${complianceResult.stats.passed}</td></tr>
|
||||
<tr><td>Beanstandungen</td><td>${complianceResult.stats.failed}</td></tr>
|
||||
</table>
|
||||
`
|
||||
if (complianceResult.issues.length > 0) {
|
||||
html += ` <p style="margin-top: 12px;"><strong>Befunde nach Schweregrad:</strong></p>
|
||||
<table>
|
||||
<tr><th>Schweregrad</th><th>Anzahl</th><th>Befunde</th></tr>
|
||||
`
|
||||
const severityOrder: TOMComplianceIssueSeverity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
for (const sev of severityOrder) {
|
||||
const count = complianceResult.stats.bySeverity[sev]
|
||||
if (count === 0) continue
|
||||
const issuesForSev = complianceResult.issues.filter(i => i.severity === sev)
|
||||
html += ` <tr>
|
||||
<td><span class="badge badge-${sev.toLowerCase()}" style="color: ${SEVERITY_COLORS[sev]}">${SEVERITY_LABELS_DE[sev]}</span></td>
|
||||
<td>${count}</td>
|
||||
<td>${issuesForSev.map(i => escHtml(i.title)).join('; ')}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
html += ` </table>
|
||||
`
|
||||
} else {
|
||||
html += ` <p style="margin-top: 8px;"><em>Keine Beanstandungen. Alle Massnahmen sind konform.</em></p>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <p><em>Compliance-Check wurde noch nicht ausgefuehrt. Fuehren Sie den Check im
|
||||
Export-Tab durch, um den Status in das Dokument aufzunehmen.</em></p>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
|
||||
function buildSection11(
|
||||
revisions: TOMDocumentRevision[],
|
||||
orgHeader: TOMDocumentOrgHeader,
|
||||
today: string
|
||||
): string {
|
||||
let html = `
|
||||
<div class="section">
|
||||
<div class="section-header">11. Aenderungshistorie</div>
|
||||
<div class="section-body">
|
||||
<table>
|
||||
<tr><th>Version</th><th>Datum</th><th>Autor</th><th>Aenderungen</th></tr>
|
||||
`
|
||||
if (revisions.length > 0) {
|
||||
for (const rev of revisions) {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(rev.version)}</td>
|
||||
<td>${formatDateDE(rev.date)}</td>
|
||||
<td>${escHtml(rev.author)}</td>
|
||||
<td>${escHtml(rev.changes)}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
} else {
|
||||
html += ` <tr>
|
||||
<td>${escHtml(orgHeader.documentVersion)}</td>
|
||||
<td>${today}</td>
|
||||
<td>${escHtml(orgHeader.dpoName || orgHeader.responsiblePerson || '-')}</td>
|
||||
<td>Erstversion der TOM-Dokumentation</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
html += ` </table>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
85
admin-compliance/lib/sdk/tom-document/html-styles.ts
Normal file
85
admin-compliance/lib/sdk/tom-document/html-styles.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
// =============================================================================
|
||||
// TOM Document — HTML/CSS Styles
|
||||
// =============================================================================
|
||||
|
||||
export function buildDocumentStyles(orgNameEscaped: string): string {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>TOM-Dokumentation — ${orgNameEscaped}</title>
|
||||
<style>
|
||||
@page { size: A4; margin: 20mm 18mm 22mm 18mm; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
color: #1e293b;
|
||||
}
|
||||
.cover {
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
page-break-after: always;
|
||||
}
|
||||
.cover h1 { font-size: 28pt; color: #5b21b6; margin-bottom: 8px; font-weight: 700; }
|
||||
.cover .subtitle { font-size: 14pt; color: #7c3aed; margin-bottom: 40px; }
|
||||
.cover .org-info {
|
||||
background: #f5f3ff;
|
||||
border: 1px solid #ddd6fe;
|
||||
border-radius: 8px;
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
width: 400px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.cover .org-info div { margin-bottom: 6px; }
|
||||
.cover .org-info .label { font-weight: 600; color: #5b21b6; display: inline-block; min-width: 160px; }
|
||||
.cover .legal-ref { font-size: 9pt; color: #64748b; margin-top: 20px; }
|
||||
.toc { page-break-after: always; padding-top: 40px; }
|
||||
.toc h2 { font-size: 18pt; color: #5b21b6; margin-bottom: 20px; border-bottom: 2px solid #5b21b6; padding-bottom: 8px; }
|
||||
.toc-entry { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px dotted #cbd5e1; font-size: 10pt; }
|
||||
.toc-entry .toc-num { font-weight: 600; color: #5b21b6; min-width: 40px; }
|
||||
.section { page-break-inside: avoid; margin-bottom: 24px; }
|
||||
.section-header { font-size: 14pt; color: #5b21b6; font-weight: 700; margin: 30px 0 12px 0; border-bottom: 2px solid #ddd6fe; padding-bottom: 6px; }
|
||||
.section-body { margin-bottom: 16px; }
|
||||
table { width: 100%; border-collapse: collapse; margin: 10px 0 16px 0; font-size: 9pt; }
|
||||
th, td { border: 1px solid #e2e8f0; padding: 6px 8px; text-align: left; vertical-align: top; }
|
||||
th { background: #f5f3ff; color: #5b21b6; font-weight: 600; font-size: 8.5pt; text-transform: uppercase; letter-spacing: 0.3px; }
|
||||
tr:nth-child(even) td { background: #faf5ff; }
|
||||
.policy-detail { page-break-inside: avoid; border: 1px solid #e2e8f0; border-radius: 6px; margin-bottom: 16px; overflow: hidden; }
|
||||
.policy-detail-header { background: #f5f3ff; padding: 8px 12px; font-weight: 700; color: #5b21b6; border-bottom: 1px solid #ddd6fe; display: flex; justify-content: space-between; }
|
||||
.policy-detail-body { padding: 0; }
|
||||
.policy-detail-body table { margin: 0; }
|
||||
.policy-detail-body th { width: 200px; }
|
||||
.badge { display: inline-block; padding: 1px 8px; border-radius: 9999px; font-size: 8pt; font-weight: 600; }
|
||||
.badge-active { background: #dcfce7; color: #166534; }
|
||||
.badge-draft { background: #f3f4f6; color: #374151; }
|
||||
.badge-review { background: #fef9c3; color: #854d0e; }
|
||||
.badge-critical { background: #fecaca; color: #991b1b; }
|
||||
.badge-high { background: #fed7aa; color: #9a3412; }
|
||||
.badge-medium { background: #fef3c7; color: #92400e; }
|
||||
.badge-low { background: #f3f4f6; color: #4b5563; }
|
||||
.principle { margin-bottom: 10px; padding-left: 20px; position: relative; }
|
||||
.principle::before { content: ''; position: absolute; left: 0; top: 6px; width: 10px; height: 10px; background: #7c3aed; border-radius: 50%; }
|
||||
.principle strong { color: #5b21b6; }
|
||||
.score-box { display: inline-block; padding: 4px 16px; border-radius: 8px; font-size: 18pt; font-weight: 700; margin-right: 12px; }
|
||||
.score-excellent { background: #dcfce7; color: #166534; }
|
||||
.score-good { background: #dbeafe; color: #1e40af; }
|
||||
.score-needs-work { background: #fef3c7; color: #92400e; }
|
||||
.score-poor { background: #fecaca; color: #991b1b; }
|
||||
.page-footer { position: fixed; bottom: 0; left: 0; right: 0; padding: 8px 18mm; font-size: 7.5pt; color: #94a3b8; display: flex; justify-content: space-between; border-top: 1px solid #e2e8f0; }
|
||||
@media print {
|
||||
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
.no-print { display: none !important; }
|
||||
.page-break { page-break-after: always; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
}
|
||||
98
admin-compliance/lib/sdk/tom-document/types-defaults.ts
Normal file
98
admin-compliance/lib/sdk/tom-document/types-defaults.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
// =============================================================================
|
||||
// TOM Document — Types, Defaults & Label Constants
|
||||
// =============================================================================
|
||||
|
||||
import type { ControlCategory } from '../tom-generator/types'
|
||||
import type { TOMComplianceIssueSeverity } from '../tom-compliance'
|
||||
|
||||
export interface TOMDocumentOrgHeader {
|
||||
organizationName: string
|
||||
industry: string
|
||||
dpoName: string
|
||||
dpoContact: string
|
||||
responsiblePerson: string
|
||||
itSecurityContact: string
|
||||
locations: string[]
|
||||
employeeCount: string
|
||||
documentVersion: string
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: string
|
||||
}
|
||||
|
||||
export interface TOMDocumentRevision {
|
||||
version: string
|
||||
date: string
|
||||
author: string
|
||||
changes: string
|
||||
}
|
||||
|
||||
export function createDefaultTOMDocumentOrgHeader(): TOMDocumentOrgHeader {
|
||||
const now = new Date()
|
||||
const nextYear = new Date()
|
||||
nextYear.setFullYear(nextYear.getFullYear() + 1)
|
||||
|
||||
return {
|
||||
organizationName: '',
|
||||
industry: '',
|
||||
dpoName: '',
|
||||
dpoContact: '',
|
||||
responsiblePerson: '',
|
||||
itSecurityContact: '',
|
||||
locations: [],
|
||||
employeeCount: '',
|
||||
documentVersion: '1.0',
|
||||
lastReviewDate: now.toISOString().split('T')[0],
|
||||
nextReviewDate: nextYear.toISOString().split('T')[0],
|
||||
reviewInterval: 'Jaehrlich',
|
||||
}
|
||||
}
|
||||
|
||||
export const SEVERITY_LABELS_DE: Record<TOMComplianceIssueSeverity, string> = {
|
||||
CRITICAL: 'Kritisch',
|
||||
HIGH: 'Hoch',
|
||||
MEDIUM: 'Mittel',
|
||||
LOW: 'Niedrig',
|
||||
}
|
||||
|
||||
export const SEVERITY_COLORS: Record<TOMComplianceIssueSeverity, string> = {
|
||||
CRITICAL: '#dc2626',
|
||||
HIGH: '#ea580c',
|
||||
MEDIUM: '#d97706',
|
||||
LOW: '#6b7280',
|
||||
}
|
||||
|
||||
export const CATEGORY_LABELS_DE: Record<ControlCategory, string> = {
|
||||
ACCESS_CONTROL: 'Zutrittskontrolle',
|
||||
ADMISSION_CONTROL: 'Zugangskontrolle',
|
||||
ACCESS_AUTHORIZATION: 'Zugriffskontrolle',
|
||||
TRANSFER_CONTROL: 'Weitergabekontrolle',
|
||||
INPUT_CONTROL: 'Eingabekontrolle',
|
||||
ORDER_CONTROL: 'Auftragskontrolle',
|
||||
AVAILABILITY: 'Verfuegbarkeit',
|
||||
SEPARATION: 'Trennbarkeit',
|
||||
ENCRYPTION: 'Verschluesselung',
|
||||
PSEUDONYMIZATION: 'Pseudonymisierung',
|
||||
RESILIENCE: 'Belastbarkeit',
|
||||
RECOVERY: 'Wiederherstellbarkeit',
|
||||
REVIEW: 'Ueberpruefung & Bewertung',
|
||||
}
|
||||
|
||||
export const STATUS_LABELS_DE: Record<string, string> = {
|
||||
IMPLEMENTED: 'Umgesetzt',
|
||||
PARTIAL: 'Teilweise umgesetzt',
|
||||
NOT_IMPLEMENTED: 'Nicht umgesetzt',
|
||||
}
|
||||
|
||||
export const STATUS_BADGE_CLASSES: Record<string, string> = {
|
||||
IMPLEMENTED: 'badge-active',
|
||||
PARTIAL: 'badge-review',
|
||||
NOT_IMPLEMENTED: 'badge-critical',
|
||||
}
|
||||
|
||||
export const APPLICABILITY_LABELS_DE: Record<string, string> = {
|
||||
REQUIRED: 'Erforderlich',
|
||||
RECOMMENDED: 'Empfohlen',
|
||||
OPTIONAL: 'Optional',
|
||||
NOT_APPLICABLE: 'Nicht anwendbar',
|
||||
}
|
||||
@@ -1,813 +1,12 @@
|
||||
/**
|
||||
* Standard Processing Activities Catalog
|
||||
* Standard Processing Activities Catalog — Barrel
|
||||
*
|
||||
* 28 predefined processing activities templates following Art. 30 DSGVO
|
||||
* Split into domain modules; re-exported here for backward compatibility.
|
||||
*/
|
||||
|
||||
import {
|
||||
ProcessingActivityFormData,
|
||||
DataSubjectCategory,
|
||||
PersonalDataCategory,
|
||||
LegalBasisType,
|
||||
ProtectionLevel,
|
||||
LocalizedText,
|
||||
} from '../types'
|
||||
|
||||
export interface ProcessingActivityTemplate {
|
||||
id: string
|
||||
category: ProcessingActivityCategory
|
||||
name: LocalizedText
|
||||
description: LocalizedText
|
||||
purposes: LocalizedText[]
|
||||
dataSubjectCategories: DataSubjectCategory[]
|
||||
personalDataCategories: PersonalDataCategory[]
|
||||
suggestedLegalBasis: LegalBasisType[]
|
||||
suggestedRetentionYears: number
|
||||
suggestedProtectionLevel: ProtectionLevel
|
||||
dpiaLikely: boolean
|
||||
commonSystems: string[]
|
||||
commonVendorCategories: string[]
|
||||
}
|
||||
|
||||
export type ProcessingActivityCategory =
|
||||
| 'HR' // Human Resources
|
||||
| 'SALES' // Vertrieb
|
||||
| 'MARKETING' // Marketing
|
||||
| 'FINANCE' // Finanzen
|
||||
| 'IT' // IT & Sicherheit
|
||||
| 'CUSTOMER_SERVICE' // Kundenservice
|
||||
| 'WEBSITE' // Website & Apps
|
||||
| 'GENERAL' // Allgemein
|
||||
|
||||
export const PROCESSING_ACTIVITY_CATEGORY_META: Record<ProcessingActivityCategory, LocalizedText> = {
|
||||
HR: { de: 'Personal', en: 'Human Resources' },
|
||||
SALES: { de: 'Vertrieb', en: 'Sales' },
|
||||
MARKETING: { de: 'Marketing', en: 'Marketing' },
|
||||
FINANCE: { de: 'Finanzen', en: 'Finance' },
|
||||
IT: { de: 'IT & Sicherheit', en: 'IT & Security' },
|
||||
CUSTOMER_SERVICE: { de: 'Kundenservice', en: 'Customer Service' },
|
||||
WEBSITE: { de: 'Website & Apps', en: 'Website & Apps' },
|
||||
GENERAL: { de: 'Allgemein', en: 'General' },
|
||||
}
|
||||
|
||||
export const PROCESSING_ACTIVITY_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
// ==========================================
|
||||
// HR - Human Resources
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-hr-recruitment',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Bewerbermanagement',
|
||||
en: 'Recruitment Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verarbeitung von Bewerberdaten im Rahmen des Recruiting-Prozesses',
|
||||
en: 'Processing of applicant data as part of the recruitment process',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Durchführung des Bewerbungsverfahrens', en: 'Conducting the application process' },
|
||||
{ de: 'Prüfung der Eignung', en: 'Assessing suitability' },
|
||||
{ de: 'Aufbau eines Talentpools (bei Einwilligung)', en: 'Building a talent pool (with consent)' },
|
||||
],
|
||||
dataSubjectCategories: ['APPLICANTS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'DOB', 'EDUCATION_DATA',
|
||||
'EMPLOYMENT_DATA', 'PHOTO_VIDEO',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'CONSENT'],
|
||||
suggestedRetentionYears: 0.5, // 6 Monate nach Absage
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['E-Recruiting', 'Personio', 'Workday'],
|
||||
commonVendorCategories: ['HR_SOFTWARE', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-personnel',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Personalverwaltung',
|
||||
en: 'Personnel Administration',
|
||||
},
|
||||
description: {
|
||||
de: 'Führung der Personalakte und Verwaltung des Beschäftigungsverhältnisses',
|
||||
en: 'Maintaining personnel files and managing employment relationships',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Führung der Personalakte', en: 'Maintaining personnel files' },
|
||||
{ de: 'Durchführung des Arbeitsverhältnisses', en: 'Executing the employment relationship' },
|
||||
{ de: 'Erfüllung gesetzlicher Pflichten', en: 'Fulfilling legal obligations' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'DOB', 'ID_NUMBER',
|
||||
'SOCIAL_SECURITY', 'TAX_ID', 'BANK_ACCOUNT', 'EMPLOYMENT_DATA',
|
||||
'SALARY_DATA', 'EDUCATION_DATA', 'PHOTO_VIDEO',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10, // Nach Beendigung
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['SAP HCM', 'Personio', 'DATEV'],
|
||||
commonVendorCategories: ['HR_SOFTWARE', 'ERP'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-payroll',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Lohn- und Gehaltsabrechnung',
|
||||
en: 'Payroll Processing',
|
||||
},
|
||||
description: {
|
||||
de: 'Berechnung und Auszahlung von Gehältern, Abführung von Steuern und Sozialabgaben',
|
||||
en: 'Calculation and payment of salaries, tax and social security contributions',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Gehaltsberechnung und -auszahlung', en: 'Salary calculation and payment' },
|
||||
{ de: 'Abführung von Lohnsteuer und Sozialabgaben', en: 'Payment of payroll taxes and social contributions' },
|
||||
{ de: 'Erstellung von Lohnabrechnungen', en: 'Creating payslips' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'ADDRESS', 'DOB', 'SOCIAL_SECURITY', 'TAX_ID',
|
||||
'BANK_ACCOUNT', 'SALARY_DATA', 'EMPLOYMENT_DATA',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10, // Handels- und Steuerrecht
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['DATEV', 'SAP', 'Lexware'],
|
||||
commonVendorCategories: ['ACCOUNTING', 'HR_SOFTWARE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-time-tracking',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Arbeitszeiterfassung',
|
||||
en: 'Time Tracking',
|
||||
},
|
||||
description: {
|
||||
de: 'Erfassung der Arbeitszeiten zur Einhaltung des Arbeitszeitgesetzes',
|
||||
en: 'Recording working hours for compliance with working time regulations',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Erfassung der Arbeitszeiten', en: 'Recording working hours' },
|
||||
{ de: 'Einhaltung des Arbeitszeitgesetzes', en: 'Compliance with working time regulations' },
|
||||
{ de: 'Grundlage für Gehaltsabrechnung', en: 'Basis for payroll' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'EMPLOYMENT_DATA', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['LEGAL_OBLIGATION', 'CONTRACT'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ATOSS', 'Clockodo', 'Toggl'],
|
||||
commonVendorCategories: ['HR_SOFTWARE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-health-management',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Betriebliches Gesundheitsmanagement',
|
||||
en: 'Occupational Health Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Arbeitsunfähigkeitsbescheinigungen und betriebsärztlichen Untersuchungen',
|
||||
en: 'Management of sick notes and occupational health examinations',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Verwaltung von Krankmeldungen', en: 'Managing sick leave' },
|
||||
{ de: 'Organisation betriebsärztlicher Untersuchungen', en: 'Organizing occupational health examinations' },
|
||||
{ de: 'Betriebliches Eingliederungsmanagement', en: 'Occupational reintegration management' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'EMPLOYMENT_DATA', 'HEALTH_DATA'],
|
||||
suggestedLegalBasis: ['ART9_EMPLOYMENT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 3,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['HR-Software', 'BEM-System'],
|
||||
commonVendorCategories: ['HR_SOFTWARE', 'CONSULTING'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// SALES - Vertrieb
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-sales-crm',
|
||||
category: 'SALES',
|
||||
name: {
|
||||
de: 'Kundenbeziehungsmanagement (CRM)',
|
||||
en: 'Customer Relationship Management (CRM)',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Kundenbeziehungen, Kontakthistorie und Verkaufschancen',
|
||||
en: 'Managing customer relationships, contact history, and sales opportunities',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Pflege von Kundenbeziehungen', en: 'Maintaining customer relationships' },
|
||||
{ de: 'Dokumentation von Kundenkontakten', en: 'Documenting customer contacts' },
|
||||
{ de: 'Vertriebssteuerung', en: 'Sales management' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'PROSPECTIVE_CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'CONTRACT_DATA', 'COMMUNICATION_DATA',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 3, // Nach letztem Kontakt
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Salesforce', 'HubSpot', 'Pipedrive', 'Microsoft Dynamics'],
|
||||
commonVendorCategories: ['CRM'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-sales-contract-management',
|
||||
category: 'SALES',
|
||||
name: {
|
||||
de: 'Vertragsmanagement',
|
||||
en: 'Contract Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Kundenverträgen, Angeboten und Aufträgen',
|
||||
en: 'Managing customer contracts, quotes, and orders',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Erstellung und Verwaltung von Verträgen', en: 'Creating and managing contracts' },
|
||||
{ de: 'Angebotsverfolgung', en: 'Quote tracking' },
|
||||
{ de: 'Auftragsabwicklung', en: 'Order processing' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'CONTRACT_DATA', 'PAYMENT_DATA',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10, // Handelsrechtlich
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ERP', 'CRM', 'Vertragsverwaltung'],
|
||||
commonVendorCategories: ['ERP', 'CRM'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// MARKETING
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-marketing-newsletter',
|
||||
category: 'MARKETING',
|
||||
name: {
|
||||
de: 'Newsletter-Versand',
|
||||
en: 'Newsletter Distribution',
|
||||
},
|
||||
description: {
|
||||
de: 'Versand von E-Mail-Newslettern und Marketing-Kommunikation',
|
||||
en: 'Sending email newsletters and marketing communications',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Versand von Newsletter und Marketing-E-Mails', en: 'Sending newsletters and marketing emails' },
|
||||
{ de: 'Messung von Öffnungs- und Klickraten', en: 'Measuring open and click rates' },
|
||||
],
|
||||
dataSubjectCategories: ['NEWSLETTER_SUBSCRIBERS', 'CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT'],
|
||||
suggestedRetentionYears: 0, // Bis Widerruf
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Mailchimp', 'CleverReach', 'Sendinblue'],
|
||||
commonVendorCategories: ['EMAIL', 'MARKETING'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-marketing-advertising',
|
||||
category: 'MARKETING',
|
||||
name: {
|
||||
de: 'Online-Werbung',
|
||||
en: 'Online Advertising',
|
||||
},
|
||||
description: {
|
||||
de: 'Schaltung und Auswertung von Online-Werbeanzeigen',
|
||||
en: 'Running and analyzing online advertisements',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Schaltung von Online-Werbung', en: 'Running online advertisements' },
|
||||
{ de: 'Conversion-Tracking', en: 'Conversion tracking' },
|
||||
{ de: 'Retargeting', en: 'Retargeting' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['Google Ads', 'Meta Ads', 'LinkedIn Ads'],
|
||||
commonVendorCategories: ['MARKETING', 'ANALYTICS'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-marketing-events',
|
||||
category: 'MARKETING',
|
||||
name: {
|
||||
de: 'Veranstaltungsmanagement',
|
||||
en: 'Event Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Organisation und Durchführung von Veranstaltungen, Messen und Webinaren',
|
||||
en: 'Organizing and conducting events, trade shows, and webinars',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Teilnehmerregistrierung', en: 'Participant registration' },
|
||||
{ de: 'Veranstaltungsdurchführung', en: 'Event execution' },
|
||||
{ de: 'Nachbereitung und Follow-up', en: 'Follow-up activities' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'PROSPECTIVE_CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'PHOTO_VIDEO'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'CONSENT'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Eventbrite', 'GoToWebinar', 'Zoom'],
|
||||
commonVendorCategories: ['MARKETING', 'COMMUNICATION'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// FINANCE
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-finance-accounting',
|
||||
category: 'FINANCE',
|
||||
name: {
|
||||
de: 'Finanzbuchhaltung',
|
||||
en: 'Financial Accounting',
|
||||
},
|
||||
description: {
|
||||
de: 'Führung der Finanzbuchhaltung, Rechnungsstellung und Zahlungsabwicklung',
|
||||
en: 'Financial accounting, invoicing, and payment processing',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Buchführung und Rechnungswesen', en: 'Bookkeeping and accounting' },
|
||||
{ de: 'Rechnungsstellung', en: 'Invoicing' },
|
||||
{ de: 'Zahlungsabwicklung', en: 'Payment processing' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'SUPPLIERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'ADDRESS', 'BANK_ACCOUNT', 'PAYMENT_DATA', 'CONTRACT_DATA', 'TAX_ID',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10, // HGB/AO
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['DATEV', 'SAP', 'Lexware', 'Xero'],
|
||||
commonVendorCategories: ['ACCOUNTING', 'ERP'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-finance-debt-collection',
|
||||
category: 'FINANCE',
|
||||
name: {
|
||||
de: 'Forderungsmanagement',
|
||||
en: 'Debt Collection',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung offener Forderungen und Mahnwesen',
|
||||
en: 'Managing outstanding receivables and dunning',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Überwachung offener Forderungen', en: 'Monitoring outstanding receivables' },
|
||||
{ de: 'Mahnwesen', en: 'Dunning process' },
|
||||
{ de: 'Inkasso bei Bedarf', en: 'Debt collection if necessary' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'ADDRESS', 'CONTACT', 'PAYMENT_DATA', 'CONTRACT_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ERP', 'Inkasso-Software'],
|
||||
commonVendorCategories: ['ACCOUNTING', 'LEGAL'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// IT & SICHERHEIT
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-it-user-management',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'IT-Benutzerverwaltung',
|
||||
en: 'IT User Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Benutzerkonten, Zugriffsrechten und Authentifizierung',
|
||||
en: 'Managing user accounts, access rights, and authentication',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Verwaltung von Benutzerkonten', en: 'Managing user accounts' },
|
||||
{ de: 'Zugriffssteuerung', en: 'Access control' },
|
||||
{ de: 'Single Sign-On', en: 'Single Sign-On' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'LOGIN_DATA', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1, // Nach Kontoschließung
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Active Directory', 'Okta', 'Azure AD'],
|
||||
commonVendorCategories: ['SECURITY', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-it-logging',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'IT-Protokollierung',
|
||||
en: 'IT Logging',
|
||||
},
|
||||
description: {
|
||||
de: 'Protokollierung von IT-Aktivitäten zur Sicherheit und Fehleranalyse',
|
||||
en: 'Logging IT activities for security and error analysis',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Sicherheitsüberwachung', en: 'Security monitoring' },
|
||||
{ de: 'Fehleranalyse', en: 'Error analysis' },
|
||||
{ de: 'Nachvollziehbarkeit', en: 'Traceability' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'CUSTOMERS', 'WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA', 'LOGIN_DATA'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Splunk', 'ELK Stack', 'Datadog'],
|
||||
commonVendorCategories: ['SECURITY', 'ANALYTICS'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-it-video-surveillance',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'Videoüberwachung',
|
||||
en: 'Video Surveillance',
|
||||
},
|
||||
description: {
|
||||
de: 'Videoüberwachung von Geschäftsräumen zum Schutz vor Diebstahl und Vandalismus',
|
||||
en: 'Video surveillance of business premises for theft and vandalism prevention',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Schutz vor Diebstahl und Vandalismus', en: 'Protection against theft and vandalism' },
|
||||
{ de: 'Zugangskontrolle', en: 'Access control' },
|
||||
{ de: 'Beweissicherung', en: 'Evidence preservation' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'VISITORS', 'CUSTOMERS'],
|
||||
personalDataCategories: ['PHOTO_VIDEO', 'BIOMETRIC_DATA'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 0.1, // 72 Stunden
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['CCTV-System'],
|
||||
commonVendorCategories: ['SECURITY'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-it-backup',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'Datensicherung (Backup)',
|
||||
en: 'Data Backup',
|
||||
},
|
||||
description: {
|
||||
de: 'Regelmäßige Sicherung von Unternehmensdaten',
|
||||
en: 'Regular backup of company data',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Datensicherung', en: 'Data backup' },
|
||||
{ de: 'Disaster Recovery', en: 'Disaster Recovery' },
|
||||
{ de: 'Geschäftskontinuität', en: 'Business continuity' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'CUSTOMERS', 'SUPPLIERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'CONTRACT_DATA', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 1, // Je nach Backup-Konzept
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Veeam', 'AWS Backup', 'Azure Backup'],
|
||||
commonVendorCategories: ['BACKUP', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// CUSTOMER SERVICE
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-cs-support',
|
||||
category: 'CUSTOMER_SERVICE',
|
||||
name: {
|
||||
de: 'Kundenbetreuung und Support',
|
||||
en: 'Customer Support',
|
||||
},
|
||||
description: {
|
||||
de: 'Bearbeitung von Kundenanfragen, Beschwerden und Support-Tickets',
|
||||
en: 'Handling customer inquiries, complaints, and support tickets',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Bearbeitung von Kundenanfragen', en: 'Handling customer inquiries' },
|
||||
{ de: 'Beschwerdemanagement', en: 'Complaint management' },
|
||||
{ de: 'Technischer Support', en: 'Technical support' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'CONTRACT_DATA', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 3,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Zendesk', 'Freshdesk', 'Intercom'],
|
||||
commonVendorCategories: ['SUPPORT', 'CRM'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-cs-satisfaction',
|
||||
category: 'CUSTOMER_SERVICE',
|
||||
name: {
|
||||
de: 'Kundenzufriedenheitsbefragungen',
|
||||
en: 'Customer Satisfaction Surveys',
|
||||
},
|
||||
description: {
|
||||
de: 'Durchführung von Umfragen zur Messung der Kundenzufriedenheit',
|
||||
en: 'Conducting surveys to measure customer satisfaction',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Messung der Kundenzufriedenheit', en: 'Measuring customer satisfaction' },
|
||||
{ de: 'Qualitätsverbesserung', en: 'Quality improvement' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['SurveyMonkey', 'Typeform', 'NPS-Tools'],
|
||||
commonVendorCategories: ['ANALYTICS', 'MARKETING'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// WEBSITE & APPS
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-web-analytics',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Web-Analyse',
|
||||
en: 'Web Analytics',
|
||||
},
|
||||
description: {
|
||||
de: 'Analyse des Nutzerverhaltens auf der Website zur Optimierung',
|
||||
en: 'Analyzing user behavior on the website for optimization',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Analyse des Nutzerverhaltens', en: 'Analyzing user behavior' },
|
||||
{ de: 'Website-Optimierung', en: 'Website optimization' },
|
||||
{ de: 'Conversion-Tracking', en: 'Conversion tracking' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA', 'LOCATION_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Google Analytics', 'Matomo', 'Plausible'],
|
||||
commonVendorCategories: ['ANALYTICS'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-web-contact-form',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Kontaktformular',
|
||||
en: 'Contact Form',
|
||||
},
|
||||
description: {
|
||||
de: 'Verarbeitung von Anfragen über das Website-Kontaktformular',
|
||||
en: 'Processing inquiries submitted via the website contact form',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Bearbeitung von Kontaktanfragen', en: 'Processing contact inquiries' },
|
||||
{ de: 'Kommunikation mit Interessenten', en: 'Communication with prospects' },
|
||||
],
|
||||
dataSubjectCategories: ['PROSPECTIVE_CUSTOMERS', 'WEBSITE_USERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['CRM', 'E-Mail-System'],
|
||||
commonVendorCategories: ['CRM', 'EMAIL'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-web-user-accounts',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Benutzerkonten / Kundenportal',
|
||||
en: 'User Accounts / Customer Portal',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Benutzerkonten im Kundenportal oder Online-Shop',
|
||||
en: 'Managing user accounts in customer portal or online shop',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Bereitstellung des Kundenportals', en: 'Providing customer portal' },
|
||||
{ de: 'Benutzerverwaltung', en: 'User management' },
|
||||
{ de: 'Personalisierung', en: 'Personalization' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'APP_USERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'LOGIN_DATA', 'USAGE_DATA', 'CONTRACT_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT'],
|
||||
suggestedRetentionYears: 1, // Nach Kontoschließung
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['E-Commerce', 'CRM', 'Auth0'],
|
||||
commonVendorCategories: ['HOSTING', 'CRM', 'SECURITY'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-web-cookies',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Cookie-Verwaltung',
|
||||
en: 'Cookie Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Cookies und Einholung von Cookie-Einwilligungen',
|
||||
en: 'Managing cookies and obtaining cookie consents',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Speicherung von Cookie-Präferenzen', en: 'Storing cookie preferences' },
|
||||
{ de: 'Einwilligungsmanagement', en: 'Consent management' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Cookiebot', 'Usercentrics', 'OneTrust'],
|
||||
commonVendorCategories: ['ANALYTICS', 'SECURITY'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// GENERAL
|
||||
// ==========================================
|
||||
{
|
||||
id: 'tpl-gen-communication',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Geschäftliche Kommunikation',
|
||||
en: 'Business Communication',
|
||||
},
|
||||
description: {
|
||||
de: 'E-Mail-Kommunikation, Telefonie und Messaging im Geschäftsverkehr',
|
||||
en: 'Email communication, telephony, and messaging in business operations',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Geschäftliche Kommunikation', en: 'Business communication' },
|
||||
{ de: 'Dokumentation von Korrespondenz', en: 'Documentation of correspondence' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'SUPPLIERS', 'BUSINESS_PARTNERS', 'EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 6, // Handelsrechtlich relevant
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Microsoft 365', 'Google Workspace', 'Slack'],
|
||||
commonVendorCategories: ['EMAIL', 'COMMUNICATION', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-gen-visitor',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Besucherverwaltung',
|
||||
en: 'Visitor Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Erfassung und Verwaltung von Besuchern in Geschäftsräumen',
|
||||
en: 'Recording and managing visitors in business premises',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Zutrittskontrolle', en: 'Access control' },
|
||||
{ de: 'Sicherheit', en: 'Security' },
|
||||
{ de: 'Nachvollziehbarkeit', en: 'Traceability' },
|
||||
],
|
||||
dataSubjectCategories: ['VISITORS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'PHOTO_VIDEO'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 0.1, // 1 Monat
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Besuchermanagement-System'],
|
||||
commonVendorCategories: ['SECURITY'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-gen-supplier',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Lieferantenverwaltung',
|
||||
en: 'Supplier Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Lieferantenbeziehungen und Beschaffung',
|
||||
en: 'Managing supplier relationships and procurement',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Lieferantenverwaltung', en: 'Supplier management' },
|
||||
{ de: 'Beschaffung', en: 'Procurement' },
|
||||
{ de: 'Qualitätsmanagement', en: 'Quality management' },
|
||||
],
|
||||
dataSubjectCategories: ['SUPPLIERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'CONTRACT_DATA', 'BANK_ACCOUNT'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ERP', 'Lieferantenportal'],
|
||||
commonVendorCategories: ['ERP'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-gen-whistleblower',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Hinweisgebersystem',
|
||||
en: 'Whistleblower System',
|
||||
},
|
||||
description: {
|
||||
de: 'Entgegennahme und Bearbeitung von Hinweisen gemäß Hinweisgeberschutzgesetz',
|
||||
en: 'Receiving and processing reports according to whistleblower protection law',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Entgegennahme von Hinweisen', en: 'Receiving reports' },
|
||||
{ de: 'Untersuchung von Verstößen', en: 'Investigating violations' },
|
||||
{ de: 'Schutz von Hinweisgebern', en: 'Protecting whistleblowers' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 3,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['Hinweisgeberportal'],
|
||||
commonVendorCategories: ['SECURITY', 'LEGAL'],
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Get templates by category
|
||||
*/
|
||||
export function getTemplatesByCategory(
|
||||
category: ProcessingActivityCategory
|
||||
): ProcessingActivityTemplate[] {
|
||||
return PROCESSING_ACTIVITY_TEMPLATES.filter((t) => t.category === category)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template by ID
|
||||
*/
|
||||
export function getTemplateById(id: string): ProcessingActivityTemplate | undefined {
|
||||
return PROCESSING_ACTIVITY_TEMPLATES.find((t) => t.id === id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all categories with their templates
|
||||
*/
|
||||
export function getGroupedTemplates(): Map<ProcessingActivityCategory, ProcessingActivityTemplate[]> {
|
||||
const grouped = new Map<ProcessingActivityCategory, ProcessingActivityTemplate[]>()
|
||||
|
||||
for (const template of PROCESSING_ACTIVITY_TEMPLATES) {
|
||||
const existing = grouped.get(template.category) || []
|
||||
grouped.set(template.category, [...existing, template])
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
/**
|
||||
* Create form data from template
|
||||
*/
|
||||
export function createFormDataFromTemplate(
|
||||
template: ProcessingActivityTemplate,
|
||||
organizationDefaults?: {
|
||||
responsible?: ProcessingActivityFormData['responsible']
|
||||
dpoContact?: ProcessingActivityFormData['dpoContact']
|
||||
}
|
||||
): Partial<ProcessingActivityFormData> {
|
||||
return {
|
||||
vvtId: '', // Will be generated
|
||||
name: template.name,
|
||||
purposes: template.purposes,
|
||||
dataSubjectCategories: template.dataSubjectCategories,
|
||||
personalDataCategories: template.personalDataCategories,
|
||||
legalBasis: template.suggestedLegalBasis.map((type) => ({ type })),
|
||||
protectionLevel: template.suggestedProtectionLevel,
|
||||
dpiaRequired: template.dpiaLikely,
|
||||
retentionPeriod: {
|
||||
duration: template.suggestedRetentionYears,
|
||||
durationUnit: 'YEARS',
|
||||
description: { de: '', en: '' },
|
||||
},
|
||||
recipientCategories: [],
|
||||
thirdCountryTransfers: [],
|
||||
technicalMeasures: [],
|
||||
dataSources: [],
|
||||
systems: [],
|
||||
dataFlows: [],
|
||||
subProcessors: [],
|
||||
owner: '',
|
||||
responsible: organizationDefaults?.responsible,
|
||||
dpoContact: organizationDefaults?.dpoContact,
|
||||
}
|
||||
}
|
||||
export * from './processing-activities/types'
|
||||
export * from './processing-activities/hr-sales'
|
||||
export * from './processing-activities/marketing-finance'
|
||||
export * from './processing-activities/it-customer-web-general'
|
||||
export * from './processing-activities/helpers'
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Processing Activities — Helper Functions
|
||||
*/
|
||||
|
||||
import type { ProcessingActivityFormData } from '../../types'
|
||||
import type { ProcessingActivityTemplate, ProcessingActivityCategory } from './types'
|
||||
import { HR_TEMPLATES, SALES_TEMPLATES } from './hr-sales'
|
||||
import { MARKETING_TEMPLATES, FINANCE_TEMPLATES } from './marketing-finance'
|
||||
import { IT_TEMPLATES, CUSTOMER_SERVICE_TEMPLATES, WEBSITE_TEMPLATES, GENERAL_TEMPLATES } from './it-customer-web-general'
|
||||
|
||||
export const PROCESSING_ACTIVITY_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
...HR_TEMPLATES,
|
||||
...SALES_TEMPLATES,
|
||||
...MARKETING_TEMPLATES,
|
||||
...FINANCE_TEMPLATES,
|
||||
...IT_TEMPLATES,
|
||||
...CUSTOMER_SERVICE_TEMPLATES,
|
||||
...WEBSITE_TEMPLATES,
|
||||
...GENERAL_TEMPLATES,
|
||||
]
|
||||
|
||||
/**
|
||||
* Get templates by category
|
||||
*/
|
||||
export function getTemplatesByCategory(
|
||||
category: ProcessingActivityCategory
|
||||
): ProcessingActivityTemplate[] {
|
||||
return PROCESSING_ACTIVITY_TEMPLATES.filter((t) => t.category === category)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template by ID
|
||||
*/
|
||||
export function getTemplateById(id: string): ProcessingActivityTemplate | undefined {
|
||||
return PROCESSING_ACTIVITY_TEMPLATES.find((t) => t.id === id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all categories with their templates
|
||||
*/
|
||||
export function getGroupedTemplates(): Map<ProcessingActivityCategory, ProcessingActivityTemplate[]> {
|
||||
const grouped = new Map<ProcessingActivityCategory, ProcessingActivityTemplate[]>()
|
||||
|
||||
for (const template of PROCESSING_ACTIVITY_TEMPLATES) {
|
||||
const existing = grouped.get(template.category) || []
|
||||
grouped.set(template.category, [...existing, template])
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
/**
|
||||
* Create form data from template
|
||||
*/
|
||||
export function createFormDataFromTemplate(
|
||||
template: ProcessingActivityTemplate,
|
||||
organizationDefaults?: {
|
||||
responsible?: ProcessingActivityFormData['responsible']
|
||||
dpoContact?: ProcessingActivityFormData['dpoContact']
|
||||
}
|
||||
): Partial<ProcessingActivityFormData> {
|
||||
return {
|
||||
vvtId: '',
|
||||
name: template.name,
|
||||
purposes: template.purposes,
|
||||
dataSubjectCategories: template.dataSubjectCategories,
|
||||
personalDataCategories: template.personalDataCategories,
|
||||
legalBasis: template.suggestedLegalBasis.map((type) => ({ type })),
|
||||
protectionLevel: template.suggestedProtectionLevel,
|
||||
dpiaRequired: template.dpiaLikely,
|
||||
retentionPeriod: {
|
||||
duration: template.suggestedRetentionYears,
|
||||
durationUnit: 'YEARS',
|
||||
description: { de: '', en: '' },
|
||||
},
|
||||
recipientCategories: [],
|
||||
thirdCountryTransfers: [],
|
||||
technicalMeasures: [],
|
||||
dataSources: [],
|
||||
systems: [],
|
||||
dataFlows: [],
|
||||
subProcessors: [],
|
||||
owner: '',
|
||||
responsible: organizationDefaults?.responsible,
|
||||
dpoContact: organizationDefaults?.dpoContact,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Processing Activities — HR & Sales Templates
|
||||
*/
|
||||
|
||||
import { ProcessingActivityTemplate } from './types'
|
||||
|
||||
export const HR_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-hr-recruitment',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Bewerbermanagement',
|
||||
en: 'Recruitment Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verarbeitung von Bewerberdaten im Rahmen des Recruiting-Prozesses',
|
||||
en: 'Processing of applicant data as part of the recruitment process',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Durchführung des Bewerbungsverfahrens', en: 'Conducting the application process' },
|
||||
{ de: 'Prüfung der Eignung', en: 'Assessing suitability' },
|
||||
{ de: 'Aufbau eines Talentpools (bei Einwilligung)', en: 'Building a talent pool (with consent)' },
|
||||
],
|
||||
dataSubjectCategories: ['APPLICANTS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'DOB', 'EDUCATION_DATA',
|
||||
'EMPLOYMENT_DATA', 'PHOTO_VIDEO',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'CONSENT'],
|
||||
suggestedRetentionYears: 0.5,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['E-Recruiting', 'Personio', 'Workday'],
|
||||
commonVendorCategories: ['HR_SOFTWARE', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-personnel',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Personalverwaltung',
|
||||
en: 'Personnel Administration',
|
||||
},
|
||||
description: {
|
||||
de: 'Führung der Personalakte und Verwaltung des Beschäftigungsverhältnisses',
|
||||
en: 'Maintaining personnel files and managing employment relationships',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Führung der Personalakte', en: 'Maintaining personnel files' },
|
||||
{ de: 'Durchführung des Arbeitsverhältnisses', en: 'Executing the employment relationship' },
|
||||
{ de: 'Erfüllung gesetzlicher Pflichten', en: 'Fulfilling legal obligations' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'DOB', 'ID_NUMBER',
|
||||
'SOCIAL_SECURITY', 'TAX_ID', 'BANK_ACCOUNT', 'EMPLOYMENT_DATA',
|
||||
'SALARY_DATA', 'EDUCATION_DATA', 'PHOTO_VIDEO',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['SAP HCM', 'Personio', 'DATEV'],
|
||||
commonVendorCategories: ['HR_SOFTWARE', 'ERP'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-payroll',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Lohn- und Gehaltsabrechnung',
|
||||
en: 'Payroll Processing',
|
||||
},
|
||||
description: {
|
||||
de: 'Berechnung und Auszahlung von Gehältern, Abführung von Steuern und Sozialabgaben',
|
||||
en: 'Calculation and payment of salaries, tax and social security contributions',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Gehaltsberechnung und -auszahlung', en: 'Salary calculation and payment' },
|
||||
{ de: 'Abführung von Lohnsteuer und Sozialabgaben', en: 'Payment of payroll taxes and social contributions' },
|
||||
{ de: 'Erstellung von Lohnabrechnungen', en: 'Creating payslips' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'ADDRESS', 'DOB', 'SOCIAL_SECURITY', 'TAX_ID',
|
||||
'BANK_ACCOUNT', 'SALARY_DATA', 'EMPLOYMENT_DATA',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['DATEV', 'SAP', 'Lexware'],
|
||||
commonVendorCategories: ['ACCOUNTING', 'HR_SOFTWARE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-time-tracking',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Arbeitszeiterfassung',
|
||||
en: 'Time Tracking',
|
||||
},
|
||||
description: {
|
||||
de: 'Erfassung der Arbeitszeiten zur Einhaltung des Arbeitszeitgesetzes',
|
||||
en: 'Recording working hours for compliance with working time regulations',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Erfassung der Arbeitszeiten', en: 'Recording working hours' },
|
||||
{ de: 'Einhaltung des Arbeitszeitgesetzes', en: 'Compliance with working time regulations' },
|
||||
{ de: 'Grundlage für Gehaltsabrechnung', en: 'Basis for payroll' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'EMPLOYMENT_DATA', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['LEGAL_OBLIGATION', 'CONTRACT'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ATOSS', 'Clockodo', 'Toggl'],
|
||||
commonVendorCategories: ['HR_SOFTWARE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-hr-health-management',
|
||||
category: 'HR',
|
||||
name: {
|
||||
de: 'Betriebliches Gesundheitsmanagement',
|
||||
en: 'Occupational Health Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Arbeitsunfähigkeitsbescheinigungen und betriebsärztlichen Untersuchungen',
|
||||
en: 'Management of sick notes and occupational health examinations',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Verwaltung von Krankmeldungen', en: 'Managing sick leave' },
|
||||
{ de: 'Organisation betriebsärztlicher Untersuchungen', en: 'Organizing occupational health examinations' },
|
||||
{ de: 'Betriebliches Eingliederungsmanagement', en: 'Occupational reintegration management' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'EMPLOYMENT_DATA', 'HEALTH_DATA'],
|
||||
suggestedLegalBasis: ['ART9_EMPLOYMENT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 3,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['HR-Software', 'BEM-System'],
|
||||
commonVendorCategories: ['HR_SOFTWARE', 'CONSULTING'],
|
||||
},
|
||||
]
|
||||
|
||||
export const SALES_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-sales-crm',
|
||||
category: 'SALES',
|
||||
name: {
|
||||
de: 'Kundenbeziehungsmanagement (CRM)',
|
||||
en: 'Customer Relationship Management (CRM)',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Kundenbeziehungen, Kontakthistorie und Verkaufschancen',
|
||||
en: 'Managing customer relationships, contact history, and sales opportunities',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Pflege von Kundenbeziehungen', en: 'Maintaining customer relationships' },
|
||||
{ de: 'Dokumentation von Kundenkontakten', en: 'Documenting customer contacts' },
|
||||
{ de: 'Vertriebssteuerung', en: 'Sales management' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'PROSPECTIVE_CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'CONTRACT_DATA', 'COMMUNICATION_DATA',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 3,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Salesforce', 'HubSpot', 'Pipedrive', 'Microsoft Dynamics'],
|
||||
commonVendorCategories: ['CRM'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-sales-contract-management',
|
||||
category: 'SALES',
|
||||
name: {
|
||||
de: 'Vertragsmanagement',
|
||||
en: 'Contract Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Kundenverträgen, Angeboten und Aufträgen',
|
||||
en: 'Managing customer contracts, quotes, and orders',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Erstellung und Verwaltung von Verträgen', en: 'Creating and managing contracts' },
|
||||
{ de: 'Angebotsverfolgung', en: 'Quote tracking' },
|
||||
{ de: 'Auftragsabwicklung', en: 'Order processing' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'CONTACT', 'ADDRESS', 'CONTRACT_DATA', 'PAYMENT_DATA',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ERP', 'CRM', 'Vertragsverwaltung'],
|
||||
commonVendorCategories: ['ERP', 'CRM'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,363 @@
|
||||
/**
|
||||
* Processing Activities — IT, Customer Service, Website & General Templates
|
||||
*/
|
||||
|
||||
import { ProcessingActivityTemplate } from './types'
|
||||
|
||||
export const IT_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-it-user-management',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'IT-Benutzerverwaltung',
|
||||
en: 'IT User Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Benutzerkonten, Zugriffsrechten und Authentifizierung',
|
||||
en: 'Managing user accounts, access rights, and authentication',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Verwaltung von Benutzerkonten', en: 'Managing user accounts' },
|
||||
{ de: 'Zugriffssteuerung', en: 'Access control' },
|
||||
{ de: 'Single Sign-On', en: 'Single Sign-On' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'LOGIN_DATA', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Active Directory', 'Okta', 'Azure AD'],
|
||||
commonVendorCategories: ['SECURITY', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-it-logging',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'IT-Protokollierung',
|
||||
en: 'IT Logging',
|
||||
},
|
||||
description: {
|
||||
de: 'Protokollierung von IT-Aktivitäten zur Sicherheit und Fehleranalyse',
|
||||
en: 'Logging IT activities for security and error analysis',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Sicherheitsüberwachung', en: 'Security monitoring' },
|
||||
{ de: 'Fehleranalyse', en: 'Error analysis' },
|
||||
{ de: 'Nachvollziehbarkeit', en: 'Traceability' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'CUSTOMERS', 'WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA', 'LOGIN_DATA'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Splunk', 'ELK Stack', 'Datadog'],
|
||||
commonVendorCategories: ['SECURITY', 'ANALYTICS'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-it-video-surveillance',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'Videoüberwachung',
|
||||
en: 'Video Surveillance',
|
||||
},
|
||||
description: {
|
||||
de: 'Videoüberwachung von Geschäftsräumen zum Schutz vor Diebstahl und Vandalismus',
|
||||
en: 'Video surveillance of business premises for theft and vandalism prevention',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Schutz vor Diebstahl und Vandalismus', en: 'Protection against theft and vandalism' },
|
||||
{ de: 'Zugangskontrolle', en: 'Access control' },
|
||||
{ de: 'Beweissicherung', en: 'Evidence preservation' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'VISITORS', 'CUSTOMERS'],
|
||||
personalDataCategories: ['PHOTO_VIDEO', 'BIOMETRIC_DATA'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 0.1,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['CCTV-System'],
|
||||
commonVendorCategories: ['SECURITY'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-it-backup',
|
||||
category: 'IT',
|
||||
name: {
|
||||
de: 'Datensicherung (Backup)',
|
||||
en: 'Data Backup',
|
||||
},
|
||||
description: {
|
||||
de: 'Regelmäßige Sicherung von Unternehmensdaten',
|
||||
en: 'Regular backup of company data',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Datensicherung', en: 'Data backup' },
|
||||
{ de: 'Disaster Recovery', en: 'Disaster Recovery' },
|
||||
{ de: 'Geschäftskontinuität', en: 'Business continuity' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'CUSTOMERS', 'SUPPLIERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'CONTRACT_DATA', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Veeam', 'AWS Backup', 'Azure Backup'],
|
||||
commonVendorCategories: ['BACKUP', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
]
|
||||
|
||||
export const CUSTOMER_SERVICE_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-cs-support',
|
||||
category: 'CUSTOMER_SERVICE',
|
||||
name: {
|
||||
de: 'Kundenbetreuung und Support',
|
||||
en: 'Customer Support',
|
||||
},
|
||||
description: {
|
||||
de: 'Bearbeitung von Kundenanfragen, Beschwerden und Support-Tickets',
|
||||
en: 'Handling customer inquiries, complaints, and support tickets',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Bearbeitung von Kundenanfragen', en: 'Handling customer inquiries' },
|
||||
{ de: 'Beschwerdemanagement', en: 'Complaint management' },
|
||||
{ de: 'Technischer Support', en: 'Technical support' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'CONTRACT_DATA', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 3,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Zendesk', 'Freshdesk', 'Intercom'],
|
||||
commonVendorCategories: ['SUPPORT', 'CRM'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-cs-satisfaction',
|
||||
category: 'CUSTOMER_SERVICE',
|
||||
name: {
|
||||
de: 'Kundenzufriedenheitsbefragungen',
|
||||
en: 'Customer Satisfaction Surveys',
|
||||
},
|
||||
description: {
|
||||
de: 'Durchführung von Umfragen zur Messung der Kundenzufriedenheit',
|
||||
en: 'Conducting surveys to measure customer satisfaction',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Messung der Kundenzufriedenheit', en: 'Measuring customer satisfaction' },
|
||||
{ de: 'Qualitätsverbesserung', en: 'Quality improvement' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['SurveyMonkey', 'Typeform', 'NPS-Tools'],
|
||||
commonVendorCategories: ['ANALYTICS', 'MARKETING'],
|
||||
},
|
||||
]
|
||||
|
||||
export const WEBSITE_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-web-analytics',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Web-Analyse',
|
||||
en: 'Web Analytics',
|
||||
},
|
||||
description: {
|
||||
de: 'Analyse des Nutzerverhaltens auf der Website zur Optimierung',
|
||||
en: 'Analyzing user behavior on the website for optimization',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Analyse des Nutzerverhaltens', en: 'Analyzing user behavior' },
|
||||
{ de: 'Website-Optimierung', en: 'Website optimization' },
|
||||
{ de: 'Conversion-Tracking', en: 'Conversion tracking' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA', 'LOCATION_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Google Analytics', 'Matomo', 'Plausible'],
|
||||
commonVendorCategories: ['ANALYTICS'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-web-contact-form',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Kontaktformular',
|
||||
en: 'Contact Form',
|
||||
},
|
||||
description: {
|
||||
de: 'Verarbeitung von Anfragen über das Website-Kontaktformular',
|
||||
en: 'Processing inquiries submitted via the website contact form',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Bearbeitung von Kontaktanfragen', en: 'Processing contact inquiries' },
|
||||
{ de: 'Kommunikation mit Interessenten', en: 'Communication with prospects' },
|
||||
],
|
||||
dataSubjectCategories: ['PROSPECTIVE_CUSTOMERS', 'WEBSITE_USERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['CRM', 'E-Mail-System'],
|
||||
commonVendorCategories: ['CRM', 'EMAIL'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-web-user-accounts',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Benutzerkonten / Kundenportal',
|
||||
en: 'User Accounts / Customer Portal',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Benutzerkonten im Kundenportal oder Online-Shop',
|
||||
en: 'Managing user accounts in customer portal or online shop',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Bereitstellung des Kundenportals', en: 'Providing customer portal' },
|
||||
{ de: 'Benutzerverwaltung', en: 'User management' },
|
||||
{ de: 'Personalisierung', en: 'Personalization' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'APP_USERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'LOGIN_DATA', 'USAGE_DATA', 'CONTRACT_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['E-Commerce', 'CRM', 'Auth0'],
|
||||
commonVendorCategories: ['HOSTING', 'CRM', 'SECURITY'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-web-cookies',
|
||||
category: 'WEBSITE',
|
||||
name: {
|
||||
de: 'Cookie-Verwaltung',
|
||||
en: 'Cookie Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Cookies und Einholung von Cookie-Einwilligungen',
|
||||
en: 'Managing cookies and obtaining cookie consents',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Speicherung von Cookie-Präferenzen', en: 'Storing cookie preferences' },
|
||||
{ de: 'Einwilligungsmanagement', en: 'Consent management' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Cookiebot', 'Usercentrics', 'OneTrust'],
|
||||
commonVendorCategories: ['ANALYTICS', 'SECURITY'],
|
||||
},
|
||||
]
|
||||
|
||||
export const GENERAL_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-gen-communication',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Geschäftliche Kommunikation',
|
||||
en: 'Business Communication',
|
||||
},
|
||||
description: {
|
||||
de: 'E-Mail-Kommunikation, Telefonie und Messaging im Geschäftsverkehr',
|
||||
en: 'Email communication, telephony, and messaging in business operations',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Geschäftliche Kommunikation', en: 'Business communication' },
|
||||
{ de: 'Dokumentation von Korrespondenz', en: 'Documentation of correspondence' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'SUPPLIERS', 'BUSINESS_PARTNERS', 'EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 6,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Microsoft 365', 'Google Workspace', 'Slack'],
|
||||
commonVendorCategories: ['EMAIL', 'COMMUNICATION', 'CLOUD_INFRASTRUCTURE'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-gen-visitor',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Besucherverwaltung',
|
||||
en: 'Visitor Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Erfassung und Verwaltung von Besuchern in Geschäftsräumen',
|
||||
en: 'Recording and managing visitors in business premises',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Zutrittskontrolle', en: 'Access control' },
|
||||
{ de: 'Sicherheit', en: 'Security' },
|
||||
{ de: 'Nachvollziehbarkeit', en: 'Traceability' },
|
||||
],
|
||||
dataSubjectCategories: ['VISITORS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'PHOTO_VIDEO'],
|
||||
suggestedLegalBasis: ['LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 0.1,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Besuchermanagement-System'],
|
||||
commonVendorCategories: ['SECURITY'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-gen-supplier',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Lieferantenverwaltung',
|
||||
en: 'Supplier Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung von Lieferantenbeziehungen und Beschaffung',
|
||||
en: 'Managing supplier relationships and procurement',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Lieferantenverwaltung', en: 'Supplier management' },
|
||||
{ de: 'Beschaffung', en: 'Procurement' },
|
||||
{ de: 'Qualitätsmanagement', en: 'Quality management' },
|
||||
],
|
||||
dataSubjectCategories: ['SUPPLIERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'CONTRACT_DATA', 'BANK_ACCOUNT'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ERP', 'Lieferantenportal'],
|
||||
commonVendorCategories: ['ERP'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-gen-whistleblower',
|
||||
category: 'GENERAL',
|
||||
name: {
|
||||
de: 'Hinweisgebersystem',
|
||||
en: 'Whistleblower System',
|
||||
},
|
||||
description: {
|
||||
de: 'Entgegennahme und Bearbeitung von Hinweisen gemäß Hinweisgeberschutzgesetz',
|
||||
en: 'Receiving and processing reports according to whistleblower protection law',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Entgegennahme von Hinweisen', en: 'Receiving reports' },
|
||||
{ de: 'Untersuchung von Verstößen', en: 'Investigating violations' },
|
||||
{ de: 'Schutz von Hinweisgebern', en: 'Protecting whistleblowers' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'COMMUNICATION_DATA'],
|
||||
suggestedLegalBasis: ['LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 3,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['Hinweisgeberportal'],
|
||||
commonVendorCategories: ['SECURITY', 'LEGAL'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Processing Activities — Marketing & Finance Templates
|
||||
*/
|
||||
|
||||
import { ProcessingActivityTemplate } from './types'
|
||||
|
||||
export const MARKETING_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-marketing-newsletter',
|
||||
category: 'MARKETING',
|
||||
name: {
|
||||
de: 'Newsletter-Versand',
|
||||
en: 'Newsletter Distribution',
|
||||
},
|
||||
description: {
|
||||
de: 'Versand von E-Mail-Newslettern und Marketing-Kommunikation',
|
||||
en: 'Sending email newsletters and marketing communications',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Versand von Newsletter und Marketing-E-Mails', en: 'Sending newsletters and marketing emails' },
|
||||
{ de: 'Messung von Öffnungs- und Klickraten', en: 'Measuring open and click rates' },
|
||||
],
|
||||
dataSubjectCategories: ['NEWSLETTER_SUBSCRIBERS', 'CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT'],
|
||||
suggestedRetentionYears: 0,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Mailchimp', 'CleverReach', 'Sendinblue'],
|
||||
commonVendorCategories: ['EMAIL', 'MARKETING'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-marketing-advertising',
|
||||
category: 'MARKETING',
|
||||
name: {
|
||||
de: 'Online-Werbung',
|
||||
en: 'Online Advertising',
|
||||
},
|
||||
description: {
|
||||
de: 'Schaltung und Auswertung von Online-Werbeanzeigen',
|
||||
en: 'Running and analyzing online advertisements',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Schaltung von Online-Werbung', en: 'Running online advertisements' },
|
||||
{ de: 'Conversion-Tracking', en: 'Conversion tracking' },
|
||||
{ de: 'Retargeting', en: 'Retargeting' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA'],
|
||||
suggestedLegalBasis: ['CONSENT'],
|
||||
suggestedRetentionYears: 1,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: true,
|
||||
commonSystems: ['Google Ads', 'Meta Ads', 'LinkedIn Ads'],
|
||||
commonVendorCategories: ['MARKETING', 'ANALYTICS'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-marketing-events',
|
||||
category: 'MARKETING',
|
||||
name: {
|
||||
de: 'Veranstaltungsmanagement',
|
||||
en: 'Event Management',
|
||||
},
|
||||
description: {
|
||||
de: 'Organisation und Durchführung von Veranstaltungen, Messen und Webinaren',
|
||||
en: 'Organizing and conducting events, trade shows, and webinars',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Teilnehmerregistrierung', en: 'Participant registration' },
|
||||
{ de: 'Veranstaltungsdurchführung', en: 'Event execution' },
|
||||
{ de: 'Nachbereitung und Follow-up', en: 'Follow-up activities' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'PROSPECTIVE_CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'PHOTO_VIDEO'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'CONSENT'],
|
||||
suggestedRetentionYears: 2,
|
||||
suggestedProtectionLevel: 'LOW',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['Eventbrite', 'GoToWebinar', 'Zoom'],
|
||||
commonVendorCategories: ['MARKETING', 'COMMUNICATION'],
|
||||
},
|
||||
]
|
||||
|
||||
export const FINANCE_TEMPLATES: ProcessingActivityTemplate[] = [
|
||||
{
|
||||
id: 'tpl-finance-accounting',
|
||||
category: 'FINANCE',
|
||||
name: {
|
||||
de: 'Finanzbuchhaltung',
|
||||
en: 'Financial Accounting',
|
||||
},
|
||||
description: {
|
||||
de: 'Führung der Finanzbuchhaltung, Rechnungsstellung und Zahlungsabwicklung',
|
||||
en: 'Financial accounting, invoicing, and payment processing',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Buchführung und Rechnungswesen', en: 'Bookkeeping and accounting' },
|
||||
{ de: 'Rechnungsstellung', en: 'Invoicing' },
|
||||
{ de: 'Zahlungsabwicklung', en: 'Payment processing' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'SUPPLIERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: [
|
||||
'NAME', 'ADDRESS', 'BANK_ACCOUNT', 'PAYMENT_DATA', 'CONTRACT_DATA', 'TAX_ID',
|
||||
],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGAL_OBLIGATION'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'HIGH',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['DATEV', 'SAP', 'Lexware', 'Xero'],
|
||||
commonVendorCategories: ['ACCOUNTING', 'ERP'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-finance-debt-collection',
|
||||
category: 'FINANCE',
|
||||
name: {
|
||||
de: 'Forderungsmanagement',
|
||||
en: 'Debt Collection',
|
||||
},
|
||||
description: {
|
||||
de: 'Verwaltung offener Forderungen und Mahnwesen',
|
||||
en: 'Managing outstanding receivables and dunning',
|
||||
},
|
||||
purposes: [
|
||||
{ de: 'Überwachung offener Forderungen', en: 'Monitoring outstanding receivables' },
|
||||
{ de: 'Mahnwesen', en: 'Dunning process' },
|
||||
{ de: 'Inkasso bei Bedarf', en: 'Debt collection if necessary' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'ADDRESS', 'CONTACT', 'PAYMENT_DATA', 'CONTRACT_DATA'],
|
||||
suggestedLegalBasis: ['CONTRACT', 'LEGITIMATE_INTEREST'],
|
||||
suggestedRetentionYears: 10,
|
||||
suggestedProtectionLevel: 'MEDIUM',
|
||||
dpiaLikely: false,
|
||||
commonSystems: ['ERP', 'Inkasso-Software'],
|
||||
commonVendorCategories: ['ACCOUNTING', 'LEGAL'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Processing Activities Catalog — Shared Types & Metadata
|
||||
*/
|
||||
|
||||
import {
|
||||
DataSubjectCategory,
|
||||
PersonalDataCategory,
|
||||
LegalBasisType,
|
||||
ProtectionLevel,
|
||||
LocalizedText,
|
||||
} from '../../types'
|
||||
|
||||
export interface ProcessingActivityTemplate {
|
||||
id: string
|
||||
category: ProcessingActivityCategory
|
||||
name: LocalizedText
|
||||
description: LocalizedText
|
||||
purposes: LocalizedText[]
|
||||
dataSubjectCategories: DataSubjectCategory[]
|
||||
personalDataCategories: PersonalDataCategory[]
|
||||
suggestedLegalBasis: LegalBasisType[]
|
||||
suggestedRetentionYears: number
|
||||
suggestedProtectionLevel: ProtectionLevel
|
||||
dpiaLikely: boolean
|
||||
commonSystems: string[]
|
||||
commonVendorCategories: string[]
|
||||
}
|
||||
|
||||
export type ProcessingActivityCategory =
|
||||
| 'HR' // Human Resources
|
||||
| 'SALES' // Vertrieb
|
||||
| 'MARKETING' // Marketing
|
||||
| 'FINANCE' // Finanzen
|
||||
| 'IT' // IT & Sicherheit
|
||||
| 'CUSTOMER_SERVICE' // Kundenservice
|
||||
| 'WEBSITE' // Website & Apps
|
||||
| 'GENERAL' // Allgemein
|
||||
|
||||
export const PROCESSING_ACTIVITY_CATEGORY_META: Record<ProcessingActivityCategory, LocalizedText> = {
|
||||
HR: { de: 'Personal', en: 'Human Resources' },
|
||||
SALES: { de: 'Vertrieb', en: 'Sales' },
|
||||
MARKETING: { de: 'Marketing', en: 'Marketing' },
|
||||
FINANCE: { de: 'Finanzen', en: 'Finance' },
|
||||
IT: { de: 'IT & Sicherheit', en: 'IT & Security' },
|
||||
CUSTOMER_SERVICE: { de: 'Kundenservice', en: 'Customer Service' },
|
||||
WEBSITE: { de: 'Website & Apps', en: 'Website & Apps' },
|
||||
GENERAL: { de: 'Allgemein', en: 'General' },
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Contract Review Checklist Data
|
||||
*
|
||||
* Pure data arrays for AVV, Incident, Transfer, and SLA/Liability checklists.
|
||||
* Barrel-split from checklists.ts. Do NOT import directly; use checklists.ts.
|
||||
*/
|
||||
|
||||
import { LocalizedText, FindingCategory } from '../types'
|
||||
|
||||
export interface ChecklistItem {
|
||||
id: string
|
||||
category: FindingCategory
|
||||
requirement: LocalizedText
|
||||
article: string
|
||||
description: LocalizedText
|
||||
checkPoints: LocalizedText[]
|
||||
isRequired: boolean
|
||||
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// ART. 28 DSGVO CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const AVV_CHECKLIST: ChecklistItem[] = [
|
||||
{
|
||||
id: 'art28_3_a',
|
||||
category: 'INSTRUCTION',
|
||||
requirement: { de: 'Weisungsgebundenheit', en: 'Instruction binding' },
|
||||
article: 'Art. 28 Abs. 3 lit. a DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter verarbeitet personenbezogene Daten nur auf dokumentierte Weisung des Verantwortlichen.',
|
||||
en: 'The processor processes personal data only on documented instructions from the controller.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Weisungsgebundenheit explizit vereinbart', en: 'Instruction binding explicitly agreed' },
|
||||
{ de: 'Dokumentierte Weisungen vorgesehen', en: 'Documented instructions provided for' },
|
||||
{ de: 'Hinweispflicht bei rechtswidrigen Weisungen', en: 'Obligation to notify of unlawful instructions' },
|
||||
{ de: 'Keine eigenständige Verarbeitung erlaubt', en: 'No independent processing allowed' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'art28_3_b',
|
||||
category: 'CONFIDENTIALITY',
|
||||
requirement: { de: 'Vertraulichkeitsverpflichtung', en: 'Confidentiality obligation' },
|
||||
article: 'Art. 28 Abs. 3 lit. b DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter gewährleistet, dass sich die zur Verarbeitung befugten Personen zur Vertraulichkeit verpflichtet haben.',
|
||||
en: 'The processor ensures that persons authorised to process personal data have committed themselves to confidentiality.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Vertraulichkeitsverpflichtung der Mitarbeiter', en: 'Confidentiality obligation of employees' },
|
||||
{ de: 'Gesetzliche Verschwiegenheitspflicht oder', en: 'Statutory confidentiality obligation or' },
|
||||
{ de: 'Vertragliche Verpflichtung', en: 'Contractual obligation' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
{
|
||||
id: 'art28_3_c',
|
||||
category: 'TOM',
|
||||
requirement: { de: 'Technische und organisatorische Maßnahmen', en: 'Technical and organisational measures' },
|
||||
article: 'Art. 28 Abs. 3 lit. c DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter trifft alle gemäß Art. 32 erforderlichen Maßnahmen.',
|
||||
en: 'The processor takes all measures required pursuant to Art. 32.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'TOM-Anlage vorhanden', en: 'TOM annex present' },
|
||||
{ de: 'TOM konkret und aktuell', en: 'TOM specific and current' },
|
||||
{ de: 'Bezug zu Art. 32 DSGVO', en: 'Reference to Art. 32 GDPR' },
|
||||
{ de: 'Aktualisierungspflicht vereinbart', en: 'Update obligation agreed' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'art28_3_d',
|
||||
category: 'SUBPROCESSOR',
|
||||
requirement: { de: 'Unterauftragsverarbeitung', en: 'Sub-processing' },
|
||||
article: 'Art. 28 Abs. 3 lit. d DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter nimmt keinen weiteren Auftragsverarbeiter ohne vorherige Genehmigung in Anspruch.',
|
||||
en: 'The processor does not engage another processor without prior authorisation.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Genehmigungserfordernis (allgemein oder spezifisch)', en: 'Authorisation requirement (general or specific)' },
|
||||
{ de: 'Bei allgemeiner Genehmigung: Informationspflicht', en: 'With general authorisation: notification obligation' },
|
||||
{ de: 'Einspruchsrecht des Verantwortlichen', en: 'Right of objection for controller' },
|
||||
{ de: 'Liste aktueller Unterauftragnehmer', en: 'List of current sub-processors' },
|
||||
{ de: 'Weitergabe der Pflichten an Unterauftragnehmer', en: 'Transfer of obligations to sub-processors' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'art28_3_e',
|
||||
category: 'DATA_SUBJECT_RIGHTS',
|
||||
requirement: { de: 'Unterstützung bei Betroffenenrechten', en: 'Assistance with data subject rights' },
|
||||
article: 'Art. 28 Abs. 3 lit. e DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter unterstützt den Verantwortlichen bei der Erfüllung der Betroffenenrechte.',
|
||||
en: 'The processor assists the controller in fulfilling data subject rights obligations.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Unterstützungspflicht vereinbart', en: 'Assistance obligation agreed' },
|
||||
{ de: 'Verfahren zur Weiterleitung von Anfragen', en: 'Procedure for forwarding requests' },
|
||||
{ de: 'Fristen für Unterstützung', en: 'Deadlines for assistance' },
|
||||
{ de: 'Kostenregelung', en: 'Cost arrangement' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
{
|
||||
id: 'art28_3_f',
|
||||
category: 'GENERAL',
|
||||
requirement: { de: 'Unterstützung bei DSFA und Konsultation', en: 'Assistance with DPIA and consultation' },
|
||||
article: 'Art. 28 Abs. 3 lit. f DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter unterstützt den Verantwortlichen bei der Einhaltung der Pflichten gemäß Art. 32-36.',
|
||||
en: 'The processor assists the controller in ensuring compliance with obligations pursuant to Art. 32-36.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Unterstützung bei DSFA', en: 'Assistance with DPIA' },
|
||||
{ de: 'Unterstützung bei vorheriger Konsultation', en: 'Assistance with prior consultation' },
|
||||
{ de: 'Bereitstellung notwendiger Informationen', en: 'Provision of necessary information' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'MEDIUM',
|
||||
},
|
||||
{
|
||||
id: 'art28_3_g',
|
||||
category: 'DELETION',
|
||||
requirement: { de: 'Löschung/Rückgabe nach Vertragsende', en: 'Deletion/return after contract end' },
|
||||
article: 'Art. 28 Abs. 3 lit. g DSGVO',
|
||||
description: {
|
||||
de: 'Nach Abschluss der Verarbeitung werden alle personenbezogenen Daten gelöscht oder zurückgegeben.',
|
||||
en: 'After the end of processing, all personal data is deleted or returned.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Löschung oder Rückgabe nach Wahl des Verantwortlichen', en: 'Deletion or return at controller choice' },
|
||||
{ de: 'Frist für Löschung/Rückgabe (max. 30 Tage empfohlen)', en: 'Deadline for deletion/return (max. 30 days recommended)' },
|
||||
{ de: 'Löschung auch bei Unterauftragnehmern', en: 'Deletion also at sub-processors' },
|
||||
{ de: 'Löschbestätigung/Nachweis', en: 'Deletion confirmation/proof' },
|
||||
{ de: 'Ausnahme nur bei gesetzlicher Aufbewahrungspflicht', en: 'Exception only for legal retention obligation' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'art28_3_h',
|
||||
category: 'AUDIT_RIGHTS',
|
||||
requirement: { de: 'Audit- und Inspektionsrechte', en: 'Audit and inspection rights' },
|
||||
article: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter ermöglicht und unterstützt Überprüfungen durch den Verantwortlichen.',
|
||||
en: 'The processor enables and contributes to audits and inspections by the controller.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Auditrecht ausdrücklich vereinbart', en: 'Audit right explicitly agreed' },
|
||||
{ de: 'Vor-Ort-Inspektionen möglich', en: 'On-site inspections possible' },
|
||||
{ de: 'Angemessene Vorlaufzeit (max. 30 Tage)', en: 'Reasonable notice period (max. 30 days)' },
|
||||
{ de: 'Keine unangemessenen Einschränkungen', en: 'No unreasonable restrictions' },
|
||||
{ de: 'Bereitstellung aller relevanten Informationen', en: 'Provision of all relevant information' },
|
||||
{ de: 'Akzeptanz unabhängiger Prüfer', en: 'Acceptance of independent auditors' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// INCIDENT RESPONSE CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const INCIDENT_CHECKLIST: ChecklistItem[] = [
|
||||
{
|
||||
id: 'incident_notification',
|
||||
category: 'INCIDENT',
|
||||
requirement: { de: 'Meldung von Datenschutzverletzungen', en: 'Notification of data breaches' },
|
||||
article: 'Art. 33 Abs. 2 DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter meldet dem Verantwortlichen unverzüglich jede Datenschutzverletzung.',
|
||||
en: 'The processor notifies the controller without undue delay of any personal data breach.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Meldepflicht vereinbart', en: 'Notification obligation agreed' },
|
||||
{ de: 'Frist: Unverzüglich (max. 24-48h empfohlen)', en: 'Deadline: Without undue delay (max. 24-48h recommended)' },
|
||||
{ de: 'Mindestinhalt der Meldung definiert', en: 'Minimum content of notification defined' },
|
||||
{ de: 'Kontaktstelle für Meldungen', en: 'Contact point for notifications' },
|
||||
{ de: 'Unterstützung bei Dokumentation', en: 'Assistance with documentation' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'incident_content',
|
||||
category: 'INCIDENT',
|
||||
requirement: { de: 'Inhalt der Incident-Meldung', en: 'Content of incident notification' },
|
||||
article: 'Art. 33 Abs. 3 DSGVO',
|
||||
description: {
|
||||
de: 'Die Meldung muss bestimmte Mindestinformationen enthalten.',
|
||||
en: 'The notification must contain certain minimum information.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Art der Verletzung', en: 'Nature of the breach' },
|
||||
{ de: 'Betroffene Datenkategorien', en: 'Affected data categories' },
|
||||
{ de: 'Ungefähre Anzahl betroffener Personen', en: 'Approximate number of affected persons' },
|
||||
{ de: 'Wahrscheinliche Folgen', en: 'Likely consequences' },
|
||||
{ de: 'Ergriffene Maßnahmen', en: 'Measures taken' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// THIRD COUNTRY TRANSFER CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const TRANSFER_CHECKLIST: ChecklistItem[] = [
|
||||
{
|
||||
id: 'transfer_basis',
|
||||
category: 'TRANSFER',
|
||||
requirement: { de: 'Rechtsgrundlage für Drittlandtransfer', en: 'Legal basis for third country transfer' },
|
||||
article: 'Art. 44-49 DSGVO',
|
||||
description: {
|
||||
de: 'Drittlandtransfers nur auf Basis geeigneter Garantien.',
|
||||
en: 'Third country transfers only on the basis of appropriate safeguards.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Angemessenheitsbeschluss oder', en: 'Adequacy decision or' },
|
||||
{ de: 'Standardvertragsklauseln (SCC) oder', en: 'Standard contractual clauses (SCC) or' },
|
||||
{ de: 'Binding Corporate Rules (BCR) oder', en: 'Binding Corporate Rules (BCR) or' },
|
||||
{ de: 'Sonstige Ausnahme Art. 49', en: 'Other derogation Art. 49' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'transfer_scc',
|
||||
category: 'TRANSFER',
|
||||
requirement: { de: 'Standardvertragsklauseln', en: 'Standard Contractual Clauses' },
|
||||
article: 'Art. 46 Abs. 2 lit. c DSGVO',
|
||||
description: {
|
||||
de: 'Bei SCC: Verwendung der aktuellen EU-Kommission-Klauseln.',
|
||||
en: 'With SCC: Use of current EU Commission clauses.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'SCC 2021 verwendet', en: 'SCC 2021 used' },
|
||||
{ de: 'Korrektes Modul gewählt', en: 'Correct module selected' },
|
||||
{ de: 'Anhänge vollständig ausgefüllt', en: 'Annexes completely filled out' },
|
||||
{ de: 'TIA durchgeführt (bei Risiko)', en: 'TIA conducted (if risk)' },
|
||||
{ de: 'Zusätzliche Maßnahmen dokumentiert', en: 'Additional measures documented' },
|
||||
],
|
||||
isRequired: false,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
{
|
||||
id: 'transfer_tia',
|
||||
category: 'TRANSFER',
|
||||
requirement: { de: 'Transfer Impact Assessment', en: 'Transfer Impact Assessment' },
|
||||
article: 'Schrems II, EDSA 01/2020',
|
||||
description: {
|
||||
de: 'Bewertung der Risiken im Drittland.',
|
||||
en: 'Assessment of risks in the third country.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Rechtslage im Drittland analysiert', en: 'Legal situation in third country analyzed' },
|
||||
{ de: 'Zugriff durch Behörden bewertet', en: 'Access by authorities assessed' },
|
||||
{ de: 'Zusätzliche technische Maßnahmen', en: 'Additional technical measures' },
|
||||
{ de: 'Dokumentation der Bewertung', en: 'Documentation of assessment' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// SLA & LIABILITY CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const SLA_LIABILITY_CHECKLIST: ChecklistItem[] = [
|
||||
{
|
||||
id: 'sla_availability',
|
||||
category: 'SLA',
|
||||
requirement: { de: 'Verfügbarkeit', en: 'Availability' },
|
||||
article: 'Vertragliche Vereinbarung',
|
||||
description: {
|
||||
de: 'Service Level Agreement für Verfügbarkeit des Dienstes.',
|
||||
en: 'Service Level Agreement for service availability.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Verfügbarkeit definiert (z.B. 99,9%)', en: 'Availability defined (e.g., 99.9%)' },
|
||||
{ de: 'Messzeitraum festgelegt', en: 'Measurement period defined' },
|
||||
{ de: 'Ausnahmen klar definiert', en: 'Exceptions clearly defined' },
|
||||
{ de: 'Konsequenzen bei Nichteinhaltung', en: 'Consequences of non-compliance' },
|
||||
],
|
||||
isRequired: false,
|
||||
severity: 'MEDIUM',
|
||||
},
|
||||
{
|
||||
id: 'liability_cap',
|
||||
category: 'LIABILITY',
|
||||
requirement: { de: 'Haftungsbegrenzung', en: 'Liability cap' },
|
||||
article: 'Vertragliche Vereinbarung',
|
||||
description: {
|
||||
de: 'Prüfung von Haftungsbegrenzungen und deren Auswirkungen.',
|
||||
en: 'Review of liability caps and their implications.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Haftungshöchstgrenze prüfen', en: 'Check liability cap' },
|
||||
{ de: 'Ausschluss von Vorsatz/grober Fahrlässigkeit', en: 'Exclusion of intent/gross negligence' },
|
||||
{ de: 'Freistellungsklauseln (Indemnity)', en: 'Indemnification clauses' },
|
||||
{ de: 'Versicherungsnachweis', en: 'Insurance proof' },
|
||||
],
|
||||
isRequired: false,
|
||||
severity: 'MEDIUM',
|
||||
},
|
||||
]
|
||||
@@ -1,21 +1,32 @@
|
||||
/**
|
||||
* Contract Review Checklists
|
||||
*
|
||||
* DSGVO Art. 28 compliance checklists for contract reviews
|
||||
* DSGVO Art. 28 compliance checklists for contract reviews.
|
||||
* Checklist data arrays live in checklists-data.ts.
|
||||
*/
|
||||
|
||||
import { LocalizedText, FindingCategory } from '../types'
|
||||
|
||||
export interface ChecklistItem {
|
||||
id: string
|
||||
category: FindingCategory
|
||||
requirement: LocalizedText
|
||||
article: string
|
||||
description: LocalizedText
|
||||
checkPoints: LocalizedText[]
|
||||
isRequired: boolean
|
||||
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
|
||||
}
|
||||
// Re-export data arrays and item type
|
||||
export type { ChecklistItem } from './checklists-data'
|
||||
export {
|
||||
AVV_CHECKLIST,
|
||||
INCIDENT_CHECKLIST,
|
||||
TRANSFER_CHECKLIST,
|
||||
SLA_LIABILITY_CHECKLIST,
|
||||
} from './checklists-data'
|
||||
|
||||
import type { ChecklistItem } from './checklists-data'
|
||||
import {
|
||||
AVV_CHECKLIST,
|
||||
INCIDENT_CHECKLIST,
|
||||
TRANSFER_CHECKLIST,
|
||||
SLA_LIABILITY_CHECKLIST,
|
||||
} from './checklists-data'
|
||||
|
||||
// ==========================================
|
||||
// CHECKLIST GROUP TYPE
|
||||
// ==========================================
|
||||
|
||||
export interface ChecklistGroup {
|
||||
id: string
|
||||
@@ -24,369 +35,6 @@ export interface ChecklistGroup {
|
||||
items: ChecklistItem[]
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// ART. 28 DSGVO CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const AVV_CHECKLIST: ChecklistItem[] = [
|
||||
// Art. 28 Abs. 3 lit. a - Weisungsgebundenheit
|
||||
{
|
||||
id: 'art28_3_a',
|
||||
category: 'INSTRUCTION',
|
||||
requirement: {
|
||||
de: 'Weisungsgebundenheit',
|
||||
en: 'Instruction binding',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. a DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter verarbeitet personenbezogene Daten nur auf dokumentierte Weisung des Verantwortlichen.',
|
||||
en: 'The processor processes personal data only on documented instructions from the controller.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Weisungsgebundenheit explizit vereinbart', en: 'Instruction binding explicitly agreed' },
|
||||
{ de: 'Dokumentierte Weisungen vorgesehen', en: 'Documented instructions provided for' },
|
||||
{ de: 'Hinweispflicht bei rechtswidrigen Weisungen', en: 'Obligation to notify of unlawful instructions' },
|
||||
{ de: 'Keine eigenständige Verarbeitung erlaubt', en: 'No independent processing allowed' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
|
||||
// Art. 28 Abs. 3 lit. b - Vertraulichkeit
|
||||
{
|
||||
id: 'art28_3_b',
|
||||
category: 'CONFIDENTIALITY',
|
||||
requirement: {
|
||||
de: 'Vertraulichkeitsverpflichtung',
|
||||
en: 'Confidentiality obligation',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. b DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter gewährleistet, dass sich die zur Verarbeitung befugten Personen zur Vertraulichkeit verpflichtet haben.',
|
||||
en: 'The processor ensures that persons authorised to process personal data have committed themselves to confidentiality.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Vertraulichkeitsverpflichtung der Mitarbeiter', en: 'Confidentiality obligation of employees' },
|
||||
{ de: 'Gesetzliche Verschwiegenheitspflicht oder', en: 'Statutory confidentiality obligation or' },
|
||||
{ de: 'Vertragliche Verpflichtung', en: 'Contractual obligation' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
|
||||
// Art. 28 Abs. 3 lit. c - Technische und organisatorische Maßnahmen
|
||||
{
|
||||
id: 'art28_3_c',
|
||||
category: 'TOM',
|
||||
requirement: {
|
||||
de: 'Technische und organisatorische Maßnahmen',
|
||||
en: 'Technical and organisational measures',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. c DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter trifft alle gemäß Art. 32 erforderlichen Maßnahmen.',
|
||||
en: 'The processor takes all measures required pursuant to Art. 32.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'TOM-Anlage vorhanden', en: 'TOM annex present' },
|
||||
{ de: 'TOM konkret und aktuell', en: 'TOM specific and current' },
|
||||
{ de: 'Bezug zu Art. 32 DSGVO', en: 'Reference to Art. 32 GDPR' },
|
||||
{ de: 'Aktualisierungspflicht vereinbart', en: 'Update obligation agreed' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
|
||||
// Art. 28 Abs. 3 lit. d - Unterauftragsverarbeitung
|
||||
{
|
||||
id: 'art28_3_d',
|
||||
category: 'SUBPROCESSOR',
|
||||
requirement: {
|
||||
de: 'Unterauftragsverarbeitung',
|
||||
en: 'Sub-processing',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. d DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter nimmt keinen weiteren Auftragsverarbeiter ohne vorherige Genehmigung in Anspruch.',
|
||||
en: 'The processor does not engage another processor without prior authorisation.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Genehmigungserfordernis (allgemein oder spezifisch)', en: 'Authorisation requirement (general or specific)' },
|
||||
{ de: 'Bei allgemeiner Genehmigung: Informationspflicht', en: 'With general authorisation: notification obligation' },
|
||||
{ de: 'Einspruchsrecht des Verantwortlichen', en: 'Right of objection for controller' },
|
||||
{ de: 'Liste aktueller Unterauftragnehmer', en: 'List of current sub-processors' },
|
||||
{ de: 'Weitergabe der Pflichten an Unterauftragnehmer', en: 'Transfer of obligations to sub-processors' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
|
||||
// Art. 28 Abs. 3 lit. e - Unterstützung bei Betroffenenrechten
|
||||
{
|
||||
id: 'art28_3_e',
|
||||
category: 'DATA_SUBJECT_RIGHTS',
|
||||
requirement: {
|
||||
de: 'Unterstützung bei Betroffenenrechten',
|
||||
en: 'Assistance with data subject rights',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. e DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter unterstützt den Verantwortlichen bei der Erfüllung der Betroffenenrechte.',
|
||||
en: 'The processor assists the controller in fulfilling data subject rights obligations.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Unterstützungspflicht vereinbart', en: 'Assistance obligation agreed' },
|
||||
{ de: 'Verfahren zur Weiterleitung von Anfragen', en: 'Procedure for forwarding requests' },
|
||||
{ de: 'Fristen für Unterstützung', en: 'Deadlines for assistance' },
|
||||
{ de: 'Kostenregelung', en: 'Cost arrangement' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
|
||||
// Art. 28 Abs. 3 lit. f - Unterstützung bei DSFA
|
||||
{
|
||||
id: 'art28_3_f',
|
||||
category: 'GENERAL',
|
||||
requirement: {
|
||||
de: 'Unterstützung bei DSFA und Konsultation',
|
||||
en: 'Assistance with DPIA and consultation',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. f DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter unterstützt den Verantwortlichen bei der Einhaltung der Pflichten gemäß Art. 32-36.',
|
||||
en: 'The processor assists the controller in ensuring compliance with obligations pursuant to Art. 32-36.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Unterstützung bei DSFA', en: 'Assistance with DPIA' },
|
||||
{ de: 'Unterstützung bei vorheriger Konsultation', en: 'Assistance with prior consultation' },
|
||||
{ de: 'Bereitstellung notwendiger Informationen', en: 'Provision of necessary information' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'MEDIUM',
|
||||
},
|
||||
|
||||
// Art. 28 Abs. 3 lit. g - Löschung/Rückgabe
|
||||
{
|
||||
id: 'art28_3_g',
|
||||
category: 'DELETION',
|
||||
requirement: {
|
||||
de: 'Löschung/Rückgabe nach Vertragsende',
|
||||
en: 'Deletion/return after contract end',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. g DSGVO',
|
||||
description: {
|
||||
de: 'Nach Abschluss der Verarbeitung werden alle personenbezogenen Daten gelöscht oder zurückgegeben.',
|
||||
en: 'After the end of processing, all personal data is deleted or returned.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Löschung oder Rückgabe nach Wahl des Verantwortlichen', en: 'Deletion or return at controller choice' },
|
||||
{ de: 'Frist für Löschung/Rückgabe (max. 30 Tage empfohlen)', en: 'Deadline for deletion/return (max. 30 days recommended)' },
|
||||
{ de: 'Löschung auch bei Unterauftragnehmern', en: 'Deletion also at sub-processors' },
|
||||
{ de: 'Löschbestätigung/Nachweis', en: 'Deletion confirmation/proof' },
|
||||
{ de: 'Ausnahme nur bei gesetzlicher Aufbewahrungspflicht', en: 'Exception only for legal retention obligation' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
|
||||
// Art. 28 Abs. 3 lit. h - Audit/Inspektion
|
||||
{
|
||||
id: 'art28_3_h',
|
||||
category: 'AUDIT_RIGHTS',
|
||||
requirement: {
|
||||
de: 'Audit- und Inspektionsrechte',
|
||||
en: 'Audit and inspection rights',
|
||||
},
|
||||
article: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter ermöglicht und unterstützt Überprüfungen durch den Verantwortlichen.',
|
||||
en: 'The processor enables and contributes to audits and inspections by the controller.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Auditrecht ausdrücklich vereinbart', en: 'Audit right explicitly agreed' },
|
||||
{ de: 'Vor-Ort-Inspektionen möglich', en: 'On-site inspections possible' },
|
||||
{ de: 'Angemessene Vorlaufzeit (max. 30 Tage)', en: 'Reasonable notice period (max. 30 days)' },
|
||||
{ de: 'Keine unangemessenen Einschränkungen', en: 'No unreasonable restrictions' },
|
||||
{ de: 'Bereitstellung aller relevanten Informationen', en: 'Provision of all relevant information' },
|
||||
{ de: 'Akzeptanz unabhängiger Prüfer', en: 'Acceptance of independent auditors' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// INCIDENT RESPONSE CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const INCIDENT_CHECKLIST: ChecklistItem[] = [
|
||||
{
|
||||
id: 'incident_notification',
|
||||
category: 'INCIDENT',
|
||||
requirement: {
|
||||
de: 'Meldung von Datenschutzverletzungen',
|
||||
en: 'Notification of data breaches',
|
||||
},
|
||||
article: 'Art. 33 Abs. 2 DSGVO',
|
||||
description: {
|
||||
de: 'Der Auftragsverarbeiter meldet dem Verantwortlichen unverzüglich jede Datenschutzverletzung.',
|
||||
en: 'The processor notifies the controller without undue delay of any personal data breach.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Meldepflicht vereinbart', en: 'Notification obligation agreed' },
|
||||
{ de: 'Frist: Unverzüglich (max. 24-48h empfohlen)', en: 'Deadline: Without undue delay (max. 24-48h recommended)' },
|
||||
{ de: 'Mindestinhalt der Meldung definiert', en: 'Minimum content of notification defined' },
|
||||
{ de: 'Kontaktstelle für Meldungen', en: 'Contact point for notifications' },
|
||||
{ de: 'Unterstützung bei Dokumentation', en: 'Assistance with documentation' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'incident_content',
|
||||
category: 'INCIDENT',
|
||||
requirement: {
|
||||
de: 'Inhalt der Incident-Meldung',
|
||||
en: 'Content of incident notification',
|
||||
},
|
||||
article: 'Art. 33 Abs. 3 DSGVO',
|
||||
description: {
|
||||
de: 'Die Meldung muss bestimmte Mindestinformationen enthalten.',
|
||||
en: 'The notification must contain certain minimum information.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Art der Verletzung', en: 'Nature of the breach' },
|
||||
{ de: 'Betroffene Datenkategorien', en: 'Affected data categories' },
|
||||
{ de: 'Ungefähre Anzahl betroffener Personen', en: 'Approximate number of affected persons' },
|
||||
{ de: 'Wahrscheinliche Folgen', en: 'Likely consequences' },
|
||||
{ de: 'Ergriffene Maßnahmen', en: 'Measures taken' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// THIRD COUNTRY TRANSFER CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const TRANSFER_CHECKLIST: ChecklistItem[] = [
|
||||
{
|
||||
id: 'transfer_basis',
|
||||
category: 'TRANSFER',
|
||||
requirement: {
|
||||
de: 'Rechtsgrundlage für Drittlandtransfer',
|
||||
en: 'Legal basis for third country transfer',
|
||||
},
|
||||
article: 'Art. 44-49 DSGVO',
|
||||
description: {
|
||||
de: 'Drittlandtransfers nur auf Basis geeigneter Garantien.',
|
||||
en: 'Third country transfers only on the basis of appropriate safeguards.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Angemessenheitsbeschluss oder', en: 'Adequacy decision or' },
|
||||
{ de: 'Standardvertragsklauseln (SCC) oder', en: 'Standard contractual clauses (SCC) or' },
|
||||
{ de: 'Binding Corporate Rules (BCR) oder', en: 'Binding Corporate Rules (BCR) or' },
|
||||
{ de: 'Sonstige Ausnahme Art. 49', en: 'Other derogation Art. 49' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'CRITICAL',
|
||||
},
|
||||
{
|
||||
id: 'transfer_scc',
|
||||
category: 'TRANSFER',
|
||||
requirement: {
|
||||
de: 'Standardvertragsklauseln',
|
||||
en: 'Standard Contractual Clauses',
|
||||
},
|
||||
article: 'Art. 46 Abs. 2 lit. c DSGVO',
|
||||
description: {
|
||||
de: 'Bei SCC: Verwendung der aktuellen EU-Kommission-Klauseln.',
|
||||
en: 'With SCC: Use of current EU Commission clauses.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'SCC 2021 verwendet', en: 'SCC 2021 used' },
|
||||
{ de: 'Korrektes Modul gewählt', en: 'Correct module selected' },
|
||||
{ de: 'Anhänge vollständig ausgefüllt', en: 'Annexes completely filled out' },
|
||||
{ de: 'TIA durchgeführt (bei Risiko)', en: 'TIA conducted (if risk)' },
|
||||
{ de: 'Zusätzliche Maßnahmen dokumentiert', en: 'Additional measures documented' },
|
||||
],
|
||||
isRequired: false,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
{
|
||||
id: 'transfer_tia',
|
||||
category: 'TRANSFER',
|
||||
requirement: {
|
||||
de: 'Transfer Impact Assessment',
|
||||
en: 'Transfer Impact Assessment',
|
||||
},
|
||||
article: 'Schrems II, EDSA 01/2020',
|
||||
description: {
|
||||
de: 'Bewertung der Risiken im Drittland.',
|
||||
en: 'Assessment of risks in the third country.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Rechtslage im Drittland analysiert', en: 'Legal situation in third country analyzed' },
|
||||
{ de: 'Zugriff durch Behörden bewertet', en: 'Access by authorities assessed' },
|
||||
{ de: 'Zusätzliche technische Maßnahmen', en: 'Additional technical measures' },
|
||||
{ de: 'Dokumentation der Bewertung', en: 'Documentation of assessment' },
|
||||
],
|
||||
isRequired: true,
|
||||
severity: 'HIGH',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// SLA & LIABILITY CHECKLIST
|
||||
// ==========================================
|
||||
|
||||
export const SLA_LIABILITY_CHECKLIST: ChecklistItem[] = [
|
||||
{
|
||||
id: 'sla_availability',
|
||||
category: 'SLA',
|
||||
requirement: {
|
||||
de: 'Verfügbarkeit',
|
||||
en: 'Availability',
|
||||
},
|
||||
article: 'Vertragliche Vereinbarung',
|
||||
description: {
|
||||
de: 'Service Level Agreement für Verfügbarkeit des Dienstes.',
|
||||
en: 'Service Level Agreement for service availability.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Verfügbarkeit definiert (z.B. 99,9%)', en: 'Availability defined (e.g., 99.9%)' },
|
||||
{ de: 'Messzeitraum festgelegt', en: 'Measurement period defined' },
|
||||
{ de: 'Ausnahmen klar definiert', en: 'Exceptions clearly defined' },
|
||||
{ de: 'Konsequenzen bei Nichteinhaltung', en: 'Consequences of non-compliance' },
|
||||
],
|
||||
isRequired: false,
|
||||
severity: 'MEDIUM',
|
||||
},
|
||||
{
|
||||
id: 'liability_cap',
|
||||
category: 'LIABILITY',
|
||||
requirement: {
|
||||
de: 'Haftungsbegrenzung',
|
||||
en: 'Liability cap',
|
||||
},
|
||||
article: 'Vertragliche Vereinbarung',
|
||||
description: {
|
||||
de: 'Prüfung von Haftungsbegrenzungen und deren Auswirkungen.',
|
||||
en: 'Review of liability caps and their implications.',
|
||||
},
|
||||
checkPoints: [
|
||||
{ de: 'Haftungshöchstgrenze prüfen', en: 'Check liability cap' },
|
||||
{ de: 'Ausschluss von Vorsatz/grober Fahrlässigkeit', en: 'Exclusion of intent/gross negligence' },
|
||||
{ de: 'Freistellungsklauseln (Indemnity)', en: 'Indemnification clauses' },
|
||||
{ de: 'Versicherungsnachweis', en: 'Insurance proof' },
|
||||
],
|
||||
isRequired: false,
|
||||
severity: 'MEDIUM',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// GROUPED CHECKLISTS
|
||||
// ==========================================
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
/**
|
||||
* Finding Templates — pre-defined templates for contract reviews
|
||||
*
|
||||
* Barrel-split from findings.ts to stay under 500 LOC.
|
||||
* Do NOT import this file directly; use findings.ts instead.
|
||||
*/
|
||||
|
||||
import { FindingType, FindingCategory, FindingSeverity, LocalizedText } from '../types'
|
||||
|
||||
export interface FindingTemplate {
|
||||
id: string
|
||||
type: FindingType
|
||||
category: FindingCategory
|
||||
severity: FindingSeverity
|
||||
title: LocalizedText
|
||||
description: LocalizedText
|
||||
recommendation: LocalizedText
|
||||
affectedRequirement: string
|
||||
triggerControls: string[]
|
||||
}
|
||||
|
||||
export const FINDING_TEMPLATES: FindingTemplate[] = [
|
||||
// AVV_CONTENT - Weisungsgebundenheit
|
||||
{
|
||||
id: 'tpl-avv-instruction-missing',
|
||||
type: 'GAP',
|
||||
category: 'AVV_CONTENT',
|
||||
severity: 'CRITICAL',
|
||||
title: { de: 'Weisungsgebundenheit fehlt', en: 'Instruction binding missing' },
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Regelung zur Weisungsgebundenheit des Auftragsverarbeiters.',
|
||||
en: 'The contract does not contain a provision on the processor\'s instruction binding.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die den Auftragsverarbeiter verpflichtet, personenbezogene Daten nur auf dokumentierte Weisung des Verantwortlichen zu verarbeiten.',
|
||||
en: 'Add a clause obligating the processor to process personal data only on documented instructions from the controller.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. a DSGVO',
|
||||
triggerControls: ['VND-CON-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-avv-instruction-weak',
|
||||
type: 'RISK',
|
||||
category: 'AVV_CONTENT',
|
||||
severity: 'MEDIUM',
|
||||
title: { de: 'Weisungsgebundenheit unvollständig', en: 'Instruction binding incomplete' },
|
||||
description: {
|
||||
de: 'Die Regelung zur Weisungsgebundenheit ist vorhanden, aber es fehlt die Hinweispflicht bei rechtswidrigen Weisungen.',
|
||||
en: 'The instruction binding provision exists, but the obligation to notify of unlawful instructions is missing.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Pflicht des Auftragsverarbeiters, den Verantwortlichen unverzüglich zu informieren, wenn eine Weisung nach seiner Auffassung gegen Datenschutzrecht verstößt.',
|
||||
en: 'Add an obligation for the processor to immediately inform the controller if an instruction, in their opinion, violates data protection law.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. a DSGVO',
|
||||
triggerControls: ['VND-CON-01'],
|
||||
},
|
||||
|
||||
// AVV_CONTENT - TOM
|
||||
{
|
||||
id: 'tpl-avv-tom-missing',
|
||||
type: 'GAP',
|
||||
category: 'TOM',
|
||||
severity: 'CRITICAL',
|
||||
title: { de: 'TOM-Anlage fehlt', en: 'TOM annex missing' },
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine technischen und organisatorischen Maßnahmen (TOM) als Anlage.',
|
||||
en: 'The contract does not contain technical and organizational measures (TOM) as an annex.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Fordern Sie eine detaillierte TOM-Anlage an, die die Maßnahmen gemäß Art. 32 DSGVO beschreibt.',
|
||||
en: 'Request a detailed TOM annex describing the measures according to Art. 32 GDPR.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. c DSGVO',
|
||||
triggerControls: ['VND-TOM-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-avv-tom-generic',
|
||||
type: 'RISK',
|
||||
category: 'TOM',
|
||||
severity: 'MEDIUM',
|
||||
title: { de: 'TOM zu unspezifisch', en: 'TOM too generic' },
|
||||
description: {
|
||||
de: 'Die TOM-Anlage enthält nur allgemeine Aussagen ohne konkrete Maßnahmen.',
|
||||
en: 'The TOM annex contains only general statements without specific measures.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Fordern Sie eine konkretere Beschreibung der Maßnahmen mit Bezug zum spezifischen Verarbeitungskontext an.',
|
||||
en: 'Request a more specific description of measures with reference to the specific processing context.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. c DSGVO',
|
||||
triggerControls: ['VND-TOM-01'],
|
||||
},
|
||||
|
||||
// SUBPROCESSOR
|
||||
{
|
||||
id: 'tpl-subprocessor-no-approval',
|
||||
type: 'GAP',
|
||||
category: 'SUBPROCESSOR',
|
||||
severity: 'CRITICAL',
|
||||
title: { de: 'Keine Genehmigungspflicht für Unterauftragnehmer', en: 'No approval requirement for sub-processors' },
|
||||
description: {
|
||||
de: 'Der Vertrag regelt nicht, ob und wie der Einsatz von Unterauftragnehmern zu genehmigen ist.',
|
||||
en: 'The contract does not regulate whether and how the use of sub-processors must be approved.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die entweder eine spezifische oder allgemeine Genehmigung für Unterauftragnehmer vorsieht, einschließlich Informations- und Einspruchsrechten.',
|
||||
en: 'Add a clause providing either specific or general authorization for sub-processors, including information and objection rights.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. d DSGVO',
|
||||
triggerControls: ['VND-SUB-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-subprocessor-no-list',
|
||||
type: 'RISK',
|
||||
category: 'SUBPROCESSOR',
|
||||
severity: 'HIGH',
|
||||
title: { de: 'Keine Liste der Unterauftragnehmer', en: 'No list of sub-processors' },
|
||||
description: {
|
||||
de: 'Es liegt keine aktuelle Liste der eingesetzten Unterauftragnehmer vor.',
|
||||
en: 'There is no current list of sub-processors used.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Fordern Sie eine vollständige Liste aller Unterauftragnehmer mit Name, Sitz und Verarbeitungszweck an.',
|
||||
en: 'Request a complete list of all sub-processors with name, location, and processing purpose.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. d DSGVO',
|
||||
triggerControls: ['VND-SUB-02'],
|
||||
},
|
||||
|
||||
// INCIDENT
|
||||
{
|
||||
id: 'tpl-incident-no-notification',
|
||||
type: 'GAP',
|
||||
category: 'INCIDENT',
|
||||
severity: 'CRITICAL',
|
||||
title: { de: 'Keine Meldepflicht bei Datenpannen', en: 'No notification obligation for data breaches' },
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Regelung zur Meldung von Datenschutzverletzungen.',
|
||||
en: 'The contract does not contain a provision for reporting data breaches.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die den Auftragsverarbeiter verpflichtet, Datenschutzverletzungen unverzüglich (innerhalb von 24-48h) zu melden.',
|
||||
en: 'Add a clause obligating the processor to report data breaches without undue delay (within 24-48h).',
|
||||
},
|
||||
affectedRequirement: 'Art. 33 Abs. 2 DSGVO',
|
||||
triggerControls: ['VND-INC-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-incident-long-deadline',
|
||||
type: 'RISK',
|
||||
category: 'INCIDENT',
|
||||
severity: 'HIGH',
|
||||
title: { de: 'Zu lange Meldefrist', en: 'Notification deadline too long' },
|
||||
description: {
|
||||
de: 'Die vereinbarte Meldefrist für Datenschutzverletzungen ist zu lang (>72h), um die eigene Meldepflicht einhalten zu können.',
|
||||
en: 'The agreed notification deadline for data breaches is too long (>72h) to meet own notification obligations.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Verkürzen Sie die Meldefrist auf maximal 24-48 Stunden, um ausreichend Zeit für die eigene Meldung an die Aufsichtsbehörde zu haben.',
|
||||
en: 'Reduce the notification deadline to a maximum of 24-48 hours to have sufficient time for own notification to the supervisory authority.',
|
||||
},
|
||||
affectedRequirement: 'Art. 33 DSGVO',
|
||||
triggerControls: ['VND-INC-01'],
|
||||
},
|
||||
|
||||
// AUDIT_RIGHTS
|
||||
{
|
||||
id: 'tpl-audit-no-right',
|
||||
type: 'GAP',
|
||||
category: 'AUDIT_RIGHTS',
|
||||
severity: 'HIGH',
|
||||
title: { de: 'Kein Auditrecht vereinbart', en: 'No audit right agreed' },
|
||||
description: {
|
||||
de: 'Der Vertrag enthält kein Recht des Verantwortlichen auf Prüfungen und Inspektionen.',
|
||||
en: 'The contract does not contain a right of the controller to audits and inspections.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie ein ausdrückliches Recht auf Vor-Ort-Inspektionen und die Einsicht in relevante Unterlagen.',
|
||||
en: 'Add an explicit right to on-site inspections and access to relevant documents.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
triggerControls: ['VND-AUD-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-audit-restricted',
|
||||
type: 'RISK',
|
||||
category: 'AUDIT_RIGHTS',
|
||||
severity: 'MEDIUM',
|
||||
title: { de: 'Auditrecht eingeschränkt', en: 'Audit right restricted' },
|
||||
description: {
|
||||
de: 'Das Auditrecht ist durch unangemessene Einschränkungen (z.B. sehr lange Vorlaufzeit, Ausschluss von Vor-Ort-Inspektionen) begrenzt.',
|
||||
en: 'The audit right is limited by unreasonable restrictions (e.g., very long notice period, exclusion of on-site inspections).',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Verhandeln Sie angemessene Bedingungen für Audits (max. 30 Tage Vorlaufzeit, Möglichkeit zur Vor-Ort-Inspektion).',
|
||||
en: 'Negotiate reasonable audit conditions (max. 30 days notice, possibility for on-site inspection).',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
triggerControls: ['VND-AUD-01'],
|
||||
},
|
||||
|
||||
// DELETION
|
||||
{
|
||||
id: 'tpl-deletion-no-clause',
|
||||
type: 'GAP',
|
||||
category: 'DELETION',
|
||||
severity: 'CRITICAL',
|
||||
title: { de: 'Keine Lösch-/Rückgaberegelung', en: 'No deletion/return clause' },
|
||||
description: {
|
||||
de: 'Der Vertrag regelt nicht, was mit den Daten nach Vertragsende geschieht.',
|
||||
en: 'The contract does not regulate what happens to the data after contract termination.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel zur Löschung oder Rückgabe aller personenbezogenen Daten nach Vertragsende (max. 30 Tage).',
|
||||
en: 'Add a clause for deletion or return of all personal data after contract end (max. 30 days).',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. g DSGVO',
|
||||
triggerControls: ['VND-DEL-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-deletion-no-confirmation',
|
||||
type: 'RISK',
|
||||
category: 'DELETION',
|
||||
severity: 'MEDIUM',
|
||||
title: { de: 'Keine Löschbestätigung vorgesehen', en: 'No deletion confirmation provided' },
|
||||
description: {
|
||||
de: 'Der Vertrag sieht keine Bestätigung der Löschung durch den Auftragsverarbeiter vor.',
|
||||
en: 'The contract does not provide for confirmation of deletion by the processor.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Pflicht zur schriftlichen Bestätigung der vollständigen Löschung.',
|
||||
en: 'Add an obligation for written confirmation of complete deletion.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. g DSGVO',
|
||||
triggerControls: ['VND-DEL-01'],
|
||||
},
|
||||
|
||||
// TRANSFER
|
||||
{
|
||||
id: 'tpl-transfer-no-basis',
|
||||
type: 'GAP',
|
||||
category: 'TRANSFER',
|
||||
severity: 'CRITICAL',
|
||||
title: { de: 'Drittlandtransfer ohne Rechtsgrundlage', en: 'Third country transfer without legal basis' },
|
||||
description: {
|
||||
de: 'Der Vertrag erlaubt oder impliziert Transfers in Drittländer ohne geeignete Garantien.',
|
||||
en: 'The contract allows or implies transfers to third countries without appropriate safeguards.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Vereinbaren Sie geeignete Garantien (SCC, BCR) oder beschränken Sie die Verarbeitung auf EU/EWR.',
|
||||
en: 'Agree on appropriate safeguards (SCC, BCR) or restrict processing to EU/EEA.',
|
||||
},
|
||||
affectedRequirement: 'Art. 44-49 DSGVO',
|
||||
triggerControls: ['VND-TRF-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-transfer-old-scc',
|
||||
type: 'RISK',
|
||||
category: 'TRANSFER',
|
||||
severity: 'HIGH',
|
||||
title: { de: 'Veraltete Standardvertragsklauseln', en: 'Outdated Standard Contractual Clauses' },
|
||||
description: {
|
||||
de: 'Der Vertrag verwendet die alten SCC (vor 2021), die nicht mehr gültig sind.',
|
||||
en: 'The contract uses old SCC (pre-2021) that are no longer valid.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Aktualisieren Sie auf die SCC 2021 (Durchführungsbeschluss (EU) 2021/914).',
|
||||
en: 'Update to SCC 2021 (Implementing Decision (EU) 2021/914).',
|
||||
},
|
||||
affectedRequirement: 'Art. 46 Abs. 2 lit. c DSGVO',
|
||||
triggerControls: ['VND-TRF-02'],
|
||||
},
|
||||
|
||||
// LIABILITY
|
||||
{
|
||||
id: 'tpl-liability-excessive-cap',
|
||||
type: 'RISK',
|
||||
category: 'LIABILITY',
|
||||
severity: 'MEDIUM',
|
||||
title: { de: 'Unangemessene Haftungsbegrenzung', en: 'Inappropriate liability cap' },
|
||||
description: {
|
||||
de: 'Die Haftungsbegrenzung ist sehr niedrig und könnte bei Datenschutzverletzungen problematisch sein.',
|
||||
en: 'The liability cap is very low and could be problematic in case of data protection violations.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Prüfen Sie, ob die Haftungsbegrenzung angemessen ist. Erwägen Sie eine Ausnahme für Datenschutzverletzungen oder eine höhere Obergrenze.',
|
||||
en: 'Check if the liability cap is appropriate. Consider an exception for data protection violations or a higher limit.',
|
||||
},
|
||||
affectedRequirement: 'Vertragliche Vereinbarung',
|
||||
triggerControls: [],
|
||||
},
|
||||
|
||||
// DATA_SUBJECT_RIGHTS
|
||||
{
|
||||
id: 'tpl-dsr-no-support',
|
||||
type: 'GAP',
|
||||
category: 'DATA_SUBJECT_RIGHTS',
|
||||
severity: 'HIGH',
|
||||
title: { de: 'Keine Unterstützung bei Betroffenenrechten', en: 'No support for data subject rights' },
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Regelung zur Unterstützung bei der Erfüllung von Betroffenenrechten.',
|
||||
en: 'The contract does not contain a provision for support in fulfilling data subject rights.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel zur Unterstützung bei Auskunft, Berichtigung, Löschung und anderen Betroffenenrechten.',
|
||||
en: 'Add a clause for support with access, rectification, deletion, and other data subject rights.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. e DSGVO',
|
||||
triggerControls: ['VND-DSR-01'],
|
||||
},
|
||||
|
||||
// CONFIDENTIALITY
|
||||
{
|
||||
id: 'tpl-confidentiality-missing',
|
||||
type: 'GAP',
|
||||
category: 'CONFIDENTIALITY',
|
||||
severity: 'HIGH',
|
||||
title: { de: 'Keine Vertraulichkeitsverpflichtung', en: 'No confidentiality obligation' },
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Verpflichtung zur Vertraulichkeit der Mitarbeiter.',
|
||||
en: 'The contract does not contain an obligation for employee confidentiality.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die die Verpflichtung der Mitarbeiter zur Vertraulichkeit sicherstellt.',
|
||||
en: 'Add a clause ensuring the obligation of employees to confidentiality.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. b DSGVO',
|
||||
triggerControls: ['VND-CON-02'],
|
||||
},
|
||||
]
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Finding Types and Templates
|
||||
*
|
||||
* Pre-defined finding templates for contract reviews
|
||||
* Pre-defined finding templates for contract reviews.
|
||||
* Barrel re-export — FINDING_TEMPLATES lives in findings-templates.ts.
|
||||
*/
|
||||
|
||||
import {
|
||||
@@ -11,18 +12,6 @@ import {
|
||||
LocalizedText,
|
||||
} from '../types'
|
||||
|
||||
export interface FindingTemplate {
|
||||
id: string
|
||||
type: FindingType
|
||||
category: FindingCategory
|
||||
severity: FindingSeverity
|
||||
title: LocalizedText
|
||||
description: LocalizedText
|
||||
recommendation: LocalizedText
|
||||
affectedRequirement: string
|
||||
triggerControls: string[]
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// FINDING SEVERITY DEFINITIONS
|
||||
// ==========================================
|
||||
@@ -126,371 +115,11 @@ export const FINDING_TYPE_DEFINITIONS: Record<FindingType, {
|
||||
},
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// FINDING TEMPLATES
|
||||
// ==========================================
|
||||
import { FINDING_TEMPLATES } from './findings-templates'
|
||||
import type { FindingTemplate } from './findings-templates'
|
||||
|
||||
export const FINDING_TEMPLATES: FindingTemplate[] = [
|
||||
// AVV_CONTENT - Weisungsgebundenheit
|
||||
{
|
||||
id: 'tpl-avv-instruction-missing',
|
||||
type: 'GAP',
|
||||
category: 'AVV_CONTENT',
|
||||
severity: 'CRITICAL',
|
||||
title: {
|
||||
de: 'Weisungsgebundenheit fehlt',
|
||||
en: 'Instruction binding missing',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Regelung zur Weisungsgebundenheit des Auftragsverarbeiters.',
|
||||
en: 'The contract does not contain a provision on the processor\'s instruction binding.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die den Auftragsverarbeiter verpflichtet, personenbezogene Daten nur auf dokumentierte Weisung des Verantwortlichen zu verarbeiten.',
|
||||
en: 'Add a clause obligating the processor to process personal data only on documented instructions from the controller.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. a DSGVO',
|
||||
triggerControls: ['VND-CON-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-avv-instruction-weak',
|
||||
type: 'RISK',
|
||||
category: 'AVV_CONTENT',
|
||||
severity: 'MEDIUM',
|
||||
title: {
|
||||
de: 'Weisungsgebundenheit unvollständig',
|
||||
en: 'Instruction binding incomplete',
|
||||
},
|
||||
description: {
|
||||
de: 'Die Regelung zur Weisungsgebundenheit ist vorhanden, aber es fehlt die Hinweispflicht bei rechtswidrigen Weisungen.',
|
||||
en: 'The instruction binding provision exists, but the obligation to notify of unlawful instructions is missing.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Pflicht des Auftragsverarbeiters, den Verantwortlichen unverzüglich zu informieren, wenn eine Weisung nach seiner Auffassung gegen Datenschutzrecht verstößt.',
|
||||
en: 'Add an obligation for the processor to immediately inform the controller if an instruction, in their opinion, violates data protection law.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. a DSGVO',
|
||||
triggerControls: ['VND-CON-01'],
|
||||
},
|
||||
|
||||
// AVV_CONTENT - TOM
|
||||
{
|
||||
id: 'tpl-avv-tom-missing',
|
||||
type: 'GAP',
|
||||
category: 'TOM',
|
||||
severity: 'CRITICAL',
|
||||
title: {
|
||||
de: 'TOM-Anlage fehlt',
|
||||
en: 'TOM annex missing',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine technischen und organisatorischen Maßnahmen (TOM) als Anlage.',
|
||||
en: 'The contract does not contain technical and organizational measures (TOM) as an annex.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Fordern Sie eine detaillierte TOM-Anlage an, die die Maßnahmen gemäß Art. 32 DSGVO beschreibt.',
|
||||
en: 'Request a detailed TOM annex describing the measures according to Art. 32 GDPR.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. c DSGVO',
|
||||
triggerControls: ['VND-TOM-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-avv-tom-generic',
|
||||
type: 'RISK',
|
||||
category: 'TOM',
|
||||
severity: 'MEDIUM',
|
||||
title: {
|
||||
de: 'TOM zu unspezifisch',
|
||||
en: 'TOM too generic',
|
||||
},
|
||||
description: {
|
||||
de: 'Die TOM-Anlage enthält nur allgemeine Aussagen ohne konkrete Maßnahmen.',
|
||||
en: 'The TOM annex contains only general statements without specific measures.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Fordern Sie eine konkretere Beschreibung der Maßnahmen mit Bezug zum spezifischen Verarbeitungskontext an.',
|
||||
en: 'Request a more specific description of measures with reference to the specific processing context.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. c DSGVO',
|
||||
triggerControls: ['VND-TOM-01'],
|
||||
},
|
||||
|
||||
// SUBPROCESSOR
|
||||
{
|
||||
id: 'tpl-subprocessor-no-approval',
|
||||
type: 'GAP',
|
||||
category: 'SUBPROCESSOR',
|
||||
severity: 'CRITICAL',
|
||||
title: {
|
||||
de: 'Keine Genehmigungspflicht für Unterauftragnehmer',
|
||||
en: 'No approval requirement for sub-processors',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag regelt nicht, ob und wie der Einsatz von Unterauftragnehmern zu genehmigen ist.',
|
||||
en: 'The contract does not regulate whether and how the use of sub-processors must be approved.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die entweder eine spezifische oder allgemeine Genehmigung für Unterauftragnehmer vorsieht, einschließlich Informations- und Einspruchsrechten.',
|
||||
en: 'Add a clause providing either specific or general authorization for sub-processors, including information and objection rights.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. d DSGVO',
|
||||
triggerControls: ['VND-SUB-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-subprocessor-no-list',
|
||||
type: 'RISK',
|
||||
category: 'SUBPROCESSOR',
|
||||
severity: 'HIGH',
|
||||
title: {
|
||||
de: 'Keine Liste der Unterauftragnehmer',
|
||||
en: 'No list of sub-processors',
|
||||
},
|
||||
description: {
|
||||
de: 'Es liegt keine aktuelle Liste der eingesetzten Unterauftragnehmer vor.',
|
||||
en: 'There is no current list of sub-processors used.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Fordern Sie eine vollständige Liste aller Unterauftragnehmer mit Name, Sitz und Verarbeitungszweck an.',
|
||||
en: 'Request a complete list of all sub-processors with name, location, and processing purpose.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. d DSGVO',
|
||||
triggerControls: ['VND-SUB-02'],
|
||||
},
|
||||
|
||||
// INCIDENT
|
||||
{
|
||||
id: 'tpl-incident-no-notification',
|
||||
type: 'GAP',
|
||||
category: 'INCIDENT',
|
||||
severity: 'CRITICAL',
|
||||
title: {
|
||||
de: 'Keine Meldepflicht bei Datenpannen',
|
||||
en: 'No notification obligation for data breaches',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Regelung zur Meldung von Datenschutzverletzungen.',
|
||||
en: 'The contract does not contain a provision for reporting data breaches.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die den Auftragsverarbeiter verpflichtet, Datenschutzverletzungen unverzüglich (innerhalb von 24-48h) zu melden.',
|
||||
en: 'Add a clause obligating the processor to report data breaches without undue delay (within 24-48h).',
|
||||
},
|
||||
affectedRequirement: 'Art. 33 Abs. 2 DSGVO',
|
||||
triggerControls: ['VND-INC-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-incident-long-deadline',
|
||||
type: 'RISK',
|
||||
category: 'INCIDENT',
|
||||
severity: 'HIGH',
|
||||
title: {
|
||||
de: 'Zu lange Meldefrist',
|
||||
en: 'Notification deadline too long',
|
||||
},
|
||||
description: {
|
||||
de: 'Die vereinbarte Meldefrist für Datenschutzverletzungen ist zu lang (>72h), um die eigene Meldepflicht einhalten zu können.',
|
||||
en: 'The agreed notification deadline for data breaches is too long (>72h) to meet own notification obligations.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Verkürzen Sie die Meldefrist auf maximal 24-48 Stunden, um ausreichend Zeit für die eigene Meldung an die Aufsichtsbehörde zu haben.',
|
||||
en: 'Reduce the notification deadline to a maximum of 24-48 hours to have sufficient time for own notification to the supervisory authority.',
|
||||
},
|
||||
affectedRequirement: 'Art. 33 DSGVO',
|
||||
triggerControls: ['VND-INC-01'],
|
||||
},
|
||||
|
||||
// AUDIT_RIGHTS
|
||||
{
|
||||
id: 'tpl-audit-no-right',
|
||||
type: 'GAP',
|
||||
category: 'AUDIT_RIGHTS',
|
||||
severity: 'HIGH',
|
||||
title: {
|
||||
de: 'Kein Auditrecht vereinbart',
|
||||
en: 'No audit right agreed',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag enthält kein Recht des Verantwortlichen auf Prüfungen und Inspektionen.',
|
||||
en: 'The contract does not contain a right of the controller to audits and inspections.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie ein ausdrückliches Recht auf Vor-Ort-Inspektionen und die Einsicht in relevante Unterlagen.',
|
||||
en: 'Add an explicit right to on-site inspections and access to relevant documents.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
triggerControls: ['VND-AUD-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-audit-restricted',
|
||||
type: 'RISK',
|
||||
category: 'AUDIT_RIGHTS',
|
||||
severity: 'MEDIUM',
|
||||
title: {
|
||||
de: 'Auditrecht eingeschränkt',
|
||||
en: 'Audit right restricted',
|
||||
},
|
||||
description: {
|
||||
de: 'Das Auditrecht ist durch unangemessene Einschränkungen (z.B. sehr lange Vorlaufzeit, Ausschluss von Vor-Ort-Inspektionen) begrenzt.',
|
||||
en: 'The audit right is limited by unreasonable restrictions (e.g., very long notice period, exclusion of on-site inspections).',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Verhandeln Sie angemessene Bedingungen für Audits (max. 30 Tage Vorlaufzeit, Möglichkeit zur Vor-Ort-Inspektion).',
|
||||
en: 'Negotiate reasonable audit conditions (max. 30 days notice, possibility for on-site inspection).',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. h DSGVO',
|
||||
triggerControls: ['VND-AUD-01'],
|
||||
},
|
||||
|
||||
// DELETION
|
||||
{
|
||||
id: 'tpl-deletion-no-clause',
|
||||
type: 'GAP',
|
||||
category: 'DELETION',
|
||||
severity: 'CRITICAL',
|
||||
title: {
|
||||
de: 'Keine Lösch-/Rückgaberegelung',
|
||||
en: 'No deletion/return clause',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag regelt nicht, was mit den Daten nach Vertragsende geschieht.',
|
||||
en: 'The contract does not regulate what happens to the data after contract termination.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel zur Löschung oder Rückgabe aller personenbezogenen Daten nach Vertragsende (max. 30 Tage).',
|
||||
en: 'Add a clause for deletion or return of all personal data after contract end (max. 30 days).',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. g DSGVO',
|
||||
triggerControls: ['VND-DEL-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-deletion-no-confirmation',
|
||||
type: 'RISK',
|
||||
category: 'DELETION',
|
||||
severity: 'MEDIUM',
|
||||
title: {
|
||||
de: 'Keine Löschbestätigung vorgesehen',
|
||||
en: 'No deletion confirmation provided',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag sieht keine Bestätigung der Löschung durch den Auftragsverarbeiter vor.',
|
||||
en: 'The contract does not provide for confirmation of deletion by the processor.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Pflicht zur schriftlichen Bestätigung der vollständigen Löschung.',
|
||||
en: 'Add an obligation for written confirmation of complete deletion.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. g DSGVO',
|
||||
triggerControls: ['VND-DEL-01'],
|
||||
},
|
||||
|
||||
// TRANSFER
|
||||
{
|
||||
id: 'tpl-transfer-no-basis',
|
||||
type: 'GAP',
|
||||
category: 'TRANSFER',
|
||||
severity: 'CRITICAL',
|
||||
title: {
|
||||
de: 'Drittlandtransfer ohne Rechtsgrundlage',
|
||||
en: 'Third country transfer without legal basis',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag erlaubt oder impliziert Transfers in Drittländer ohne geeignete Garantien.',
|
||||
en: 'The contract allows or implies transfers to third countries without appropriate safeguards.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Vereinbaren Sie geeignete Garantien (SCC, BCR) oder beschränken Sie die Verarbeitung auf EU/EWR.',
|
||||
en: 'Agree on appropriate safeguards (SCC, BCR) or restrict processing to EU/EEA.',
|
||||
},
|
||||
affectedRequirement: 'Art. 44-49 DSGVO',
|
||||
triggerControls: ['VND-TRF-01'],
|
||||
},
|
||||
{
|
||||
id: 'tpl-transfer-old-scc',
|
||||
type: 'RISK',
|
||||
category: 'TRANSFER',
|
||||
severity: 'HIGH',
|
||||
title: {
|
||||
de: 'Veraltete Standardvertragsklauseln',
|
||||
en: 'Outdated Standard Contractual Clauses',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag verwendet die alten SCC (vor 2021), die nicht mehr gültig sind.',
|
||||
en: 'The contract uses old SCC (pre-2021) that are no longer valid.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Aktualisieren Sie auf die SCC 2021 (Durchführungsbeschluss (EU) 2021/914).',
|
||||
en: 'Update to SCC 2021 (Implementing Decision (EU) 2021/914).',
|
||||
},
|
||||
affectedRequirement: 'Art. 46 Abs. 2 lit. c DSGVO',
|
||||
triggerControls: ['VND-TRF-02'],
|
||||
},
|
||||
|
||||
// LIABILITY
|
||||
{
|
||||
id: 'tpl-liability-excessive-cap',
|
||||
type: 'RISK',
|
||||
category: 'LIABILITY',
|
||||
severity: 'MEDIUM',
|
||||
title: {
|
||||
de: 'Unangemessene Haftungsbegrenzung',
|
||||
en: 'Inappropriate liability cap',
|
||||
},
|
||||
description: {
|
||||
de: 'Die Haftungsbegrenzung ist sehr niedrig und könnte bei Datenschutzverletzungen problematisch sein.',
|
||||
en: 'The liability cap is very low and could be problematic in case of data protection violations.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Prüfen Sie, ob die Haftungsbegrenzung angemessen ist. Erwägen Sie eine Ausnahme für Datenschutzverletzungen oder eine höhere Obergrenze.',
|
||||
en: 'Check if the liability cap is appropriate. Consider an exception for data protection violations or a higher limit.',
|
||||
},
|
||||
affectedRequirement: 'Vertragliche Vereinbarung',
|
||||
triggerControls: [],
|
||||
},
|
||||
|
||||
// DATA_SUBJECT_RIGHTS
|
||||
{
|
||||
id: 'tpl-dsr-no-support',
|
||||
type: 'GAP',
|
||||
category: 'DATA_SUBJECT_RIGHTS',
|
||||
severity: 'HIGH',
|
||||
title: {
|
||||
de: 'Keine Unterstützung bei Betroffenenrechten',
|
||||
en: 'No support for data subject rights',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Regelung zur Unterstützung bei der Erfüllung von Betroffenenrechten.',
|
||||
en: 'The contract does not contain a provision for support in fulfilling data subject rights.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel zur Unterstützung bei Auskunft, Berichtigung, Löschung und anderen Betroffenenrechten.',
|
||||
en: 'Add a clause for support with access, rectification, deletion, and other data subject rights.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. e DSGVO',
|
||||
triggerControls: ['VND-DSR-01'],
|
||||
},
|
||||
|
||||
// CONFIDENTIALITY
|
||||
{
|
||||
id: 'tpl-confidentiality-missing',
|
||||
type: 'GAP',
|
||||
category: 'CONFIDENTIALITY',
|
||||
severity: 'HIGH',
|
||||
title: {
|
||||
de: 'Keine Vertraulichkeitsverpflichtung',
|
||||
en: 'No confidentiality obligation',
|
||||
},
|
||||
description: {
|
||||
de: 'Der Vertrag enthält keine Verpflichtung zur Vertraulichkeit der Mitarbeiter.',
|
||||
en: 'The contract does not contain an obligation for employee confidentiality.',
|
||||
},
|
||||
recommendation: {
|
||||
de: 'Ergänzen Sie eine Klausel, die die Verpflichtung der Mitarbeiter zur Vertraulichkeit sicherstellt.',
|
||||
en: 'Add a clause ensuring the obligation of employees to confidentiality.',
|
||||
},
|
||||
affectedRequirement: 'Art. 28 Abs. 3 lit. b DSGVO',
|
||||
triggerControls: ['VND-CON-02'],
|
||||
},
|
||||
]
|
||||
export type { FindingTemplate }
|
||||
export { FINDING_TEMPLATES }
|
||||
|
||||
// ==========================================
|
||||
// HELPER FUNCTIONS
|
||||
|
||||
191
admin-compliance/lib/sdk/vvt-baseline-catalog/hr-finance.ts
Normal file
191
admin-compliance/lib/sdk/vvt-baseline-catalog/hr-finance.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* VVT Baseline-Katalog — HR & Finanzen
|
||||
*/
|
||||
|
||||
import type { BaselineTemplate } from './types'
|
||||
|
||||
export const HR_VVT_TEMPLATES: BaselineTemplate[] = [
|
||||
{
|
||||
templateId: 'hr-mitarbeiterverwaltung',
|
||||
businessFunction: 'hr',
|
||||
name: 'Mitarbeiterverwaltung',
|
||||
description: 'Verwaltung von Stammdaten, Vertraegen und Personalakten der Beschaeftigten',
|
||||
purposes: ['Durchfuehrung des Beschaeftigungsverhaeltnisses', 'Personalverwaltung und -planung'],
|
||||
legalBases: [
|
||||
{ type: 'CONTRACT', description: 'Arbeitsvertrag', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
{ type: 'LEGAL_OBLIGATION', description: 'Arbeitsrechtliche Pflichten', reference: '§ 26 BDSG' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'DOB', 'SOCIAL_SECURITY', 'TAX_ID', 'BANK_ACCOUNT', 'EMPLOYMENT_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'Personalabteilung' },
|
||||
{ type: 'AUTHORITY', name: 'Finanzamt' },
|
||||
{ type: 'AUTHORITY', name: 'Sozialversicherungstraeger' },
|
||||
],
|
||||
retentionPeriod: { duration: 10, durationUnit: 'YEARS', description: '10 Jahre nach Ende des Beschaeftigungsverhaeltnisses', legalBasis: 'HGB § 257, AO § 147', deletionProcedure: 'Sichere Loeschung nach Ablauf' },
|
||||
tomDescription: 'Zugriffskontrolle auf Personalakten, Verschluesselung, Protokollierung',
|
||||
structuredToms: {
|
||||
accessControl: ['RBAC', 'Need-to-know-Prinzip', 'Personalakten nur fuer HR'],
|
||||
confidentiality: ['Verschluesselung personenbezogener Daten', 'Vertraulichkeitsvereinbarungen'],
|
||||
integrity: ['Aenderungsprotokollierung', 'Vier-Augen-Prinzip bei Gehaltsaenderungen'],
|
||||
availability: ['Regelmaessige Backups', 'Redundante Speicherung'],
|
||||
separation: ['Trennung Personal-/Gehaltsdaten'],
|
||||
},
|
||||
typicalSystems: ['HR-Software', 'Gehaltsabrechnung', 'Dokumentenmanagement'],
|
||||
protectionLevel: 'HIGH',
|
||||
dpiaRequired: false,
|
||||
tags: ['hr', 'mitarbeiter', 'personal'],
|
||||
},
|
||||
{
|
||||
templateId: 'hr-gehaltsabrechnung',
|
||||
businessFunction: 'hr',
|
||||
name: 'Gehaltsabrechnung',
|
||||
description: 'Berechnung und Auszahlung von Gehaeltern, Sozialabgaben und Steuern',
|
||||
purposes: ['Lohn- und Gehaltsabrechnung', 'Erfuellung steuer- und sozialversicherungsrechtlicher Pflichten'],
|
||||
legalBases: [
|
||||
{ type: 'CONTRACT', description: 'Arbeitsvertrag', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
{ type: 'LEGAL_OBLIGATION', description: 'Steuer-/Sozialversicherungsrecht', reference: 'Art. 6 Abs. 1 lit. c DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'ADDRESS', 'SOCIAL_SECURITY', 'TAX_ID', 'BANK_ACCOUNT', 'SALARY_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'PROCESSOR', name: 'Lohnbuero / Steuerberater' },
|
||||
{ type: 'AUTHORITY', name: 'Finanzamt' },
|
||||
{ type: 'AUTHORITY', name: 'Krankenkassen' },
|
||||
],
|
||||
retentionPeriod: { duration: 10, durationUnit: 'YEARS', description: '10 Jahre (steuerrechtlich)', legalBasis: 'AO § 147 Abs. 1 Nr. 1', deletionProcedure: 'Automatische Loeschung nach Fristablauf' },
|
||||
tomDescription: 'Strenge Zugriffskontrolle, Verschluesselung, Trennung von Stamm- und Gehaltsdaten',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur Lohnbuchhaltung/Steuerberater', 'MFA'],
|
||||
confidentiality: ['Verschluesselung at-rest und in-transit', 'Vertraulichkeitsklausel'],
|
||||
integrity: ['Revisionssichere Ablage', 'Pruefprotokoll'],
|
||||
availability: ['Monatliche Backups', 'Jahresabschluss-Archiv'],
|
||||
separation: ['Gehaltsdaten getrennt von allgemeinen Personaldaten'],
|
||||
},
|
||||
typicalSystems: ['DATEV', 'Lohnabrechnungssoftware'],
|
||||
protectionLevel: 'HIGH',
|
||||
dpiaRequired: false,
|
||||
tags: ['hr', 'gehalt', 'lohn', 'steuer'],
|
||||
},
|
||||
{
|
||||
templateId: 'hr-bewerbermanagement',
|
||||
businessFunction: 'hr',
|
||||
name: 'Bewerbermanagement',
|
||||
description: 'Entgegennahme, Verwaltung und Bewertung von Bewerbungen',
|
||||
purposes: ['Bearbeitung eingehender Bewerbungen', 'Bewerberauswahl'],
|
||||
legalBases: [
|
||||
{ type: 'CONTRACT', description: 'Vorvertragliche Massnahmen', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'Bewerberauswahl', reference: '§ 26 Abs. 1 BDSG' },
|
||||
],
|
||||
dataSubjectCategories: ['APPLICANTS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'EDUCATION_DATA', 'EMPLOYMENT_DATA', 'PHOTO_VIDEO'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'Personalabteilung' },
|
||||
{ type: 'INTERNAL', name: 'Fachabteilung' },
|
||||
],
|
||||
retentionPeriod: { duration: 6, durationUnit: 'MONTHS', description: '6 Monate nach Absage (AGG-Frist)', legalBasis: 'AGG § 15 Abs. 4', deletionProcedure: 'Automatische Loeschung 6 Monate nach Absage' },
|
||||
tomDescription: 'Zugriffsbeschraenkung auf beteiligte Entscheidungstraeger, verschluesselte Uebertragung',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur HR + Fachabteilung', 'Zeitlich begrenzter Zugriff'],
|
||||
confidentiality: ['TLS fuer Bewerbungsportale', 'Verschluesselter E-Mail-Empfang'],
|
||||
integrity: ['Unveraenderbare Bewerbungseingaenge'],
|
||||
availability: ['Regelmaessige Backups'],
|
||||
separation: ['Getrennte Bewerberdatenbank'],
|
||||
},
|
||||
typicalSystems: ['Bewerbermanagementsystem', 'E-Mail'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['hr', 'bewerbung', 'recruiting'],
|
||||
},
|
||||
{
|
||||
templateId: 'hr-zeiterfassung',
|
||||
businessFunction: 'hr',
|
||||
name: 'Zeiterfassung',
|
||||
description: 'Erfassung von Arbeitszeiten, Urlaub und Fehlzeiten',
|
||||
purposes: ['Arbeitszeiterfassung gemaess ArbZG', 'Urlaubsverwaltung'],
|
||||
legalBases: [
|
||||
{ type: 'LEGAL_OBLIGATION', description: 'Arbeitszeitgesetz', reference: 'Art. 6 Abs. 1 lit. c DSGVO, ArbZG § 16 Abs. 2' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES'],
|
||||
personalDataCategories: ['NAME', 'EMPLOYMENT_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'Personalabteilung' },
|
||||
{ type: 'INTERNAL', name: 'Vorgesetzte' },
|
||||
],
|
||||
retentionPeriod: { duration: 2, durationUnit: 'YEARS', description: '2 Jahre (ArbZG)', legalBasis: 'ArbZG § 16 Abs. 2', deletionProcedure: 'Automatische Loeschung' },
|
||||
tomDescription: 'Zugriffskontrolle nach Abteilung, Protokollierung von Aenderungen',
|
||||
structuredToms: {
|
||||
accessControl: ['Vorgesetzte sehen nur eigene Abteilung', 'HR sieht alle'],
|
||||
confidentiality: ['Krankmeldungen nur HR'],
|
||||
integrity: ['Aenderungshistorie'],
|
||||
availability: ['Taegliches Backup'],
|
||||
separation: ['Trennung Zeitdaten / Gehaltsdaten'],
|
||||
},
|
||||
typicalSystems: ['Zeiterfassungssystem', 'HR-Software'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['hr', 'zeiterfassung', 'arbeitszeit'],
|
||||
},
|
||||
]
|
||||
|
||||
export const FINANCE_VVT_TEMPLATES: BaselineTemplate[] = [
|
||||
{
|
||||
templateId: 'finance-buchhaltung',
|
||||
businessFunction: 'finance',
|
||||
name: 'Buchhaltung & Rechnungswesen',
|
||||
description: 'Finanzbuchhaltung, Rechnungsstellung und Zahlungsverkehr',
|
||||
purposes: ['Finanzbuchhaltung', 'Rechnungsstellung', 'Erfuellung handels-/steuerrechtlicher Aufbewahrungspflichten'],
|
||||
legalBases: [
|
||||
{ type: 'LEGAL_OBLIGATION', description: 'HGB, AO', reference: 'Art. 6 Abs. 1 lit. c DSGVO' },
|
||||
{ type: 'CONTRACT', description: 'Vertragserfuellung', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'SUPPLIERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'BANK_ACCOUNT', 'PAYMENT_DATA', 'CONTRACT_DATA', 'TAX_ID'],
|
||||
recipientCategories: [
|
||||
{ type: 'PROCESSOR', name: 'Steuerberater / Wirtschaftspruefer' },
|
||||
{ type: 'AUTHORITY', name: 'Finanzamt' },
|
||||
],
|
||||
retentionPeriod: { duration: 10, durationUnit: 'YEARS', description: '10 Jahre (HGB) / 6 Jahre (Geschaeftsbriefe)', legalBasis: 'HGB § 257, AO § 147', deletionProcedure: 'Loeschung nach Ablauf der jeweiligen Frist' },
|
||||
tomDescription: 'Zugriffskontrolle nach Vier-Augen-Prinzip, revisionssichere Archivierung',
|
||||
structuredToms: {
|
||||
accessControl: ['Vier-Augen-Prinzip', 'RBAC nach Buchhaltungsrollen'],
|
||||
confidentiality: ['Verschluesselung Finanzdaten'],
|
||||
integrity: ['Revisionssichere Archivierung (GoBD)', 'Aenderungsprotokoll'],
|
||||
availability: ['Redundante Speicherung', 'Jaehrliche Backups'],
|
||||
separation: ['Trennung Debitoren/Kreditoren'],
|
||||
},
|
||||
typicalSystems: ['DATEV', 'ERP-System', 'Buchhaltungssoftware'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['finance', 'buchhaltung', 'rechnungswesen'],
|
||||
},
|
||||
{
|
||||
templateId: 'finance-zahlungsverkehr',
|
||||
businessFunction: 'finance',
|
||||
name: 'Zahlungsverkehr',
|
||||
description: 'Abwicklung von Zahlungen, SEPA-Lastschriften und Ueberweisungen',
|
||||
purposes: ['Zahlungsabwicklung', 'Mahnwesen'],
|
||||
legalBases: [
|
||||
{ type: 'CONTRACT', description: 'Vertragserfuellung', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'SUPPLIERS'],
|
||||
personalDataCategories: ['NAME', 'BANK_ACCOUNT', 'PAYMENT_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'PROCESSOR', name: 'Zahlungsdienstleister' },
|
||||
{ type: 'PROCESSOR', name: 'Kreditinstitut' },
|
||||
],
|
||||
retentionPeriod: { duration: 10, durationUnit: 'YEARS', description: '10 Jahre', legalBasis: 'HGB § 257', deletionProcedure: 'Automatische Loeschung' },
|
||||
tomDescription: 'PCI-DSS-konforme Verarbeitung, Verschluesselung, Zugriffsbeschraenkung',
|
||||
structuredToms: {
|
||||
accessControl: ['Streng limitierter Zugriff', 'MFA'],
|
||||
confidentiality: ['TLS 1.3', 'Tokenisierung von Zahlungsdaten'],
|
||||
integrity: ['Transaktionsprotokoll'],
|
||||
availability: ['Hochverfuegbarer Zahlungsservice'],
|
||||
separation: ['Zahlungsdaten getrennt von CRM'],
|
||||
},
|
||||
typicalSystems: ['Banking-Software', 'Payment Gateway'],
|
||||
protectionLevel: 'HIGH',
|
||||
dpiaRequired: false,
|
||||
tags: ['finance', 'zahlung', 'payment'],
|
||||
},
|
||||
]
|
||||
30
admin-compliance/lib/sdk/vvt-baseline-catalog/types.ts
Normal file
30
admin-compliance/lib/sdk/vvt-baseline-catalog/types.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* VVT Baseline-Katalog — Types
|
||||
*/
|
||||
|
||||
import type { BusinessFunction } from '../vvt-types'
|
||||
|
||||
export interface BaselineTemplate {
|
||||
templateId: string
|
||||
businessFunction: BusinessFunction
|
||||
name: string
|
||||
description: string
|
||||
purposes: string[]
|
||||
legalBases: { type: string; description?: string; reference?: string }[]
|
||||
dataSubjectCategories: string[]
|
||||
personalDataCategories: string[]
|
||||
recipientCategories: { type: string; name: string; description?: string }[]
|
||||
retentionPeriod: { duration?: number; durationUnit?: string; description: string; legalBasis?: string; deletionProcedure?: string }
|
||||
tomDescription: string
|
||||
structuredToms: {
|
||||
accessControl: string[]
|
||||
confidentiality: string[]
|
||||
integrity: string[]
|
||||
availability: string[]
|
||||
separation: string[]
|
||||
}
|
||||
typicalSystems: string[]
|
||||
protectionLevel: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
dpiaRequired: boolean
|
||||
tags: string[]
|
||||
}
|
||||
Reference in New Issue
Block a user