feat(admin-v2): Major SDK/Compliance overhaul and new modules
SDK modules added/enhanced: - compliance-hub, compliance-scope, consent-management, notfallplan - audit-report, workflow, source-policy, dsms - advisory-board documentation section - TOM dashboard components, TOM generator SDM mapping - DSFA: mitigation library, risk catalog, threshold analysis, source attribution - VVT: baseline catalog, profiling engine, types - Loeschfristen: baseline catalog, compliance engine, export, profiling, types - Compliance scope: engine, profiling, golden tests, types Existing SDK pages updated: - dsfa/[id], tom, vvt, loeschfristen, advisory-board — expanded functionality - SDKSidebar, StepHeader — new navigation items and layout - SDK layout, context, types — expanded type system Other admin-v2 changes: - AI agents page, RAG pipeline DSFA integration - GridOverlay component updates - Companion feature (development + education) - Compliance advisor SOUL definition Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1508
admin-v2/lib/sdk/compliance-scope-engine.ts
Normal file
1508
admin-v2/lib/sdk/compliance-scope-engine.ts
Normal file
File diff suppressed because it is too large
Load Diff
722
admin-v2/lib/sdk/compliance-scope-golden-tests.ts
Normal file
722
admin-v2/lib/sdk/compliance-scope-golden-tests.ts
Normal file
@@ -0,0 +1,722 @@
|
||||
import type { ScopeProfilingAnswer, ComplianceDepthLevel, ScopeDocumentType } from './compliance-scope-types'
|
||||
|
||||
export interface GoldenTest {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
answers: ScopeProfilingAnswer[]
|
||||
expectedLevel: ComplianceDepthLevel | null // null for prefill tests
|
||||
expectedMinDocuments?: ScopeDocumentType[]
|
||||
expectedHardTriggerIds?: string[]
|
||||
expectedDsfaRequired?: boolean
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export const GOLDEN_TESTS: GoldenTest[] = [
|
||||
// GT-01: 2-Person Freelancer, nur B2B, DE-Hosting → L1
|
||||
{
|
||||
id: 'GT-01',
|
||||
name: '2-Person Freelancer B2B',
|
||||
description: 'Kleinstes Setup ohne besondere Risiken',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '2' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'consulting' },
|
||||
{ questionId: 'data_health', value: false },
|
||||
{ questionId: 'data_genetic', value: false },
|
||||
{ questionId: 'data_biometric', value: false },
|
||||
{ questionId: 'data_racial_ethnic', value: false },
|
||||
{ questionId: 'data_political_opinion', value: false },
|
||||
{ questionId: 'data_religious', value: false },
|
||||
{ questionId: 'data_union_membership', value: false },
|
||||
{ questionId: 'data_sexual_orientation', value: false },
|
||||
{ questionId: 'data_criminal', value: false },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
{ questionId: 'process_has_dsfa', value: true },
|
||||
{ questionId: 'process_has_incident_plan', value: true },
|
||||
{ questionId: 'data_volume', value: '<1000' },
|
||||
{ questionId: 'org_customer_count', value: '<100' },
|
||||
],
|
||||
expectedLevel: 'L1',
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'COOKIE_BANNER'],
|
||||
expectedHardTriggerIds: [],
|
||||
expectedDsfaRequired: false,
|
||||
tags: ['baseline', 'freelancer', 'b2b'],
|
||||
},
|
||||
|
||||
// GT-02: Solo IT-Berater → L1
|
||||
{
|
||||
id: 'GT-02',
|
||||
name: 'Solo IT-Berater',
|
||||
description: 'Einzelperson, minimale Datenverarbeitung',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '1' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'it_services' },
|
||||
{ questionId: 'data_health', value: false },
|
||||
{ questionId: 'data_genetic', value: false },
|
||||
{ questionId: 'data_biometric', value: false },
|
||||
{ questionId: 'data_volume', value: '<1000' },
|
||||
{ questionId: 'org_customer_count', value: '<50' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L1',
|
||||
expectedHardTriggerIds: [],
|
||||
tags: ['baseline', 'solo', 'minimal'],
|
||||
},
|
||||
|
||||
// GT-03: 5-Person Agentur, Website, kein Tracking → L1
|
||||
{
|
||||
id: 'GT-03',
|
||||
name: '5-Person Agentur ohne Tracking',
|
||||
description: 'Kleine Agentur, einfache Website ohne Analytics',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '5' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'marketing' },
|
||||
{ questionId: 'tech_has_website', value: true },
|
||||
{ questionId: 'tech_has_tracking', value: false },
|
||||
{ questionId: 'data_volume', value: '1000-10000' },
|
||||
{ questionId: 'org_customer_count', value: '100-1000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L1',
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'COOKIE_BANNER'],
|
||||
tags: ['baseline', 'agency', 'simple'],
|
||||
},
|
||||
|
||||
// GT-04: 30-Person SaaS B2B, EU-Cloud → L2 (scale trigger)
|
||||
{
|
||||
id: 'GT-04',
|
||||
name: '30-Person SaaS B2B',
|
||||
description: 'Scale-Trigger durch Mitarbeiterzahl',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '30' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'software' },
|
||||
{ questionId: 'tech_has_cloud', value: true },
|
||||
{ questionId: 'data_volume', value: '10000-100000' },
|
||||
{ questionId: 'org_customer_count', value: '1000-10000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
{ questionId: 'process_has_dsfa', value: false },
|
||||
],
|
||||
expectedLevel: 'L2',
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'AVV', 'COOKIE_BANNER'],
|
||||
tags: ['scale', 'saas', 'growth'],
|
||||
},
|
||||
|
||||
// GT-05: 50-Person Handel B2C, Webshop → L2 (B2C+Webshop)
|
||||
{
|
||||
id: 'GT-05',
|
||||
name: '50-Person E-Commerce B2C',
|
||||
description: 'B2C mit Webshop erhöht Anforderungen',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '50' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'retail' },
|
||||
{ questionId: 'tech_has_webshop', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'org_customer_count', value: '10000-100000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L2',
|
||||
expectedHardTriggerIds: ['HT-H01'],
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'AVV', 'COOKIE_BANNER', 'EINWILLIGUNG'],
|
||||
tags: ['b2c', 'webshop', 'retail'],
|
||||
},
|
||||
|
||||
// GT-06: 80-Person Dienstleister, Cloud → L2 (scale)
|
||||
{
|
||||
id: 'GT-06',
|
||||
name: '80-Person Dienstleister',
|
||||
description: 'Größerer Betrieb mit Cloud-Services',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '80' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'professional_services' },
|
||||
{ questionId: 'tech_has_cloud', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'org_customer_count', value: '1000-10000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L2',
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'AVV'],
|
||||
tags: ['scale', 'services'],
|
||||
},
|
||||
|
||||
// GT-07: 20-Person Startup mit GA4 Tracking → L2 (tracking)
|
||||
{
|
||||
id: 'GT-07',
|
||||
name: 'Startup mit Google Analytics',
|
||||
description: 'Tracking-Tools erhöhen Compliance-Anforderungen',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '20' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'technology' },
|
||||
{ questionId: 'tech_has_website', value: true },
|
||||
{ questionId: 'tech_has_tracking', value: true },
|
||||
{ questionId: 'tech_tracking_tools', value: 'google_analytics' },
|
||||
{ questionId: 'data_volume', value: '10000-100000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L2',
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'COOKIE_BANNER', 'EINWILLIGUNG'],
|
||||
tags: ['tracking', 'analytics', 'startup'],
|
||||
},
|
||||
|
||||
// GT-08: Kita-App (Minderjaehrige) → L3 (HT-B01)
|
||||
{
|
||||
id: 'GT-08',
|
||||
name: 'Kita-App für Eltern',
|
||||
description: 'Datenverarbeitung von Minderjährigen unter 16',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '15' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'education' },
|
||||
{ questionId: 'data_subjects_minors', value: true },
|
||||
{ questionId: 'data_subjects_minors_age', value: '<16' },
|
||||
{ questionId: 'data_volume', value: '1000-10000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-B01'],
|
||||
expectedDsfaRequired: true,
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'DSFA', 'EINWILLIGUNG', 'AVV'],
|
||||
tags: ['hard-trigger', 'minors', 'education'],
|
||||
},
|
||||
|
||||
// GT-09: Krankenhaus-Software → L3 (HT-A01)
|
||||
{
|
||||
id: 'GT-09',
|
||||
name: 'Krankenhaus-Verwaltungssoftware',
|
||||
description: 'Gesundheitsdaten Art. 9 DSGVO',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '200' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'healthcare' },
|
||||
{ questionId: 'data_health', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'org_customer_count', value: '10-50' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-A01'],
|
||||
expectedDsfaRequired: true,
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'DSFA', 'AVV'],
|
||||
tags: ['hard-trigger', 'health', 'art9'],
|
||||
},
|
||||
|
||||
// GT-10: HR-Scoring-Plattform → L3 (HT-C01)
|
||||
{
|
||||
id: 'GT-10',
|
||||
name: 'HR-Scoring für Bewerbungen',
|
||||
description: 'Automatisierte Entscheidungen im HR-Bereich',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '40' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'hr_tech' },
|
||||
{ questionId: 'tech_has_adm', value: true },
|
||||
{ questionId: 'tech_adm_type', value: 'profiling' },
|
||||
{ questionId: 'tech_adm_impact', value: 'employment' },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-C01'],
|
||||
expectedDsfaRequired: true,
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'DSFA', 'AVV'],
|
||||
tags: ['hard-trigger', 'adm', 'profiling'],
|
||||
},
|
||||
|
||||
// GT-11: Fintech Kreditscoring → L3 (HT-H05 + C01)
|
||||
{
|
||||
id: 'GT-11',
|
||||
name: 'Fintech Kreditscoring',
|
||||
description: 'Finanzsektor mit automatisierten Entscheidungen',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '120' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'finance' },
|
||||
{ questionId: 'tech_has_adm', value: true },
|
||||
{ questionId: 'tech_adm_type', value: 'scoring' },
|
||||
{ questionId: 'tech_adm_impact', value: 'credit' },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-H05', 'HT-C01'],
|
||||
expectedDsfaRequired: true,
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'DSFA', 'AVV'],
|
||||
tags: ['hard-trigger', 'finance', 'scoring'],
|
||||
},
|
||||
|
||||
// GT-12: Bildungsplattform Minderjaehrige → L3 (HT-B01)
|
||||
{
|
||||
id: 'GT-12',
|
||||
name: 'Online-Lernplattform für Schüler',
|
||||
description: 'Bildungssektor mit minderjährigen Nutzern',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '35' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'education' },
|
||||
{ questionId: 'data_subjects_minors', value: true },
|
||||
{ questionId: 'data_subjects_minors_age', value: '<16' },
|
||||
{ questionId: 'tech_has_tracking', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-B01'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'education', 'minors'],
|
||||
},
|
||||
|
||||
// GT-13: Datenbroker → L3 (HT-H02)
|
||||
{
|
||||
id: 'GT-13',
|
||||
name: 'Datenbroker / Adresshandel',
|
||||
description: 'Geschäftsmodell basiert auf Datenhandel',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '25' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'data_broker' },
|
||||
{ questionId: 'data_is_core_business', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'org_customer_count', value: '100-1000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-H02'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'data-broker'],
|
||||
},
|
||||
|
||||
// GT-14: Video + ADM → L3 (HT-D05)
|
||||
{
|
||||
id: 'GT-14',
|
||||
name: 'Videoüberwachung mit Gesichtserkennung',
|
||||
description: 'Biometrische Daten mit automatisierter Verarbeitung',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '60' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'security' },
|
||||
{ questionId: 'data_biometric', value: true },
|
||||
{ questionId: 'tech_has_video_surveillance', value: true },
|
||||
{ questionId: 'tech_has_adm', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-D05'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'biometric', 'video'],
|
||||
},
|
||||
|
||||
// GT-15: 500-MA Konzern ohne Zert → L3 (HT-G04)
|
||||
{
|
||||
id: 'GT-15',
|
||||
name: 'Großunternehmen ohne Zertifizierung',
|
||||
description: 'Scale-Trigger durch Unternehmensgröße',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '500' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'manufacturing' },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'org_customer_count', value: '>100000' },
|
||||
{ questionId: 'cert_has_iso27001', value: false },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-G04'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'scale', 'enterprise'],
|
||||
},
|
||||
|
||||
// GT-16: ISO 27001 Anbieter → L4 (HT-F01)
|
||||
{
|
||||
id: 'GT-16',
|
||||
name: 'ISO 27001 zertifizierter Cloud-Provider',
|
||||
description: 'Zertifizierung erfordert höchste Compliance',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '150' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'cloud_services' },
|
||||
{ questionId: 'cert_has_iso27001', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
{ questionId: 'process_has_dsfa', value: true },
|
||||
],
|
||||
expectedLevel: 'L4',
|
||||
expectedHardTriggerIds: ['HT-F01'],
|
||||
expectedMinDocuments: ['VVT', 'TOM', 'DSFA', 'AVV', 'CERT_ISO27001'],
|
||||
tags: ['hard-trigger', 'certification', 'iso'],
|
||||
},
|
||||
|
||||
// GT-17: TISAX Automobilzulieferer → L4 (HT-F04)
|
||||
{
|
||||
id: 'GT-17',
|
||||
name: 'TISAX-zertifizierter Automobilzulieferer',
|
||||
description: 'Automotive-Branche mit TISAX-Anforderungen',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '300' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'automotive' },
|
||||
{ questionId: 'cert_has_tisax', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'org_customer_count', value: '10-50' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L4',
|
||||
expectedHardTriggerIds: ['HT-F04'],
|
||||
tags: ['hard-trigger', 'certification', 'tisax'],
|
||||
},
|
||||
|
||||
// GT-18: ISO 27701 Cloud-Provider → L4 (HT-F02)
|
||||
{
|
||||
id: 'GT-18',
|
||||
name: 'ISO 27701 Privacy-zertifiziert',
|
||||
description: 'Privacy-spezifische Zertifizierung',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '200' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'cloud_services' },
|
||||
{ questionId: 'cert_has_iso27701', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
{ questionId: 'process_has_dsfa', value: true },
|
||||
],
|
||||
expectedLevel: 'L4',
|
||||
expectedHardTriggerIds: ['HT-F02'],
|
||||
tags: ['hard-trigger', 'certification', 'privacy'],
|
||||
},
|
||||
|
||||
// GT-19: Grosskonzern + Art.9 + >1M DS → L4 (HT-G05)
|
||||
{
|
||||
id: 'GT-19',
|
||||
name: 'Konzern mit sensiblen Massendaten',
|
||||
description: 'Kombination aus Scale und Art. 9 Daten',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '2000' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'insurance' },
|
||||
{ questionId: 'data_health', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'org_customer_count', value: '>100000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
],
|
||||
expectedLevel: 'L4',
|
||||
expectedHardTriggerIds: ['HT-G05'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'scale', 'art9'],
|
||||
},
|
||||
|
||||
// GT-20: Nur B2C Webshop → L2 (HT-H01)
|
||||
{
|
||||
id: 'GT-20',
|
||||
name: 'Reiner B2C Webshop',
|
||||
description: 'B2C-Trigger ohne weitere Risiken',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '12' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'retail' },
|
||||
{ questionId: 'tech_has_webshop', value: true },
|
||||
{ questionId: 'data_volume', value: '10000-100000' },
|
||||
{ questionId: 'org_customer_count', value: '1000-10000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L2',
|
||||
expectedHardTriggerIds: ['HT-H01'],
|
||||
tags: ['b2c', 'webshop'],
|
||||
},
|
||||
|
||||
// GT-21: Keine Daten, keine MA → L1
|
||||
{
|
||||
id: 'GT-21',
|
||||
name: 'Minimale Datenverarbeitung',
|
||||
description: 'Absolute Baseline ohne Risiken',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '1' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'consulting' },
|
||||
{ questionId: 'data_volume', value: '<1000' },
|
||||
{ questionId: 'org_customer_count', value: '<50' },
|
||||
{ questionId: 'tech_has_website', value: false },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L1',
|
||||
expectedHardTriggerIds: [],
|
||||
tags: ['baseline', 'minimal'],
|
||||
},
|
||||
|
||||
// GT-22: Alle Art.9 Kategorien → L3 (HT-A09)
|
||||
{
|
||||
id: 'GT-22',
|
||||
name: 'Alle Art. 9 Kategorien',
|
||||
description: 'Multiple sensible Datenkategorien',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '50' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'research' },
|
||||
{ questionId: 'data_health', value: true },
|
||||
{ questionId: 'data_genetic', value: true },
|
||||
{ questionId: 'data_biometric', value: true },
|
||||
{ questionId: 'data_racial_ethnic', value: true },
|
||||
{ questionId: 'data_political_opinion', value: true },
|
||||
{ questionId: 'data_religious', value: true },
|
||||
{ questionId: 'data_union_membership', value: true },
|
||||
{ questionId: 'data_sexual_orientation', value: true },
|
||||
{ questionId: 'data_criminal', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-A09'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'art9', 'multiple-categories'],
|
||||
},
|
||||
|
||||
// GT-23: Drittland + Art.9 → L3 (HT-E04)
|
||||
{
|
||||
id: 'GT-23',
|
||||
name: 'Drittlandtransfer mit Art. 9 Daten',
|
||||
description: 'Kombination aus Drittland und sensiblen Daten',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '45' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'us' },
|
||||
{ questionId: 'org_industry', value: 'healthcare' },
|
||||
{ questionId: 'data_health', value: true },
|
||||
{ questionId: 'tech_has_third_country_transfer', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-E04'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'third-country', 'art9'],
|
||||
},
|
||||
|
||||
// GT-24: Minderjaehrige + Art.9 → L4 (HT-B02)
|
||||
{
|
||||
id: 'GT-24',
|
||||
name: 'Minderjährige mit Gesundheitsdaten',
|
||||
description: 'Kombination aus vulnerabler Gruppe und Art. 9',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '30' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'healthcare' },
|
||||
{ questionId: 'data_subjects_minors', value: true },
|
||||
{ questionId: 'data_subjects_minors_age', value: '<16' },
|
||||
{ questionId: 'data_health', value: true },
|
||||
{ questionId: 'data_volume', value: '10000-100000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L4',
|
||||
expectedHardTriggerIds: ['HT-B02'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'minors', 'health', 'combined-risk'],
|
||||
},
|
||||
|
||||
// GT-25: KI autonome Entscheidungen → L3 (HT-C02)
|
||||
{
|
||||
id: 'GT-25',
|
||||
name: 'KI mit autonomen Entscheidungen',
|
||||
description: 'AI Act relevante autonome Systeme',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '70' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'ai_services' },
|
||||
{ questionId: 'tech_has_adm', value: true },
|
||||
{ questionId: 'tech_adm_type', value: 'autonomous_decision' },
|
||||
{ questionId: 'tech_has_ai', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-C02'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'ai', 'adm'],
|
||||
},
|
||||
|
||||
// GT-26: Multiple Zertifizierungen → L4 (HT-F01-05)
|
||||
{
|
||||
id: 'GT-26',
|
||||
name: 'Multiple Zertifizierungen',
|
||||
description: 'Mehrere Zertifizierungen kombiniert',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '250' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'cloud_services' },
|
||||
{ questionId: 'cert_has_iso27001', value: true },
|
||||
{ questionId: 'cert_has_iso27701', value: true },
|
||||
{ questionId: 'cert_has_soc2', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
{ questionId: 'process_has_dsfa', value: true },
|
||||
],
|
||||
expectedLevel: 'L4',
|
||||
expectedHardTriggerIds: ['HT-F01', 'HT-F02', 'HT-F03'],
|
||||
tags: ['hard-trigger', 'certification', 'multiple'],
|
||||
},
|
||||
|
||||
// GT-27: Oeffentlicher Sektor + Gesundheit → L3 (HT-H07 + A01)
|
||||
{
|
||||
id: 'GT-27',
|
||||
name: 'Öffentlicher Sektor mit Gesundheitsdaten',
|
||||
description: 'Behörde mit Art. 9 Datenverarbeitung',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '120' },
|
||||
{ questionId: 'org_business_model', value: 'b2g' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'public_sector' },
|
||||
{ questionId: 'org_is_public_sector', value: true },
|
||||
{ questionId: 'data_health', value: true },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-H07', 'HT-A01'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'public-sector', 'health'],
|
||||
},
|
||||
|
||||
// GT-28: Bildung + KI + Minderjaehrige → L4 (HT-B03)
|
||||
{
|
||||
id: 'GT-28',
|
||||
name: 'EdTech mit KI für Minderjährige',
|
||||
description: 'Triple-Risiko: Bildung, KI, vulnerable Gruppe',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '55' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'education' },
|
||||
{ questionId: 'data_subjects_minors', value: true },
|
||||
{ questionId: 'data_subjects_minors_age', value: '<16' },
|
||||
{ questionId: 'tech_has_ai', value: true },
|
||||
{ questionId: 'tech_has_adm', value: true },
|
||||
{ questionId: 'data_volume', value: '100000-1000000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L4',
|
||||
expectedHardTriggerIds: ['HT-B03'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'education', 'ai', 'minors', 'triple-risk'],
|
||||
},
|
||||
|
||||
// GT-29: Freelancer mit 1 Art.9 → L3 (hard trigger override despite low score)
|
||||
{
|
||||
id: 'GT-29',
|
||||
name: 'Freelancer mit Gesundheitsdaten',
|
||||
description: 'Hard Trigger überschreibt niedrige Score-Bewertung',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '1' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'de' },
|
||||
{ questionId: 'org_industry', value: 'healthcare' },
|
||||
{ questionId: 'data_health', value: true },
|
||||
{ questionId: 'data_volume', value: '<1000' },
|
||||
{ questionId: 'org_customer_count', value: '<50' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-A01'],
|
||||
expectedDsfaRequired: true,
|
||||
tags: ['hard-trigger', 'override', 'art9', 'freelancer'],
|
||||
},
|
||||
|
||||
// GT-30: Enterprise, alle Prozesse vorhanden → L3 (good process maturity)
|
||||
{
|
||||
id: 'GT-30',
|
||||
name: 'Enterprise mit reifer Prozesslandschaft',
|
||||
description: 'Große Organisation mit allen Compliance-Prozessen',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '450' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
{ questionId: 'org_industry', value: 'manufacturing' },
|
||||
{ questionId: 'data_volume', value: '>1000000' },
|
||||
{ questionId: 'org_customer_count', value: '10000-100000' },
|
||||
{ questionId: 'process_has_vvt', value: true },
|
||||
{ questionId: 'process_has_tom', value: true },
|
||||
{ questionId: 'process_has_dsfa', value: true },
|
||||
{ questionId: 'process_has_incident_plan', value: true },
|
||||
{ questionId: 'process_has_dsb', value: true },
|
||||
{ questionId: 'process_has_training', value: true },
|
||||
],
|
||||
expectedLevel: 'L3',
|
||||
expectedHardTriggerIds: ['HT-G04'],
|
||||
tags: ['enterprise', 'mature', 'all-processes'],
|
||||
},
|
||||
|
||||
// GT-31: SMB, nur 1 Block beantwortet → L1 (graceful degradation)
|
||||
{
|
||||
id: 'GT-31',
|
||||
name: 'Unvollständige Profilerstellung',
|
||||
description: 'Test für graceful degradation bei unvollständigen Antworten',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '8' },
|
||||
{ questionId: 'org_business_model', value: 'b2b' },
|
||||
{ questionId: 'org_industry', value: 'consulting' },
|
||||
// Nur Block 1 (Organization) beantwortet, Rest fehlt
|
||||
],
|
||||
expectedLevel: 'L1',
|
||||
expectedHardTriggerIds: [],
|
||||
tags: ['incomplete', 'degradation', 'edge-case'],
|
||||
},
|
||||
|
||||
// GT-32: CompanyProfile Prefill Konsistenz → null (prefill test, no expected level)
|
||||
{
|
||||
id: 'GT-32',
|
||||
name: 'CompanyProfile Prefill Test',
|
||||
description: 'Prüft ob CompanyProfile-Daten korrekt in ScopeProfile übernommen werden',
|
||||
answers: [
|
||||
{ questionId: 'org_employee_count', value: '25' },
|
||||
{ questionId: 'org_business_model', value: 'b2c' },
|
||||
{ questionId: 'org_industry', value: 'retail' },
|
||||
{ questionId: 'tech_hosting_location', value: 'eu' },
|
||||
// Diese Werte sollten mit CompanyProfile-Prefill übereinstimmen
|
||||
],
|
||||
expectedLevel: null,
|
||||
tags: ['prefill', 'integration', 'consistency'],
|
||||
},
|
||||
]
|
||||
821
admin-v2/lib/sdk/compliance-scope-profiling.ts
Normal file
821
admin-v2/lib/sdk/compliance-scope-profiling.ts
Normal file
@@ -0,0 +1,821 @@
|
||||
import type {
|
||||
ScopeQuestionBlock,
|
||||
ScopeQuestionBlockId,
|
||||
ScopeProfilingQuestion,
|
||||
ScopeProfilingAnswer,
|
||||
ComplianceScopeState,
|
||||
} from './compliance-scope-types'
|
||||
import type { CompanyProfile } from './types'
|
||||
|
||||
/**
|
||||
* Block 1: Organisation & Reife
|
||||
*/
|
||||
const BLOCK_1_ORGANISATION: ScopeQuestionBlock = {
|
||||
id: 'organisation',
|
||||
title: 'Organisation & Reife',
|
||||
description: 'Grundlegende Informationen zu Ihrer Organisation und Compliance-Zielen',
|
||||
order: 1,
|
||||
questions: [
|
||||
{
|
||||
id: 'org_employee_count',
|
||||
type: 'number',
|
||||
label: 'Wie viele Mitarbeiter hat Ihre Organisation?',
|
||||
helpText: 'Geben Sie die Gesamtzahl aller Beschäftigten an (inkl. Teilzeit, Minijobs)',
|
||||
required: true,
|
||||
scoreWeights: { risk: 5, complexity: 8, assurance: 6 },
|
||||
mapsToCompanyProfile: 'employeeCount',
|
||||
},
|
||||
{
|
||||
id: 'org_customer_count',
|
||||
type: 'single',
|
||||
label: 'Wie viele Kunden/Nutzer betreuen Sie?',
|
||||
helpText: 'Schätzen Sie die Anzahl aktiver Kunden oder Nutzer',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: '<100', label: 'Weniger als 100' },
|
||||
{ value: '100-1000', label: '100 bis 1.000' },
|
||||
{ value: '1000-10000', label: '1.000 bis 10.000' },
|
||||
{ value: '10000-100000', label: '10.000 bis 100.000' },
|
||||
{ value: '100000+', label: 'Mehr als 100.000' },
|
||||
],
|
||||
scoreWeights: { risk: 6, complexity: 7, assurance: 6 },
|
||||
},
|
||||
{
|
||||
id: 'org_annual_revenue',
|
||||
type: 'single',
|
||||
label: 'Wie hoch ist Ihr jährlicher Umsatz?',
|
||||
helpText: 'Wählen Sie die zutreffende Umsatzklasse',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: '<2Mio', label: 'Unter 2 Mio. EUR' },
|
||||
{ value: '2-10Mio', label: '2 bis 10 Mio. EUR' },
|
||||
{ value: '10-50Mio', label: '10 bis 50 Mio. EUR' },
|
||||
{ value: '>50Mio', label: 'Über 50 Mio. EUR' },
|
||||
],
|
||||
scoreWeights: { risk: 4, complexity: 6, assurance: 7 },
|
||||
mapsToCompanyProfile: 'annualRevenue',
|
||||
},
|
||||
{
|
||||
id: 'org_cert_target',
|
||||
type: 'multi',
|
||||
label: 'Welche Zertifizierungen streben Sie an oder besitzen Sie bereits?',
|
||||
helpText: 'Mehrfachauswahl möglich. Zertifizierungen erhöhen den Assurance-Bedarf',
|
||||
required: false,
|
||||
options: [
|
||||
{ value: 'ISO27001', label: 'ISO 27001 (Informationssicherheit)' },
|
||||
{ value: 'ISO27701', label: 'ISO 27701 (Datenschutz-Erweiterung)' },
|
||||
{ value: 'TISAX', label: 'TISAX (Automotive)' },
|
||||
{ value: 'SOC2', label: 'SOC 2 (US-Standard)' },
|
||||
{ value: 'BSI-Grundschutz', label: 'BSI IT-Grundschutz' },
|
||||
{ value: 'Keine', label: 'Keine Zertifizierung geplant' },
|
||||
],
|
||||
scoreWeights: { risk: 3, complexity: 5, assurance: 10 },
|
||||
},
|
||||
{
|
||||
id: 'org_industry',
|
||||
type: 'single',
|
||||
label: 'In welcher Branche sind Sie tätig?',
|
||||
helpText: 'Ihre Branche beeinflusst Risikobewertung und regulatorische Anforderungen',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 'it_software', label: 'IT & Software' },
|
||||
{ value: 'healthcare', label: 'Gesundheitswesen' },
|
||||
{ value: 'education', label: 'Bildung & Forschung' },
|
||||
{ value: 'finance', label: 'Finanzdienstleistungen' },
|
||||
{ value: 'retail', label: 'Einzelhandel & E-Commerce' },
|
||||
{ value: 'manufacturing', label: 'Produktion & Fertigung' },
|
||||
{ value: 'consulting', label: 'Beratung & Dienstleistungen' },
|
||||
{ value: 'public', label: 'Öffentliche Verwaltung' },
|
||||
{ value: 'other', label: 'Sonstige' },
|
||||
],
|
||||
scoreWeights: { risk: 7, complexity: 5, assurance: 6 },
|
||||
mapsToCompanyProfile: 'industry',
|
||||
mapsToVVTQuestion: 'org_industry',
|
||||
mapsToLFQuestion: 'org-branche',
|
||||
},
|
||||
{
|
||||
id: 'org_business_model',
|
||||
type: 'single',
|
||||
label: 'Was ist Ihr primäres Geschäftsmodell?',
|
||||
helpText: 'B2C-Modelle haben höhere Datenschutzanforderungen',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 'b2b', label: 'B2B (Business-to-Business)' },
|
||||
{ value: 'b2c', label: 'B2C (Business-to-Consumer)' },
|
||||
{ value: 'both', label: 'B2B und B2C gemischt' },
|
||||
{ value: 'b2g', label: 'B2G (Business-to-Government)' },
|
||||
],
|
||||
scoreWeights: { risk: 6, complexity: 5, assurance: 5 },
|
||||
mapsToCompanyProfile: 'businessModel',
|
||||
mapsToVVTQuestion: 'org_b2b_b2c',
|
||||
mapsToLFQuestion: 'org-geschaeftsmodell',
|
||||
},
|
||||
{
|
||||
id: 'org_has_dsb',
|
||||
type: 'boolean',
|
||||
label: 'Haben Sie einen Datenschutzbeauftragten bestellt?',
|
||||
helpText: 'Ein DSB ist bei mehr als 20 Personen mit regelmäßiger Datenverarbeitung Pflicht',
|
||||
required: true,
|
||||
scoreWeights: { risk: 5, complexity: 3, assurance: 6 },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Block 2: Daten & Betroffene
|
||||
*/
|
||||
const BLOCK_2_DATA: ScopeQuestionBlock = {
|
||||
id: 'data',
|
||||
title: 'Daten & Betroffene',
|
||||
description: 'Art und Umfang der verarbeiteten personenbezogenen Daten',
|
||||
order: 2,
|
||||
questions: [
|
||||
{
|
||||
id: 'data_minors',
|
||||
type: 'boolean',
|
||||
label: 'Verarbeiten Sie Daten von Minderjährigen?',
|
||||
helpText: 'Besondere Schutzpflichten für unter 16-Jährige (bzw. 13-Jährige bei Online-Diensten)',
|
||||
required: true,
|
||||
scoreWeights: { risk: 10, complexity: 5, assurance: 7 },
|
||||
mapsToVVTQuestion: 'data_minors',
|
||||
},
|
||||
{
|
||||
id: 'data_art9',
|
||||
type: 'multi',
|
||||
label: 'Verarbeiten Sie besondere Kategorien personenbezogener Daten (Art. 9 DSGVO)?',
|
||||
helpText: 'Diese Daten unterliegen erhöhten Schutzanforderungen',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 'gesundheit', label: 'Gesundheitsdaten' },
|
||||
{ value: 'biometrie', label: 'Biometrische Daten (z.B. Fingerabdruck, Gesichtserkennung)' },
|
||||
{ value: 'genetik', label: 'Genetische Daten' },
|
||||
{ value: 'politisch', label: 'Politische Meinungen' },
|
||||
{ value: 'religion', label: 'Religiöse/weltanschauliche Überzeugungen' },
|
||||
{ value: 'gewerkschaft', label: 'Gewerkschaftszugehörigkeit' },
|
||||
{ value: 'sexualleben', label: 'Sexualleben/sexuelle Orientierung' },
|
||||
{ value: 'strafrechtlich', label: 'Strafrechtliche Verurteilungen/Straftaten' },
|
||||
{ value: 'ethnisch', label: 'Ethnische Herkunft' },
|
||||
],
|
||||
scoreWeights: { risk: 10, complexity: 8, assurance: 9 },
|
||||
mapsToVVTQuestion: 'data_health',
|
||||
},
|
||||
{
|
||||
id: 'data_hr',
|
||||
type: 'boolean',
|
||||
label: 'Verarbeiten Sie Personaldaten (HR)?',
|
||||
helpText: 'Bewerberdaten, Gehälter, Leistungsbeurteilungen etc.',
|
||||
required: true,
|
||||
scoreWeights: { risk: 6, complexity: 4, assurance: 5 },
|
||||
mapsToVVTQuestion: 'dept_hr',
|
||||
mapsToLFQuestion: 'data-hr',
|
||||
},
|
||||
{
|
||||
id: 'data_communication',
|
||||
type: 'boolean',
|
||||
label: 'Verarbeiten Sie Kommunikationsdaten (E-Mail, Chat, Telefonie)?',
|
||||
helpText: 'Inhalte oder Metadaten von Kommunikationsvorgängen',
|
||||
required: true,
|
||||
scoreWeights: { risk: 7, complexity: 5, assurance: 6 },
|
||||
},
|
||||
{
|
||||
id: 'data_financial',
|
||||
type: 'boolean',
|
||||
label: 'Verarbeiten Sie Finanzdaten (Konten, Zahlungen)?',
|
||||
helpText: 'Bankdaten, Kreditkartendaten, Buchhaltungsdaten',
|
||||
required: true,
|
||||
scoreWeights: { risk: 8, complexity: 6, assurance: 7 },
|
||||
mapsToVVTQuestion: 'dept_finance',
|
||||
mapsToLFQuestion: 'data-buchhaltung',
|
||||
},
|
||||
{
|
||||
id: 'data_volume',
|
||||
type: 'single',
|
||||
label: 'Wie viele Personendatensätze verarbeiten Sie insgesamt?',
|
||||
helpText: 'Schätzen Sie die Gesamtzahl betroffener Personen',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: '<1000', label: 'Unter 1.000' },
|
||||
{ value: '1000-10000', label: '1.000 bis 10.000' },
|
||||
{ value: '10000-100000', label: '10.000 bis 100.000' },
|
||||
{ value: '100000-1000000', label: '100.000 bis 1 Mio.' },
|
||||
{ value: '>1000000', label: 'Über 1 Mio.' },
|
||||
],
|
||||
scoreWeights: { risk: 7, complexity: 6, assurance: 6 },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Block 3: Verarbeitung & Zweck
|
||||
*/
|
||||
const BLOCK_3_PROCESSING: ScopeQuestionBlock = {
|
||||
id: 'processing',
|
||||
title: 'Verarbeitung & Zweck',
|
||||
description: 'Wie und wofür werden personenbezogene Daten verarbeitet?',
|
||||
order: 3,
|
||||
questions: [
|
||||
{
|
||||
id: 'proc_tracking',
|
||||
type: 'boolean',
|
||||
label: 'Setzen Sie Tracking oder Profiling ein?',
|
||||
helpText: 'Web-Analytics, Werbe-Tracking, Nutzungsprofile etc.',
|
||||
required: true,
|
||||
scoreWeights: { risk: 7, complexity: 6, assurance: 6 },
|
||||
},
|
||||
{
|
||||
id: 'proc_adm_scoring',
|
||||
type: 'boolean',
|
||||
label: 'Treffen Sie automatisierte Entscheidungen (Art. 22 DSGVO)?',
|
||||
helpText: 'Scoring, Bonitätsprüfung, automatische Ablehnung ohne menschliche Beteiligung',
|
||||
required: true,
|
||||
scoreWeights: { risk: 9, complexity: 8, assurance: 8 },
|
||||
},
|
||||
{
|
||||
id: 'proc_ai_usage',
|
||||
type: 'multi',
|
||||
label: 'Setzen Sie KI-Systeme ein?',
|
||||
helpText: 'KI-Einsatz kann zusätzliche Anforderungen (EU AI Act) auslösen',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 'keine', label: 'Keine KI im Einsatz' },
|
||||
{ value: 'chatbot', label: 'Chatbots/Virtuelle Assistenten' },
|
||||
{ value: 'scoring', label: 'Scoring/Risikobewertung' },
|
||||
{ value: 'profiling', label: 'Profiling/Verhaltensvorhersage' },
|
||||
{ value: 'generativ', label: 'Generative KI (Text, Bild, Code)' },
|
||||
{ value: 'autonom', label: 'Autonome Systeme/Entscheidungen' },
|
||||
],
|
||||
scoreWeights: { risk: 8, complexity: 9, assurance: 7 },
|
||||
},
|
||||
{
|
||||
id: 'proc_data_combination',
|
||||
type: 'boolean',
|
||||
label: 'Führen Sie Daten aus verschiedenen Quellen zusammen?',
|
||||
helpText: 'Data Matching, Anreicherung aus externen Quellen',
|
||||
required: true,
|
||||
scoreWeights: { risk: 7, complexity: 7, assurance: 6 },
|
||||
},
|
||||
{
|
||||
id: 'proc_employee_monitoring',
|
||||
type: 'boolean',
|
||||
label: 'Überwachen Sie Mitarbeiter (Zeiterfassung, Standort, IT-Nutzung)?',
|
||||
helpText: 'Beschäftigtendatenschutz nach § 26 BDSG',
|
||||
required: true,
|
||||
scoreWeights: { risk: 8, complexity: 6, assurance: 7 },
|
||||
},
|
||||
{
|
||||
id: 'proc_video_surveillance',
|
||||
type: 'boolean',
|
||||
label: 'Setzen Sie Videoüberwachung ein?',
|
||||
helpText: 'Kameras in Büros, Produktionsstätten, Verkaufsräumen etc.',
|
||||
required: true,
|
||||
scoreWeights: { risk: 8, complexity: 5, assurance: 7 },
|
||||
mapsToVVTQuestion: 'special_video_surveillance',
|
||||
mapsToLFQuestion: 'data-video',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Block 4: Technik/Hosting/Transfers
|
||||
*/
|
||||
const BLOCK_4_TECH: ScopeQuestionBlock = {
|
||||
id: 'tech',
|
||||
title: 'Technik, Hosting & Transfers',
|
||||
description: 'Technische Infrastruktur und Datenübermittlung',
|
||||
order: 4,
|
||||
questions: [
|
||||
{
|
||||
id: 'tech_hosting_location',
|
||||
type: 'single',
|
||||
label: 'Wo werden Ihre Daten primär gehostet?',
|
||||
helpText: 'Standort bestimmt anwendbares Datenschutzrecht',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 'de', label: 'Deutschland' },
|
||||
{ value: 'eu', label: 'EU (ohne Deutschland)' },
|
||||
{ value: 'ewr', label: 'EWR (z.B. Norwegen, Island)' },
|
||||
{ value: 'us_adequacy', label: 'USA (mit Angemessenheitsbeschluss/DPF)' },
|
||||
{ value: 'drittland', label: 'Drittland ohne Angemessenheitsbeschluss' },
|
||||
],
|
||||
scoreWeights: { risk: 7, complexity: 6, assurance: 7 },
|
||||
},
|
||||
{
|
||||
id: 'tech_subprocessors',
|
||||
type: 'boolean',
|
||||
label: 'Nutzen Sie Auftragsverarbeiter (externe Dienstleister)?',
|
||||
helpText: 'Cloud-Anbieter, Hosting, E-Mail-Service, CRM etc. – erfordert AVV nach Art. 28 DSGVO',
|
||||
required: true,
|
||||
scoreWeights: { risk: 6, complexity: 7, assurance: 7 },
|
||||
},
|
||||
{
|
||||
id: 'tech_third_country',
|
||||
type: 'boolean',
|
||||
label: 'Übermitteln Sie Daten in Drittländer?',
|
||||
helpText: 'Transfer außerhalb EU/EWR erfordert Schutzmaßnahmen (SCC, BCR etc.)',
|
||||
required: true,
|
||||
scoreWeights: { risk: 9, complexity: 8, assurance: 8 },
|
||||
mapsToVVTQuestion: 'transfer_cloud_us',
|
||||
},
|
||||
{
|
||||
id: 'tech_encryption_rest',
|
||||
type: 'boolean',
|
||||
label: 'Sind Daten im Ruhezustand verschlüsselt (at rest)?',
|
||||
helpText: 'Datenbank-, Dateisystem- oder Volume-Verschlüsselung',
|
||||
required: true,
|
||||
scoreWeights: { risk: -5, complexity: 3, assurance: 7 },
|
||||
},
|
||||
{
|
||||
id: 'tech_encryption_transit',
|
||||
type: 'boolean',
|
||||
label: 'Sind Daten bei Übertragung verschlüsselt (in transit)?',
|
||||
helpText: 'TLS/SSL für alle Verbindungen',
|
||||
required: true,
|
||||
scoreWeights: { risk: -5, complexity: 2, assurance: 7 },
|
||||
},
|
||||
{
|
||||
id: 'tech_cloud_providers',
|
||||
type: 'multi',
|
||||
label: 'Welche Cloud-Anbieter nutzen Sie?',
|
||||
helpText: 'Mehrfachauswahl möglich',
|
||||
required: false,
|
||||
options: [
|
||||
{ value: 'aws', label: 'Amazon Web Services (AWS)' },
|
||||
{ value: 'azure', label: 'Microsoft Azure' },
|
||||
{ value: 'gcp', label: 'Google Cloud Platform (GCP)' },
|
||||
{ value: 'hetzner', label: 'Hetzner' },
|
||||
{ value: 'ionos', label: 'IONOS' },
|
||||
{ value: 'ovh', label: 'OVH' },
|
||||
{ value: 'andere', label: 'Andere Anbieter' },
|
||||
{ value: 'keine', label: 'Keine Cloud-Nutzung (On-Premise)' },
|
||||
],
|
||||
scoreWeights: { risk: 5, complexity: 6, assurance: 6 },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Block 5: Rechte & Prozesse
|
||||
*/
|
||||
const BLOCK_5_PROCESSES: ScopeQuestionBlock = {
|
||||
id: 'processes',
|
||||
title: 'Rechte & Prozesse',
|
||||
description: 'Etablierte Datenschutz- und Sicherheitsprozesse',
|
||||
order: 5,
|
||||
questions: [
|
||||
{
|
||||
id: 'proc_dsar_process',
|
||||
type: 'boolean',
|
||||
label: 'Haben Sie einen Prozess für Betroffenenrechte (DSAR)?',
|
||||
helpText: 'Auskunft, Löschung, Berichtigung, Widerspruch etc. – Art. 15-22 DSGVO',
|
||||
required: true,
|
||||
scoreWeights: { risk: 6, complexity: 5, assurance: 8 },
|
||||
},
|
||||
{
|
||||
id: 'proc_deletion_concept',
|
||||
type: 'boolean',
|
||||
label: 'Haben Sie ein Löschkonzept?',
|
||||
helpText: 'Definierte Löschfristen und automatisierte Löschroutinen',
|
||||
required: true,
|
||||
scoreWeights: { risk: 7, complexity: 6, assurance: 8 },
|
||||
},
|
||||
{
|
||||
id: 'proc_incident_response',
|
||||
type: 'boolean',
|
||||
label: 'Haben Sie einen Notfallplan für Datenschutzvorfälle?',
|
||||
helpText: 'Incident Response Plan, 72h-Meldepflicht an Aufsichtsbehörde (Art. 33 DSGVO)',
|
||||
required: true,
|
||||
scoreWeights: { risk: 8, complexity: 6, assurance: 9 },
|
||||
},
|
||||
{
|
||||
id: 'proc_regular_audits',
|
||||
type: 'boolean',
|
||||
label: 'Führen Sie regelmäßige Datenschutz-Audits durch?',
|
||||
helpText: 'Interne oder externe Prüfungen mindestens jährlich',
|
||||
required: true,
|
||||
scoreWeights: { risk: 5, complexity: 4, assurance: 9 },
|
||||
},
|
||||
{
|
||||
id: 'proc_training',
|
||||
type: 'boolean',
|
||||
label: 'Schulen Sie Ihre Mitarbeiter im Datenschutz?',
|
||||
helpText: 'Awareness-Trainings, Onboarding, jährliche Auffrischung',
|
||||
required: true,
|
||||
scoreWeights: { risk: 6, complexity: 3, assurance: 7 },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Block 6: Produktkontext
|
||||
*/
|
||||
const BLOCK_6_PRODUCT: ScopeQuestionBlock = {
|
||||
id: 'product',
|
||||
title: 'Produktkontext',
|
||||
description: 'Spezifische Merkmale Ihrer Produkte und Services',
|
||||
order: 6,
|
||||
questions: [
|
||||
{
|
||||
id: 'prod_type',
|
||||
type: 'multi',
|
||||
label: 'Welche Art von Produkten/Services bieten Sie an?',
|
||||
helpText: 'Mehrfachauswahl möglich',
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 'webapp', label: 'Web-Anwendung' },
|
||||
{ value: 'mobile', label: 'Mobile App (iOS/Android)' },
|
||||
{ value: 'saas', label: 'SaaS-Plattform' },
|
||||
{ value: 'onpremise', label: 'On-Premise Software' },
|
||||
{ value: 'api', label: 'API/Schnittstellen' },
|
||||
{ value: 'iot', label: 'IoT/Hardware' },
|
||||
{ value: 'beratung', label: 'Beratungsleistungen' },
|
||||
{ value: 'handel', label: 'Handel/Vertrieb' },
|
||||
],
|
||||
scoreWeights: { risk: 5, complexity: 6, assurance: 5 },
|
||||
},
|
||||
{
|
||||
id: 'prod_cookies_consent',
|
||||
type: 'boolean',
|
||||
label: 'Benötigen Sie Cookie-Consent (Tracking-Cookies)?',
|
||||
helpText: 'Nicht-essenzielle Cookies erfordern opt-in Einwilligung',
|
||||
required: true,
|
||||
scoreWeights: { risk: 5, complexity: 4, assurance: 6 },
|
||||
},
|
||||
{
|
||||
id: 'prod_webshop',
|
||||
type: 'boolean',
|
||||
label: 'Betreiben Sie einen Online-Shop?',
|
||||
helpText: 'E-Commerce mit Zahlungsabwicklung, Bestellverwaltung',
|
||||
required: true,
|
||||
scoreWeights: { risk: 7, complexity: 6, assurance: 6 },
|
||||
},
|
||||
{
|
||||
id: 'prod_api_external',
|
||||
type: 'boolean',
|
||||
label: 'Bieten Sie externe APIs an (Daten-Weitergabe an Dritte)?',
|
||||
helpText: 'Programmierschnittstellen für Partner, Entwickler etc.',
|
||||
required: true,
|
||||
scoreWeights: { risk: 7, complexity: 7, assurance: 7 },
|
||||
},
|
||||
{
|
||||
id: 'prod_data_broker',
|
||||
type: 'boolean',
|
||||
label: 'Handeln Sie mit Daten (Data Brokerage, Adresshandel)?',
|
||||
helpText: 'Verkauf oder Vermittlung personenbezogener Daten',
|
||||
required: true,
|
||||
scoreWeights: { risk: 10, complexity: 8, assurance: 9 },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* All question blocks in order
|
||||
*/
|
||||
export const SCOPE_QUESTION_BLOCKS: ScopeQuestionBlock[] = [
|
||||
BLOCK_1_ORGANISATION,
|
||||
BLOCK_2_DATA,
|
||||
BLOCK_3_PROCESSING,
|
||||
BLOCK_4_TECH,
|
||||
BLOCK_5_PROCESSES,
|
||||
BLOCK_6_PRODUCT,
|
||||
]
|
||||
|
||||
/**
|
||||
* Prefill scope answers from CompanyProfile
|
||||
*/
|
||||
export function prefillFromCompanyProfile(
|
||||
profile: CompanyProfile
|
||||
): ScopeProfilingAnswer[] {
|
||||
const answers: ScopeProfilingAnswer[] = []
|
||||
|
||||
// employeeCount
|
||||
if (profile.employeeCount != null) {
|
||||
answers.push({
|
||||
questionId: 'org_employee_count',
|
||||
value: profile.employeeCount,
|
||||
})
|
||||
}
|
||||
|
||||
// annualRevenue
|
||||
if (profile.annualRevenue) {
|
||||
answers.push({
|
||||
questionId: 'org_annual_revenue',
|
||||
value: profile.annualRevenue,
|
||||
})
|
||||
}
|
||||
|
||||
// industry
|
||||
if (profile.industry) {
|
||||
answers.push({
|
||||
questionId: 'org_industry',
|
||||
value: profile.industry,
|
||||
})
|
||||
}
|
||||
|
||||
// businessModel
|
||||
if (profile.businessModel) {
|
||||
answers.push({
|
||||
questionId: 'org_business_model',
|
||||
value: profile.businessModel,
|
||||
})
|
||||
}
|
||||
|
||||
// dpoName -> org_has_dsb
|
||||
if (profile.dpoName && profile.dpoName.trim() !== '') {
|
||||
answers.push({
|
||||
questionId: 'org_has_dsb',
|
||||
value: true,
|
||||
})
|
||||
}
|
||||
|
||||
// usesAI -> proc_ai_usage
|
||||
if (profile.usesAI === true) {
|
||||
// We don't know which specific AI type, so just mark as "generativ" as a default
|
||||
answers.push({
|
||||
questionId: 'proc_ai_usage',
|
||||
value: ['generativ'],
|
||||
})
|
||||
} else if (profile.usesAI === false) {
|
||||
answers.push({
|
||||
questionId: 'proc_ai_usage',
|
||||
value: ['keine'],
|
||||
})
|
||||
}
|
||||
|
||||
// offerings -> prod_type mapping
|
||||
if (profile.offerings && profile.offerings.length > 0) {
|
||||
const prodTypes: string[] = []
|
||||
const offeringsLower = profile.offerings.map((o) => o.toLowerCase())
|
||||
|
||||
if (offeringsLower.some((o) => o.includes('webapp') || o.includes('web'))) {
|
||||
prodTypes.push('webapp')
|
||||
}
|
||||
if (
|
||||
offeringsLower.some((o) => o.includes('mobile') || o.includes('app'))
|
||||
) {
|
||||
prodTypes.push('mobile')
|
||||
}
|
||||
if (offeringsLower.some((o) => o.includes('saas') || o.includes('cloud'))) {
|
||||
prodTypes.push('saas')
|
||||
}
|
||||
if (
|
||||
offeringsLower.some(
|
||||
(o) => o.includes('onpremise') || o.includes('on-premise')
|
||||
)
|
||||
) {
|
||||
prodTypes.push('onpremise')
|
||||
}
|
||||
if (offeringsLower.some((o) => o.includes('api'))) {
|
||||
prodTypes.push('api')
|
||||
}
|
||||
if (offeringsLower.some((o) => o.includes('iot') || o.includes('hardware'))) {
|
||||
prodTypes.push('iot')
|
||||
}
|
||||
if (
|
||||
offeringsLower.some(
|
||||
(o) => o.includes('beratung') || o.includes('consulting')
|
||||
)
|
||||
) {
|
||||
prodTypes.push('beratung')
|
||||
}
|
||||
if (
|
||||
offeringsLower.some(
|
||||
(o) => o.includes('handel') || o.includes('shop') || o.includes('commerce')
|
||||
)
|
||||
) {
|
||||
prodTypes.push('handel')
|
||||
}
|
||||
|
||||
if (prodTypes.length > 0) {
|
||||
answers.push({
|
||||
questionId: 'prod_type',
|
||||
value: prodTypes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return answers
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefill scope answers from VVT profiling answers
|
||||
*/
|
||||
export function prefillFromVVTAnswers(
|
||||
vvtAnswers: Record<string, unknown>
|
||||
): ScopeProfilingAnswer[] {
|
||||
const answers: ScopeProfilingAnswer[] = []
|
||||
|
||||
// Build reverse mapping: VVT question -> Scope question
|
||||
const reverseMap: Record<string, string> = {}
|
||||
for (const block of SCOPE_QUESTION_BLOCKS) {
|
||||
for (const q of block.questions) {
|
||||
if (q.mapsToVVTQuestion) {
|
||||
reverseMap[q.mapsToVVTQuestion] = q.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map VVT answers to scope answers
|
||||
for (const [vvtQuestionId, vvtValue] of Object.entries(vvtAnswers)) {
|
||||
const scopeQuestionId = reverseMap[vvtQuestionId]
|
||||
if (scopeQuestionId) {
|
||||
answers.push({
|
||||
questionId: scopeQuestionId,
|
||||
value: vvtValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return answers
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefill scope answers from Loeschfristen profiling answers
|
||||
*/
|
||||
export function prefillFromLoeschfristenAnswers(
|
||||
lfAnswers: Array<{ questionId: string; value: unknown }>
|
||||
): ScopeProfilingAnswer[] {
|
||||
const answers: ScopeProfilingAnswer[] = []
|
||||
|
||||
// Build reverse mapping: LF question -> Scope question
|
||||
const reverseMap: Record<string, string> = {}
|
||||
for (const block of SCOPE_QUESTION_BLOCKS) {
|
||||
for (const q of block.questions) {
|
||||
if (q.mapsToLFQuestion) {
|
||||
reverseMap[q.mapsToLFQuestion] = q.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map LF answers to scope answers
|
||||
for (const lfAnswer of lfAnswers) {
|
||||
const scopeQuestionId = reverseMap[lfAnswer.questionId]
|
||||
if (scopeQuestionId) {
|
||||
answers.push({
|
||||
questionId: scopeQuestionId,
|
||||
value: lfAnswer.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return answers
|
||||
}
|
||||
|
||||
/**
|
||||
* Export scope answers in VVT format
|
||||
*/
|
||||
export function exportToVVTAnswers(
|
||||
scopeAnswers: ScopeProfilingAnswer[]
|
||||
): Record<string, unknown> {
|
||||
const vvtAnswers: Record<string, unknown> = {}
|
||||
|
||||
for (const answer of scopeAnswers) {
|
||||
// Find the question
|
||||
let question: ScopeProfilingQuestion | undefined
|
||||
for (const block of SCOPE_QUESTION_BLOCKS) {
|
||||
question = block.questions.find((q) => q.id === answer.questionId)
|
||||
if (question) break
|
||||
}
|
||||
|
||||
if (question?.mapsToVVTQuestion) {
|
||||
vvtAnswers[question.mapsToVVTQuestion] = answer.value
|
||||
}
|
||||
}
|
||||
|
||||
return vvtAnswers
|
||||
}
|
||||
|
||||
/**
|
||||
* Export scope answers in Loeschfristen format
|
||||
*/
|
||||
export function exportToLoeschfristenAnswers(
|
||||
scopeAnswers: ScopeProfilingAnswer[]
|
||||
): Array<{ questionId: string; value: unknown }> {
|
||||
const lfAnswers: Array<{ questionId: string; value: unknown }> = []
|
||||
|
||||
for (const answer of scopeAnswers) {
|
||||
// Find the question
|
||||
let question: ScopeProfilingQuestion | undefined
|
||||
for (const block of SCOPE_QUESTION_BLOCKS) {
|
||||
question = block.questions.find((q) => q.id === answer.questionId)
|
||||
if (question) break
|
||||
}
|
||||
|
||||
if (question?.mapsToLFQuestion) {
|
||||
lfAnswers.push({
|
||||
questionId: question.mapsToLFQuestion,
|
||||
value: answer.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return lfAnswers
|
||||
}
|
||||
|
||||
/**
|
||||
* Export scope answers for TOM generator
|
||||
*/
|
||||
export function exportToTOMProfile(
|
||||
scopeAnswers: ScopeProfilingAnswer[]
|
||||
): Record<string, unknown> {
|
||||
const tomProfile: Record<string, unknown> = {}
|
||||
|
||||
// Get answer values
|
||||
const getVal = (qId: string) => getAnswerValue(scopeAnswers, qId)
|
||||
|
||||
// Map relevant scope answers to TOM profile fields
|
||||
tomProfile.industry = getVal('org_industry')
|
||||
tomProfile.employeeCount = getVal('org_employee_count')
|
||||
tomProfile.hasDataMinors = getVal('data_minors')
|
||||
tomProfile.hasSpecialCategories = Array.isArray(getVal('data_art9'))
|
||||
? (getVal('data_art9') as string[]).length > 0
|
||||
: false
|
||||
tomProfile.hasAutomatedDecisions = getVal('proc_adm_scoring')
|
||||
tomProfile.usesAI = Array.isArray(getVal('proc_ai_usage'))
|
||||
? !(getVal('proc_ai_usage') as string[]).includes('keine')
|
||||
: false
|
||||
tomProfile.hasThirdCountryTransfer = getVal('tech_third_country')
|
||||
tomProfile.hasEncryptionRest = getVal('tech_encryption_rest')
|
||||
tomProfile.hasEncryptionTransit = getVal('tech_encryption_transit')
|
||||
tomProfile.hasIncidentResponse = getVal('proc_incident_response')
|
||||
tomProfile.hasDeletionConcept = getVal('proc_deletion_concept')
|
||||
tomProfile.hasRegularAudits = getVal('proc_regular_audits')
|
||||
tomProfile.hasTraining = getVal('proc_training')
|
||||
|
||||
return tomProfile
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a block is complete (all required questions answered)
|
||||
*/
|
||||
export function isBlockComplete(
|
||||
answers: ScopeProfilingAnswer[],
|
||||
blockId: ScopeQuestionBlockId
|
||||
): boolean {
|
||||
const block = SCOPE_QUESTION_BLOCKS.find((b) => b.id === blockId)
|
||||
if (!block) return false
|
||||
|
||||
const requiredQuestions = block.questions.filter((q) => q.required)
|
||||
const answeredQuestionIds = new Set(answers.map((a) => a.questionId))
|
||||
|
||||
return requiredQuestions.every((q) => answeredQuestionIds.has(q.id))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get progress for a specific block (0-100)
|
||||
*/
|
||||
export function getBlockProgress(
|
||||
answers: ScopeProfilingAnswer[],
|
||||
blockId: ScopeQuestionBlockId
|
||||
): number {
|
||||
const block = SCOPE_QUESTION_BLOCKS.find((b) => b.id === blockId)
|
||||
if (!block) return 0
|
||||
|
||||
const requiredQuestions = block.questions.filter((q) => q.required)
|
||||
if (requiredQuestions.length === 0) return 100
|
||||
|
||||
const answeredQuestionIds = new Set(answers.map((a) => a.questionId))
|
||||
const answeredCount = requiredQuestions.filter((q) =>
|
||||
answeredQuestionIds.has(q.id)
|
||||
).length
|
||||
|
||||
return Math.round((answeredCount / requiredQuestions.length) * 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total progress across all blocks (0-100)
|
||||
*/
|
||||
export function getTotalProgress(answers: ScopeProfilingAnswer[]): number {
|
||||
let totalRequired = 0
|
||||
let totalAnswered = 0
|
||||
|
||||
const answeredQuestionIds = new Set(answers.map((a) => a.questionId))
|
||||
|
||||
for (const block of SCOPE_QUESTION_BLOCKS) {
|
||||
const requiredQuestions = block.questions.filter((q) => q.required)
|
||||
totalRequired += requiredQuestions.length
|
||||
totalAnswered += requiredQuestions.filter((q) =>
|
||||
answeredQuestionIds.has(q.id)
|
||||
).length
|
||||
}
|
||||
|
||||
if (totalRequired === 0) return 100
|
||||
return Math.round((totalAnswered / totalRequired) * 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get answer value for a specific question
|
||||
*/
|
||||
export function getAnswerValue(
|
||||
answers: ScopeProfilingAnswer[],
|
||||
questionId: string
|
||||
): unknown {
|
||||
const answer = answers.find((a) => a.questionId === questionId)
|
||||
return answer?.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all questions as a flat array
|
||||
*/
|
||||
export function getAllQuestions(): ScopeProfilingQuestion[] {
|
||||
return SCOPE_QUESTION_BLOCKS.flatMap((block) => block.questions)
|
||||
}
|
||||
1355
admin-v2/lib/sdk/compliance-scope-types.ts
Normal file
1355
admin-v2/lib/sdk/compliance-scope-types.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -62,6 +62,9 @@ const initialState: SDKState = {
|
||||
// Company Profile
|
||||
companyProfile: null,
|
||||
|
||||
// Compliance Scope
|
||||
complianceScope: null,
|
||||
|
||||
// Progress
|
||||
currentPhase: 1,
|
||||
currentStep: 'company-profile',
|
||||
@@ -179,6 +182,16 @@ function sdkReducer(state: SDKState, action: ExtendedSDKAction): SDKState {
|
||||
: null,
|
||||
})
|
||||
|
||||
case 'SET_COMPLIANCE_SCOPE':
|
||||
return updateState({ complianceScope: action.payload })
|
||||
|
||||
case 'UPDATE_COMPLIANCE_SCOPE':
|
||||
return updateState({
|
||||
complianceScope: state.complianceScope
|
||||
? { ...state.complianceScope, ...action.payload }
|
||||
: null,
|
||||
})
|
||||
|
||||
case 'ADD_IMPORTED_DOCUMENT':
|
||||
return updateState({
|
||||
importedDocuments: [...state.importedDocuments, action.payload],
|
||||
@@ -448,6 +461,10 @@ interface SDKContextValue {
|
||||
setCompanyProfile: (profile: CompanyProfile) => void
|
||||
updateCompanyProfile: (updates: Partial<CompanyProfile>) => void
|
||||
|
||||
// Compliance Scope
|
||||
setComplianceScope: (scope: import('./compliance-scope-types').ComplianceScopeState) => void
|
||||
updateComplianceScope: (updates: Partial<import('./compliance-scope-types').ComplianceScopeState>) => void
|
||||
|
||||
// Import (for existing customers)
|
||||
addImportedDocument: (doc: ImportedDocument) => void
|
||||
setGapAnalysis: (analysis: GapAnalysis) => void
|
||||
@@ -740,6 +757,15 @@ export function SDKProvider({
|
||||
dispatch({ type: 'UPDATE_COMPANY_PROFILE', payload: updates })
|
||||
}, [])
|
||||
|
||||
// Compliance Scope
|
||||
const setComplianceScope = useCallback((scope: import('./compliance-scope-types').ComplianceScopeState) => {
|
||||
dispatch({ type: 'SET_COMPLIANCE_SCOPE', payload: scope })
|
||||
}, [])
|
||||
|
||||
const updateComplianceScope = useCallback((updates: Partial<import('./compliance-scope-types').ComplianceScopeState>) => {
|
||||
dispatch({ type: 'UPDATE_COMPLIANCE_SCOPE', payload: updates })
|
||||
}, [])
|
||||
|
||||
// Import Document
|
||||
const addImportedDocument = useCallback((doc: ImportedDocument) => {
|
||||
dispatch({ type: 'ADD_IMPORTED_DOCUMENT', payload: doc })
|
||||
@@ -1040,6 +1066,8 @@ export function SDKProvider({
|
||||
setCustomerType,
|
||||
setCompanyProfile,
|
||||
updateCompanyProfile,
|
||||
setComplianceScope,
|
||||
updateComplianceScope,
|
||||
addImportedDocument,
|
||||
setGapAnalysis,
|
||||
validateCheckpoint,
|
||||
|
||||
@@ -6,3 +6,5 @@
|
||||
|
||||
export * from './types'
|
||||
export * from './api'
|
||||
export * from './risk-catalog'
|
||||
export * from './mitigation-library'
|
||||
|
||||
694
admin-v2/lib/sdk/dsfa/mitigation-library.ts
Normal file
694
admin-v2/lib/sdk/dsfa/mitigation-library.ts
Normal file
@@ -0,0 +1,694 @@
|
||||
/**
|
||||
* DSFA Massnahmenbibliothek - Vordefinierte Massnahmen
|
||||
*
|
||||
* ~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
|
||||
*/
|
||||
|
||||
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',
|
||||
}
|
||||
615
admin-v2/lib/sdk/dsfa/risk-catalog.ts
Normal file
615
admin-v2/lib/sdk/dsfa/risk-catalog.ts
Normal file
@@ -0,0 +1,615 @@
|
||||
/**
|
||||
* DSFA Risikokatalog - Vordefinierte Risikoszenarien
|
||||
*
|
||||
* ~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
|
||||
*/
|
||||
|
||||
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',
|
||||
}
|
||||
@@ -5,6 +5,57 @@
|
||||
* aligned with the backend Go models.
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// SDM GEWAEHRLEISTUNGSZIELE (Standard-Datenschutzmodell V2.0)
|
||||
// =============================================================================
|
||||
|
||||
export type SDMGoal =
|
||||
| 'datenminimierung'
|
||||
| 'verfuegbarkeit'
|
||||
| 'integritaet'
|
||||
| 'vertraulichkeit'
|
||||
| 'nichtverkettung'
|
||||
| 'transparenz'
|
||||
| 'intervenierbarkeit'
|
||||
|
||||
export const SDM_GOALS: Record<SDMGoal, { name: string; description: string; article: string }> = {
|
||||
datenminimierung: {
|
||||
name: 'Datenminimierung',
|
||||
description: 'Verarbeitung personenbezogener Daten auf das dem Zweck angemessene, erhebliche und notwendige Mass beschraenken.',
|
||||
article: 'Art. 5 Abs. 1 lit. c DSGVO',
|
||||
},
|
||||
verfuegbarkeit: {
|
||||
name: 'Verfuegbarkeit',
|
||||
description: 'Personenbezogene Daten muessen dem Verantwortlichen zur Verfuegung stehen und ordnungsgemaess im vorgesehenen Prozess verwendet werden koennen.',
|
||||
article: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
},
|
||||
integritaet: {
|
||||
name: 'Integritaet',
|
||||
description: 'Personenbezogene Daten bleiben waehrend der Verarbeitung unversehrt, vollstaendig und aktuell.',
|
||||
article: 'Art. 5 Abs. 1 lit. d DSGVO',
|
||||
},
|
||||
vertraulichkeit: {
|
||||
name: 'Vertraulichkeit',
|
||||
description: 'Kein unbefugter Zugriff auf personenbezogene Daten. Nur befugte Personen koennen auf Daten zugreifen.',
|
||||
article: 'Art. 32 Abs. 1 lit. b DSGVO',
|
||||
},
|
||||
nichtverkettung: {
|
||||
name: 'Nichtverkettung',
|
||||
description: 'Personenbezogene Daten duerfen nicht ohne Weiteres fuer einen anderen als den erhobenen Zweck zusammengefuehrt werden (Zweckbindung).',
|
||||
article: 'Art. 5 Abs. 1 lit. b DSGVO',
|
||||
},
|
||||
transparenz: {
|
||||
name: 'Transparenz',
|
||||
description: 'Die Verarbeitung personenbezogener Daten muss fuer Betroffene und Aufsichtsbehoerden nachvollziehbar sein.',
|
||||
article: 'Art. 5 Abs. 1 lit. a DSGVO',
|
||||
},
|
||||
intervenierbarkeit: {
|
||||
name: 'Intervenierbarkeit',
|
||||
description: 'Den Betroffenen werden wirksame Moeglichkeiten der Einflussnahme (Auskunft, Berichtigung, Loeschung, Widerspruch) auf die Verarbeitung gewaehrt.',
|
||||
article: 'Art. 15-21 DSGVO',
|
||||
},
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ENUMS & CONSTANTS
|
||||
// =============================================================================
|
||||
|
||||
@@ -55,6 +55,25 @@ export type {
|
||||
GenerateResponse,
|
||||
} from './sdk-client'
|
||||
|
||||
// Compliance Scope Engine
|
||||
export type {
|
||||
ComplianceDepthLevel,
|
||||
ComplianceScores,
|
||||
ComplianceScopeState,
|
||||
ScopeDecision,
|
||||
ScopeProfilingAnswer,
|
||||
ScopeDocumentType,
|
||||
} from './compliance-scope-types'
|
||||
export {
|
||||
DEPTH_LEVEL_LABELS,
|
||||
DEPTH_LEVEL_DESCRIPTIONS,
|
||||
DEPTH_LEVEL_COLORS,
|
||||
DOCUMENT_TYPE_LABELS,
|
||||
STORAGE_KEY as SCOPE_STORAGE_KEY,
|
||||
createEmptyScopeState,
|
||||
} from './compliance-scope-types'
|
||||
export { complianceScopeEngine } from './compliance-scope-engine'
|
||||
|
||||
// Demo Data Seeding (stored via API like real customer data)
|
||||
export {
|
||||
generateDemoState,
|
||||
|
||||
578
admin-v2/lib/sdk/loeschfristen-baseline-catalog.ts
Normal file
578
admin-v2/lib/sdk/loeschfristen-baseline-catalog.ts
Normal file
@@ -0,0 +1,578 @@
|
||||
/**
|
||||
* Loeschfristen Baseline-Katalog
|
||||
*
|
||||
* 18 vordefinierte Aufbewahrungsfristen-Templates fuer gaengige
|
||||
* Datenobjekte in deutschen Unternehmen. Basierend auf AO, HGB,
|
||||
* UStG, BGB, ArbZG, AGG, BDSG und BSIG.
|
||||
*
|
||||
* Werden genutzt, um neue Loeschfrist-Policies schnell aus
|
||||
* bewaehrten Vorlagen zu erstellen.
|
||||
*/
|
||||
|
||||
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 (18 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'],
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// 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()
|
||||
}
|
||||
325
admin-v2/lib/sdk/loeschfristen-compliance.ts
Normal file
325
admin-v2/lib/sdk/loeschfristen-compliance.ts
Normal file
@@ -0,0 +1,325 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Module - Compliance Check Engine
|
||||
// Prueft Policies auf Vollstaendigkeit, Konsistenz und DSGVO-Konformitaet
|
||||
// =============================================================================
|
||||
|
||||
import {
|
||||
LoeschfristPolicy,
|
||||
PolicyStatus,
|
||||
isPolicyOverdue,
|
||||
getActiveLegalHolds,
|
||||
} from './loeschfristen-types'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export type ComplianceIssueType =
|
||||
| 'MISSING_TRIGGER'
|
||||
| 'MISSING_LEGAL_BASIS'
|
||||
| 'OVERDUE_REVIEW'
|
||||
| 'NO_RESPONSIBLE'
|
||||
| 'LEGAL_HOLD_CONFLICT'
|
||||
| 'STALE_DRAFT'
|
||||
| 'UNCOVERED_VVT_CATEGORY'
|
||||
|
||||
export type ComplianceIssueSeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
|
||||
|
||||
export interface ComplianceIssue {
|
||||
id: string
|
||||
policyId: string
|
||||
policyName: string
|
||||
type: ComplianceIssueType
|
||||
severity: ComplianceIssueSeverity
|
||||
title: string
|
||||
description: string
|
||||
recommendation: string
|
||||
}
|
||||
|
||||
export interface ComplianceCheckResult {
|
||||
issues: ComplianceIssue[]
|
||||
score: number // 0-100
|
||||
stats: {
|
||||
total: number
|
||||
passed: number
|
||||
failed: number
|
||||
bySeverity: Record<ComplianceIssueSeverity, number>
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPERS
|
||||
// =============================================================================
|
||||
|
||||
let issueCounter = 0
|
||||
|
||||
function createIssueId(): string {
|
||||
issueCounter++
|
||||
return `CI-${Date.now()}-${String(issueCounter).padStart(4, '0')}`
|
||||
}
|
||||
|
||||
function createIssue(
|
||||
policy: LoeschfristPolicy,
|
||||
type: ComplianceIssueType,
|
||||
severity: ComplianceIssueSeverity,
|
||||
title: string,
|
||||
description: string,
|
||||
recommendation: string
|
||||
): ComplianceIssue {
|
||||
return {
|
||||
id: createIssueId(),
|
||||
policyId: policy.policyId,
|
||||
policyName: policy.dataObjectName || policy.policyId,
|
||||
type,
|
||||
severity,
|
||||
title,
|
||||
description,
|
||||
recommendation,
|
||||
}
|
||||
}
|
||||
|
||||
function daysBetween(dateStr: string, now: Date): number {
|
||||
const date = new Date(dateStr)
|
||||
const diffMs = now.getTime() - date.getTime()
|
||||
return Math.floor(diffMs / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// INDIVIDUAL CHECKS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Check 1: MISSING_TRIGGER (HIGH)
|
||||
* Policy has no deletionTrigger set, or trigger is PURPOSE_END but no startEvent defined.
|
||||
*/
|
||||
function checkMissingTrigger(policy: LoeschfristPolicy): ComplianceIssue | null {
|
||||
if (!policy.deletionTrigger) {
|
||||
return createIssue(
|
||||
policy,
|
||||
'MISSING_TRIGGER',
|
||||
'HIGH',
|
||||
'Kein Loeschtrigger definiert',
|
||||
`Die Policy "${policy.dataObjectName}" hat keinen Loeschtrigger gesetzt. Ohne Trigger ist unklar, wann die Daten geloescht werden.`,
|
||||
'Definieren Sie einen Loeschtrigger (Zweckende, Aufbewahrungspflicht oder Legal Hold) fuer diese Policy.'
|
||||
)
|
||||
}
|
||||
|
||||
if (policy.deletionTrigger === 'PURPOSE_END' && !policy.startEvent.trim()) {
|
||||
return createIssue(
|
||||
policy,
|
||||
'MISSING_TRIGGER',
|
||||
'HIGH',
|
||||
'Zweckende ohne Startereignis',
|
||||
`Die Policy "${policy.dataObjectName}" nutzt "Zweckende" als Trigger, hat aber kein Startereignis definiert. Ohne Startereignis laesst sich der Loeschzeitpunkt nicht berechnen.`,
|
||||
'Definieren Sie ein konkretes Startereignis (z.B. "Vertragsende", "Abmeldung", "Projektabschluss").'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 2: MISSING_LEGAL_BASIS (HIGH)
|
||||
* Policy with RETENTION_DRIVER trigger but no retentionDriver set.
|
||||
*/
|
||||
function checkMissingLegalBasis(policy: LoeschfristPolicy): ComplianceIssue | null {
|
||||
if (policy.deletionTrigger === 'RETENTION_DRIVER' && !policy.retentionDriver) {
|
||||
return createIssue(
|
||||
policy,
|
||||
'MISSING_LEGAL_BASIS',
|
||||
'HIGH',
|
||||
'Aufbewahrungspflicht ohne Rechtsgrundlage',
|
||||
`Die Policy "${policy.dataObjectName}" hat "Aufbewahrungspflicht" als Trigger, aber keinen konkreten Aufbewahrungstreiber (z.B. AO 147, HGB 257) zugeordnet.`,
|
||||
'Waehlen Sie den passenden gesetzlichen Aufbewahrungstreiber aus oder wechseln Sie den Trigger-Typ.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 3: OVERDUE_REVIEW (MEDIUM)
|
||||
* Policy where nextReviewDate is in the past.
|
||||
*/
|
||||
function checkOverdueReview(policy: LoeschfristPolicy): ComplianceIssue | null {
|
||||
if (isPolicyOverdue(policy)) {
|
||||
const overdueDays = daysBetween(policy.nextReviewDate, new Date())
|
||||
return createIssue(
|
||||
policy,
|
||||
'OVERDUE_REVIEW',
|
||||
'MEDIUM',
|
||||
'Ueberfaellige Pruefung',
|
||||
`Die Policy "${policy.dataObjectName}" haette am ${new Date(policy.nextReviewDate).toLocaleDateString('de-DE')} geprueft werden muessen. Die Pruefung ist ${overdueDays} Tag(e) ueberfaellig.`,
|
||||
'Fuehren Sie umgehend eine Pruefung dieser Policy durch und aktualisieren Sie das naechste Pruefungsdatum.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 4: NO_RESPONSIBLE (MEDIUM)
|
||||
* Policy with no responsiblePerson AND no responsibleRole.
|
||||
*/
|
||||
function checkNoResponsible(policy: LoeschfristPolicy): ComplianceIssue | null {
|
||||
if (!policy.responsiblePerson.trim() && !policy.responsibleRole.trim()) {
|
||||
return createIssue(
|
||||
policy,
|
||||
'NO_RESPONSIBLE',
|
||||
'MEDIUM',
|
||||
'Keine verantwortliche Person/Rolle',
|
||||
`Die Policy "${policy.dataObjectName}" hat weder eine verantwortliche Person noch eine verantwortliche Rolle zugewiesen. Ohne Verantwortlichkeit kann die Loeschung nicht zuverlaessig durchgefuehrt werden.`,
|
||||
'Weisen Sie eine verantwortliche Person oder zumindest eine verantwortliche Rolle (z.B. "Datenschutzbeauftragter", "IT-Leitung") zu.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 5: LEGAL_HOLD_CONFLICT (CRITICAL)
|
||||
* Policy has active legal hold but deletionMethod is AUTO_DELETE.
|
||||
*/
|
||||
function checkLegalHoldConflict(policy: LoeschfristPolicy): ComplianceIssue | null {
|
||||
const activeHolds = getActiveLegalHolds(policy)
|
||||
if (activeHolds.length > 0 && policy.deletionMethod === 'AUTO_DELETE') {
|
||||
const holdReasons = activeHolds.map((h) => h.reason).join(', ')
|
||||
return createIssue(
|
||||
policy,
|
||||
'LEGAL_HOLD_CONFLICT',
|
||||
'CRITICAL',
|
||||
'Legal Hold mit automatischer Loeschung',
|
||||
`Die Policy "${policy.dataObjectName}" hat ${activeHolds.length} aktive(n) Legal Hold(s) (${holdReasons}), aber die Loeschmethode ist auf "Automatische Loeschung" gesetzt. Dies kann zu unbeabsichtigter Vernichtung von Beweismitteln fuehren.`,
|
||||
'Aendern Sie die Loeschmethode auf "Manuelle Pruefung & Loeschung" oder deaktivieren Sie die automatische Loeschung, solange der Legal Hold aktiv ist.'
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 6: STALE_DRAFT (LOW)
|
||||
* Policy in DRAFT status older than 90 days.
|
||||
*/
|
||||
function checkStaleDraft(policy: LoeschfristPolicy): ComplianceIssue | null {
|
||||
if (policy.status === 'DRAFT') {
|
||||
const ageInDays = daysBetween(policy.createdAt, new Date())
|
||||
if (ageInDays > 90) {
|
||||
return createIssue(
|
||||
policy,
|
||||
'STALE_DRAFT',
|
||||
'LOW',
|
||||
'Veralteter Entwurf',
|
||||
`Die Policy "${policy.dataObjectName}" ist seit ${ageInDays} Tagen im Entwurfsstatus. Entwuerfe, die laenger als 90 Tage nicht finalisiert werden, deuten auf unvollstaendige Dokumentation hin.`,
|
||||
'Finalisieren Sie den Entwurf und setzen Sie den Status auf "Aktiv", oder archivieren Sie die Policy, falls sie nicht mehr benoetigt wird.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MAIN COMPLIANCE CHECK
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Fuehrt einen vollstaendigen Compliance-Check ueber alle Policies durch.
|
||||
*
|
||||
* @param policies - Alle Loeschfrist-Policies
|
||||
* @param vvtDataCategories - Optionale Datenkategorien aus dem VVT (localStorage)
|
||||
* @returns ComplianceCheckResult mit Issues, Score und Statistiken
|
||||
*/
|
||||
export function runComplianceCheck(
|
||||
policies: LoeschfristPolicy[],
|
||||
vvtDataCategories?: string[]
|
||||
): ComplianceCheckResult {
|
||||
// Reset counter for deterministic IDs within a single check run
|
||||
issueCounter = 0
|
||||
|
||||
const issues: ComplianceIssue[] = []
|
||||
|
||||
// Run checks 1-6 for each policy
|
||||
for (const policy of policies) {
|
||||
const checks = [
|
||||
checkMissingTrigger(policy),
|
||||
checkMissingLegalBasis(policy),
|
||||
checkOverdueReview(policy),
|
||||
checkNoResponsible(policy),
|
||||
checkLegalHoldConflict(policy),
|
||||
checkStaleDraft(policy),
|
||||
]
|
||||
|
||||
for (const issue of checks) {
|
||||
if (issue !== null) {
|
||||
issues.push(issue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check 7: UNCOVERED_VVT_CATEGORY (MEDIUM)
|
||||
if (vvtDataCategories && vvtDataCategories.length > 0) {
|
||||
const coveredCategories = new Set<string>()
|
||||
for (const policy of policies) {
|
||||
for (const category of policy.dataCategories) {
|
||||
coveredCategories.add(category.toLowerCase().trim())
|
||||
}
|
||||
}
|
||||
|
||||
for (const vvtCategory of vvtDataCategories) {
|
||||
const normalized = vvtCategory.toLowerCase().trim()
|
||||
if (!coveredCategories.has(normalized)) {
|
||||
issues.push({
|
||||
id: createIssueId(),
|
||||
policyId: '-',
|
||||
policyName: '-',
|
||||
type: 'UNCOVERED_VVT_CATEGORY',
|
||||
severity: 'MEDIUM',
|
||||
title: `Datenkategorie ohne Loeschfrist: "${vvtCategory}"`,
|
||||
description: `Die Datenkategorie "${vvtCategory}" ist im Verzeichnis der Verarbeitungstaetigkeiten (VVT) erfasst, hat aber keine zugehoerige Loeschfrist-Policy. Gemaess DSGVO Art. 5 Abs. 1 lit. e muss fuer jede Datenkategorie eine Speicherbegrenzung definiert sein.`,
|
||||
recommendation: `Erstellen Sie eine neue Loeschfrist-Policy fuer die Datenkategorie "${vvtCategory}" oder ordnen Sie sie einer bestehenden Policy zu.`,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate score
|
||||
const bySeverity: Record<ComplianceIssueSeverity, number> = {
|
||||
LOW: 0,
|
||||
MEDIUM: 0,
|
||||
HIGH: 0,
|
||||
CRITICAL: 0,
|
||||
}
|
||||
|
||||
for (const issue of issues) {
|
||||
bySeverity[issue.severity]++
|
||||
}
|
||||
|
||||
const rawScore =
|
||||
100 -
|
||||
(bySeverity.CRITICAL * 15 +
|
||||
bySeverity.HIGH * 10 +
|
||||
bySeverity.MEDIUM * 5 +
|
||||
bySeverity.LOW * 2)
|
||||
|
||||
const score = Math.max(0, rawScore)
|
||||
|
||||
// Calculate pass/fail per policy
|
||||
const failedPolicyIds = new Set(
|
||||
issues.filter((i) => i.policyId !== '-').map((i) => i.policyId)
|
||||
)
|
||||
const totalPolicies = policies.length
|
||||
const failedCount = failedPolicyIds.size
|
||||
const passedCount = totalPolicies - failedCount
|
||||
|
||||
return {
|
||||
issues,
|
||||
score,
|
||||
stats: {
|
||||
total: totalPolicies,
|
||||
passed: passedCount,
|
||||
failed: failedCount,
|
||||
bySeverity,
|
||||
},
|
||||
}
|
||||
}
|
||||
353
admin-v2/lib/sdk/loeschfristen-export.ts
Normal file
353
admin-v2/lib/sdk/loeschfristen-export.ts
Normal file
@@ -0,0 +1,353 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Module - Export & Report Generation
|
||||
// JSON, CSV, Markdown-Compliance-Report und Browser-Download
|
||||
// =============================================================================
|
||||
|
||||
import {
|
||||
LoeschfristPolicy,
|
||||
RETENTION_DRIVER_META,
|
||||
DELETION_METHOD_LABELS,
|
||||
STATUS_LABELS,
|
||||
TRIGGER_LABELS,
|
||||
formatRetentionDuration,
|
||||
getEffectiveDeletionTrigger,
|
||||
} from './loeschfristen-types'
|
||||
|
||||
import {
|
||||
runComplianceCheck,
|
||||
ComplianceCheckResult,
|
||||
ComplianceIssueSeverity,
|
||||
} from './loeschfristen-compliance'
|
||||
|
||||
// =============================================================================
|
||||
// JSON EXPORT
|
||||
// =============================================================================
|
||||
|
||||
interface PolicyExportEnvelope {
|
||||
exportDate: string
|
||||
version: string
|
||||
totalPolicies: number
|
||||
policies: LoeschfristPolicy[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Exportiert alle Policies als pretty-printed JSON.
|
||||
* Enthaelt Metadaten (Exportdatum, Version, Anzahl).
|
||||
*/
|
||||
export function exportPoliciesAsJSON(policies: LoeschfristPolicy[]): string {
|
||||
const exportData: PolicyExportEnvelope = {
|
||||
exportDate: new Date().toISOString(),
|
||||
version: '1.0',
|
||||
totalPolicies: policies.length,
|
||||
policies: policies,
|
||||
}
|
||||
return JSON.stringify(exportData, null, 2)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CSV EXPORT
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Escapes a CSV field value according to RFC 4180.
|
||||
* Fields containing commas, double quotes, or newlines are wrapped in quotes.
|
||||
* Existing double quotes are doubled.
|
||||
*/
|
||||
function escapeCSVField(value: string): string {
|
||||
if (
|
||||
value.includes(',') ||
|
||||
value.includes('"') ||
|
||||
value.includes('\n') ||
|
||||
value.includes('\r') ||
|
||||
value.includes(';')
|
||||
) {
|
||||
return `"${value.replace(/"/g, '""')}"`
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a date string to German locale format (DD.MM.YYYY).
|
||||
* Returns empty string for null/undefined/empty values.
|
||||
*/
|
||||
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 ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exportiert alle Policies als CSV mit BOM fuer Excel-Kompatibilitaet.
|
||||
* Trennzeichen ist Semikolon (;) fuer deutschsprachige Excel-Versionen.
|
||||
*/
|
||||
export function exportPoliciesAsCSV(policies: LoeschfristPolicy[]): string {
|
||||
const BOM = '\uFEFF'
|
||||
const SEPARATOR = ';'
|
||||
|
||||
const headers = [
|
||||
'LF-Nr.',
|
||||
'Datenobjekt',
|
||||
'Beschreibung',
|
||||
'Loeschtrigger',
|
||||
'Aufbewahrungstreiber',
|
||||
'Frist',
|
||||
'Startereignis',
|
||||
'Loeschmethode',
|
||||
'Verantwortlich',
|
||||
'Status',
|
||||
'Legal Hold aktiv',
|
||||
'Letzte Pruefung',
|
||||
'Naechste Pruefung',
|
||||
]
|
||||
|
||||
const rows: string[] = []
|
||||
|
||||
// Header row
|
||||
rows.push(headers.map(escapeCSVField).join(SEPARATOR))
|
||||
|
||||
// Data rows
|
||||
for (const policy of policies) {
|
||||
const effectiveTrigger = getEffectiveDeletionTrigger(policy)
|
||||
const triggerLabel = TRIGGER_LABELS[effectiveTrigger]
|
||||
|
||||
const driverLabel = policy.retentionDriver
|
||||
? RETENTION_DRIVER_META[policy.retentionDriver].label
|
||||
: ''
|
||||
|
||||
const durationLabel = formatRetentionDuration(
|
||||
policy.retentionDuration,
|
||||
policy.retentionUnit
|
||||
)
|
||||
|
||||
const methodLabel = DELETION_METHOD_LABELS[policy.deletionMethod]
|
||||
const statusLabel = STATUS_LABELS[policy.status]
|
||||
|
||||
// Combine responsiblePerson and responsibleRole
|
||||
const responsible = [policy.responsiblePerson, policy.responsibleRole]
|
||||
.filter((s) => s.trim())
|
||||
.join(' / ')
|
||||
|
||||
const legalHoldActive = policy.hasActiveLegalHold ? 'Ja' : 'Nein'
|
||||
|
||||
const row = [
|
||||
policy.policyId,
|
||||
policy.dataObjectName,
|
||||
policy.description,
|
||||
triggerLabel,
|
||||
driverLabel,
|
||||
durationLabel,
|
||||
policy.startEvent,
|
||||
methodLabel,
|
||||
responsible || '-',
|
||||
statusLabel,
|
||||
legalHoldActive,
|
||||
formatDateDE(policy.lastReviewDate),
|
||||
formatDateDE(policy.nextReviewDate),
|
||||
]
|
||||
|
||||
rows.push(row.map(escapeCSVField).join(SEPARATOR))
|
||||
}
|
||||
|
||||
return BOM + rows.join('\r\n')
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// COMPLIANCE SUMMARY (MARKDOWN)
|
||||
// =============================================================================
|
||||
|
||||
const SEVERITY_LABELS: Record<ComplianceIssueSeverity, string> = {
|
||||
CRITICAL: 'Kritisch',
|
||||
HIGH: 'Hoch',
|
||||
MEDIUM: 'Mittel',
|
||||
LOW: 'Niedrig',
|
||||
}
|
||||
|
||||
const SEVERITY_EMOJI: Record<ComplianceIssueSeverity, string> = {
|
||||
CRITICAL: '[!!!]',
|
||||
HIGH: '[!!]',
|
||||
MEDIUM: '[!]',
|
||||
LOW: '[i]',
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a textual rating based on the compliance score.
|
||||
*/
|
||||
function getScoreRating(score: number): string {
|
||||
if (score >= 90) return 'Ausgezeichnet'
|
||||
if (score >= 75) return 'Gut'
|
||||
if (score >= 50) return 'Verbesserungswuerdig'
|
||||
if (score >= 25) return 'Mangelhaft'
|
||||
return 'Kritisch'
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert einen Markdown-formatierten Compliance-Bericht.
|
||||
* Enthaelt: Uebersicht, Score, Issue-Liste, Empfehlungen.
|
||||
*/
|
||||
export function generateComplianceSummary(
|
||||
policies: LoeschfristPolicy[],
|
||||
vvtDataCategories?: string[]
|
||||
): string {
|
||||
const result: ComplianceCheckResult = runComplianceCheck(policies, vvtDataCategories)
|
||||
const now = new Date()
|
||||
|
||||
const lines: string[] = []
|
||||
|
||||
// Header
|
||||
lines.push('# Compliance-Bericht: Loeschfristen')
|
||||
lines.push('')
|
||||
lines.push(
|
||||
`**Erstellt am:** ${now.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })} um ${now.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })} Uhr`
|
||||
)
|
||||
lines.push('')
|
||||
|
||||
// Overview
|
||||
lines.push('## Uebersicht')
|
||||
lines.push('')
|
||||
lines.push(`| Kennzahl | Wert |`)
|
||||
lines.push(`|----------|------|`)
|
||||
lines.push(`| Gepruefte Policies | ${result.stats.total} |`)
|
||||
lines.push(`| Bestanden | ${result.stats.passed} |`)
|
||||
lines.push(`| Beanstandungen | ${result.stats.failed} |`)
|
||||
lines.push(`| Compliance-Score | **${result.score}/100** (${getScoreRating(result.score)}) |`)
|
||||
lines.push('')
|
||||
|
||||
// Severity breakdown
|
||||
lines.push('## Befunde nach Schweregrad')
|
||||
lines.push('')
|
||||
lines.push('| Schweregrad | Anzahl |')
|
||||
lines.push('|-------------|--------|')
|
||||
|
||||
const severityOrder: ComplianceIssueSeverity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']
|
||||
for (const severity of severityOrder) {
|
||||
const count = result.stats.bySeverity[severity]
|
||||
lines.push(`| ${SEVERITY_LABELS[severity]} | ${count} |`)
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
// Status distribution of policies
|
||||
const statusCounts: Record<string, number> = {}
|
||||
for (const policy of policies) {
|
||||
const label = STATUS_LABELS[policy.status]
|
||||
statusCounts[label] = (statusCounts[label] || 0) + 1
|
||||
}
|
||||
|
||||
lines.push('## Policy-Status-Verteilung')
|
||||
lines.push('')
|
||||
lines.push('| Status | Anzahl |')
|
||||
lines.push('|--------|--------|')
|
||||
for (const [label, count] of Object.entries(statusCounts)) {
|
||||
lines.push(`| ${label} | ${count} |`)
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
// Issues list
|
||||
if (result.issues.length === 0) {
|
||||
lines.push('## Befunde')
|
||||
lines.push('')
|
||||
lines.push('Keine Beanstandungen gefunden. Alle Policies sind konform.')
|
||||
lines.push('')
|
||||
} else {
|
||||
lines.push('## Befunde')
|
||||
lines.push('')
|
||||
|
||||
// Group issues by severity
|
||||
for (const severity of severityOrder) {
|
||||
const issuesForSeverity = result.issues.filter((i) => i.severity === severity)
|
||||
if (issuesForSeverity.length === 0) continue
|
||||
|
||||
lines.push(`### ${SEVERITY_LABELS[severity]} ${SEVERITY_EMOJI[severity]}`)
|
||||
lines.push('')
|
||||
|
||||
for (const issue of issuesForSeverity) {
|
||||
const policyRef =
|
||||
issue.policyId !== '-' ? ` (${issue.policyId})` : ''
|
||||
lines.push(`**${issue.title}**${policyRef}`)
|
||||
lines.push('')
|
||||
lines.push(`> ${issue.description}`)
|
||||
lines.push('')
|
||||
lines.push(`Empfehlung: ${issue.recommendation}`)
|
||||
lines.push('')
|
||||
lines.push('---')
|
||||
lines.push('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recommendations summary
|
||||
lines.push('## Zusammenfassung der Empfehlungen')
|
||||
lines.push('')
|
||||
|
||||
if (result.stats.bySeverity.CRITICAL > 0) {
|
||||
lines.push(
|
||||
`1. **Sofortmassnahmen erforderlich:** ${result.stats.bySeverity.CRITICAL} kritische(r) Befund(e) muessen umgehend behoben werden (Legal Hold-Konflikte).`
|
||||
)
|
||||
}
|
||||
if (result.stats.bySeverity.HIGH > 0) {
|
||||
lines.push(
|
||||
`${result.stats.bySeverity.CRITICAL > 0 ? '2' : '1'}. **Hohe Prioritaet:** ${result.stats.bySeverity.HIGH} Befund(e) mit hoher Prioritaet (fehlende Trigger/Rechtsgrundlagen) sollten zeitnah bearbeitet werden.`
|
||||
)
|
||||
}
|
||||
if (result.stats.bySeverity.MEDIUM > 0) {
|
||||
lines.push(
|
||||
`- **Mittlere Prioritaet:** ${result.stats.bySeverity.MEDIUM} Befund(e) betreffen ueberfaellige Pruefungen, fehlende Verantwortlichkeiten oder nicht abgedeckte Datenkategorien.`
|
||||
)
|
||||
}
|
||||
if (result.stats.bySeverity.LOW > 0) {
|
||||
lines.push(
|
||||
`- **Niedrige Prioritaet:** ${result.stats.bySeverity.LOW} Befund(e) betreffen veraltete Entwuerfe, die finalisiert oder archiviert werden sollten.`
|
||||
)
|
||||
}
|
||||
if (result.issues.length === 0) {
|
||||
lines.push(
|
||||
'Alle Policies sind konform. Stellen Sie sicher, dass die naechsten Pruefungstermine eingehalten werden.'
|
||||
)
|
||||
}
|
||||
lines.push('')
|
||||
|
||||
// Footer
|
||||
lines.push('---')
|
||||
lines.push('')
|
||||
lines.push(
|
||||
'*Dieser Bericht wurde automatisch generiert und ersetzt keine rechtliche Beratung. Die Verantwortung fuer die DSGVO-Konformitaet liegt beim Verantwortlichen (Art. 4 Nr. 7 DSGVO).*'
|
||||
)
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// BROWSER DOWNLOAD UTILITY
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Loest einen Datei-Download im Browser aus.
|
||||
* Erstellt ein temporaeres Blob-URL und simuliert einen Link-Klick.
|
||||
*
|
||||
* @param content - Der Dateiinhalt als String
|
||||
* @param filename - Der gewuenschte Dateiname (z.B. "loeschfristen-export.json")
|
||||
* @param mimeType - Der MIME-Typ (z.B. "application/json", "text/csv;charset=utf-8")
|
||||
*/
|
||||
export function downloadFile(
|
||||
content: string,
|
||||
filename: string,
|
||||
mimeType: string
|
||||
): void {
|
||||
const blob = new Blob([content], { type: mimeType })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
538
admin-v2/lib/sdk/loeschfristen-profiling.ts
Normal file
538
admin-v2/lib/sdk/loeschfristen-profiling.ts
Normal file
@@ -0,0 +1,538 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Module - Profiling Wizard
|
||||
// 4-Step Profiling (15 Fragen) zur Generierung von Baseline-Loeschrichtlinien
|
||||
// =============================================================================
|
||||
|
||||
import type { LoeschfristPolicy, StorageLocation } from './loeschfristen-types'
|
||||
import { BASELINE_TEMPLATES, type BaselineTemplate, templateToPolicy } from './loeschfristen-baseline-catalog'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export type ProfilingStepId = 'organization' | 'data-categories' | 'systems' | 'special'
|
||||
|
||||
export interface ProfilingQuestion {
|
||||
id: string
|
||||
step: ProfilingStepId
|
||||
question: string // German
|
||||
helpText?: string
|
||||
type: 'single' | 'multi' | 'boolean' | 'number'
|
||||
options?: { value: string; label: string }[]
|
||||
required: boolean
|
||||
}
|
||||
|
||||
export interface ProfilingAnswer {
|
||||
questionId: string
|
||||
value: string | string[] | boolean | number
|
||||
}
|
||||
|
||||
export interface ProfilingStep {
|
||||
id: ProfilingStepId
|
||||
title: string
|
||||
description: string
|
||||
questions: ProfilingQuestion[]
|
||||
}
|
||||
|
||||
export interface ProfilingResult {
|
||||
matchedTemplates: BaselineTemplate[]
|
||||
generatedPolicies: LoeschfristPolicy[]
|
||||
additionalStorageLocations: StorageLocation[]
|
||||
hasLegalHoldRequirement: boolean
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PROFILING STEPS (4 Steps, 15 Questions)
|
||||
// =============================================================================
|
||||
|
||||
export const PROFILING_STEPS: ProfilingStep[] = [
|
||||
// =========================================================================
|
||||
// Step 1: Organisation (4 Fragen)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'organization',
|
||||
title: 'Organisation',
|
||||
description: 'Allgemeine Informationen zu Ihrem Unternehmen, um branchenspezifische Loeschfristen zu ermitteln.',
|
||||
questions: [
|
||||
{
|
||||
id: 'org-branche',
|
||||
step: 'organization',
|
||||
question: 'In welcher Branche ist Ihr Unternehmen taetig?',
|
||||
helpText: 'Die Branche bestimmt, welche branchenspezifischen Aufbewahrungspflichten relevant sind.',
|
||||
type: 'single',
|
||||
options: [
|
||||
{ value: 'it-software', label: 'IT / Software' },
|
||||
{ value: 'handel', label: 'Handel' },
|
||||
{ value: 'dienstleistung', label: 'Dienstleistung' },
|
||||
{ value: 'gesundheitswesen', label: 'Gesundheitswesen' },
|
||||
{ value: 'bildung', label: 'Bildung' },
|
||||
{ value: 'fertigung-industrie', label: 'Fertigung / Industrie' },
|
||||
{ value: 'finanzwesen', label: 'Finanzwesen' },
|
||||
{ value: 'oeffentlicher-sektor', label: 'Oeffentlicher Sektor' },
|
||||
{ value: 'sonstige', label: 'Sonstige' },
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'org-mitarbeiter',
|
||||
step: 'organization',
|
||||
question: 'Wie viele Mitarbeiter hat Ihr Unternehmen?',
|
||||
helpText: 'Die Unternehmensgroesse beeinflusst den Umfang der erforderlichen Loeschkonzepte.',
|
||||
type: 'single',
|
||||
options: [
|
||||
{ value: '<10', label: 'Weniger als 10' },
|
||||
{ value: '10-49', label: '10 bis 49' },
|
||||
{ value: '50-249', label: '50 bis 249' },
|
||||
{ value: '250+', label: '250 und mehr' },
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'org-geschaeftsmodell',
|
||||
step: 'organization',
|
||||
question: 'Welches Geschaeftsmodell verfolgen Sie?',
|
||||
helpText: 'B2B und B2C haben unterschiedliche Anforderungen an die Datenhaltung.',
|
||||
type: 'single',
|
||||
options: [
|
||||
{ value: 'b2b', label: 'B2B (Geschaeftskunden)' },
|
||||
{ value: 'b2c', label: 'B2C (Endkunden)' },
|
||||
{ value: 'beides', label: 'Beides (B2B und B2C)' },
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'org-website',
|
||||
step: 'organization',
|
||||
question: 'Betreiben Sie eine Website oder Online-Praesenz?',
|
||||
helpText: 'Websites erzeugen Webserver-Logs und erfordern Cookie-Consent-Verwaltung.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// Step 2: Datenkategorien (5 Fragen)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'data-categories',
|
||||
title: 'Datenkategorien',
|
||||
description: 'Welche Arten personenbezogener Daten verarbeiten Sie? Dies bestimmt die relevanten Aufbewahrungsfristen.',
|
||||
questions: [
|
||||
{
|
||||
id: 'data-hr',
|
||||
step: 'data-categories',
|
||||
question: 'Verarbeiten Sie HR-/Personaldaten (Personalakten, Gehaltsabrechnungen, Zeiterfassung)?',
|
||||
helpText: 'Personalakten unterliegen umfangreichen gesetzlichen Aufbewahrungspflichten (bis zu 10 Jahre).',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'data-buchhaltung',
|
||||
step: 'data-categories',
|
||||
question: 'Fuehren Sie eine Buchhaltung mit Finanzdaten (Rechnungen, Belege, Steuererklarungen)?',
|
||||
helpText: 'Buchhaltungsunterlagen muessen gemaess HGB und AO bis zu 10 Jahre aufbewahrt werden.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'data-vertraege',
|
||||
step: 'data-categories',
|
||||
question: 'Verwalten Sie Vertraege mit Kunden oder Lieferanten?',
|
||||
helpText: 'Vertragsunterlagen und Geschaeftsbriefe haben spezifische Aufbewahrungspflichten.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'data-marketing',
|
||||
step: 'data-categories',
|
||||
question: 'Betreiben Sie Marketing-Aktivitaeten (Newsletter, CRM-Kampagnen)?',
|
||||
helpText: 'Marketing-Einwilligungen und Kontakthistorien muessen dokumentiert und verwaltet werden.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'data-video',
|
||||
step: 'data-categories',
|
||||
question: 'Setzen Sie Videoueberwachung ein?',
|
||||
helpText: 'Videoueberwachungsdaten haben besonders kurze Loeschfristen (in der Regel 72 Stunden).',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// Step 3: Systeme (3 Fragen)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'systems',
|
||||
title: 'Systeme & Infrastruktur',
|
||||
description: 'Welche IT-Systeme und Infrastruktur nutzen Sie? Dies beeinflusst die Speicherorte in Ihrem Loeschkonzept.',
|
||||
questions: [
|
||||
{
|
||||
id: 'sys-cloud',
|
||||
step: 'systems',
|
||||
question: 'Nutzen Sie Cloud-Dienste zur Datenspeicherung oder -verarbeitung?',
|
||||
helpText: 'Cloud-Speicherorte muessen in den Loeschrichtlinien als separate Speicherorte dokumentiert werden.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'sys-backup',
|
||||
step: 'systems',
|
||||
question: 'Haben Sie Backup-Systeme im Einsatz?',
|
||||
helpText: 'Backups erfordern eine eigene Loeschstrategie, da Daten dort nach der primaeren Loeschung weiter existieren koennen.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'sys-erp',
|
||||
step: 'systems',
|
||||
question: 'Setzen Sie ein ERP- oder CRM-System ein?',
|
||||
helpText: 'ERP-/CRM-Systeme sind haeufig zentrale Speicherorte fuer Kunden- und Geschaeftsdaten.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// Step 4: Spezielle Anforderungen (3 Fragen)
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'special',
|
||||
title: 'Spezielle Anforderungen',
|
||||
description: 'Gibt es besondere rechtliche oder organisatorische Anforderungen, die Ihr Loeschkonzept beeinflussen?',
|
||||
questions: [
|
||||
{
|
||||
id: 'special-legal-hold',
|
||||
step: 'special',
|
||||
question: 'Gibt es Legal-Hold-Anforderungen (z.B. laufende Rechtsstreitigkeiten, behoerdliche Untersuchungen)?',
|
||||
helpText: 'Bei einem Legal Hold muessen betroffene Daten trotz abgelaufener Loeschfristen aufbewahrt werden.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'special-archivierung',
|
||||
step: 'special',
|
||||
question: 'Benoetigen Sie eine Langzeitarchivierung von Dokumenten?',
|
||||
helpText: 'Langzeitarchivierung kann ueber die gesetzlichen Mindestfristen hinausgehen und erfordert eine gesonderte Rechtfertigung.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'special-gesundheit',
|
||||
step: 'special',
|
||||
question: 'Verarbeiten Sie Gesundheitsdaten (z.B. Krankmeldungen, Arbeitsmedizin)?',
|
||||
helpText: 'Gesundheitsdaten sind besonders schuetzenswerte Daten nach Art. 9 DSGVO und unterliegen strengeren Anforderungen.',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Retrieve the value of a specific answer by question ID.
|
||||
*/
|
||||
export function getAnswerValue(answers: ProfilingAnswer[], questionId: string): unknown {
|
||||
const answer = answers.find(a => a.questionId === questionId)
|
||||
return answer?.value ?? undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether all required questions in a given step have been answered.
|
||||
*/
|
||||
export function isStepComplete(answers: ProfilingAnswer[], stepId: ProfilingStepId): boolean {
|
||||
const step = PROFILING_STEPS.find(s => s.id === stepId)
|
||||
if (!step) return false
|
||||
|
||||
return step.questions
|
||||
.filter(q => q.required)
|
||||
.every(q => {
|
||||
const answer = answers.find(a => a.questionId === q.id)
|
||||
if (!answer) return false
|
||||
|
||||
// Check that the value is not empty
|
||||
const val = answer.value
|
||||
if (val === undefined || val === null) return false
|
||||
if (typeof val === 'string' && val.trim() === '') return false
|
||||
if (Array.isArray(val) && val.length === 0) return false
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate overall profiling progress as a percentage (0-100).
|
||||
*/
|
||||
export function getProfilingProgress(answers: ProfilingAnswer[]): number {
|
||||
const totalRequired = PROFILING_STEPS.reduce(
|
||||
(sum, step) => sum + step.questions.filter(q => q.required).length,
|
||||
0
|
||||
)
|
||||
if (totalRequired === 0) return 100
|
||||
|
||||
const answeredRequired = PROFILING_STEPS.reduce((sum, step) => {
|
||||
return (
|
||||
sum +
|
||||
step.questions.filter(q => q.required).filter(q => {
|
||||
const answer = answers.find(a => a.questionId === q.id)
|
||||
if (!answer) return false
|
||||
const val = answer.value
|
||||
if (val === undefined || val === null) return false
|
||||
if (typeof val === 'string' && val.trim() === '') return false
|
||||
if (Array.isArray(val) && val.length === 0) return false
|
||||
return true
|
||||
}).length
|
||||
)
|
||||
}, 0)
|
||||
|
||||
return Math.round((answeredRequired / totalRequired) * 100)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CORE GENERATOR
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Generate deletion policies based on the profiling answers.
|
||||
*
|
||||
* Logic:
|
||||
* - Match baseline templates based on boolean and categorical answers
|
||||
* - Deduplicate matched templates by templateId
|
||||
* - Convert matched templates to full LoeschfristPolicy objects
|
||||
* - Add additional storage locations (Cloud, Backup) if applicable
|
||||
* - Detect legal hold requirements
|
||||
*/
|
||||
export function generatePoliciesFromProfile(answers: ProfilingAnswer[]): ProfilingResult {
|
||||
const matchedTemplateIds = new Set<string>()
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helper to get a boolean answer
|
||||
// -------------------------------------------------------------------------
|
||||
const getBool = (questionId: string): boolean => {
|
||||
const val = getAnswerValue(answers, questionId)
|
||||
return val === true
|
||||
}
|
||||
|
||||
const getString = (questionId: string): string => {
|
||||
const val = getAnswerValue(answers, questionId)
|
||||
return typeof val === 'string' ? val : ''
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Always-included templates (universally recommended)
|
||||
// -------------------------------------------------------------------------
|
||||
matchedTemplateIds.add('protokolle-gesellschafter')
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// HR data (data-hr = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('data-hr')) {
|
||||
matchedTemplateIds.add('personal-akten')
|
||||
matchedTemplateIds.add('gehaltsabrechnungen')
|
||||
matchedTemplateIds.add('zeiterfassung')
|
||||
matchedTemplateIds.add('bewerbungsunterlagen')
|
||||
matchedTemplateIds.add('krankmeldungen')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Buchhaltung (data-buchhaltung = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('data-buchhaltung')) {
|
||||
matchedTemplateIds.add('buchhaltungsbelege')
|
||||
matchedTemplateIds.add('rechnungen')
|
||||
matchedTemplateIds.add('steuererklaerungen')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Vertraege (data-vertraege = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('data-vertraege')) {
|
||||
matchedTemplateIds.add('vertraege')
|
||||
matchedTemplateIds.add('geschaeftsbriefe')
|
||||
matchedTemplateIds.add('kundenstammdaten')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Marketing (data-marketing = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('data-marketing')) {
|
||||
matchedTemplateIds.add('newsletter-einwilligungen')
|
||||
matchedTemplateIds.add('crm-kontakthistorie')
|
||||
matchedTemplateIds.add('cookie-consent-logs')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Video (data-video = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('data-video')) {
|
||||
matchedTemplateIds.add('videoueberwachung')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Website (org-website = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('org-website')) {
|
||||
matchedTemplateIds.add('webserver-logs')
|
||||
matchedTemplateIds.add('cookie-consent-logs')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ERP/CRM (sys-erp = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('sys-erp')) {
|
||||
matchedTemplateIds.add('kundenstammdaten')
|
||||
matchedTemplateIds.add('crm-kontakthistorie')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Backup (sys-backup = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('sys-backup')) {
|
||||
matchedTemplateIds.add('backup-daten')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Gesundheitsdaten (special-gesundheit = true)
|
||||
// -------------------------------------------------------------------------
|
||||
if (getBool('special-gesundheit')) {
|
||||
// Ensure krankmeldungen is included even without full HR data
|
||||
matchedTemplateIds.add('krankmeldungen')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Resolve matched templates from catalog
|
||||
// -------------------------------------------------------------------------
|
||||
const matchedTemplates: BaselineTemplate[] = []
|
||||
for (const templateId of matchedTemplateIds) {
|
||||
const template = BASELINE_TEMPLATES.find(t => t.templateId === templateId)
|
||||
if (template) {
|
||||
matchedTemplates.push(template)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Convert to policies
|
||||
// -------------------------------------------------------------------------
|
||||
const generatedPolicies: LoeschfristPolicy[] = matchedTemplates.map(template =>
|
||||
templateToPolicy(template)
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Additional storage locations
|
||||
// -------------------------------------------------------------------------
|
||||
const additionalStorageLocations: StorageLocation[] = []
|
||||
|
||||
if (getBool('sys-cloud')) {
|
||||
const cloudLocation: StorageLocation = {
|
||||
id: crypto.randomUUID(),
|
||||
name: 'Cloud-Speicher',
|
||||
type: 'CLOUD',
|
||||
isBackup: false,
|
||||
provider: null,
|
||||
deletionCapable: true,
|
||||
}
|
||||
additionalStorageLocations.push(cloudLocation)
|
||||
|
||||
// Add Cloud storage location to all generated policies
|
||||
for (const policy of generatedPolicies) {
|
||||
policy.storageLocations.push({ ...cloudLocation, id: crypto.randomUUID() })
|
||||
}
|
||||
}
|
||||
|
||||
if (getBool('sys-backup')) {
|
||||
const backupLocation: StorageLocation = {
|
||||
id: crypto.randomUUID(),
|
||||
name: 'Backup-System',
|
||||
type: 'BACKUP',
|
||||
isBackup: true,
|
||||
provider: null,
|
||||
deletionCapable: true,
|
||||
}
|
||||
additionalStorageLocations.push(backupLocation)
|
||||
|
||||
// Add Backup storage location to all generated policies
|
||||
for (const policy of generatedPolicies) {
|
||||
policy.storageLocations.push({ ...backupLocation, id: crypto.randomUUID() })
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Legal Hold
|
||||
// -------------------------------------------------------------------------
|
||||
const hasLegalHoldRequirement = getBool('special-legal-hold')
|
||||
|
||||
// If legal hold is active, mark all generated policies accordingly
|
||||
if (hasLegalHoldRequirement) {
|
||||
for (const policy of generatedPolicies) {
|
||||
policy.hasActiveLegalHold = true
|
||||
policy.deletionTrigger = 'LEGAL_HOLD'
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tag policies with profiling metadata
|
||||
// -------------------------------------------------------------------------
|
||||
const branche = getString('org-branche')
|
||||
const mitarbeiter = getString('org-mitarbeiter')
|
||||
|
||||
for (const policy of generatedPolicies) {
|
||||
policy.tags = [
|
||||
...policy.tags,
|
||||
'profiling-generated',
|
||||
...(branche ? [`branche:${branche}`] : []),
|
||||
...(mitarbeiter ? [`groesse:${mitarbeiter}`] : []),
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
matchedTemplates,
|
||||
generatedPolicies,
|
||||
additionalStorageLocations,
|
||||
hasLegalHoldRequirement,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// COMPLIANCE SCOPE INTEGRATION
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Prefill Loeschfristen profiling answers from Compliance Scope Engine answers.
|
||||
* The Scope Engine acts as the "Single Source of Truth" for organizational questions.
|
||||
*/
|
||||
export function prefillFromScopeAnswers(
|
||||
scopeAnswers: import('./compliance-scope-types').ScopeProfilingAnswer[]
|
||||
): ProfilingAnswer[] {
|
||||
const { exportToLoeschfristenAnswers } = require('./compliance-scope-profiling')
|
||||
const exported = exportToLoeschfristenAnswers(scopeAnswers) as Array<{ questionId: string; value: unknown }>
|
||||
return exported.map(item => ({
|
||||
questionId: item.questionId,
|
||||
value: item.value as string | string[] | boolean | number,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of Loeschfristen question IDs that are prefilled from Scope answers.
|
||||
* These questions should show "Aus Scope-Analyse uebernommen" hint.
|
||||
*/
|
||||
export const SCOPE_PREFILLED_LF_QUESTIONS = [
|
||||
'org-branche',
|
||||
'org-mitarbeiter',
|
||||
'org-geschaeftsmodell',
|
||||
'org-website',
|
||||
'data-hr',
|
||||
'data-buchhaltung',
|
||||
'data-vertraege',
|
||||
'data-marketing',
|
||||
'data-video',
|
||||
'sys-cloud',
|
||||
'sys-erp',
|
||||
]
|
||||
346
admin-v2/lib/sdk/loeschfristen-types.ts
Normal file
346
admin-v2/lib/sdk/loeschfristen-types.ts
Normal file
@@ -0,0 +1,346 @@
|
||||
// =============================================================================
|
||||
// Loeschfristen Module - TypeScript Types
|
||||
// 3-Level Loeschlogik: Zweckende -> Aufbewahrungstreiber -> Legal Hold
|
||||
// =============================================================================
|
||||
|
||||
// =============================================================================
|
||||
// ENUMS & LITERAL TYPES
|
||||
// =============================================================================
|
||||
|
||||
export type DeletionTriggerLevel = 'PURPOSE_END' | 'RETENTION_DRIVER' | 'LEGAL_HOLD'
|
||||
|
||||
export type RetentionDriverType =
|
||||
| 'AO_147' // 10 Jahre Steuerunterlagen
|
||||
| 'HGB_257' // 10/6 Jahre Handelsbuecher/-briefe
|
||||
| 'USTG_14B' // 10 Jahre Rechnungen
|
||||
| 'BGB_195' // 3 Jahre Verjaehrung
|
||||
| 'ARBZG_16' // 2 Jahre Zeiterfassung
|
||||
| 'AGG_15' // 6 Monate Bewerbungen
|
||||
| 'BDSG_35' // Unverzuegliche Loeschung
|
||||
| 'BSIG' // 90 Tage Sicherheitslogs
|
||||
| 'CUSTOM'
|
||||
|
||||
export type DeletionMethodType =
|
||||
| 'AUTO_DELETE'
|
||||
| 'MANUAL_REVIEW_DELETE'
|
||||
| 'ANONYMIZATION'
|
||||
| 'AGGREGATION'
|
||||
| 'CRYPTO_ERASE'
|
||||
| 'PHYSICAL_DESTROY'
|
||||
|
||||
export type PolicyStatus = 'DRAFT' | 'ACTIVE' | 'REVIEW_NEEDED' | 'ARCHIVED'
|
||||
|
||||
export type ReviewInterval = 'QUARTERLY' | 'SEMI_ANNUAL' | 'ANNUAL'
|
||||
|
||||
export type RetentionUnit = 'DAYS' | 'MONTHS' | 'YEARS'
|
||||
|
||||
export type StorageLocationType =
|
||||
| 'DATABASE' | 'FILE_SYSTEM' | 'CLOUD' | 'EMAIL' | 'BACKUP' | 'PAPER' | 'OTHER'
|
||||
|
||||
export type LegalHoldStatus = 'ACTIVE' | 'RELEASED' | 'EXPIRED'
|
||||
|
||||
// =============================================================================
|
||||
// INTERFACES
|
||||
// =============================================================================
|
||||
|
||||
export interface LegalHold {
|
||||
id: string
|
||||
reason: string
|
||||
legalBasis: string
|
||||
responsiblePerson: string
|
||||
startDate: string
|
||||
expectedEndDate: string | null
|
||||
actualEndDate: string | null
|
||||
status: LegalHoldStatus
|
||||
affectedDataCategories: string[]
|
||||
}
|
||||
|
||||
export interface StorageLocation {
|
||||
id: string
|
||||
name: string
|
||||
type: StorageLocationType
|
||||
isBackup: boolean
|
||||
provider: string | null
|
||||
deletionCapable: boolean
|
||||
}
|
||||
|
||||
export interface LoeschfristPolicy {
|
||||
id: string
|
||||
policyId: string // LF-2026-001
|
||||
dataObjectName: string
|
||||
description: string
|
||||
affectedGroups: string[]
|
||||
dataCategories: string[]
|
||||
primaryPurpose: string
|
||||
// 3-Level Loeschlogik
|
||||
deletionTrigger: DeletionTriggerLevel
|
||||
retentionDriver: RetentionDriverType | null
|
||||
retentionDriverDetail: string
|
||||
retentionDuration: number | null
|
||||
retentionUnit: RetentionUnit | null
|
||||
retentionDescription: string
|
||||
startEvent: string
|
||||
hasActiveLegalHold: boolean
|
||||
legalHolds: LegalHold[]
|
||||
// Speicherorte & Loeschung
|
||||
storageLocations: StorageLocation[]
|
||||
deletionMethod: DeletionMethodType
|
||||
deletionMethodDetail: string
|
||||
// Verantwortung & Workflow
|
||||
responsibleRole: string
|
||||
responsiblePerson: string
|
||||
releaseProcess: string
|
||||
linkedVVTActivityIds: string[]
|
||||
// Status & Review
|
||||
status: PolicyStatus
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: ReviewInterval
|
||||
tags: string[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CONSTANTS
|
||||
// =============================================================================
|
||||
|
||||
export interface RetentionDriverMeta {
|
||||
label: string
|
||||
statute: string
|
||||
defaultDuration: number | null
|
||||
defaultUnit: RetentionUnit | null
|
||||
description: string
|
||||
}
|
||||
|
||||
export const RETENTION_DRIVER_META: Record<RetentionDriverType, RetentionDriverMeta> = {
|
||||
AO_147: {
|
||||
label: 'Abgabenordnung (AO) 147',
|
||||
statute: '147 AO',
|
||||
defaultDuration: 10,
|
||||
defaultUnit: 'YEARS',
|
||||
description: 'Aufbewahrung steuerrelevanter Unterlagen (Buchungsbelege, Bilanzen, Jahresabschluesse)',
|
||||
},
|
||||
HGB_257: {
|
||||
label: 'Handelsgesetzbuch (HGB) 257',
|
||||
statute: '257 HGB',
|
||||
defaultDuration: 10,
|
||||
defaultUnit: 'YEARS',
|
||||
description: 'Handelsbuecher und Buchungsbelege (10 J.), empfangene/gesendete Handelsbriefe (6 J.)',
|
||||
},
|
||||
USTG_14B: {
|
||||
label: 'Umsatzsteuergesetz (UStG) 14b',
|
||||
statute: '14b UStG',
|
||||
defaultDuration: 10,
|
||||
defaultUnit: 'YEARS',
|
||||
description: 'Aufbewahrung von Rechnungen und rechnungsbegruendenden Unterlagen',
|
||||
},
|
||||
BGB_195: {
|
||||
label: 'Buergerliches Gesetzbuch (BGB) 195',
|
||||
statute: '195 BGB',
|
||||
defaultDuration: 3,
|
||||
defaultUnit: 'YEARS',
|
||||
description: 'Regelmaessige Verjaehrungsfrist fuer vertragliche Ansprueche',
|
||||
},
|
||||
ARBZG_16: {
|
||||
label: 'Arbeitszeitgesetz (ArbZG) 16',
|
||||
statute: '16 Abs. 2 ArbZG',
|
||||
defaultDuration: 2,
|
||||
defaultUnit: 'YEARS',
|
||||
description: 'Aufbewahrung von Arbeitszeitaufzeichnungen',
|
||||
},
|
||||
AGG_15: {
|
||||
label: 'Allg. Gleichbehandlungsgesetz (AGG) 15',
|
||||
statute: '15 Abs. 4 AGG',
|
||||
defaultDuration: 6,
|
||||
defaultUnit: 'MONTHS',
|
||||
description: 'Frist fuer Geltendmachung von Entschaedigungsanspruechen nach Absage',
|
||||
},
|
||||
BDSG_35: {
|
||||
label: 'BDSG 35 / DSGVO Art. 17',
|
||||
statute: '35 BDSG / Art. 17 DSGVO',
|
||||
defaultDuration: null,
|
||||
defaultUnit: null,
|
||||
description: 'Unverzuegliche Loeschung nach Zweckwegfall (kein fester Zeitraum)',
|
||||
},
|
||||
BSIG: {
|
||||
label: 'BSI-Gesetz (BSIG)',
|
||||
statute: 'BSIG / IT-SiG 2.0',
|
||||
defaultDuration: 90,
|
||||
defaultUnit: 'DAYS',
|
||||
description: 'Aufbewahrung von Sicherheitslogs fuer Vorfallsanalyse',
|
||||
},
|
||||
CUSTOM: {
|
||||
label: 'Individuelle Frist',
|
||||
statute: 'Individuell',
|
||||
defaultDuration: null,
|
||||
defaultUnit: null,
|
||||
description: 'Benutzerdefinierte Aufbewahrungsfrist',
|
||||
},
|
||||
}
|
||||
|
||||
export const DELETION_METHOD_LABELS: Record<DeletionMethodType, string> = {
|
||||
AUTO_DELETE: 'Automatische Loeschung',
|
||||
MANUAL_REVIEW_DELETE: 'Manuelle Pruefung & Loeschung',
|
||||
ANONYMIZATION: 'Anonymisierung',
|
||||
AGGREGATION: 'Aggregation (statistische Verdichtung)',
|
||||
CRYPTO_ERASE: 'Kryptographische Loeschung',
|
||||
PHYSICAL_DESTROY: 'Physische Vernichtung',
|
||||
}
|
||||
|
||||
export const STATUS_LABELS: Record<PolicyStatus, string> = {
|
||||
DRAFT: 'Entwurf',
|
||||
ACTIVE: 'Aktiv',
|
||||
REVIEW_NEEDED: 'Pruefung erforderlich',
|
||||
ARCHIVED: 'Archiviert',
|
||||
}
|
||||
|
||||
export const STATUS_COLORS: Record<PolicyStatus, string> = {
|
||||
DRAFT: 'bg-gray-100 text-gray-700 border-gray-200',
|
||||
ACTIVE: 'bg-green-100 text-green-700 border-green-200',
|
||||
REVIEW_NEEDED: 'bg-yellow-100 text-yellow-700 border-yellow-200',
|
||||
ARCHIVED: 'bg-blue-100 text-blue-700 border-blue-200',
|
||||
}
|
||||
|
||||
export const TRIGGER_LABELS: Record<DeletionTriggerLevel, string> = {
|
||||
PURPOSE_END: 'Zweckende',
|
||||
RETENTION_DRIVER: 'Aufbewahrungspflicht',
|
||||
LEGAL_HOLD: 'Legal Hold',
|
||||
}
|
||||
|
||||
export const TRIGGER_COLORS: Record<DeletionTriggerLevel, string> = {
|
||||
PURPOSE_END: 'bg-green-100 text-green-700',
|
||||
RETENTION_DRIVER: 'bg-blue-100 text-blue-700',
|
||||
LEGAL_HOLD: 'bg-red-100 text-red-700',
|
||||
}
|
||||
|
||||
export const REVIEW_INTERVAL_LABELS: Record<ReviewInterval, string> = {
|
||||
QUARTERLY: 'Vierteljaehrlich',
|
||||
SEMI_ANNUAL: 'Halbjaehrlich',
|
||||
ANNUAL: 'Jaehrlich',
|
||||
}
|
||||
|
||||
export const STORAGE_LOCATION_LABELS: Record<StorageLocationType, string> = {
|
||||
DATABASE: 'Datenbank',
|
||||
FILE_SYSTEM: 'Dateisystem',
|
||||
CLOUD: 'Cloud-Speicher',
|
||||
EMAIL: 'E-Mail-System',
|
||||
BACKUP: 'Backup-System',
|
||||
PAPER: 'Papierarchiv',
|
||||
OTHER: 'Sonstiges',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
let policyCounter = 0
|
||||
|
||||
export function generatePolicyId(): string {
|
||||
policyCounter++
|
||||
const year = new Date().getFullYear()
|
||||
const num = String(policyCounter).padStart(3, '0')
|
||||
return `LF-${year}-${num}`
|
||||
}
|
||||
|
||||
export function createEmptyPolicy(): LoeschfristPolicy {
|
||||
const now = new Date().toISOString()
|
||||
const nextYear = new Date()
|
||||
nextYear.setFullYear(nextYear.getFullYear() + 1)
|
||||
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
policyId: generatePolicyId(),
|
||||
dataObjectName: '',
|
||||
description: '',
|
||||
affectedGroups: [],
|
||||
dataCategories: [],
|
||||
primaryPurpose: '',
|
||||
deletionTrigger: 'PURPOSE_END',
|
||||
retentionDriver: null,
|
||||
retentionDriverDetail: '',
|
||||
retentionDuration: null,
|
||||
retentionUnit: null,
|
||||
retentionDescription: '',
|
||||
startEvent: '',
|
||||
hasActiveLegalHold: false,
|
||||
legalHolds: [],
|
||||
storageLocations: [],
|
||||
deletionMethod: 'AUTO_DELETE',
|
||||
deletionMethodDetail: '',
|
||||
responsibleRole: '',
|
||||
responsiblePerson: '',
|
||||
releaseProcess: '',
|
||||
linkedVVTActivityIds: [],
|
||||
status: 'DRAFT',
|
||||
lastReviewDate: now,
|
||||
nextReviewDate: nextYear.toISOString(),
|
||||
reviewInterval: 'ANNUAL',
|
||||
tags: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
}
|
||||
|
||||
export function createEmptyLegalHold(): LegalHold {
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
reason: '',
|
||||
legalBasis: '',
|
||||
responsiblePerson: '',
|
||||
startDate: new Date().toISOString().split('T')[0],
|
||||
expectedEndDate: null,
|
||||
actualEndDate: null,
|
||||
status: 'ACTIVE',
|
||||
affectedDataCategories: [],
|
||||
}
|
||||
}
|
||||
|
||||
export function createEmptyStorageLocation(): StorageLocation {
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
name: '',
|
||||
type: 'DATABASE',
|
||||
isBackup: false,
|
||||
provider: null,
|
||||
deletionCapable: true,
|
||||
}
|
||||
}
|
||||
|
||||
export function formatRetentionDuration(
|
||||
duration: number | null,
|
||||
unit: RetentionUnit | null
|
||||
): string {
|
||||
if (duration === null || unit === null) return 'Bis Zweckwegfall'
|
||||
const unitLabels: Record<RetentionUnit, string> = {
|
||||
DAYS: duration === 1 ? 'Tag' : 'Tage',
|
||||
MONTHS: duration === 1 ? 'Monat' : 'Monate',
|
||||
YEARS: duration === 1 ? 'Jahr' : 'Jahre',
|
||||
}
|
||||
return `${duration} ${unitLabels[unit]}`
|
||||
}
|
||||
|
||||
export function isPolicyOverdue(policy: LoeschfristPolicy): boolean {
|
||||
if (!policy.nextReviewDate) return false
|
||||
return new Date(policy.nextReviewDate) <= new Date()
|
||||
}
|
||||
|
||||
export function getActiveLegalHolds(policy: LoeschfristPolicy): LegalHold[] {
|
||||
return policy.legalHolds.filter(h => h.status === 'ACTIVE')
|
||||
}
|
||||
|
||||
export function getEffectiveDeletionTrigger(policy: LoeschfristPolicy): DeletionTriggerLevel {
|
||||
if (policy.hasActiveLegalHold && getActiveLegalHolds(policy).length > 0) {
|
||||
return 'LEGAL_HOLD'
|
||||
}
|
||||
if (policy.retentionDriver && policy.retentionDriver !== 'CUSTOM') {
|
||||
return 'RETENTION_DRIVER'
|
||||
}
|
||||
return 'PURPOSE_END'
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LOCALSTORAGE KEY
|
||||
// =============================================================================
|
||||
|
||||
export const LOESCHFRISTEN_STORAGE_KEY = 'bp_loeschfristen'
|
||||
@@ -63,6 +63,7 @@ type TOMGeneratorAction =
|
||||
| { type: 'UPDATE_DERIVED_TOM'; payload: { id: string; data: Partial<DerivedTOM> } }
|
||||
| { type: 'SET_GAP_ANALYSIS'; payload: GapAnalysisResult }
|
||||
| { type: 'ADD_EXPORT'; payload: ExportRecord }
|
||||
| { type: 'BULK_UPDATE_TOMS'; payload: { updates: Array<{ id: string; data: Partial<DerivedTOM> }> } }
|
||||
| { type: 'LOAD_STATE'; payload: TOMGeneratorState }
|
||||
|
||||
// =============================================================================
|
||||
@@ -236,6 +237,16 @@ function tomGeneratorReducer(
|
||||
})
|
||||
}
|
||||
|
||||
case 'BULK_UPDATE_TOMS': {
|
||||
let updatedTOMs = [...state.derivedTOMs]
|
||||
for (const update of action.payload.updates) {
|
||||
updatedTOMs = updatedTOMs.map((tom) =>
|
||||
tom.id === update.id ? { ...tom, ...update.data } : tom
|
||||
)
|
||||
}
|
||||
return updateState({ derivedTOMs: updatedTOMs })
|
||||
}
|
||||
|
||||
case 'LOAD_STATE': {
|
||||
return action.payload
|
||||
}
|
||||
@@ -283,6 +294,7 @@ interface TOMGeneratorContextValue {
|
||||
// TOM derivation
|
||||
deriveTOMs: () => void
|
||||
updateDerivedTOM: (id: string, data: Partial<DerivedTOM>) => void
|
||||
bulkUpdateTOMs: (updates: Array<{ id: string; data: Partial<DerivedTOM> }>) => void
|
||||
|
||||
// Gap analysis
|
||||
runGapAnalysis: () => void
|
||||
|
||||
@@ -2072,6 +2072,287 @@ const CONTROL_LIBRARY_DATA: ControlLibrary = {
|
||||
complexity: 'HIGH',
|
||||
tags: ['dpia', 'dsfa', 'risk-assessment'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// DELETION / VERNICHTUNG — Sichere Datenloeschung & Datentraegervernichtung
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'TOM-DL-01',
|
||||
code: 'TOM-DL-01',
|
||||
category: 'SEPARATION',
|
||||
type: 'TECHNICAL',
|
||||
name: {
|
||||
de: 'Sichere Datenloeschung',
|
||||
en: 'Secure Data Deletion',
|
||||
},
|
||||
description: {
|
||||
de: 'Implementierung sicherer Loeschverfahren, die personenbezogene Daten unwiederbringlich entfernen (z.B. nach DIN 66399).',
|
||||
en: 'Implementation of secure deletion procedures that irrecoverably remove personal data (e.g. per DIN 66399).',
|
||||
},
|
||||
mappings: [
|
||||
{ framework: 'GDPR_ART17', reference: 'Art. 17' },
|
||||
{ framework: 'GDPR_ART5', reference: 'Art. 5 Abs. 1 lit. e' },
|
||||
{ framework: 'ISO27001_ANNEX_A', reference: 'A.8.10' },
|
||||
{ framework: 'BSI_C5', reference: 'SY-09' },
|
||||
],
|
||||
applicabilityConditions: [
|
||||
{
|
||||
field: 'dataProfile.dataVolume',
|
||||
operator: 'NOT_EQUALS',
|
||||
value: 'NONE',
|
||||
result: 'REQUIRED',
|
||||
priority: 30,
|
||||
},
|
||||
],
|
||||
defaultApplicability: 'REQUIRED',
|
||||
evidenceRequirements: [
|
||||
'Loeschkonzept / Loeschrichtlinie',
|
||||
'Loeschprotokolle mit Zeitstempeln',
|
||||
'DIN 66399 Konformitaetsnachweis',
|
||||
],
|
||||
reviewFrequency: 'ANNUAL',
|
||||
priority: 'HIGH',
|
||||
complexity: 'MEDIUM',
|
||||
tags: ['deletion', 'loeschung', 'data-lifecycle', 'din-66399'],
|
||||
},
|
||||
{
|
||||
id: 'TOM-DL-02',
|
||||
code: 'TOM-DL-02',
|
||||
category: 'SEPARATION',
|
||||
type: 'TECHNICAL',
|
||||
name: {
|
||||
de: 'Datentraegervernichtung',
|
||||
en: 'Media Destruction',
|
||||
},
|
||||
description: {
|
||||
de: 'Physische Vernichtung von Datentraegern (Festplatten, SSDs, USB-Sticks, Papier) gemaess DIN 66399 Schutzklassen.',
|
||||
en: 'Physical destruction of storage media (hard drives, SSDs, USB sticks, paper) per DIN 66399 protection classes.',
|
||||
},
|
||||
mappings: [
|
||||
{ framework: 'GDPR_ART32', reference: 'Art. 32 Abs. 1' },
|
||||
{ framework: 'ISO27001_ANNEX_A', reference: 'A.7.14' },
|
||||
{ framework: 'BSI_C5', reference: 'AM-08' },
|
||||
],
|
||||
applicabilityConditions: [
|
||||
{
|
||||
field: 'dataProfile.dataVolume',
|
||||
operator: 'NOT_EQUALS',
|
||||
value: 'NONE',
|
||||
result: 'RECOMMENDED',
|
||||
priority: 20,
|
||||
},
|
||||
],
|
||||
defaultApplicability: 'RECOMMENDED',
|
||||
evidenceRequirements: [
|
||||
'Vernichtungsprotokoll mit Seriennummern',
|
||||
'Zertifikat des Vernichtungsdienstleisters',
|
||||
'DIN 66399 Sicherheitsstufe-Nachweis',
|
||||
],
|
||||
reviewFrequency: 'ANNUAL',
|
||||
priority: 'MEDIUM',
|
||||
complexity: 'LOW',
|
||||
tags: ['deletion', 'media-destruction', 'physical-security', 'din-66399'],
|
||||
},
|
||||
{
|
||||
id: 'TOM-DL-03',
|
||||
code: 'TOM-DL-03',
|
||||
category: 'SEPARATION',
|
||||
type: 'ORGANIZATIONAL',
|
||||
name: {
|
||||
de: 'Loeschprotokollierung',
|
||||
en: 'Deletion Logging',
|
||||
},
|
||||
description: {
|
||||
de: 'Systematische Protokollierung aller Loeschvorgaenge mit Zeitstempel, Verantwortlichem, Datenobjekt und Loeschmethode.',
|
||||
en: 'Systematic logging of all deletion operations with timestamp, responsible person, data object, and deletion method.',
|
||||
},
|
||||
mappings: [
|
||||
{ framework: 'GDPR_ART5', reference: 'Art. 5 Abs. 2 (Rechenschaftspflicht)' },
|
||||
{ framework: 'ISO27001_ANNEX_A', reference: 'A.8.10' },
|
||||
],
|
||||
applicabilityConditions: [
|
||||
{
|
||||
field: 'dataProfile.dataVolume',
|
||||
operator: 'NOT_EQUALS',
|
||||
value: 'NONE',
|
||||
result: 'REQUIRED',
|
||||
priority: 25,
|
||||
},
|
||||
],
|
||||
defaultApplicability: 'REQUIRED',
|
||||
evidenceRequirements: [
|
||||
'Loeschprotokoll-Template',
|
||||
'Archivierte Loeschprotokolle (Stichprobe)',
|
||||
'Automatisierungsnachweis (bei automatischen Loeschungen)',
|
||||
],
|
||||
reviewFrequency: 'SEMI_ANNUAL',
|
||||
priority: 'HIGH',
|
||||
complexity: 'LOW',
|
||||
tags: ['deletion', 'logging', 'accountability', 'documentation'],
|
||||
},
|
||||
{
|
||||
id: 'TOM-DL-04',
|
||||
code: 'TOM-DL-04',
|
||||
category: 'SEPARATION',
|
||||
type: 'TECHNICAL',
|
||||
name: {
|
||||
de: 'Backup-Bereinigung',
|
||||
en: 'Backup Sanitization',
|
||||
},
|
||||
description: {
|
||||
de: 'Sicherstellung, dass personenbezogene Daten auch in Backup-Systemen nach Ablauf der Loeschfrist entfernt werden.',
|
||||
en: 'Ensuring that personal data is also removed from backup systems after the retention period expires.',
|
||||
},
|
||||
mappings: [
|
||||
{ framework: 'GDPR_ART17', reference: 'Art. 17 Abs. 2' },
|
||||
{ framework: 'ISO27001_ANNEX_A', reference: 'A.8.13' },
|
||||
],
|
||||
applicabilityConditions: [
|
||||
{
|
||||
field: 'techProfile.hasBackups',
|
||||
operator: 'EQUALS',
|
||||
value: true,
|
||||
result: 'REQUIRED',
|
||||
priority: 25,
|
||||
},
|
||||
],
|
||||
defaultApplicability: 'RECOMMENDED',
|
||||
evidenceRequirements: [
|
||||
'Backup-Loeschkonzept',
|
||||
'Backup-Rotationsplan',
|
||||
'Nachweis der Backup-Bereinigung',
|
||||
],
|
||||
reviewFrequency: 'SEMI_ANNUAL',
|
||||
priority: 'MEDIUM',
|
||||
complexity: 'HIGH',
|
||||
tags: ['deletion', 'backup', 'data-lifecycle', 'retention'],
|
||||
},
|
||||
|
||||
// =========================================================================
|
||||
// SCHULUNG / VERTRAULICHKEIT — Training & Awareness
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'TOM-TR-01',
|
||||
code: 'TOM-TR-01',
|
||||
category: 'REVIEW',
|
||||
type: 'ORGANIZATIONAL',
|
||||
name: {
|
||||
de: 'Datenschutzschulung',
|
||||
en: 'Data Protection Training',
|
||||
},
|
||||
description: {
|
||||
de: 'Regelmaessige Schulung aller Mitarbeiter zu Datenschutzgrundlagen, DSGVO-Anforderungen und betrieblichen Datenschutzrichtlinien.',
|
||||
en: 'Regular training of all employees on data protection fundamentals, GDPR requirements, and organizational data protection policies.',
|
||||
},
|
||||
mappings: [
|
||||
{ framework: 'GDPR_ART39', reference: 'Art. 39 Abs. 1 lit. b' },
|
||||
{ framework: 'GDPR_ART47', reference: 'Art. 47 Abs. 2 lit. n' },
|
||||
{ framework: 'ISO27001_ANNEX_A', reference: 'A.6.3' },
|
||||
],
|
||||
applicabilityConditions: [
|
||||
{
|
||||
field: 'orgProfile.employeeCount',
|
||||
operator: 'GREATER_THAN',
|
||||
value: 0,
|
||||
result: 'REQUIRED',
|
||||
priority: 30,
|
||||
},
|
||||
],
|
||||
defaultApplicability: 'REQUIRED',
|
||||
evidenceRequirements: [
|
||||
'Schulungsplan (jaehrlich)',
|
||||
'Teilnahmelisten / Schulungsnachweise',
|
||||
'Schulungsmaterialien / Praesentation',
|
||||
'Wissenstest-Ergebnisse (optional)',
|
||||
],
|
||||
reviewFrequency: 'ANNUAL',
|
||||
priority: 'HIGH',
|
||||
complexity: 'LOW',
|
||||
tags: ['training', 'schulung', 'awareness', 'organizational'],
|
||||
},
|
||||
{
|
||||
id: 'TOM-TR-02',
|
||||
code: 'TOM-TR-02',
|
||||
category: 'REVIEW',
|
||||
type: 'ORGANIZATIONAL',
|
||||
name: {
|
||||
de: 'Verpflichtung auf Datengeheimnis',
|
||||
en: 'Confidentiality Obligation',
|
||||
},
|
||||
description: {
|
||||
de: 'Schriftliche Verpflichtung aller Mitarbeiter und externen Dienstleister auf die Vertraulichkeit personenbezogener Daten.',
|
||||
en: 'Written obligation of all employees and external service providers to maintain confidentiality of personal data.',
|
||||
},
|
||||
mappings: [
|
||||
{ framework: 'GDPR_ART28', reference: 'Art. 28 Abs. 3 lit. b' },
|
||||
{ framework: 'GDPR_ART32', reference: 'Art. 32 Abs. 4' },
|
||||
{ framework: 'ISO27001_ANNEX_A', reference: 'A.6.6' },
|
||||
],
|
||||
applicabilityConditions: [
|
||||
{
|
||||
field: 'orgProfile.employeeCount',
|
||||
operator: 'GREATER_THAN',
|
||||
value: 0,
|
||||
result: 'REQUIRED',
|
||||
priority: 30,
|
||||
},
|
||||
],
|
||||
defaultApplicability: 'REQUIRED',
|
||||
evidenceRequirements: [
|
||||
'Muster-Verpflichtungserklaerung',
|
||||
'Unterschriebene Verpflichtungserklaerungen',
|
||||
'Register der verpflichteten Personen',
|
||||
],
|
||||
reviewFrequency: 'ANNUAL',
|
||||
priority: 'HIGH',
|
||||
complexity: 'LOW',
|
||||
tags: ['training', 'confidentiality', 'vertraulichkeit', 'obligation'],
|
||||
},
|
||||
{
|
||||
id: 'TOM-TR-03',
|
||||
code: 'TOM-TR-03',
|
||||
category: 'REVIEW',
|
||||
type: 'ORGANIZATIONAL',
|
||||
name: {
|
||||
de: 'Security Awareness Programm',
|
||||
en: 'Security Awareness Program',
|
||||
},
|
||||
description: {
|
||||
de: 'Fortlaufendes Awareness-Programm zu IT-Sicherheit, Phishing-Erkennung, Social Engineering und sicherem Umgang mit Daten.',
|
||||
en: 'Ongoing awareness program on IT security, phishing detection, social engineering, and safe data handling.',
|
||||
},
|
||||
mappings: [
|
||||
{ framework: 'GDPR_ART32', reference: 'Art. 32 Abs. 1 lit. d' },
|
||||
{ framework: 'ISO27001_ANNEX_A', reference: 'A.6.3' },
|
||||
{ framework: 'BSI_C5', reference: 'ORP.3' },
|
||||
],
|
||||
applicabilityConditions: [
|
||||
{
|
||||
field: 'orgProfile.employeeCount',
|
||||
operator: 'GREATER_THAN',
|
||||
value: 10,
|
||||
result: 'REQUIRED',
|
||||
priority: 20,
|
||||
},
|
||||
{
|
||||
field: 'orgProfile.employeeCount',
|
||||
operator: 'GREATER_THAN',
|
||||
value: 0,
|
||||
result: 'RECOMMENDED',
|
||||
priority: 15,
|
||||
},
|
||||
],
|
||||
defaultApplicability: 'RECOMMENDED',
|
||||
evidenceRequirements: [
|
||||
'Awareness-Programm-Dokumentation',
|
||||
'Phishing-Simulationsergebnisse',
|
||||
'Teilnahmenachweise',
|
||||
],
|
||||
reviewFrequency: 'SEMI_ANNUAL',
|
||||
priority: 'MEDIUM',
|
||||
complexity: 'MEDIUM',
|
||||
tags: ['training', 'security-awareness', 'phishing', 'social-engineering'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
192
admin-v2/lib/sdk/tom-generator/sdm-mapping.ts
Normal file
192
admin-v2/lib/sdk/tom-generator/sdm-mapping.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
// =============================================================================
|
||||
// SDM (Standard-Datenschutzmodell) Mapping
|
||||
// Maps ControlCategories to SDM Gewaehrleistungsziele and Spec Modules
|
||||
// =============================================================================
|
||||
|
||||
import { ControlCategory } from './types'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export type SDMGewaehrleistungsziel =
|
||||
| 'Verfuegbarkeit'
|
||||
| 'Integritaet'
|
||||
| 'Vertraulichkeit'
|
||||
| 'Nichtverkettung'
|
||||
| 'Intervenierbarkeit'
|
||||
| 'Transparenz'
|
||||
| 'Datenminimierung'
|
||||
|
||||
export type TOMModuleCategory =
|
||||
| 'IDENTITY_AUTH'
|
||||
| 'LOGGING'
|
||||
| 'DOCUMENTATION'
|
||||
| 'SEPARATION'
|
||||
| 'RETENTION'
|
||||
| 'DELETION'
|
||||
| 'TRAINING'
|
||||
| 'REVIEW'
|
||||
|
||||
export const SDM_GOAL_LABELS: Record<SDMGewaehrleistungsziel, string> = {
|
||||
Verfuegbarkeit: 'Verfuegbarkeit',
|
||||
Integritaet: 'Integritaet',
|
||||
Vertraulichkeit: 'Vertraulichkeit',
|
||||
Nichtverkettung: 'Nichtverkettung',
|
||||
Intervenierbarkeit: 'Intervenierbarkeit',
|
||||
Transparenz: 'Transparenz',
|
||||
Datenminimierung: 'Datenminimierung',
|
||||
}
|
||||
|
||||
export const SDM_GOAL_DESCRIPTIONS: Record<SDMGewaehrleistungsziel, string> = {
|
||||
Verfuegbarkeit: 'Personenbezogene Daten muessen zeitgerecht zur Verfuegung stehen und ordnungsgemaess verarbeitet werden koennen.',
|
||||
Integritaet: 'Personenbezogene Daten muessen unversehrt, vollstaendig und aktuell bleiben.',
|
||||
Vertraulichkeit: 'Nur Befugte duerfen personenbezogene Daten zur Kenntnis nehmen.',
|
||||
Nichtverkettung: 'Daten duerfen nicht ohne Weiteres fuer andere Zwecke zusammengefuehrt werden.',
|
||||
Intervenierbarkeit: 'Betroffene muessen ihre Rechte wahrnehmen koennen (Auskunft, Berichtigung, Loeschung).',
|
||||
Transparenz: 'Verarbeitungsvorgaenge muessen nachvollziehbar dokumentiert sein.',
|
||||
Datenminimierung: 'Nur die fuer den Zweck erforderlichen Daten duerfen verarbeitet werden.',
|
||||
}
|
||||
|
||||
export const MODULE_LABELS: Record<TOMModuleCategory, string> = {
|
||||
IDENTITY_AUTH: 'Identitaet & Authentifizierung',
|
||||
LOGGING: 'Protokollierung',
|
||||
DOCUMENTATION: 'Dokumentation',
|
||||
SEPARATION: 'Trennung',
|
||||
RETENTION: 'Aufbewahrung',
|
||||
DELETION: 'Loeschung & Vernichtung',
|
||||
TRAINING: 'Schulung & Vertraulichkeit',
|
||||
REVIEW: 'Ueberpruefung & Bewertung',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MAPPINGS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Maps ControlCategory to its primary SDM Gewaehrleistungsziele
|
||||
*/
|
||||
export const SDM_CATEGORY_MAPPING: Record<ControlCategory, SDMGewaehrleistungsziel[]> = {
|
||||
ACCESS_CONTROL: ['Vertraulichkeit'],
|
||||
ADMISSION_CONTROL: ['Vertraulichkeit', 'Integritaet'],
|
||||
ACCESS_AUTHORIZATION: ['Vertraulichkeit', 'Nichtverkettung'],
|
||||
TRANSFER_CONTROL: ['Vertraulichkeit', 'Integritaet'],
|
||||
INPUT_CONTROL: ['Integritaet', 'Transparenz'],
|
||||
ORDER_CONTROL: ['Transparenz', 'Intervenierbarkeit'],
|
||||
AVAILABILITY: ['Verfuegbarkeit'],
|
||||
SEPARATION: ['Nichtverkettung', 'Datenminimierung'],
|
||||
ENCRYPTION: ['Vertraulichkeit', 'Integritaet'],
|
||||
PSEUDONYMIZATION: ['Datenminimierung', 'Nichtverkettung'],
|
||||
RESILIENCE: ['Verfuegbarkeit'],
|
||||
RECOVERY: ['Verfuegbarkeit', 'Integritaet'],
|
||||
REVIEW: ['Transparenz', 'Intervenierbarkeit'],
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps ControlCategory to Spec Module Categories
|
||||
*/
|
||||
export const MODULE_CATEGORY_MAPPING: Record<ControlCategory, TOMModuleCategory[]> = {
|
||||
ACCESS_CONTROL: ['IDENTITY_AUTH'],
|
||||
ADMISSION_CONTROL: ['IDENTITY_AUTH'],
|
||||
ACCESS_AUTHORIZATION: ['IDENTITY_AUTH', 'DOCUMENTATION'],
|
||||
TRANSFER_CONTROL: ['DOCUMENTATION'],
|
||||
INPUT_CONTROL: ['LOGGING'],
|
||||
ORDER_CONTROL: ['DOCUMENTATION'],
|
||||
AVAILABILITY: ['REVIEW'],
|
||||
SEPARATION: ['SEPARATION'],
|
||||
ENCRYPTION: ['IDENTITY_AUTH'],
|
||||
PSEUDONYMIZATION: ['SEPARATION', 'DELETION'],
|
||||
RESILIENCE: ['REVIEW'],
|
||||
RECOVERY: ['REVIEW'],
|
||||
REVIEW: ['REVIEW', 'TRAINING'],
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
import type { DerivedTOM, ControlLibraryEntry } from './types'
|
||||
import { getControlById } from './controls/loader'
|
||||
|
||||
/**
|
||||
* Get SDM goals for a given control (by looking up its category)
|
||||
*/
|
||||
export function getSDMGoalsForControl(controlId: string): SDMGewaehrleistungsziel[] {
|
||||
const control = getControlById(controlId)
|
||||
if (!control) return []
|
||||
return SDM_CATEGORY_MAPPING[control.category] || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Get derived TOMs that map to a specific SDM goal
|
||||
*/
|
||||
export function getTOMsBySDMGoal(
|
||||
toms: DerivedTOM[],
|
||||
goal: SDMGewaehrleistungsziel
|
||||
): DerivedTOM[] {
|
||||
return toms.filter(tom => {
|
||||
const goals = getSDMGoalsForControl(tom.controlId)
|
||||
return goals.includes(goal)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get derived TOMs belonging to a specific module
|
||||
*/
|
||||
export function getTOMsByModule(
|
||||
toms: DerivedTOM[],
|
||||
module: TOMModuleCategory
|
||||
): DerivedTOM[] {
|
||||
return toms.filter(tom => {
|
||||
const control = getControlById(tom.controlId)
|
||||
if (!control) return false
|
||||
const modules = MODULE_CATEGORY_MAPPING[control.category] || []
|
||||
return modules.includes(module)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SDM goal coverage statistics
|
||||
*/
|
||||
export function getSDMCoverageStats(toms: DerivedTOM[]): Record<SDMGewaehrleistungsziel, {
|
||||
total: number
|
||||
implemented: number
|
||||
partial: number
|
||||
missing: number
|
||||
}> {
|
||||
const goals = Object.keys(SDM_GOAL_LABELS) as SDMGewaehrleistungsziel[]
|
||||
const stats = {} as Record<SDMGewaehrleistungsziel, { total: number; implemented: number; partial: number; missing: number }>
|
||||
|
||||
for (const goal of goals) {
|
||||
const goalTOMs = getTOMsBySDMGoal(toms, goal)
|
||||
stats[goal] = {
|
||||
total: goalTOMs.length,
|
||||
implemented: goalTOMs.filter(t => t.implementationStatus === 'IMPLEMENTED').length,
|
||||
partial: goalTOMs.filter(t => t.implementationStatus === 'PARTIAL').length,
|
||||
missing: goalTOMs.filter(t => t.implementationStatus === 'NOT_IMPLEMENTED').length,
|
||||
}
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module coverage statistics
|
||||
*/
|
||||
export function getModuleCoverageStats(toms: DerivedTOM[]): Record<TOMModuleCategory, {
|
||||
total: number
|
||||
implemented: number
|
||||
}> {
|
||||
const modules = Object.keys(MODULE_LABELS) as TOMModuleCategory[]
|
||||
const stats = {} as Record<TOMModuleCategory, { total: number; implemented: number }>
|
||||
|
||||
for (const mod of modules) {
|
||||
const modTOMs = getTOMsByModule(toms, mod)
|
||||
stats[mod] = {
|
||||
total: modTOMs.length,
|
||||
implemented: modTOMs.filter(t => t.implementationStatus === 'IMPLEMENTED').length,
|
||||
}
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
@@ -899,3 +899,65 @@ export function createInitialTOMGeneratorState(
|
||||
* Alias for createInitialTOMGeneratorState (for API compatibility)
|
||||
*/
|
||||
export const createEmptyTOMGeneratorState = createInitialTOMGeneratorState
|
||||
|
||||
// =============================================================================
|
||||
// SDM TYPES (Standard-Datenschutzmodell)
|
||||
// =============================================================================
|
||||
|
||||
export type SDMGewaehrleistungsziel =
|
||||
| 'Verfuegbarkeit'
|
||||
| 'Integritaet'
|
||||
| 'Vertraulichkeit'
|
||||
| 'Nichtverkettung'
|
||||
| 'Intervenierbarkeit'
|
||||
| 'Transparenz'
|
||||
| 'Datenminimierung'
|
||||
|
||||
export type TOMModuleCategory =
|
||||
| 'IDENTITY_AUTH'
|
||||
| 'LOGGING'
|
||||
| 'DOCUMENTATION'
|
||||
| 'SEPARATION'
|
||||
| 'RETENTION'
|
||||
| 'DELETION'
|
||||
| 'TRAINING'
|
||||
| 'REVIEW'
|
||||
|
||||
/**
|
||||
* Maps ControlCategory to SDM Gewaehrleistungsziele.
|
||||
* Used by the TOM Dashboard to display SDM coverage.
|
||||
*/
|
||||
export const SDM_CATEGORY_MAPPING: Record<ControlCategory, SDMGewaehrleistungsziel[]> = {
|
||||
ACCESS_CONTROL: ['Vertraulichkeit'],
|
||||
ADMISSION_CONTROL: ['Vertraulichkeit', 'Integritaet'],
|
||||
ACCESS_AUTHORIZATION: ['Vertraulichkeit', 'Nichtverkettung'],
|
||||
TRANSFER_CONTROL: ['Vertraulichkeit', 'Integritaet'],
|
||||
INPUT_CONTROL: ['Integritaet', 'Transparenz'],
|
||||
ORDER_CONTROL: ['Transparenz', 'Intervenierbarkeit'],
|
||||
AVAILABILITY: ['Verfuegbarkeit'],
|
||||
SEPARATION: ['Nichtverkettung', 'Datenminimierung'],
|
||||
ENCRYPTION: ['Vertraulichkeit', 'Integritaet'],
|
||||
PSEUDONYMIZATION: ['Datenminimierung', 'Nichtverkettung'],
|
||||
RESILIENCE: ['Verfuegbarkeit'],
|
||||
RECOVERY: ['Verfuegbarkeit', 'Integritaet'],
|
||||
REVIEW: ['Transparenz', 'Intervenierbarkeit'],
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps ControlCategory to Spec Module Categories.
|
||||
*/
|
||||
export const MODULE_CATEGORY_MAPPING: Record<ControlCategory, TOMModuleCategory[]> = {
|
||||
ACCESS_CONTROL: ['IDENTITY_AUTH'],
|
||||
ADMISSION_CONTROL: ['IDENTITY_AUTH'],
|
||||
ACCESS_AUTHORIZATION: ['IDENTITY_AUTH', 'DOCUMENTATION'],
|
||||
TRANSFER_CONTROL: ['DOCUMENTATION'],
|
||||
INPUT_CONTROL: ['LOGGING'],
|
||||
ORDER_CONTROL: ['DOCUMENTATION'],
|
||||
AVAILABILITY: ['REVIEW'],
|
||||
SEPARATION: ['SEPARATION'],
|
||||
ENCRYPTION: ['IDENTITY_AUTH'],
|
||||
PSEUDONYMIZATION: ['SEPARATION', 'DELETION'],
|
||||
RESILIENCE: ['REVIEW'],
|
||||
RECOVERY: ['REVIEW'],
|
||||
REVIEW: ['REVIEW', 'TRAINING'],
|
||||
}
|
||||
|
||||
@@ -314,10 +314,23 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'use-case-assessment',
|
||||
id: 'compliance-scope',
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 2,
|
||||
name: 'Compliance Scope',
|
||||
nameShort: 'Scope',
|
||||
description: 'Umfang und Tiefe Ihrer Compliance-Dokumentation bestimmen',
|
||||
url: '/sdk/compliance-scope',
|
||||
checkpointId: 'CP-SCOPE',
|
||||
prerequisiteSteps: ['company-profile'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'use-case-assessment',
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 3,
|
||||
name: 'Anwendungsfall-Erfassung',
|
||||
nameShort: 'Anwendung',
|
||||
description: 'AI-Anwendungsfälle strukturiert dokumentieren',
|
||||
@@ -330,7 +343,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
id: 'import',
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 3,
|
||||
order: 4,
|
||||
name: 'Dokument-Import',
|
||||
nameShort: 'Import',
|
||||
description: 'Bestehende Dokumente hochladen (Bestandskunden)',
|
||||
@@ -343,7 +356,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
id: 'screening',
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 4,
|
||||
order: 5,
|
||||
name: 'System Screening',
|
||||
nameShort: 'Screening',
|
||||
description: 'SBOM + Security Check',
|
||||
@@ -356,7 +369,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
id: 'modules',
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 5,
|
||||
order: 6,
|
||||
name: 'Compliance Modules',
|
||||
nameShort: 'Module',
|
||||
description: 'Abgleich welche Regulierungen gelten',
|
||||
@@ -365,6 +378,19 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
prerequisiteSteps: ['screening'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'source-policy',
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 7,
|
||||
name: 'Source Policy',
|
||||
nameShort: 'Quellen',
|
||||
description: 'Datenquellen-Governance & Whitelist',
|
||||
url: '/sdk/source-policy',
|
||||
checkpointId: 'CP-SPOL',
|
||||
prerequisiteSteps: ['modules'],
|
||||
isOptional: false,
|
||||
},
|
||||
|
||||
// =============================================================================
|
||||
// PAKET 2: ANALYSE (Assessment)
|
||||
@@ -379,7 +405,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
description: 'Prüfaspekte aus Regulierungen ableiten',
|
||||
url: '/sdk/requirements',
|
||||
checkpointId: 'CP-REQ',
|
||||
prerequisiteSteps: ['modules'],
|
||||
prerequisiteSteps: ['source-policy'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
@@ -447,6 +473,19 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
prerequisiteSteps: ['ai-act'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'audit-report',
|
||||
phase: 1,
|
||||
package: 'analyse',
|
||||
order: 7,
|
||||
name: 'Audit Report',
|
||||
nameShort: 'Report',
|
||||
description: 'Audit-Sitzungen & PDF-Report',
|
||||
url: '/sdk/audit-report',
|
||||
checkpointId: 'CP-AREP',
|
||||
prerequisiteSteps: ['audit-checklist'],
|
||||
isOptional: false,
|
||||
},
|
||||
|
||||
// =============================================================================
|
||||
// PAKET 3: DOKUMENTATION (Compliance Docs)
|
||||
@@ -461,7 +500,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
description: 'NIS2, DSGVO, AI Act Pflichten',
|
||||
url: '/sdk/obligations',
|
||||
checkpointId: 'CP-OBL',
|
||||
prerequisiteSteps: ['audit-checklist'],
|
||||
prerequisiteSteps: ['audit-report'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
@@ -572,6 +611,19 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
prerequisiteSteps: ['cookie-banner'],
|
||||
isOptional: true,
|
||||
},
|
||||
{
|
||||
id: 'workflow',
|
||||
phase: 2,
|
||||
package: 'rechtliche-texte',
|
||||
order: 5,
|
||||
name: 'Document Workflow',
|
||||
nameShort: 'Workflow',
|
||||
description: 'Versionierung & Freigabe-Workflow',
|
||||
url: '/sdk/workflow',
|
||||
checkpointId: 'CP-WRKF',
|
||||
prerequisiteSteps: ['document-generator'],
|
||||
isOptional: false,
|
||||
},
|
||||
|
||||
// =============================================================================
|
||||
// PAKET 5: BETRIEB (Operations)
|
||||
@@ -586,7 +638,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
description: 'Betroffenenrechte-Portal',
|
||||
url: '/sdk/dsr',
|
||||
checkpointId: 'CP-DSR',
|
||||
prerequisiteSteps: ['cookie-banner'],
|
||||
prerequisiteSteps: ['workflow'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
@@ -615,6 +667,32 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
prerequisiteSteps: ['escalations'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'consent-management',
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 4,
|
||||
name: 'Consent Verwaltung',
|
||||
nameShort: 'Consent Mgmt',
|
||||
description: 'Dokument-Lifecycle & DSGVO-Prozesse',
|
||||
url: '/sdk/consent-management',
|
||||
checkpointId: 'CP-CMGMT',
|
||||
prerequisiteSteps: ['vendor-compliance'],
|
||||
isOptional: false,
|
||||
},
|
||||
{
|
||||
id: 'notfallplan',
|
||||
phase: 2,
|
||||
package: 'betrieb',
|
||||
order: 5,
|
||||
name: 'Notfallplan & Breach Response',
|
||||
nameShort: 'Notfallplan',
|
||||
description: 'Datenpannen-Management nach Art. 33/34 DSGVO',
|
||||
url: '/sdk/notfallplan',
|
||||
checkpointId: 'CP-NOTF',
|
||||
prerequisiteSteps: ['consent-management'],
|
||||
isOptional: false,
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
@@ -1208,6 +1286,9 @@ export interface SDKState {
|
||||
// Company Profile (collected before use cases)
|
||||
companyProfile: CompanyProfile | null
|
||||
|
||||
// Compliance Scope (determines depth level L1-L4)
|
||||
complianceScope: import('./compliance-scope-types').ComplianceScopeState | null
|
||||
|
||||
// Progress
|
||||
currentPhase: SDKPhase
|
||||
currentStep: string
|
||||
@@ -1265,6 +1346,8 @@ export type SDKAction =
|
||||
| { type: 'SET_CUSTOMER_TYPE'; payload: CustomerType }
|
||||
| { type: 'SET_COMPANY_PROFILE'; payload: CompanyProfile }
|
||||
| { type: 'UPDATE_COMPANY_PROFILE'; payload: Partial<CompanyProfile> }
|
||||
| { type: 'SET_COMPLIANCE_SCOPE'; payload: import('./compliance-scope-types').ComplianceScopeState }
|
||||
| { type: 'UPDATE_COMPLIANCE_SCOPE'; payload: Partial<import('./compliance-scope-types').ComplianceScopeState> }
|
||||
| { type: 'ADD_IMPORTED_DOCUMENT'; payload: ImportedDocument }
|
||||
| { type: 'UPDATE_IMPORTED_DOCUMENT'; payload: { id: string; data: Partial<ImportedDocument> } }
|
||||
| { type: 'DELETE_IMPORTED_DOCUMENT'; payload: string }
|
||||
@@ -1783,3 +1866,243 @@ export const JURISDICTION_LABELS: Record<Jurisdiction, string> = {
|
||||
US: 'United States',
|
||||
INTL: 'International',
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// DSFA RAG TYPES (Source Attribution & Corpus Management)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* License codes for DSFA source documents
|
||||
*/
|
||||
export type DSFALicenseCode =
|
||||
| 'DL-DE-BY-2.0' // Datenlizenz Deutschland – Namensnennung
|
||||
| 'DL-DE-ZERO-2.0' // Datenlizenz Deutschland – Zero
|
||||
| 'CC-BY-4.0' // Creative Commons Attribution 4.0
|
||||
| 'EDPB-LICENSE' // EDPB Document License
|
||||
| 'PUBLIC_DOMAIN' // Public Domain
|
||||
| 'PROPRIETARY' // Internal/Proprietary
|
||||
|
||||
/**
|
||||
* Document types in the DSFA corpus
|
||||
*/
|
||||
export type DSFADocumentType = 'guideline' | 'checklist' | 'regulation' | 'template'
|
||||
|
||||
/**
|
||||
* Category for DSFA chunks (for filtering)
|
||||
*/
|
||||
export type DSFACategory =
|
||||
| 'threshold_analysis'
|
||||
| 'risk_assessment'
|
||||
| 'mitigation'
|
||||
| 'consultation'
|
||||
| 'documentation'
|
||||
| 'process'
|
||||
| 'criteria'
|
||||
|
||||
/**
|
||||
* DSFA source registry entry
|
||||
*/
|
||||
export interface DSFASource {
|
||||
id: string
|
||||
sourceCode: string
|
||||
name: string
|
||||
fullName?: string
|
||||
organization?: string
|
||||
sourceUrl?: string
|
||||
eurLexCelex?: string
|
||||
licenseCode: DSFALicenseCode
|
||||
licenseName: string
|
||||
licenseUrl?: string
|
||||
attributionRequired: boolean
|
||||
attributionText: string
|
||||
documentType?: DSFADocumentType
|
||||
language: string
|
||||
}
|
||||
|
||||
/**
|
||||
* DSFA document entry
|
||||
*/
|
||||
export interface DSFADocument {
|
||||
id: string
|
||||
sourceId: string
|
||||
title: string
|
||||
description?: string
|
||||
fileName?: string
|
||||
fileType?: string
|
||||
fileSizeBytes?: number
|
||||
minioBucket: string
|
||||
minioPath?: string
|
||||
originalUrl?: string
|
||||
ocrProcessed: boolean
|
||||
textExtracted: boolean
|
||||
chunksGenerated: number
|
||||
lastIndexedAt?: string
|
||||
metadata: Record<string, unknown>
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
/**
|
||||
* DSFA chunk with full attribution
|
||||
*/
|
||||
export interface DSFAChunk {
|
||||
chunkId: string
|
||||
content: string
|
||||
sectionTitle?: string
|
||||
pageNumber?: number
|
||||
category?: DSFACategory
|
||||
documentId: string
|
||||
documentTitle?: string
|
||||
sourceId: string
|
||||
sourceCode: string
|
||||
sourceName: string
|
||||
attributionText: string
|
||||
licenseCode: DSFALicenseCode
|
||||
licenseName: string
|
||||
licenseUrl?: string
|
||||
attributionRequired: boolean
|
||||
sourceUrl?: string
|
||||
documentType?: DSFADocumentType
|
||||
}
|
||||
|
||||
/**
|
||||
* DSFA search result with score and attribution
|
||||
*/
|
||||
export interface DSFASearchResult {
|
||||
chunkId: string
|
||||
content: string
|
||||
score: number
|
||||
sourceCode: string
|
||||
sourceName: string
|
||||
attributionText: string
|
||||
licenseCode: DSFALicenseCode
|
||||
licenseName: string
|
||||
licenseUrl?: string
|
||||
attributionRequired: boolean
|
||||
sourceUrl?: string
|
||||
documentType?: DSFADocumentType
|
||||
category?: DSFACategory
|
||||
sectionTitle?: string
|
||||
pageNumber?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* DSFA search response with aggregated attribution
|
||||
*/
|
||||
export interface DSFASearchResponse {
|
||||
query: string
|
||||
results: DSFASearchResult[]
|
||||
totalResults: number
|
||||
licensesUsed: string[]
|
||||
attributionNotice: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Source statistics for dashboard
|
||||
*/
|
||||
export interface DSFASourceStats {
|
||||
sourceId: string
|
||||
sourceCode: string
|
||||
name: string
|
||||
organization?: string
|
||||
licenseCode: DSFALicenseCode
|
||||
documentType?: DSFADocumentType
|
||||
documentCount: number
|
||||
chunkCount: number
|
||||
lastIndexedAt?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Corpus statistics for dashboard
|
||||
*/
|
||||
export interface DSFACorpusStats {
|
||||
sources: DSFASourceStats[]
|
||||
totalSources: number
|
||||
totalDocuments: number
|
||||
totalChunks: number
|
||||
qdrantCollection: string
|
||||
qdrantPointsCount: number
|
||||
qdrantStatus: string
|
||||
}
|
||||
|
||||
/**
|
||||
* License information
|
||||
*/
|
||||
export interface DSFALicenseInfo {
|
||||
code: DSFALicenseCode
|
||||
name: string
|
||||
url?: string
|
||||
attributionRequired: boolean
|
||||
modificationAllowed: boolean
|
||||
commercialUse: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingestion request for DSFA documents
|
||||
*/
|
||||
export interface DSFAIngestRequest {
|
||||
documentUrl?: string
|
||||
documentText?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingestion response
|
||||
*/
|
||||
export interface DSFAIngestResponse {
|
||||
sourceCode: string
|
||||
documentId?: string
|
||||
chunksCreated: number
|
||||
message: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for SourceAttribution component
|
||||
*/
|
||||
export interface SourceAttributionProps {
|
||||
sources: Array<{
|
||||
sourceCode: string
|
||||
sourceName: string
|
||||
attributionText: string
|
||||
licenseCode: DSFALicenseCode
|
||||
sourceUrl?: string
|
||||
score?: number
|
||||
}>
|
||||
compact?: boolean
|
||||
showScores?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* License code display labels
|
||||
*/
|
||||
export const DSFA_LICENSE_LABELS: Record<DSFALicenseCode, string> = {
|
||||
'DL-DE-BY-2.0': 'Datenlizenz DE – Namensnennung 2.0',
|
||||
'DL-DE-ZERO-2.0': 'Datenlizenz DE – Zero 2.0',
|
||||
'CC-BY-4.0': 'CC BY 4.0 International',
|
||||
'EDPB-LICENSE': 'EDPB Document License',
|
||||
'PUBLIC_DOMAIN': 'Public Domain',
|
||||
'PROPRIETARY': 'Proprietary',
|
||||
}
|
||||
|
||||
/**
|
||||
* Document type display labels
|
||||
*/
|
||||
export const DSFA_DOCUMENT_TYPE_LABELS: Record<DSFADocumentType, string> = {
|
||||
guideline: 'Leitlinie',
|
||||
checklist: 'Prüfliste',
|
||||
regulation: 'Verordnung',
|
||||
template: 'Vorlage',
|
||||
}
|
||||
|
||||
/**
|
||||
* Category display labels
|
||||
*/
|
||||
export const DSFA_CATEGORY_LABELS: Record<DSFACategory, string> = {
|
||||
threshold_analysis: 'Schwellwertanalyse',
|
||||
risk_assessment: 'Risikobewertung',
|
||||
mitigation: 'Risikominderung',
|
||||
consultation: 'Behördenkonsultation',
|
||||
documentation: 'Dokumentation',
|
||||
process: 'Prozessschritte',
|
||||
criteria: 'Kriterien',
|
||||
}
|
||||
|
||||
630
admin-v2/lib/sdk/vvt-baseline-catalog.ts
Normal file
630
admin-v2/lib/sdk/vvt-baseline-catalog.ts
Normal file
@@ -0,0 +1,630 @@
|
||||
/**
|
||||
* VVT Baseline-Katalog
|
||||
*
|
||||
* Vordefinierte Verarbeitungstaetigkeiten als Templates.
|
||||
* Werden vom Profiling-Fragebogen (Generator) genutzt, um
|
||||
* auf Basis der Antworten VVT-Eintraege vorzubefuellen.
|
||||
*/
|
||||
|
||||
import type { VVTActivity, 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[]
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// BASELINE TEMPLATES
|
||||
// =============================================================================
|
||||
|
||||
export const VVT_BASELINE_CATALOG: BaselineTemplate[] = [
|
||||
// ==================== HR ====================
|
||||
{
|
||||
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'],
|
||||
},
|
||||
|
||||
// ==================== FINANCE ====================
|
||||
{
|
||||
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'],
|
||||
},
|
||||
|
||||
// ==================== SALES / CRM ====================
|
||||
{
|
||||
templateId: 'sales-kundenverwaltung',
|
||||
businessFunction: 'sales_crm',
|
||||
name: 'Kundenverwaltung (CRM)',
|
||||
description: 'Verwaltung von Kundenbeziehungen, Kontakten und Vertriebsaktivitaeten',
|
||||
purposes: ['Kundenbetreuung', 'Vertragserfuellung', 'Vertriebssteuerung'],
|
||||
legalBases: [
|
||||
{ type: 'CONTRACT', description: 'Kundenvertrag', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'Kundenbindung', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'PROSPECTIVE_CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'ADDRESS', 'CONTRACT_DATA', 'COMMUNICATION_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'Vertrieb' },
|
||||
{ type: 'INTERNAL', name: 'Kundenservice' },
|
||||
],
|
||||
retentionPeriod: { duration: 3, durationUnit: 'YEARS', description: '3 Jahre nach letzter Interaktion (Verjaeherung)', legalBasis: 'BGB § 195', deletionProcedure: 'Loeschung nach Inaktivitaetsfrist' },
|
||||
tomDescription: 'Zugriffskontrolle nach Kundengruppen, Verschluesselung, regemaessige Datenpflege',
|
||||
structuredToms: {
|
||||
accessControl: ['RBAC nach Vertriebsgebiet', 'Kundendaten-Owner'],
|
||||
confidentiality: ['Verschluesselung in CRM', 'VPN fuer Fernzugriff'],
|
||||
integrity: ['Aenderungshistorie im CRM'],
|
||||
availability: ['Cloud-CRM mit SLA 99.9%'],
|
||||
separation: ['Mandantentrennung'],
|
||||
},
|
||||
typicalSystems: ['CRM-System', 'E-Mail', 'Telefon'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['sales', 'crm', 'kunden', 'vertrieb'],
|
||||
},
|
||||
{
|
||||
templateId: 'sales-vertriebssteuerung',
|
||||
businessFunction: 'sales_crm',
|
||||
name: 'Vertriebssteuerung',
|
||||
description: 'Analyse von Vertriebskennzahlen und Pipeline-Management',
|
||||
purposes: ['Vertriebsoptimierung', 'Umsatzplanung'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'Unternehmenssteuerung', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'PROSPECTIVE_CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTRACT_DATA', 'COMMUNICATION_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'Vertriebsleitung' },
|
||||
{ type: 'INTERNAL', name: 'Geschaeftsfuehrung' },
|
||||
],
|
||||
retentionPeriod: { duration: 3, durationUnit: 'YEARS', description: '3 Jahre', legalBasis: 'Berechtigtes Interesse', deletionProcedure: 'Anonymisierung nach Ablauf' },
|
||||
tomDescription: 'Aggregierte Auswertungen wo moeglich, Zugriffsbeschraenkung auf Fuehrungsebene',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur Management'],
|
||||
confidentiality: ['Aggregierte Reports bevorzugt'],
|
||||
integrity: ['Nachvollziehbare Berechnungen'],
|
||||
availability: ['Dashboard-Verfuegbarkeit'],
|
||||
separation: ['Reporting getrennt von Operativdaten'],
|
||||
},
|
||||
typicalSystems: ['CRM-System', 'BI-Tool'],
|
||||
protectionLevel: 'LOW',
|
||||
dpiaRequired: false,
|
||||
tags: ['sales', 'vertrieb', 'reporting'],
|
||||
},
|
||||
|
||||
// ==================== MARKETING ====================
|
||||
{
|
||||
templateId: 'marketing-newsletter',
|
||||
businessFunction: 'marketing',
|
||||
name: 'Newsletter-Marketing',
|
||||
description: 'Versand von Marketing-E-Mails und Newslettern an Abonnenten',
|
||||
purposes: ['Direktmarketing', 'Kundenbindung', 'Informationsversand'],
|
||||
legalBases: [
|
||||
{ type: 'CONSENT', description: 'Einwilligung zum Newsletter-Empfang', reference: 'Art. 6 Abs. 1 lit. a DSGVO, § 7 Abs. 2 UWG' },
|
||||
],
|
||||
dataSubjectCategories: ['NEWSLETTER_SUBSCRIBERS', 'CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'USAGE_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'PROCESSOR', name: 'E-Mail-Dienstleister' },
|
||||
{ type: 'INTERNAL', name: 'Marketing-Abteilung' },
|
||||
],
|
||||
retentionPeriod: { description: 'Bis zum Widerruf der Einwilligung', deletionProcedure: 'Sofortige Loeschung bei Abmeldung' },
|
||||
tomDescription: 'Double-Opt-In, Abmeldelink in jeder E-Mail, Einwilligungsprotokollierung',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur Marketing-Team'],
|
||||
confidentiality: ['TLS-Versand', 'Keine Weitergabe an Dritte'],
|
||||
integrity: ['Einwilligungsnachweis (Timestamp, IP, Version)'],
|
||||
availability: ['Redundanter E-Mail-Service'],
|
||||
separation: ['Newsletter-Liste getrennt von CRM'],
|
||||
},
|
||||
typicalSystems: ['Newsletter-Tool', 'E-Mail-Marketing-Plattform'],
|
||||
protectionLevel: 'LOW',
|
||||
dpiaRequired: false,
|
||||
tags: ['marketing', 'newsletter', 'email'],
|
||||
},
|
||||
{
|
||||
templateId: 'marketing-website-analytics',
|
||||
businessFunction: 'marketing',
|
||||
name: 'Website-Analytics',
|
||||
description: 'Analyse des Nutzerverhaltens auf der Website mittels Tracking-Tools',
|
||||
purposes: ['Website-Optimierung', 'Reichweitenmessung'],
|
||||
legalBases: [
|
||||
{ type: 'CONSENT', description: 'Cookie-Einwilligung', reference: 'Art. 6 Abs. 1 lit. a DSGVO, § 25 TDDDG' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'DEVICE_ID', 'USAGE_DATA', 'LOCATION_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'PROCESSOR', name: 'Analytics-Anbieter' },
|
||||
{ type: 'INTERNAL', name: 'Marketing' },
|
||||
],
|
||||
retentionPeriod: { duration: 14, durationUnit: 'MONTHS', description: '14 Monate', deletionProcedure: 'Automatische Loeschung/Anonymisierung' },
|
||||
tomDescription: 'IP-Anonymisierung, Cookie-Consent-Management, Opt-Out-Moeglichkeit',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur Webanalyse-Team'],
|
||||
confidentiality: ['IP-Anonymisierung', 'Pseudonymisierung'],
|
||||
integrity: ['Datenqualitaetspruefung'],
|
||||
availability: ['CDN-basiertes Tracking'],
|
||||
separation: ['Analytics getrennt von personenbezogenen Profilen'],
|
||||
},
|
||||
typicalSystems: ['Matomo', 'Plausible', 'Google Analytics'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['marketing', 'analytics', 'website', 'tracking'],
|
||||
},
|
||||
{
|
||||
templateId: 'marketing-social-media',
|
||||
businessFunction: 'marketing',
|
||||
name: 'Social-Media-Marketing',
|
||||
description: 'Betrieb von Social-Media-Kanaelen und Interaktion mit Nutzern',
|
||||
purposes: ['Oeffentlichkeitsarbeit', 'Kundeninteraktion'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'Marketing', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['WEBSITE_USERS', 'CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'USAGE_DATA', 'COMMUNICATION_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'CONTROLLER', name: 'Social-Media-Plattform (gemeinsame Verantwortlichkeit)' },
|
||||
],
|
||||
retentionPeriod: { description: 'Abhaengig von Plattform-Einstellungen', deletionProcedure: 'Regelmaessige Pruefung und Bereinigung' },
|
||||
tomDescription: 'Datenschutzeinstellungen der Plattform, gemeinsame Verantwortlichkeit gemaess Art. 26',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur Social-Media-Manager', 'Passwort-Manager'],
|
||||
confidentiality: ['Plattform-Datenschutzeinstellungen'],
|
||||
integrity: ['Redaktionsplan'],
|
||||
availability: ['Multi-Kanal-Management'],
|
||||
separation: ['Geschaeftlich/Privat getrennt'],
|
||||
},
|
||||
typicalSystems: ['Social-Media-Plattformen', 'Social-Media-Management-Tool'],
|
||||
protectionLevel: 'LOW',
|
||||
dpiaRequired: false,
|
||||
tags: ['marketing', 'social-media'],
|
||||
},
|
||||
|
||||
// ==================== SUPPORT ====================
|
||||
{
|
||||
templateId: 'support-ticketsystem',
|
||||
businessFunction: 'support',
|
||||
name: 'Kundenservice / Ticketsystem',
|
||||
description: 'Bearbeitung von Kundenanfragen und Support-Tickets',
|
||||
purposes: ['Kundenservice', 'Reklamationsbearbeitung', 'Vertragserfuellung'],
|
||||
legalBases: [
|
||||
{ type: 'CONTRACT', description: 'Kundenvertrag', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['CUSTOMERS', 'APP_USERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'CONTRACT_DATA', 'COMMUNICATION_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'Support-Team' },
|
||||
{ type: 'PROCESSOR', name: 'Helpdesk-Software-Anbieter' },
|
||||
],
|
||||
retentionPeriod: { duration: 3, durationUnit: 'YEARS', description: '3 Jahre nach Ticketschliessung', legalBasis: 'BGB § 195', deletionProcedure: 'Automatische Loeschung geschlossener Tickets' },
|
||||
tomDescription: 'Zugriffskontrolle nach Ticket-Owner, Verschluesselung, Audit-Trail',
|
||||
structuredToms: {
|
||||
accessControl: ['Ticket-basierte Zugriffskontrolle', 'Agent-Rollen'],
|
||||
confidentiality: ['TLS', 'Verschluesselung'],
|
||||
integrity: ['Ticket-Historie unveraenderbar'],
|
||||
availability: ['Hochverfuegbarer Helpdesk'],
|
||||
separation: ['Mandantentrennung'],
|
||||
},
|
||||
typicalSystems: ['Helpdesk-Software', 'E-Mail', 'Chat'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['support', 'kundenservice', 'tickets'],
|
||||
},
|
||||
|
||||
// ==================== IT OPERATIONS ====================
|
||||
{
|
||||
templateId: 'it-systemadministration',
|
||||
businessFunction: 'it_operations',
|
||||
name: 'Systemadministration',
|
||||
description: 'Verwaltung von IT-Systemen, Benutzerkonten und Zugriffsrechten',
|
||||
purposes: ['IT-Betrieb', 'Benutzerverwaltung', 'Sicherheitsueberwachung'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'IT-Sicherheit', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
{ type: 'CONTRACT', description: 'Bereitstellung IT-Dienste', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'APP_USERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'LOGIN_DATA', 'IP_ADDRESS', 'DEVICE_ID'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'IT-Abteilung' },
|
||||
{ type: 'PROCESSOR', name: 'IT-Dienstleister' },
|
||||
],
|
||||
retentionPeriod: { duration: 1, durationUnit: 'YEARS', description: '1 Jahr nach Kontodeaktivierung', deletionProcedure: 'Automatische Loeschung deaktivierter Konten' },
|
||||
tomDescription: 'PAM, MFA, Protokollierung, regelmaessige Rechtereviews',
|
||||
structuredToms: {
|
||||
accessControl: ['PAM (Privileged Access Management)', 'MFA', 'Regelmaessige Rechtereviews'],
|
||||
confidentiality: ['Verschluesselung', 'Passwort-Policies'],
|
||||
integrity: ['Change Management', 'Konfigurationsmanagement'],
|
||||
availability: ['Redundanz', 'Monitoring', 'Alerting'],
|
||||
separation: ['Prod/Dev/Staging getrennt', 'Admin-Netze isoliert'],
|
||||
},
|
||||
typicalSystems: ['Active Directory / IAM', 'Monitoring', 'ITSM'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['it', 'admin', 'benutzerverwaltung'],
|
||||
},
|
||||
{
|
||||
templateId: 'it-backup',
|
||||
businessFunction: 'it_operations',
|
||||
name: 'Backup & Recovery',
|
||||
description: 'Sicherung und Wiederherstellung von Daten und Systemen',
|
||||
purposes: ['Datensicherung', 'Disaster Recovery', 'Geschaeftskontinuitaet'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'Datensicherheit', reference: 'Art. 6 Abs. 1 lit. f DSGVO, Art. 32 DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'CUSTOMERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'CONTRACT_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'PROCESSOR', name: 'Backup-Dienstleister' },
|
||||
{ type: 'INTERNAL', name: 'IT-Abteilung' },
|
||||
],
|
||||
retentionPeriod: { duration: 90, durationUnit: 'DAYS', description: '90 Tage Aufbewahrung der Backups', deletionProcedure: 'Automatische Rotation und Loeschung' },
|
||||
tomDescription: 'Verschluesselung, Zugriffskontrolle, regelmaessige Wiederherstellungstests',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur Backup-Admins', 'Separater Encryption Key'],
|
||||
confidentiality: ['AES-256-Verschluesselung', 'Verschluesselter Transport'],
|
||||
integrity: ['Checksummen-Pruefung', 'Regelmaessige Restore-Tests'],
|
||||
availability: ['3-2-1-Backup-Regel', 'Georedundanz'],
|
||||
separation: ['Backup-Netzwerk isoliert'],
|
||||
},
|
||||
typicalSystems: ['Backup-Software', 'Cloud-Storage'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['it', 'backup', 'recovery'],
|
||||
},
|
||||
{
|
||||
templateId: 'it-logging',
|
||||
businessFunction: 'it_operations',
|
||||
name: 'Protokollierung & Logging',
|
||||
description: 'Erfassung von System- und Sicherheitslogs zur Fehlerbehebung und Angriffserkennung',
|
||||
purposes: ['IT-Sicherheit', 'Fehlerbehebung', 'Angriffserkennung'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'IT-Sicherheit und Betrieb', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'APP_USERS', 'WEBSITE_USERS'],
|
||||
personalDataCategories: ['IP_ADDRESS', 'LOGIN_DATA', 'USAGE_DATA', 'DEVICE_ID'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'IT-Sicherheit' },
|
||||
{ type: 'PROCESSOR', name: 'SIEM-Anbieter' },
|
||||
],
|
||||
retentionPeriod: { duration: 90, durationUnit: 'DAYS', description: '90 Tage (Standard) / 1 Jahr (Security-Logs)', deletionProcedure: 'Automatische Rotation' },
|
||||
tomDescription: 'SIEM, Integritaetsschutz der Logs, Zugriffskontrolle, Pseudonymisierung',
|
||||
structuredToms: {
|
||||
accessControl: ['Nur Security-Team', 'Read-Only fuer Auditoren'],
|
||||
confidentiality: ['Pseudonymisierung wo moeglich'],
|
||||
integrity: ['WORM-Storage fuer Security-Logs', 'Hashketten'],
|
||||
availability: ['Redundante Log-Speicherung'],
|
||||
separation: ['Zentrale Log-Infrastruktur getrennt'],
|
||||
},
|
||||
typicalSystems: ['SIEM', 'ELK Stack', 'Syslog'],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
tags: ['it', 'logging', 'sicherheit'],
|
||||
},
|
||||
{
|
||||
templateId: 'it-iam',
|
||||
businessFunction: 'it_operations',
|
||||
name: 'Identity & Access Management',
|
||||
description: 'Verwaltung von Identitaeten, Authentifizierung und Autorisierung',
|
||||
purposes: ['Zugriffskontrolle', 'Identitaetsverwaltung', 'Compliance'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'IT-Sicherheit', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
{ type: 'CONTRACT', description: 'Bereitstellung IT-Dienste', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'APP_USERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'LOGIN_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'IT-Abteilung' },
|
||||
{ type: 'PROCESSOR', name: 'IAM-Anbieter' },
|
||||
],
|
||||
retentionPeriod: { duration: 6, durationUnit: 'MONTHS', description: '6 Monate nach Kontodeaktivierung', deletionProcedure: 'Automatische Deprovisionierung' },
|
||||
tomDescription: 'MFA, SSO, regelmaessige Access Reviews, Least-Privilege-Prinzip',
|
||||
structuredToms: {
|
||||
accessControl: ['MFA', 'SSO', 'Least Privilege', 'Regelmaessige Reviews'],
|
||||
confidentiality: ['Passwort-Hashing (bcrypt)', 'Token-basierte Auth'],
|
||||
integrity: ['Audit-Trail aller Aenderungen'],
|
||||
availability: ['Hochverfuegbarer IdP'],
|
||||
separation: ['Identitaeten pro Mandant'],
|
||||
},
|
||||
typicalSystems: ['IAM-System', 'SSO Provider', 'MFA'],
|
||||
protectionLevel: 'HIGH',
|
||||
dpiaRequired: false,
|
||||
tags: ['it', 'iam', 'zugriffskontrolle'],
|
||||
},
|
||||
|
||||
// ==================== OTHER ====================
|
||||
{
|
||||
templateId: 'other-videokonferenz',
|
||||
businessFunction: 'other',
|
||||
name: 'Videokonferenzen',
|
||||
description: 'Durchfuehrung von Video-Meetings und Online-Besprechungen',
|
||||
purposes: ['Interne Kommunikation', 'Kundeninteraktion', 'Remote-Arbeit'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'Geschaeftliche Kommunikation', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
{ type: 'CONTRACT', description: 'Arbeitsvertrag (bei Mitarbeitern)', reference: 'Art. 6 Abs. 1 lit. b DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['EMPLOYEES', 'CUSTOMERS', 'BUSINESS_PARTNERS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'PHOTO_VIDEO', 'IP_ADDRESS', 'COMMUNICATION_DATA'],
|
||||
recipientCategories: [
|
||||
{ type: 'PROCESSOR', name: 'Videokonferenz-Anbieter' },
|
||||
],
|
||||
retentionPeriod: { description: 'Keine dauerhafte Speicherung von Meetings (sofern nicht aufgezeichnet)', deletionProcedure: 'Aufzeichnungen nach Verwendungszweck loeschen' },
|
||||
tomDescription: 'Ende-zu-Ende-Verschluesselung, Warteraum, Passwortschutz, Aufnahme nur mit Einwilligung',
|
||||
structuredToms: {
|
||||
accessControl: ['Meeting-Passwort', 'Warteraum', 'Host-Kontrolle'],
|
||||
confidentiality: ['TLS / E2E-Verschluesselung'],
|
||||
integrity: ['Teilnehmerliste'],
|
||||
availability: ['Redundante Infrastruktur'],
|
||||
separation: ['Separate Meeting-Raeume'],
|
||||
},
|
||||
typicalSystems: ['Jitsi', 'Zoom', 'Teams', 'Google Meet'],
|
||||
protectionLevel: 'LOW',
|
||||
dpiaRequired: false,
|
||||
tags: ['kommunikation', 'video', 'meeting'],
|
||||
},
|
||||
{
|
||||
templateId: 'other-besuchermanagement',
|
||||
businessFunction: 'other',
|
||||
name: 'Besuchermanagement',
|
||||
description: 'Erfassung und Verwaltung von Besuchern am Firmenstandort',
|
||||
purposes: ['Zutrittskontrolle', 'Sicherheit', 'Nachverfolgung'],
|
||||
legalBases: [
|
||||
{ type: 'LEGITIMATE_INTEREST', description: 'Gebaeudesicherheit', reference: 'Art. 6 Abs. 1 lit. f DSGVO' },
|
||||
],
|
||||
dataSubjectCategories: ['VISITORS'],
|
||||
personalDataCategories: ['NAME', 'CONTACT', 'PHOTO_VIDEO'],
|
||||
recipientCategories: [
|
||||
{ type: 'INTERNAL', name: 'Empfang / Sicherheit' },
|
||||
],
|
||||
retentionPeriod: { duration: 30, durationUnit: 'DAYS', description: '30 Tage nach Besuch', deletionProcedure: 'Automatische Loeschung' },
|
||||
tomDescription: 'Besucherausweise, Begleitpflicht, zeitlich begrenzter Zugang',
|
||||
structuredToms: {
|
||||
accessControl: ['Besucherausweise', 'Begleitpflicht'],
|
||||
confidentiality: ['Besucherliste nicht oeffentlich einsehbar'],
|
||||
integrity: ['Besuchsprotokoll'],
|
||||
availability: ['Papier-Backup fuer Besucherliste'],
|
||||
separation: ['Besucherbereich getrennt'],
|
||||
},
|
||||
typicalSystems: ['Besuchermanagementsystem', 'Zutrittskontrollsystem'],
|
||||
protectionLevel: 'LOW',
|
||||
dpiaRequired: false,
|
||||
tags: ['besucher', 'zutritt', 'empfang'],
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// HELPER: Convert template to VVTActivity
|
||||
// =============================================================================
|
||||
|
||||
export function templateToActivity(template: BaselineTemplate, vvtId: string): Omit<import('./vvt-types').VVTActivity, 'id'> & { id: string } {
|
||||
const now = new Date().toISOString()
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
vvtId,
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
purposes: template.purposes,
|
||||
legalBases: template.legalBases,
|
||||
dataSubjectCategories: template.dataSubjectCategories,
|
||||
personalDataCategories: template.personalDataCategories,
|
||||
recipientCategories: template.recipientCategories,
|
||||
thirdCountryTransfers: [],
|
||||
retentionPeriod: template.retentionPeriod,
|
||||
tomDescription: template.tomDescription,
|
||||
businessFunction: template.businessFunction,
|
||||
systems: template.typicalSystems.map((s, i) => ({ systemId: `sys-${i}`, name: s })),
|
||||
deploymentModel: 'cloud',
|
||||
dataSources: [{ type: 'DATA_SUBJECT', description: 'Direkt von der betroffenen Person' }],
|
||||
dataFlows: [],
|
||||
protectionLevel: template.protectionLevel,
|
||||
dpiaRequired: template.dpiaRequired,
|
||||
structuredToms: template.structuredToms,
|
||||
status: 'DRAFT',
|
||||
responsible: '',
|
||||
owner: '',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER: Get templates by business function
|
||||
// =============================================================================
|
||||
|
||||
export function getTemplatesByFunction(fn: BusinessFunction): BaselineTemplate[] {
|
||||
return VVT_BASELINE_CATALOG.filter(t => t.businessFunction === fn)
|
||||
}
|
||||
|
||||
export function getTemplateById(templateId: string): BaselineTemplate | undefined {
|
||||
return VVT_BASELINE_CATALOG.find(t => t.templateId === templateId)
|
||||
}
|
||||
492
admin-v2/lib/sdk/vvt-profiling.ts
Normal file
492
admin-v2/lib/sdk/vvt-profiling.ts
Normal file
@@ -0,0 +1,492 @@
|
||||
/**
|
||||
* VVT Profiling — Generator-Fragebogen
|
||||
*
|
||||
* ~25 Fragen in 6 Schritten, die auf Basis der Antworten
|
||||
* Baseline-Verarbeitungstaetigkeiten generieren.
|
||||
*/
|
||||
|
||||
import { VVT_BASELINE_CATALOG, templateToActivity } from './vvt-baseline-catalog'
|
||||
import { generateVVTId } from './vvt-types'
|
||||
import type { VVTActivity, BusinessFunction } from './vvt-types'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface ProfilingQuestion {
|
||||
id: string
|
||||
step: number
|
||||
question: string
|
||||
type: 'single_choice' | 'multi_choice' | 'number' | 'text' | 'boolean'
|
||||
options?: { value: string; label: string }[]
|
||||
helpText?: string
|
||||
triggersTemplates: string[] // Template-IDs that get activated when answered positively
|
||||
}
|
||||
|
||||
export interface ProfilingStep {
|
||||
step: number
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface ProfilingAnswers {
|
||||
[questionId: string]: string | string[] | number | boolean
|
||||
}
|
||||
|
||||
export interface ProfilingResult {
|
||||
answers: ProfilingAnswers
|
||||
generatedActivities: VVTActivity[]
|
||||
coverageScore: number
|
||||
art30Abs5Exempt: boolean
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// STEPS
|
||||
// =============================================================================
|
||||
|
||||
export const PROFILING_STEPS: ProfilingStep[] = [
|
||||
{ step: 1, title: 'Organisation', description: 'Grunddaten zu Ihrem Unternehmen' },
|
||||
{ step: 2, title: 'Geschaeftsbereiche', description: 'Welche Bereiche sind aktiv?' },
|
||||
{ step: 3, title: 'Systeme & Tools', description: 'Welche IT-Systeme nutzen Sie?' },
|
||||
{ step: 4, title: 'Datenkategorien', description: 'Welche besonderen Daten verarbeiten Sie?' },
|
||||
{ step: 5, title: 'Drittlandtransfers', description: 'Transfers ausserhalb der EU/EWR' },
|
||||
{ step: 6, title: 'Besondere Verarbeitungen', description: 'KI, Scoring, Ueberwachung' },
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// QUESTIONS
|
||||
// =============================================================================
|
||||
|
||||
export const PROFILING_QUESTIONS: ProfilingQuestion[] = [
|
||||
// === STEP 1: Organisation ===
|
||||
{
|
||||
id: 'org_industry',
|
||||
step: 1,
|
||||
question: 'In welcher Branche ist Ihr Unternehmen taetig?',
|
||||
type: 'single_choice',
|
||||
options: [
|
||||
{ value: 'it_software', label: 'IT & Software' },
|
||||
{ value: 'healthcare', label: 'Gesundheitswesen' },
|
||||
{ value: 'education', label: 'Bildung & Erziehung' },
|
||||
{ value: 'finance', label: 'Finanzdienstleistungen' },
|
||||
{ value: 'retail', label: 'Handel & E-Commerce' },
|
||||
{ value: 'manufacturing', label: 'Produktion & Industrie' },
|
||||
{ value: 'consulting', label: 'Beratung & Dienstleistung' },
|
||||
{ value: 'public', label: 'Oeffentlicher Sektor' },
|
||||
{ value: 'other', label: 'Sonstige' },
|
||||
],
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'org_employees',
|
||||
step: 1,
|
||||
question: 'Wie viele Mitarbeiter hat Ihr Unternehmen?',
|
||||
type: 'number',
|
||||
helpText: 'Relevant fuer Art. 30 Abs. 5 DSGVO (Ausnahme < 250 Mitarbeiter)',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'org_locations',
|
||||
step: 1,
|
||||
question: 'An wie vielen Standorten ist Ihr Unternehmen taetig?',
|
||||
type: 'single_choice',
|
||||
options: [
|
||||
{ value: '1', label: '1 Standort' },
|
||||
{ value: '2-5', label: '2-5 Standorte' },
|
||||
{ value: '6-20', label: '6-20 Standorte' },
|
||||
{ value: '20+', label: 'Mehr als 20 Standorte' },
|
||||
],
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'org_b2b_b2c',
|
||||
step: 1,
|
||||
question: 'Welches Geschaeftsmodell betreiben Sie?',
|
||||
type: 'single_choice',
|
||||
options: [
|
||||
{ value: 'b2b', label: 'B2B (Geschaeftskunden)' },
|
||||
{ value: 'b2c', label: 'B2C (Endkunden)' },
|
||||
{ value: 'both', label: 'Beides (B2B + B2C)' },
|
||||
{ value: 'b2g', label: 'B2G (Oeffentlicher Sektor)' },
|
||||
],
|
||||
triggersTemplates: [],
|
||||
},
|
||||
|
||||
// === STEP 2: Geschaeftsbereiche ===
|
||||
{
|
||||
id: 'dept_hr',
|
||||
step: 2,
|
||||
question: 'Haben Sie eine Personalabteilung / HR?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['hr-mitarbeiterverwaltung', 'hr-gehaltsabrechnung', 'hr-zeiterfassung'],
|
||||
},
|
||||
{
|
||||
id: 'dept_recruiting',
|
||||
step: 2,
|
||||
question: 'Betreiben Sie aktives Recruiting / Bewerbermanagement?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['hr-bewerbermanagement'],
|
||||
},
|
||||
{
|
||||
id: 'dept_finance',
|
||||
step: 2,
|
||||
question: 'Haben Sie eine Finanz-/Buchhaltungsabteilung?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['finance-buchhaltung', 'finance-zahlungsverkehr'],
|
||||
},
|
||||
{
|
||||
id: 'dept_sales',
|
||||
step: 2,
|
||||
question: 'Haben Sie einen Vertrieb / Kundenverwaltung?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['sales-kundenverwaltung', 'sales-vertriebssteuerung'],
|
||||
},
|
||||
{
|
||||
id: 'dept_marketing',
|
||||
step: 2,
|
||||
question: 'Betreiben Sie Marketing-Aktivitaeten?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['marketing-social-media'],
|
||||
},
|
||||
{
|
||||
id: 'dept_support',
|
||||
step: 2,
|
||||
question: 'Haben Sie einen Kundenservice / Support?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['support-ticketsystem'],
|
||||
},
|
||||
|
||||
// === STEP 3: Systeme & Tools ===
|
||||
{
|
||||
id: 'sys_crm',
|
||||
step: 3,
|
||||
question: 'Nutzen Sie ein CRM-System (z.B. Salesforce, HubSpot, Pipedrive)?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['sales-kundenverwaltung'],
|
||||
},
|
||||
{
|
||||
id: 'sys_website_analytics',
|
||||
step: 3,
|
||||
question: 'Nutzen Sie Website-Analytics (z.B. Matomo, Google Analytics)?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['marketing-website-analytics'],
|
||||
},
|
||||
{
|
||||
id: 'sys_newsletter',
|
||||
step: 3,
|
||||
question: 'Versenden Sie Newsletter (z.B. Mailchimp, CleverReach)?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['marketing-newsletter'],
|
||||
},
|
||||
{
|
||||
id: 'sys_video',
|
||||
step: 3,
|
||||
question: 'Nutzen Sie Videokonferenz-Tools (z.B. Zoom, Teams, Jitsi)?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['other-videokonferenz'],
|
||||
},
|
||||
{
|
||||
id: 'sys_erp',
|
||||
step: 3,
|
||||
question: 'Nutzen Sie ein ERP-System?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. SAP, ERPNext, Microsoft Dynamics',
|
||||
triggersTemplates: ['finance-buchhaltung'],
|
||||
},
|
||||
{
|
||||
id: 'sys_visitor',
|
||||
step: 3,
|
||||
question: 'Haben Sie ein Besuchermanagement-System?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: ['other-besuchermanagement'],
|
||||
},
|
||||
|
||||
// === STEP 4: Datenkategorien ===
|
||||
{
|
||||
id: 'data_health',
|
||||
step: 4,
|
||||
question: 'Verarbeiten Sie Gesundheitsdaten (Art. 9 DSGVO)?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. Krankmeldungen, Arbeitsmedizin, Gesundheitsversorgung',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'data_minors',
|
||||
step: 4,
|
||||
question: 'Verarbeiten Sie Daten von Minderjaehrigen?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. Schueler, Kinder unter 16 Jahren',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'data_biometric',
|
||||
step: 4,
|
||||
question: 'Verarbeiten Sie biometrische Daten zur Identifizierung?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. Fingerabdruck, Gesichtserkennung, Stimmerkennung',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'data_criminal',
|
||||
step: 4,
|
||||
question: 'Verarbeiten Sie Daten ueber strafrechtliche Verurteilungen (Art. 10 DSGVO)?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. Fuehrungszeugnisse',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
|
||||
// === STEP 5: Drittlandtransfers ===
|
||||
{
|
||||
id: 'transfer_cloud_us',
|
||||
step: 5,
|
||||
question: 'Nutzen Sie Cloud-Dienste mit Sitz in den USA?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. AWS, Azure, Google Cloud, Microsoft 365',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'transfer_support_non_eu',
|
||||
step: 5,
|
||||
question: 'Haben Sie Support-Mitarbeiter oder Dienstleister ausserhalb der EU?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'transfer_subprocessor',
|
||||
step: 5,
|
||||
question: 'Nutzen Sie Auftragsverarbeiter mit Unteraufragnehmern in Drittlaendern?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
|
||||
// === STEP 6: Besondere Verarbeitungen ===
|
||||
{
|
||||
id: 'special_ai',
|
||||
step: 6,
|
||||
question: 'Setzen Sie KI oder automatisierte Entscheidungsfindung ein?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. Chatbots, Scoring, Profiling, automatische Bewertungen',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'special_video_surveillance',
|
||||
step: 6,
|
||||
question: 'Betreiben Sie Videoueberwachung?',
|
||||
type: 'boolean',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
{
|
||||
id: 'special_tracking',
|
||||
step: 6,
|
||||
question: 'Betreiben Sie umfangreiches Nutzer-Tracking oder Profiling?',
|
||||
type: 'boolean',
|
||||
helpText: 'z.B. Verhaltensprofiling, Cross-Device-Tracking',
|
||||
triggersTemplates: [],
|
||||
},
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// GENERATOR LOGIC
|
||||
// =============================================================================
|
||||
|
||||
export function generateActivities(answers: ProfilingAnswers): ProfilingResult {
|
||||
// Collect all triggered template IDs
|
||||
const triggeredIds = new Set<string>()
|
||||
|
||||
for (const question of PROFILING_QUESTIONS) {
|
||||
const answer = answers[question.id]
|
||||
if (!answer) continue
|
||||
|
||||
// Boolean questions: if true, trigger templates
|
||||
if (question.type === 'boolean' && answer === true) {
|
||||
question.triggersTemplates.forEach(id => triggeredIds.add(id))
|
||||
}
|
||||
}
|
||||
|
||||
// Always add IT baseline templates (every company needs these)
|
||||
triggeredIds.add('it-systemadministration')
|
||||
triggeredIds.add('it-backup')
|
||||
triggeredIds.add('it-logging')
|
||||
triggeredIds.add('it-iam')
|
||||
|
||||
// Generate activities from triggered templates
|
||||
const existingIds: string[] = []
|
||||
const activities: VVTActivity[] = []
|
||||
|
||||
for (const templateId of triggeredIds) {
|
||||
const template = VVT_BASELINE_CATALOG.find(t => t.templateId === templateId)
|
||||
if (!template) continue
|
||||
|
||||
const vvtId = generateVVTId(existingIds)
|
||||
existingIds.push(vvtId)
|
||||
|
||||
const activity = templateToActivity(template, vvtId)
|
||||
|
||||
// Enrich with profiling answers
|
||||
enrichActivityFromAnswers(activity, answers)
|
||||
|
||||
activities.push(activity)
|
||||
}
|
||||
|
||||
// Calculate coverage score
|
||||
const totalFields = activities.length * 12 // 12 key fields per activity
|
||||
let filledFields = 0
|
||||
for (const a of activities) {
|
||||
if (a.name) filledFields++
|
||||
if (a.description) filledFields++
|
||||
if (a.purposes.length > 0) filledFields++
|
||||
if (a.legalBases.length > 0) filledFields++
|
||||
if (a.dataSubjectCategories.length > 0) filledFields++
|
||||
if (a.personalDataCategories.length > 0) filledFields++
|
||||
if (a.recipientCategories.length > 0) filledFields++
|
||||
if (a.retentionPeriod.description) filledFields++
|
||||
if (a.tomDescription) filledFields++
|
||||
if (a.businessFunction !== 'other') filledFields++
|
||||
if (a.structuredToms.accessControl.length > 0) filledFields++
|
||||
if (a.responsible || a.owner) filledFields++
|
||||
}
|
||||
|
||||
const coverageScore = totalFields > 0 ? Math.round((filledFields / totalFields) * 100) : 0
|
||||
|
||||
// Art. 30 Abs. 5 check
|
||||
const employeeCount = typeof answers.org_employees === 'number' ? answers.org_employees : 0
|
||||
const hasSpecialCategories = answers.data_health === true || answers.data_biometric === true || answers.data_criminal === true
|
||||
const art30Abs5Exempt = employeeCount < 250 && !hasSpecialCategories
|
||||
|
||||
return {
|
||||
answers,
|
||||
generatedActivities: activities,
|
||||
coverageScore,
|
||||
art30Abs5Exempt,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ENRICHMENT
|
||||
// =============================================================================
|
||||
|
||||
function enrichActivityFromAnswers(activity: VVTActivity, answers: ProfilingAnswers): void {
|
||||
// Add third-country transfers if US cloud is used
|
||||
if (answers.transfer_cloud_us === true) {
|
||||
activity.thirdCountryTransfers.push({
|
||||
country: 'US',
|
||||
recipient: 'Cloud-Dienstleister (USA)',
|
||||
transferMechanism: 'SCC_PROCESSOR',
|
||||
additionalMeasures: ['Verschluesselung at-rest', 'Transfer Impact Assessment'],
|
||||
})
|
||||
}
|
||||
|
||||
// Add special data categories if applicable
|
||||
if (answers.data_health === true) {
|
||||
if (!activity.personalDataCategories.includes('HEALTH_DATA')) {
|
||||
// Only add to HR activities
|
||||
if (activity.businessFunction === 'hr') {
|
||||
activity.personalDataCategories.push('HEALTH_DATA')
|
||||
// Ensure Art. 9 legal basis
|
||||
if (!activity.legalBases.some(lb => lb.type.startsWith('ART9_'))) {
|
||||
activity.legalBases.push({
|
||||
type: 'ART9_EMPLOYMENT',
|
||||
description: 'Arbeitsrechtliche Verarbeitung',
|
||||
reference: 'Art. 9 Abs. 2 lit. b DSGVO',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (answers.data_minors === true) {
|
||||
if (!activity.dataSubjectCategories.includes('MINORS')) {
|
||||
// Add to relevant activities (education, app users)
|
||||
if (activity.businessFunction === 'support' || activity.businessFunction === 'product_engineering') {
|
||||
activity.dataSubjectCategories.push('MINORS')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set DPIA required for special processing
|
||||
if (answers.special_ai === true || answers.special_video_surveillance === true || answers.special_tracking === true) {
|
||||
if (answers.special_ai === true && activity.businessFunction === 'product_engineering') {
|
||||
activity.dpiaRequired = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPERS
|
||||
// =============================================================================
|
||||
|
||||
export function getQuestionsForStep(step: number): ProfilingQuestion[] {
|
||||
return PROFILING_QUESTIONS.filter(q => q.step === step)
|
||||
}
|
||||
|
||||
export function getStepProgress(answers: ProfilingAnswers, step: number): number {
|
||||
const questions = getQuestionsForStep(step)
|
||||
if (questions.length === 0) return 100
|
||||
|
||||
const answered = questions.filter(q => {
|
||||
const a = answers[q.id]
|
||||
return a !== undefined && a !== null && a !== ''
|
||||
}).length
|
||||
|
||||
return Math.round((answered / questions.length) * 100)
|
||||
}
|
||||
|
||||
export function getTotalProgress(answers: ProfilingAnswers): number {
|
||||
const total = PROFILING_QUESTIONS.length
|
||||
if (total === 0) return 100
|
||||
|
||||
const answered = PROFILING_QUESTIONS.filter(q => {
|
||||
const a = answers[q.id]
|
||||
return a !== undefined && a !== null && a !== ''
|
||||
}).length
|
||||
|
||||
return Math.round((answered / total) * 100)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// COMPLIANCE SCOPE INTEGRATION
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Prefill VVT profiling answers from Compliance Scope Engine answers.
|
||||
* The Scope Engine acts as the "Single Source of Truth" for organizational questions.
|
||||
* Redundant questions are auto-filled with a "prefilled" marker.
|
||||
*/
|
||||
export function prefillFromScopeAnswers(
|
||||
scopeAnswers: import('./compliance-scope-types').ScopeProfilingAnswer[]
|
||||
): ProfilingAnswers {
|
||||
const { exportToVVTAnswers } = require('./compliance-scope-profiling')
|
||||
const exported = exportToVVTAnswers(scopeAnswers) as Record<string, unknown>
|
||||
const prefilled: ProfilingAnswers = {}
|
||||
|
||||
for (const [key, value] of Object.entries(exported)) {
|
||||
if (value !== undefined && value !== null) {
|
||||
prefilled[key] = value as string | string[] | number | boolean
|
||||
}
|
||||
}
|
||||
|
||||
return prefilled
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of VVT question IDs that are prefilled from Scope answers.
|
||||
* These questions should show "Aus Scope-Analyse uebernommen" hint.
|
||||
*/
|
||||
export const SCOPE_PREFILLED_VVT_QUESTIONS = [
|
||||
'org_industry',
|
||||
'org_employees',
|
||||
'org_b2b_b2c',
|
||||
'dept_hr',
|
||||
'dept_finance',
|
||||
'dept_marketing',
|
||||
'data_health',
|
||||
'data_minors',
|
||||
'data_biometric',
|
||||
'data_criminal',
|
||||
'special_ai',
|
||||
'special_video_surveillance',
|
||||
'special_tracking',
|
||||
'transfer_cloud_us',
|
||||
'transfer_subprocessor',
|
||||
'transfer_support_non_eu',
|
||||
]
|
||||
247
admin-v2/lib/sdk/vvt-types.ts
Normal file
247
admin-v2/lib/sdk/vvt-types.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* VVT (Verarbeitungsverzeichnis) Types — Art. 30 DSGVO
|
||||
*
|
||||
* Re-exports common types from vendor-compliance/types.ts and adds
|
||||
* VVT-specific interfaces for the 4-tab VVT module.
|
||||
*/
|
||||
|
||||
// Re-exports from vendor-compliance/types.ts
|
||||
export type {
|
||||
DataSubjectCategory,
|
||||
PersonalDataCategory,
|
||||
LegalBasisType,
|
||||
TransferMechanismType,
|
||||
RecipientCategoryType,
|
||||
ProcessingActivityStatus,
|
||||
ProtectionLevel,
|
||||
ThirdCountryTransfer,
|
||||
RetentionPeriod,
|
||||
LegalBasis,
|
||||
RecipientCategory,
|
||||
DataSource,
|
||||
SystemReference,
|
||||
DataFlow,
|
||||
DataSourceType,
|
||||
LocalizedText,
|
||||
} from './vendor-compliance/types'
|
||||
|
||||
export {
|
||||
DATA_SUBJECT_CATEGORY_META,
|
||||
PERSONAL_DATA_CATEGORY_META,
|
||||
LEGAL_BASIS_META,
|
||||
TRANSFER_MECHANISM_META,
|
||||
isSpecialCategory,
|
||||
hasAdequacyDecision,
|
||||
generateVVTId,
|
||||
} from './vendor-compliance/types'
|
||||
|
||||
// =============================================================================
|
||||
// VVT-SPECIFIC TYPES
|
||||
// =============================================================================
|
||||
|
||||
export interface VVTOrganizationHeader {
|
||||
organizationName: string
|
||||
industry: string
|
||||
locations: string[]
|
||||
employeeCount: number
|
||||
dpoName: string
|
||||
dpoContact: string
|
||||
vvtVersion: string
|
||||
lastReviewDate: string
|
||||
nextReviewDate: string
|
||||
reviewInterval: 'quarterly' | 'semi_annual' | 'annual'
|
||||
}
|
||||
|
||||
export type BusinessFunction =
|
||||
| 'hr'
|
||||
| 'finance'
|
||||
| 'sales_crm'
|
||||
| 'marketing'
|
||||
| 'support'
|
||||
| 'it_operations'
|
||||
| 'product_engineering'
|
||||
| 'legal'
|
||||
| 'management'
|
||||
| 'other'
|
||||
|
||||
export interface StructuredTOMs {
|
||||
accessControl: string[]
|
||||
confidentiality: string[]
|
||||
integrity: string[]
|
||||
availability: string[]
|
||||
separation: string[]
|
||||
}
|
||||
|
||||
export interface VVTActivity {
|
||||
// Pflichtfelder Art. 30 Abs. 1 (Controller)
|
||||
id: string
|
||||
vvtId: string
|
||||
name: string
|
||||
description: string
|
||||
purposes: string[]
|
||||
legalBases: { type: string; description?: string; reference?: string }[]
|
||||
dataSubjectCategories: string[]
|
||||
personalDataCategories: string[]
|
||||
recipientCategories: { type: string; name: string; description?: string; isThirdCountry?: boolean; country?: string }[]
|
||||
thirdCountryTransfers: { country: string; recipient: string; transferMechanism: string; additionalMeasures?: string[] }[]
|
||||
retentionPeriod: { duration?: number; durationUnit?: string; description: string; legalBasis?: string; deletionProcedure?: string }
|
||||
tomDescription: string
|
||||
|
||||
// Generator-Optimierung (Layer B)
|
||||
businessFunction: BusinessFunction
|
||||
systems: { systemId: string; name: string; description?: string; type?: string }[]
|
||||
deploymentModel: 'cloud' | 'on_prem' | 'hybrid'
|
||||
dataSources: { type: string; description?: string }[]
|
||||
dataFlows: { sourceSystem?: string; targetSystem?: string; description: string; dataCategories: string[] }[]
|
||||
protectionLevel: 'LOW' | 'MEDIUM' | 'HIGH'
|
||||
dpiaRequired: boolean
|
||||
structuredToms: StructuredTOMs
|
||||
|
||||
// Workflow
|
||||
status: 'DRAFT' | 'REVIEW' | 'APPROVED' | 'ARCHIVED'
|
||||
responsible: string
|
||||
owner: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
// Processor-Record (Art. 30 Abs. 2)
|
||||
export interface VVTProcessorActivity {
|
||||
id: string
|
||||
vvtId: string
|
||||
controllerReference: string
|
||||
processingCategories: string[]
|
||||
subProcessorChain: SubProcessor[]
|
||||
thirdCountryTransfers: { country: string; recipient: string; transferMechanism: string }[]
|
||||
tomDescription: string
|
||||
status: 'DRAFT' | 'REVIEW' | 'APPROVED' | 'ARCHIVED'
|
||||
}
|
||||
|
||||
export interface SubProcessor {
|
||||
name: string
|
||||
purpose: string
|
||||
country: string
|
||||
isThirdCountry: boolean
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CONSTANTS
|
||||
// =============================================================================
|
||||
|
||||
export const BUSINESS_FUNCTION_LABELS: Record<BusinessFunction, string> = {
|
||||
hr: 'Personal (HR)',
|
||||
finance: 'Finanzen & Buchhaltung',
|
||||
sales_crm: 'Vertrieb & CRM',
|
||||
marketing: 'Marketing',
|
||||
support: 'Kundenservice',
|
||||
it_operations: 'IT-Betrieb',
|
||||
product_engineering: 'Produktentwicklung',
|
||||
legal: 'Recht & Compliance',
|
||||
management: 'Geschaeftsfuehrung',
|
||||
other: 'Sonstiges',
|
||||
}
|
||||
|
||||
export const STATUS_LABELS: Record<string, string> = {
|
||||
DRAFT: 'Entwurf',
|
||||
REVIEW: 'In Pruefung',
|
||||
APPROVED: 'Genehmigt',
|
||||
ARCHIVED: 'Archiviert',
|
||||
}
|
||||
|
||||
export const STATUS_COLORS: Record<string, string> = {
|
||||
DRAFT: 'bg-gray-100 text-gray-700',
|
||||
REVIEW: 'bg-yellow-100 text-yellow-700',
|
||||
APPROVED: 'bg-green-100 text-green-700',
|
||||
ARCHIVED: 'bg-red-100 text-red-700',
|
||||
}
|
||||
|
||||
export const PROTECTION_LEVEL_LABELS: Record<string, string> = {
|
||||
LOW: 'Niedrig',
|
||||
MEDIUM: 'Mittel',
|
||||
HIGH: 'Hoch',
|
||||
}
|
||||
|
||||
export const DEPLOYMENT_LABELS: Record<string, string> = {
|
||||
cloud: 'Cloud',
|
||||
on_prem: 'On-Premise',
|
||||
hybrid: 'Hybrid',
|
||||
}
|
||||
|
||||
export const REVIEW_INTERVAL_LABELS: Record<string, string> = {
|
||||
quarterly: 'Vierteljaehrlich',
|
||||
semi_annual: 'Halbjaehrlich',
|
||||
annual: 'Jaehrlich',
|
||||
}
|
||||
|
||||
// Art. 9 special categories for highlighting
|
||||
export const ART9_CATEGORIES: string[] = [
|
||||
'HEALTH_DATA',
|
||||
'GENETIC_DATA',
|
||||
'BIOMETRIC_DATA',
|
||||
'RACIAL_ETHNIC',
|
||||
'POLITICAL_OPINIONS',
|
||||
'RELIGIOUS_BELIEFS',
|
||||
'TRADE_UNION',
|
||||
'SEX_LIFE',
|
||||
'CRIMINAL_DATA',
|
||||
]
|
||||
|
||||
// =============================================================================
|
||||
// HELPER: Create empty activity
|
||||
// =============================================================================
|
||||
|
||||
export function createEmptyActivity(vvtId: string): VVTActivity {
|
||||
const now = new Date().toISOString()
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
vvtId,
|
||||
name: '',
|
||||
description: '',
|
||||
purposes: [],
|
||||
legalBases: [],
|
||||
dataSubjectCategories: [],
|
||||
personalDataCategories: [],
|
||||
recipientCategories: [],
|
||||
thirdCountryTransfers: [],
|
||||
retentionPeriod: { description: '', legalBasis: '', deletionProcedure: '' },
|
||||
tomDescription: '',
|
||||
businessFunction: 'other',
|
||||
systems: [],
|
||||
deploymentModel: 'cloud',
|
||||
dataSources: [],
|
||||
dataFlows: [],
|
||||
protectionLevel: 'MEDIUM',
|
||||
dpiaRequired: false,
|
||||
structuredToms: {
|
||||
accessControl: [],
|
||||
confidentiality: [],
|
||||
integrity: [],
|
||||
availability: [],
|
||||
separation: [],
|
||||
},
|
||||
status: 'DRAFT',
|
||||
responsible: '',
|
||||
owner: '',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER: Default organization header
|
||||
// =============================================================================
|
||||
|
||||
export function createDefaultOrgHeader(): VVTOrganizationHeader {
|
||||
return {
|
||||
organizationName: '',
|
||||
industry: '',
|
||||
locations: [],
|
||||
employeeCount: 0,
|
||||
dpoName: '',
|
||||
dpoContact: '',
|
||||
vvtVersion: '1.0',
|
||||
lastReviewDate: new Date().toISOString().split('T')[0],
|
||||
nextReviewDate: '',
|
||||
reviewInterval: 'annual',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user