fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
488
admin-v2/lib/sdk/vendor-compliance/risk/calculator.ts
Normal file
488
admin-v2/lib/sdk/vendor-compliance/risk/calculator.ts
Normal file
@@ -0,0 +1,488 @@
|
||||
/**
|
||||
* Risk Score Calculator
|
||||
*
|
||||
* Calculate inherent and residual risk scores for vendors and processing activities
|
||||
*/
|
||||
|
||||
import {
|
||||
Vendor,
|
||||
ProcessingActivity,
|
||||
RiskScore,
|
||||
RiskLevel,
|
||||
RiskFactor,
|
||||
ControlInstance,
|
||||
DataAccessLevel,
|
||||
VendorRole,
|
||||
ServiceCategory,
|
||||
PersonalDataCategory,
|
||||
getRiskLevelFromScore,
|
||||
isSpecialCategory,
|
||||
hasAdequacyDecision,
|
||||
LocalizedText,
|
||||
} from '../types'
|
||||
|
||||
// ==========================================
|
||||
// RISK FACTOR DEFINITIONS
|
||||
// ==========================================
|
||||
|
||||
export interface RiskFactorDefinition {
|
||||
id: string
|
||||
name: LocalizedText
|
||||
category: 'DATA' | 'ACCESS' | 'LOCATION' | 'VENDOR' | 'PROCESSING'
|
||||
description: LocalizedText
|
||||
weight: number // 0-1
|
||||
evaluator: (context: RiskContext) => number // Returns 1-5
|
||||
}
|
||||
|
||||
export interface RiskContext {
|
||||
vendor?: Vendor
|
||||
processingActivity?: ProcessingActivity
|
||||
controlInstances?: ControlInstance[]
|
||||
}
|
||||
|
||||
export const RISK_FACTOR_DEFINITIONS: RiskFactorDefinition[] = [
|
||||
// DATA FACTORS
|
||||
{
|
||||
id: 'data_volume',
|
||||
name: { de: 'Datenvolumen', en: 'Data Volume' },
|
||||
category: 'DATA',
|
||||
description: { de: 'Menge der verarbeiteten Daten', en: 'Volume of data processed' },
|
||||
weight: 0.15,
|
||||
evaluator: (ctx) => {
|
||||
// Based on vendor service category or processing activity scope
|
||||
if (ctx.vendor) {
|
||||
const highVolume: ServiceCategory[] = ['CLOUD_INFRASTRUCTURE', 'ERP', 'CRM', 'HR_SOFTWARE']
|
||||
if (highVolume.includes(ctx.vendor.serviceCategory)) return 5
|
||||
const medVolume: ServiceCategory[] = ['HOSTING', 'EMAIL', 'ANALYTICS', 'BACKUP']
|
||||
if (medVolume.includes(ctx.vendor.serviceCategory)) return 3
|
||||
return 2
|
||||
}
|
||||
return 3 // Default medium
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'data_sensitivity',
|
||||
name: { de: 'Datensensibilität', en: 'Data Sensitivity' },
|
||||
category: 'DATA',
|
||||
description: { de: 'Sensibilität der verarbeiteten Daten', en: 'Sensitivity of data processed' },
|
||||
weight: 0.2,
|
||||
evaluator: (ctx) => {
|
||||
if (ctx.processingActivity) {
|
||||
const hasSpecial = ctx.processingActivity.personalDataCategories.some(isSpecialCategory)
|
||||
if (hasSpecial) return 5
|
||||
const hasFinancial = ctx.processingActivity.personalDataCategories.some(
|
||||
(c) => ['BANK_ACCOUNT', 'PAYMENT_DATA', 'SALARY_DATA', 'TAX_ID'].includes(c)
|
||||
)
|
||||
if (hasFinancial) return 4
|
||||
const hasIdentifiers = ctx.processingActivity.personalDataCategories.some(
|
||||
(c) => ['ID_NUMBER', 'SOCIAL_SECURITY'].includes(c)
|
||||
)
|
||||
if (hasIdentifiers) return 4
|
||||
return 2
|
||||
}
|
||||
return 3
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'special_categories',
|
||||
name: { de: 'Besondere Kategorien', en: 'Special Categories' },
|
||||
category: 'DATA',
|
||||
description: { de: 'Verarbeitung besonderer Datenkategorien (Art. 9)', en: 'Processing of special data categories (Art. 9)' },
|
||||
weight: 0.15,
|
||||
evaluator: (ctx) => {
|
||||
if (ctx.processingActivity) {
|
||||
const specialCount = ctx.processingActivity.personalDataCategories.filter(isSpecialCategory).length
|
||||
if (specialCount >= 3) return 5
|
||||
if (specialCount >= 1) return 4
|
||||
return 1
|
||||
}
|
||||
return 1
|
||||
},
|
||||
},
|
||||
|
||||
// ACCESS FACTORS
|
||||
{
|
||||
id: 'data_access_level',
|
||||
name: { de: 'Datenzugriffsebene', en: 'Data Access Level' },
|
||||
category: 'ACCESS',
|
||||
description: { de: 'Art des Zugriffs auf personenbezogene Daten', en: 'Type of access to personal data' },
|
||||
weight: 0.15,
|
||||
evaluator: (ctx) => {
|
||||
if (ctx.vendor) {
|
||||
const accessLevelScores: Record<DataAccessLevel, number> = {
|
||||
NONE: 1,
|
||||
POTENTIAL: 2,
|
||||
ADMINISTRATIVE: 4,
|
||||
CONTENT: 5,
|
||||
}
|
||||
return accessLevelScores[ctx.vendor.dataAccessLevel]
|
||||
}
|
||||
return 3
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'vendor_role',
|
||||
name: { de: 'Vendor-Rolle', en: 'Vendor Role' },
|
||||
category: 'ACCESS',
|
||||
description: { de: 'Rolle des Vendors im Datenschutzkontext', en: 'Role of vendor in data protection context' },
|
||||
weight: 0.1,
|
||||
evaluator: (ctx) => {
|
||||
if (ctx.vendor) {
|
||||
const roleScores: Record<VendorRole, number> = {
|
||||
THIRD_PARTY: 1,
|
||||
CONTROLLER: 3,
|
||||
JOINT_CONTROLLER: 4,
|
||||
PROCESSOR: 4,
|
||||
SUB_PROCESSOR: 5,
|
||||
}
|
||||
return roleScores[ctx.vendor.role]
|
||||
}
|
||||
return 3
|
||||
},
|
||||
},
|
||||
|
||||
// LOCATION FACTORS
|
||||
{
|
||||
id: 'third_country_transfer',
|
||||
name: { de: 'Drittlandtransfer', en: 'Third Country Transfer' },
|
||||
category: 'LOCATION',
|
||||
description: { de: 'Datenübermittlung in Drittländer', en: 'Data transfer to third countries' },
|
||||
weight: 0.15,
|
||||
evaluator: (ctx) => {
|
||||
if (ctx.vendor) {
|
||||
const locations = ctx.vendor.processingLocations
|
||||
const hasNonAdequate = locations.some((l) => !l.isEU && !l.isAdequate)
|
||||
if (hasNonAdequate) return 5
|
||||
const hasAdequate = locations.some((l) => !l.isEU && l.isAdequate)
|
||||
if (hasAdequate) return 3
|
||||
return 1 // EU only
|
||||
}
|
||||
if (ctx.processingActivity && ctx.processingActivity.thirdCountryTransfers.length > 0) {
|
||||
const hasNonAdequate = ctx.processingActivity.thirdCountryTransfers.some(
|
||||
(t) => !hasAdequacyDecision(t.country)
|
||||
)
|
||||
if (hasNonAdequate) return 5
|
||||
return 3
|
||||
}
|
||||
return 1
|
||||
},
|
||||
},
|
||||
|
||||
// VENDOR FACTORS
|
||||
{
|
||||
id: 'certification_status',
|
||||
name: { de: 'Zertifizierungsstatus', en: 'Certification Status' },
|
||||
category: 'VENDOR',
|
||||
description: { de: 'Vorhandensein relevanter Zertifizierungen', en: 'Presence of relevant certifications' },
|
||||
weight: 0.1,
|
||||
evaluator: (ctx) => {
|
||||
if (ctx.vendor) {
|
||||
const certs = ctx.vendor.certifications
|
||||
const relevantCerts = ['ISO 27001', 'SOC 2', 'SOC2', 'TISAX', 'C5', 'PCI DSS']
|
||||
const hasCert = certs.some((c) => relevantCerts.some((rc) => c.type.includes(rc)))
|
||||
const hasExpired = certs.some((c) => c.expirationDate && new Date(c.expirationDate) < new Date())
|
||||
if (hasCert && !hasExpired) return 1
|
||||
if (hasCert && hasExpired) return 3
|
||||
return 4
|
||||
}
|
||||
return 3
|
||||
},
|
||||
},
|
||||
|
||||
// PROCESSING FACTORS
|
||||
{
|
||||
id: 'processing_scope',
|
||||
name: { de: 'Verarbeitungsumfang', en: 'Processing Scope' },
|
||||
category: 'PROCESSING',
|
||||
description: { de: 'Umfang und Art der Verarbeitung', en: 'Scope and nature of processing' },
|
||||
weight: 0.1,
|
||||
evaluator: (ctx) => {
|
||||
if (ctx.processingActivity) {
|
||||
const subjectCount = ctx.processingActivity.dataSubjectCategories.length
|
||||
const categoryCount = ctx.processingActivity.personalDataCategories.length
|
||||
const totalScope = subjectCount + categoryCount
|
||||
if (totalScope > 15) return 5
|
||||
if (totalScope > 10) return 4
|
||||
if (totalScope > 5) return 3
|
||||
return 2
|
||||
}
|
||||
return 3
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// RISK CALCULATION FUNCTIONS
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Calculate inherent risk score for a vendor
|
||||
*/
|
||||
export function calculateVendorInherentRisk(vendor: Vendor): {
|
||||
score: RiskScore
|
||||
factors: RiskFactor[]
|
||||
} {
|
||||
const context: RiskContext = { vendor }
|
||||
return calculateInherentRisk(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate inherent risk score for a processing activity
|
||||
*/
|
||||
export function calculateProcessingActivityInherentRisk(activity: ProcessingActivity): {
|
||||
score: RiskScore
|
||||
factors: RiskFactor[]
|
||||
} {
|
||||
const context: RiskContext = { processingActivity: activity }
|
||||
return calculateInherentRisk(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic inherent risk calculation
|
||||
*/
|
||||
function calculateInherentRisk(context: RiskContext): {
|
||||
score: RiskScore
|
||||
factors: RiskFactor[]
|
||||
} {
|
||||
const factors: RiskFactor[] = []
|
||||
let totalWeight = 0
|
||||
let weightedSum = 0
|
||||
|
||||
for (const definition of RISK_FACTOR_DEFINITIONS) {
|
||||
const value = definition.evaluator(context)
|
||||
const factor: RiskFactor = {
|
||||
id: definition.id,
|
||||
name: definition.name,
|
||||
category: definition.category,
|
||||
weight: definition.weight,
|
||||
value,
|
||||
}
|
||||
factors.push(factor)
|
||||
|
||||
totalWeight += definition.weight
|
||||
weightedSum += value * definition.weight
|
||||
}
|
||||
|
||||
// Calculate average (1-5 scale)
|
||||
const averageScore = weightedSum / totalWeight
|
||||
|
||||
// Map to likelihood and impact
|
||||
const likelihood = Math.round(averageScore) as 1 | 2 | 3 | 4 | 5
|
||||
const impact = calculateImpact(context)
|
||||
const score = likelihood * impact
|
||||
const level = getRiskLevelFromScore(score)
|
||||
|
||||
return {
|
||||
score: {
|
||||
likelihood,
|
||||
impact,
|
||||
score,
|
||||
level,
|
||||
rationale: generateRationale(factors, likelihood, impact),
|
||||
},
|
||||
factors,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate impact based on context
|
||||
*/
|
||||
function calculateImpact(context: RiskContext): 1 | 2 | 3 | 4 | 5 {
|
||||
let impactScore = 3 // Default medium
|
||||
|
||||
if (context.vendor) {
|
||||
// Higher impact for critical services
|
||||
const criticalServices: ServiceCategory[] = ['CLOUD_INFRASTRUCTURE', 'ERP', 'PAYMENT', 'SECURITY']
|
||||
if (criticalServices.includes(context.vendor.serviceCategory)) {
|
||||
impactScore += 1
|
||||
}
|
||||
|
||||
// Higher impact for content access
|
||||
if (context.vendor.dataAccessLevel === 'CONTENT') {
|
||||
impactScore += 1
|
||||
}
|
||||
}
|
||||
|
||||
if (context.processingActivity) {
|
||||
// Higher impact for special categories
|
||||
if (context.processingActivity.personalDataCategories.some(isSpecialCategory)) {
|
||||
impactScore += 1
|
||||
}
|
||||
|
||||
// Higher impact for high protection level
|
||||
if (context.processingActivity.protectionLevel === 'HIGH') {
|
||||
impactScore += 1
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(5, Math.max(1, impactScore)) as 1 | 2 | 3 | 4 | 5
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate rationale text
|
||||
*/
|
||||
function generateRationale(
|
||||
factors: RiskFactor[],
|
||||
likelihood: number,
|
||||
impact: number
|
||||
): string {
|
||||
const highFactors = factors.filter((f) => f.value >= 4).map((f) => f.name.de)
|
||||
const lowFactors = factors.filter((f) => f.value <= 2).map((f) => f.name.de)
|
||||
|
||||
let rationale = `Bewertung: Eintrittswahrscheinlichkeit ${likelihood}/5, Auswirkung ${impact}/5.`
|
||||
|
||||
if (highFactors.length > 0) {
|
||||
rationale += ` Erhöhte Risikofaktoren: ${highFactors.join(', ')}.`
|
||||
}
|
||||
|
||||
if (lowFactors.length > 0) {
|
||||
rationale += ` Risikomindernde Faktoren: ${lowFactors.join(', ')}.`
|
||||
}
|
||||
|
||||
return rationale
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// RESIDUAL RISK CALCULATION
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Calculate residual risk based on control effectiveness
|
||||
*/
|
||||
export function calculateResidualRisk(
|
||||
inherentRisk: RiskScore,
|
||||
controlInstances: ControlInstance[]
|
||||
): RiskScore {
|
||||
// Calculate control effectiveness (0-1)
|
||||
const effectiveness = calculateControlEffectiveness(controlInstances)
|
||||
|
||||
// Reduce likelihood based on control effectiveness
|
||||
const reducedLikelihood = Math.max(1, Math.round(inherentRisk.likelihood * (1 - effectiveness * 0.6)))
|
||||
|
||||
// Impact reduction is smaller (controls primarily reduce likelihood)
|
||||
const reducedImpact = Math.max(1, Math.round(inherentRisk.impact * (1 - effectiveness * 0.3)))
|
||||
|
||||
const residualScore = reducedLikelihood * reducedImpact
|
||||
const level = getRiskLevelFromScore(residualScore)
|
||||
|
||||
return {
|
||||
likelihood: reducedLikelihood as 1 | 2 | 3 | 4 | 5,
|
||||
impact: reducedImpact as 1 | 2 | 3 | 4 | 5,
|
||||
score: residualScore,
|
||||
level,
|
||||
rationale: `Restrisiko nach Berücksichtigung von ${controlInstances.length} Kontrollen (Effektivität: ${Math.round(effectiveness * 100)}%).`,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate overall control effectiveness
|
||||
*/
|
||||
function calculateControlEffectiveness(controlInstances: ControlInstance[]): number {
|
||||
if (controlInstances.length === 0) return 0
|
||||
|
||||
const statusScores: Record<string, number> = {
|
||||
PASS: 1,
|
||||
PARTIAL: 0.5,
|
||||
FAIL: 0,
|
||||
NOT_APPLICABLE: 0,
|
||||
PLANNED: 0.2,
|
||||
}
|
||||
|
||||
const totalScore = controlInstances.reduce(
|
||||
(sum, ci) => sum + (statusScores[ci.status] || 0),
|
||||
0
|
||||
)
|
||||
|
||||
return totalScore / controlInstances.length
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// RISK MATRIX
|
||||
// ==========================================
|
||||
|
||||
export interface RiskMatrixCell {
|
||||
likelihood: number
|
||||
impact: number
|
||||
level: RiskLevel
|
||||
score: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate full risk matrix
|
||||
*/
|
||||
export function generateRiskMatrix(): RiskMatrixCell[][] {
|
||||
const matrix: RiskMatrixCell[][] = []
|
||||
|
||||
for (let likelihood = 1; likelihood <= 5; likelihood++) {
|
||||
const row: RiskMatrixCell[] = []
|
||||
for (let impact = 1; impact <= 5; impact++) {
|
||||
const score = likelihood * impact
|
||||
row.push({
|
||||
likelihood,
|
||||
impact,
|
||||
score,
|
||||
level: getRiskLevelFromScore(score),
|
||||
})
|
||||
}
|
||||
matrix.push(row)
|
||||
}
|
||||
|
||||
return matrix
|
||||
}
|
||||
|
||||
/**
|
||||
* Get risk matrix colors
|
||||
*/
|
||||
export function getRiskLevelColor(level: RiskLevel): {
|
||||
bg: string
|
||||
text: string
|
||||
border: string
|
||||
} {
|
||||
switch (level) {
|
||||
case 'LOW':
|
||||
return { bg: 'bg-green-100', text: 'text-green-800', border: 'border-green-300' }
|
||||
case 'MEDIUM':
|
||||
return { bg: 'bg-yellow-100', text: 'text-yellow-800', border: 'border-yellow-300' }
|
||||
case 'HIGH':
|
||||
return { bg: 'bg-orange-100', text: 'text-orange-800', border: 'border-orange-300' }
|
||||
case 'CRITICAL':
|
||||
return { bg: 'bg-red-100', text: 'text-red-800', border: 'border-red-300' }
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// RISK TREND ANALYSIS
|
||||
// ==========================================
|
||||
|
||||
export interface RiskTrend {
|
||||
currentScore: number
|
||||
previousScore: number
|
||||
change: number
|
||||
trend: 'IMPROVING' | 'STABLE' | 'DETERIORATING'
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate risk trend
|
||||
*/
|
||||
export function calculateRiskTrend(
|
||||
currentScore: number,
|
||||
previousScore: number
|
||||
): RiskTrend {
|
||||
const change = currentScore - previousScore
|
||||
let trend: 'IMPROVING' | 'STABLE' | 'DETERIORATING'
|
||||
|
||||
if (Math.abs(change) <= 2) {
|
||||
trend = 'STABLE'
|
||||
} else if (change < 0) {
|
||||
trend = 'IMPROVING'
|
||||
} else {
|
||||
trend = 'DETERIORATING'
|
||||
}
|
||||
|
||||
return {
|
||||
currentScore,
|
||||
previousScore,
|
||||
change,
|
||||
trend,
|
||||
}
|
||||
}
|
||||
943
admin-v2/lib/sdk/vendor-compliance/risk/controls-library.ts
Normal file
943
admin-v2/lib/sdk/vendor-compliance/risk/controls-library.ts
Normal file
@@ -0,0 +1,943 @@
|
||||
/**
|
||||
* Controls Library
|
||||
*
|
||||
* Standard controls for vendor and processing activity compliance
|
||||
*/
|
||||
|
||||
import { Control, ControlDomain, ReviewFrequency, LocalizedText } from '../types'
|
||||
|
||||
// ==========================================
|
||||
// CONTROL DEFINITIONS
|
||||
// ==========================================
|
||||
|
||||
export const CONTROLS_LIBRARY: Control[] = [
|
||||
// ==========================================
|
||||
// TRANSFER - Drittlandtransfer Controls
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-TRF-01',
|
||||
domain: 'TRANSFER',
|
||||
title: {
|
||||
de: 'Drittlandtransfer nur mit Rechtsgrundlage',
|
||||
en: 'Third country transfer with legal basis',
|
||||
},
|
||||
description: {
|
||||
de: 'Drittlandtransfers erfolgen nur auf Basis von SCC, BCR oder Angemessenheitsbeschluss',
|
||||
en: 'Third country transfers only based on SCC, BCR or adequacy decision',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'SCC oder BCR vertraglich vereinbart ODER Angemessenheitsbeschluss vorhanden',
|
||||
en: 'SCC or BCR contractually agreed OR adequacy decision exists',
|
||||
},
|
||||
requirements: ['Art. 44-49 DSGVO', 'ISO 27001 A.15.1.2'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TRF-02',
|
||||
domain: 'TRANSFER',
|
||||
title: {
|
||||
de: 'Aktuelle Standardvertragsklauseln',
|
||||
en: 'Current Standard Contractual Clauses',
|
||||
},
|
||||
description: {
|
||||
de: 'Bei SCC-Nutzung: Verwendung der aktuellen EU-Kommission-Klauseln (2021)',
|
||||
en: 'When using SCC: Current EU Commission clauses (2021) are used',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'SCC 2021 (Durchführungsbeschluss (EU) 2021/914) verwendet',
|
||||
en: 'SCC 2021 (Implementing Decision (EU) 2021/914) used',
|
||||
},
|
||||
requirements: ['Art. 46 Abs. 2 lit. c DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TRF-03',
|
||||
domain: 'TRANSFER',
|
||||
title: {
|
||||
de: 'Transfer Impact Assessment (TIA)',
|
||||
en: 'Transfer Impact Assessment (TIA)',
|
||||
},
|
||||
description: {
|
||||
de: 'Bei Transfers in Drittländer ohne Angemessenheitsbeschluss ist TIA durchzuführen',
|
||||
en: 'TIA required for transfers to third countries without adequacy decision',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'TIA dokumentiert und bewertet Risiken als akzeptabel',
|
||||
en: 'TIA documented and risks assessed as acceptable',
|
||||
},
|
||||
requirements: ['Schrems II Urteil', 'EDSA Empfehlungen 01/2020'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TRF-04',
|
||||
domain: 'TRANSFER',
|
||||
title: {
|
||||
de: 'Zusätzliche Schutzmaßnahmen',
|
||||
en: 'Supplementary Measures',
|
||||
},
|
||||
description: {
|
||||
de: 'Bei Bedarf sind zusätzliche technische/organisatorische Maßnahmen implementiert',
|
||||
en: 'Supplementary technical/organizational measures implemented where needed',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Ergänzende Maßnahmen dokumentiert (Verschlüsselung, Pseudonymisierung, etc.)',
|
||||
en: 'Supplementary measures documented (encryption, pseudonymization, etc.)',
|
||||
},
|
||||
requirements: ['EDSA Empfehlungen 01/2020'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TRF-05',
|
||||
domain: 'TRANSFER',
|
||||
title: {
|
||||
de: 'Überwachung Angemessenheitsbeschlüsse',
|
||||
en: 'Monitoring Adequacy Decisions',
|
||||
},
|
||||
description: {
|
||||
de: 'Änderungen bei Angemessenheitsbeschlüssen werden überwacht',
|
||||
en: 'Changes to adequacy decisions are monitored',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Prozess zur Überwachung und Reaktion auf Änderungen etabliert',
|
||||
en: 'Process for monitoring and responding to changes established',
|
||||
},
|
||||
requirements: ['Art. 45 DSGVO'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'QUARTERLY',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// AUDIT - Auditrechte Controls
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-AUD-01',
|
||||
domain: 'AUDIT',
|
||||
title: {
|
||||
de: 'Auditrecht vertraglich vereinbart',
|
||||
en: 'Audit right contractually agreed',
|
||||
},
|
||||
description: {
|
||||
de: 'Vertrag enthält wirksames Auditrecht ohne unangemessene Einschränkungen',
|
||||
en: 'Contract contains effective audit right without unreasonable restrictions',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Auditrecht im AVV enthalten, max. 30 Tage Vorlaufzeit, keine Ausschlussklausel',
|
||||
en: 'Audit right in DPA, max 30 days notice, no exclusion clause',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. h DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-AUD-02',
|
||||
domain: 'AUDIT',
|
||||
title: {
|
||||
de: 'Vor-Ort-Inspektionen möglich',
|
||||
en: 'On-site inspections possible',
|
||||
},
|
||||
description: {
|
||||
de: 'Vertrag erlaubt Vor-Ort-Inspektionen bei dem Auftragsverarbeiter',
|
||||
en: 'Contract allows on-site inspections at the processor',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Vor-Ort-Audit explizit erlaubt, Zugang zu relevanten Bereichen',
|
||||
en: 'On-site audit explicitly allowed, access to relevant areas',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. h DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-AUD-03',
|
||||
domain: 'AUDIT',
|
||||
title: {
|
||||
de: 'Aktuelle Zertifizierungen',
|
||||
en: 'Current Certifications',
|
||||
},
|
||||
description: {
|
||||
de: 'Relevante Sicherheitszertifizierungen sind aktuell und gültig',
|
||||
en: 'Relevant security certifications are current and valid',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'ISO 27001, SOC 2 oder vergleichbar, nicht abgelaufen',
|
||||
en: 'ISO 27001, SOC 2 or equivalent, not expired',
|
||||
},
|
||||
requirements: ['Art. 32 DSGVO', 'ISO 27001 A.15.1.1'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-AUD-04',
|
||||
domain: 'AUDIT',
|
||||
title: {
|
||||
de: 'Letzte Prüfung durchgeführt',
|
||||
en: 'Last review conducted',
|
||||
},
|
||||
description: {
|
||||
de: 'Vendor wurde innerhalb des Review-Zyklus geprüft',
|
||||
en: 'Vendor was reviewed within the review cycle',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Dokumentierte Prüfung innerhalb des festgelegten Intervalls',
|
||||
en: 'Documented review within the defined interval',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. h DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-AUD-05',
|
||||
domain: 'AUDIT',
|
||||
title: {
|
||||
de: 'Prüfberichte verfügbar',
|
||||
en: 'Audit reports available',
|
||||
},
|
||||
description: {
|
||||
de: 'Aktuelle Prüfberichte (SOC 2, Penetrationstest, etc.) liegen vor',
|
||||
en: 'Current audit reports (SOC 2, penetration test, etc.) are available',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Prüfberichte nicht älter als 12 Monate',
|
||||
en: 'Audit reports not older than 12 months',
|
||||
},
|
||||
requirements: ['ISO 27001 A.18.2.1'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// DELETION - Löschung Controls
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-DEL-01',
|
||||
domain: 'DELETION',
|
||||
title: {
|
||||
de: 'Löschung/Rückgabe nach Vertragsende',
|
||||
en: 'Deletion/return after contract end',
|
||||
},
|
||||
description: {
|
||||
de: 'Klare Regelung zur Löschung oder Rückgabe aller Daten nach Vertragsende',
|
||||
en: 'Clear provision for deletion or return of all data after contract end',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Löschfrist max. 30 Tage, Löschbestätigung vorgesehen',
|
||||
en: 'Deletion within max 30 days, deletion confirmation provided',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. g DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-DEL-02',
|
||||
domain: 'DELETION',
|
||||
title: {
|
||||
de: 'Löschbestätigung',
|
||||
en: 'Deletion confirmation',
|
||||
},
|
||||
description: {
|
||||
de: 'Schriftliche Bestätigung der vollständigen Datenlöschung',
|
||||
en: 'Written confirmation of complete data deletion',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Löschbestätigung vertraglich vereinbart und einforderbar',
|
||||
en: 'Deletion confirmation contractually agreed and enforceable',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. g DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-DEL-03',
|
||||
domain: 'DELETION',
|
||||
title: {
|
||||
de: 'Löschung bei Unterauftragnehmern',
|
||||
en: 'Deletion at sub-processors',
|
||||
},
|
||||
description: {
|
||||
de: 'Löschpflicht erstreckt sich auf alle Unterauftragnehmer',
|
||||
en: 'Deletion obligation extends to all sub-processors',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Weitergabe der Löschpflicht an Unterauftragnehmer vertraglich vereinbart',
|
||||
en: 'Transfer of deletion obligation to sub-processors contractually agreed',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. g, d DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-DEL-04',
|
||||
domain: 'DELETION',
|
||||
title: {
|
||||
de: 'Backup-Löschung',
|
||||
en: 'Backup deletion',
|
||||
},
|
||||
description: {
|
||||
de: 'Daten werden auch aus Backups gelöscht',
|
||||
en: 'Data is also deleted from backups',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Backup-Löschung geregelt, max. Aufbewahrungsfrist für Backups definiert',
|
||||
en: 'Backup deletion regulated, max retention period for backups defined',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. g DSGVO'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// INCIDENT - Incident Response Controls
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-INC-01',
|
||||
domain: 'INCIDENT',
|
||||
title: {
|
||||
de: 'Meldepflicht bei Datenpannen',
|
||||
en: 'Data breach notification obligation',
|
||||
},
|
||||
description: {
|
||||
de: 'Unverzügliche Meldung von Datenschutzverletzungen',
|
||||
en: 'Immediate notification of data protection violations',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Meldepflicht vereinbart, Frist max. 24-48h, Mindestinhalte definiert',
|
||||
en: 'Notification obligation agreed, deadline max 24-48h, minimum content defined',
|
||||
},
|
||||
requirements: ['Art. 33 Abs. 2 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-INC-02',
|
||||
domain: 'INCIDENT',
|
||||
title: {
|
||||
de: 'Incident Response Plan',
|
||||
en: 'Incident Response Plan',
|
||||
},
|
||||
description: {
|
||||
de: 'Vendor hat dokumentierten Incident Response Plan',
|
||||
en: 'Vendor has documented incident response plan',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Incident Response Plan liegt vor und wurde getestet',
|
||||
en: 'Incident response plan exists and has been tested',
|
||||
},
|
||||
requirements: ['ISO 27001 A.16.1'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-INC-03',
|
||||
domain: 'INCIDENT',
|
||||
title: {
|
||||
de: 'Kontaktstelle für Incidents',
|
||||
en: 'Contact point for incidents',
|
||||
},
|
||||
description: {
|
||||
de: 'Definierte Kontaktstelle für Datenschutzvorfälle',
|
||||
en: 'Defined contact point for data protection incidents',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Kontaktdaten für Incident-Meldungen bekannt und aktuell',
|
||||
en: 'Contact details for incident reporting known and current',
|
||||
},
|
||||
requirements: ['Art. 33 Abs. 2 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'QUARTERLY',
|
||||
},
|
||||
{
|
||||
id: 'VND-INC-04',
|
||||
domain: 'INCIDENT',
|
||||
title: {
|
||||
de: 'Unterstützung bei Incident-Dokumentation',
|
||||
en: 'Support with incident documentation',
|
||||
},
|
||||
description: {
|
||||
de: 'Vendor unterstützt bei der Dokumentation von Vorfällen',
|
||||
en: 'Vendor supports documentation of incidents',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Unterstützungspflicht bei Dokumentation vertraglich vereinbart',
|
||||
en: 'Support obligation for documentation contractually agreed',
|
||||
},
|
||||
requirements: ['Art. 33 Abs. 5 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// SUBPROCESSOR - Unterauftragnehmer Controls
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-SUB-01',
|
||||
domain: 'SUBPROCESSOR',
|
||||
title: {
|
||||
de: 'Genehmigungspflicht für Unterauftragnehmer',
|
||||
en: 'Approval requirement for sub-processors',
|
||||
},
|
||||
description: {
|
||||
de: 'Einsatz von Unterauftragnehmern nur mit Genehmigung',
|
||||
en: 'Use of sub-processors only with approval',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Genehmigungserfordernis (spezifisch oder allgemein mit Widerspruchsrecht) vereinbart',
|
||||
en: 'Approval requirement (specific or general with objection right) agreed',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 2, 4 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-SUB-02',
|
||||
domain: 'SUBPROCESSOR',
|
||||
title: {
|
||||
de: 'Aktuelle Unterauftragnehmer-Liste',
|
||||
en: 'Current sub-processor list',
|
||||
},
|
||||
description: {
|
||||
de: 'Vollständige und aktuelle Liste aller Unterauftragnehmer',
|
||||
en: 'Complete and current list of all sub-processors',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Liste liegt vor mit Name, Sitz, Verarbeitungszweck',
|
||||
en: 'List available with name, location, processing purpose',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 2 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'QUARTERLY',
|
||||
},
|
||||
{
|
||||
id: 'VND-SUB-03',
|
||||
domain: 'SUBPROCESSOR',
|
||||
title: {
|
||||
de: 'Informationspflicht bei Änderungen',
|
||||
en: 'Notification obligation for changes',
|
||||
},
|
||||
description: {
|
||||
de: 'Information über neue oder geänderte Unterauftragnehmer',
|
||||
en: 'Information about new or changed sub-processors',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Vorabinformation vereinbart, ausreichende Frist für Widerspruch',
|
||||
en: 'Advance notification agreed, sufficient time for objection',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 2 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-SUB-04',
|
||||
domain: 'SUBPROCESSOR',
|
||||
title: {
|
||||
de: 'Weitergabe der Datenschutzpflichten',
|
||||
en: 'Transfer of data protection obligations',
|
||||
},
|
||||
description: {
|
||||
de: 'Datenschutzpflichten werden an Unterauftragnehmer weitergegeben',
|
||||
en: 'Data protection obligations are transferred to sub-processors',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Vertraglich vereinbart, dass Unterauftragnehmer gleichen Pflichten unterliegen',
|
||||
en: 'Contractually agreed that sub-processors are subject to same obligations',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 4 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-SUB-05',
|
||||
domain: 'SUBPROCESSOR',
|
||||
title: {
|
||||
de: 'Haftung für Unterauftragnehmer',
|
||||
en: 'Liability for sub-processors',
|
||||
},
|
||||
description: {
|
||||
de: 'Klare Haftungsregelung für Unterauftragnehmer',
|
||||
en: 'Clear liability provision for sub-processors',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Auftragsverarbeiter haftet für Unterauftragnehmer wie für eigenes Handeln',
|
||||
en: 'Processor is liable for sub-processors as for own actions',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 4 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// TOM - Technische/Organisatorische Maßnahmen
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-TOM-01',
|
||||
domain: 'TOM',
|
||||
title: {
|
||||
de: 'TOM-Dokumentation vorhanden',
|
||||
en: 'TOM documentation available',
|
||||
},
|
||||
description: {
|
||||
de: 'Vollständige Dokumentation der technischen und organisatorischen Maßnahmen',
|
||||
en: 'Complete documentation of technical and organizational measures',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'TOM-Anlage vorhanden, aktuell, spezifisch für die Verarbeitung',
|
||||
en: 'TOM annex available, current, specific to the processing',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. c DSGVO', 'Art. 32 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TOM-02',
|
||||
domain: 'TOM',
|
||||
title: {
|
||||
de: 'Verschlüsselung',
|
||||
en: 'Encryption',
|
||||
},
|
||||
description: {
|
||||
de: 'Angemessene Verschlüsselung für Daten in Transit und at Rest',
|
||||
en: 'Appropriate encryption for data in transit and at rest',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'TLS 1.2+ für Transit, AES-256 für at Rest',
|
||||
en: 'TLS 1.2+ for transit, AES-256 for at rest',
|
||||
},
|
||||
requirements: ['Art. 32 Abs. 1 lit. a DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TOM-03',
|
||||
domain: 'TOM',
|
||||
title: {
|
||||
de: 'Zugriffskontrolle',
|
||||
en: 'Access control',
|
||||
},
|
||||
description: {
|
||||
de: 'Angemessene Zugriffskontrollmechanismen',
|
||||
en: 'Appropriate access control mechanisms',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Rollenbasierte Zugriffskontrolle, Least Privilege, Logging',
|
||||
en: 'Role-based access control, least privilege, logging',
|
||||
},
|
||||
requirements: ['Art. 32 Abs. 1 lit. b DSGVO', 'ISO 27001 A.9'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TOM-04',
|
||||
domain: 'TOM',
|
||||
title: {
|
||||
de: 'Verfügbarkeit und Wiederherstellung',
|
||||
en: 'Availability and recovery',
|
||||
},
|
||||
description: {
|
||||
de: 'Maßnahmen zur Sicherstellung der Verfügbarkeit und Wiederherstellung',
|
||||
en: 'Measures to ensure availability and recovery',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Backup-Konzept, DR-Plan, RTO/RPO definiert',
|
||||
en: 'Backup concept, DR plan, RTO/RPO defined',
|
||||
},
|
||||
requirements: ['Art. 32 Abs. 1 lit. b, c DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TOM-05',
|
||||
domain: 'TOM',
|
||||
title: {
|
||||
de: 'Regelmäßige TOM-Überprüfung',
|
||||
en: 'Regular TOM review',
|
||||
},
|
||||
description: {
|
||||
de: 'Regelmäßige Überprüfung und Aktualisierung der TOM',
|
||||
en: 'Regular review and update of TOM',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'TOM werden mindestens jährlich überprüft und bei Bedarf aktualisiert',
|
||||
en: 'TOM are reviewed at least annually and updated as needed',
|
||||
},
|
||||
requirements: ['Art. 32 Abs. 1 lit. d DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-TOM-06',
|
||||
domain: 'TOM',
|
||||
title: {
|
||||
de: 'Penetrationstest',
|
||||
en: 'Penetration testing',
|
||||
},
|
||||
description: {
|
||||
de: 'Regelmäßige Penetrationstests der relevanten Systeme',
|
||||
en: 'Regular penetration testing of relevant systems',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Jährlicher Pentest, kritische Findings behoben',
|
||||
en: 'Annual pentest, critical findings resolved',
|
||||
},
|
||||
requirements: ['ISO 27001 A.12.6.1'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// CONTRACT - Vertragliche Grundlagen
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-CON-01',
|
||||
domain: 'CONTRACT',
|
||||
title: {
|
||||
de: 'Weisungsgebundenheit',
|
||||
en: 'Instruction binding',
|
||||
},
|
||||
description: {
|
||||
de: 'Auftragsverarbeiter ist an Weisungen gebunden',
|
||||
en: 'Processor is bound by instructions',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Weisungsgebundenheit explizit vereinbart, Hinweispflicht bei rechtswidrigen Weisungen',
|
||||
en: 'Instruction binding explicitly agreed, notification obligation for unlawful instructions',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. a DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-CON-02',
|
||||
domain: 'CONTRACT',
|
||||
title: {
|
||||
de: 'Vertraulichkeitsverpflichtung',
|
||||
en: 'Confidentiality obligation',
|
||||
},
|
||||
description: {
|
||||
de: 'Mitarbeiter sind zur Vertraulichkeit verpflichtet',
|
||||
en: 'Employees are obligated to confidentiality',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Vertraulichkeitsverpflichtung für alle Mitarbeiter mit Datenzugriff',
|
||||
en: 'Confidentiality obligation for all employees with data access',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. b DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-CON-03',
|
||||
domain: 'CONTRACT',
|
||||
title: {
|
||||
de: 'Gegenstand und Dauer der Verarbeitung',
|
||||
en: 'Subject and duration of processing',
|
||||
},
|
||||
description: {
|
||||
de: 'Klare Definition von Gegenstand und Dauer der Verarbeitung',
|
||||
en: 'Clear definition of subject and duration of processing',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Verarbeitungsgegenstand, Dauer, Art der Daten, Betroffene definiert',
|
||||
en: 'Processing subject, duration, type of data, data subjects defined',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-CON-04',
|
||||
domain: 'CONTRACT',
|
||||
title: {
|
||||
de: 'Schriftform/Textform',
|
||||
en: 'Written/text form',
|
||||
},
|
||||
description: {
|
||||
de: 'AVV in Schriftform oder elektronischem Format',
|
||||
en: 'DPA in written or electronic format',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'AVV in Schriftform oder elektronisch mit qualifizierter Signatur',
|
||||
en: 'DPA in written form or electronically with qualified signature',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 9 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// DATA_SUBJECT - Betroffenenrechte
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-DSR-01',
|
||||
domain: 'DATA_SUBJECT',
|
||||
title: {
|
||||
de: 'Unterstützung bei Betroffenenrechten',
|
||||
en: 'Support for data subject rights',
|
||||
},
|
||||
description: {
|
||||
de: 'Vendor unterstützt bei der Erfüllung von Betroffenenrechten',
|
||||
en: 'Vendor supports fulfillment of data subject rights',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Unterstützungspflicht vereinbart, Prozess zur Weiterleitung definiert',
|
||||
en: 'Support obligation agreed, process for forwarding defined',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. e DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-DSR-02',
|
||||
domain: 'DATA_SUBJECT',
|
||||
title: {
|
||||
de: 'Reaktionszeit für Anfragen',
|
||||
en: 'Response time for requests',
|
||||
},
|
||||
description: {
|
||||
de: 'Definierte Reaktionszeit für Betroffenenanfragen',
|
||||
en: 'Defined response time for data subject requests',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Reaktionszeit max. 5 Werktage, um Frist von 1 Monat einhalten zu können',
|
||||
en: 'Response time max. 5 business days to meet 1 month deadline',
|
||||
},
|
||||
requirements: ['Art. 12 Abs. 3 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// SECURITY - Sicherheit
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-SEC-01',
|
||||
domain: 'SECURITY',
|
||||
title: {
|
||||
de: 'Sicherheitsbewertung',
|
||||
en: 'Security assessment',
|
||||
},
|
||||
description: {
|
||||
de: 'Regelmäßige Sicherheitsbewertung des Vendors',
|
||||
en: 'Regular security assessment of the vendor',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Sicherheitsfragebogen ausgefüllt, keine kritischen Lücken',
|
||||
en: 'Security questionnaire completed, no critical gaps',
|
||||
},
|
||||
requirements: ['Art. 32 DSGVO', 'ISO 27001 A.15.2.1'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-SEC-02',
|
||||
domain: 'SECURITY',
|
||||
title: {
|
||||
de: 'Vulnerability Management',
|
||||
en: 'Vulnerability management',
|
||||
},
|
||||
description: {
|
||||
de: 'Etabliertes Vulnerability Management beim Vendor',
|
||||
en: 'Established vulnerability management at the vendor',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Regelmäßige Schwachstellen-Scans, Patch-Management dokumentiert',
|
||||
en: 'Regular vulnerability scans, patch management documented',
|
||||
},
|
||||
requirements: ['ISO 27001 A.12.6'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-SEC-03',
|
||||
domain: 'SECURITY',
|
||||
title: {
|
||||
de: 'Mitarbeiter-Schulung',
|
||||
en: 'Employee training',
|
||||
},
|
||||
description: {
|
||||
de: 'Datenschutz-Schulung für Mitarbeiter des Vendors',
|
||||
en: 'Data protection training for vendor employees',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Regelmäßige Schulungen (mind. jährlich), Nachweis verfügbar',
|
||||
en: 'Regular training (at least annually), proof available',
|
||||
},
|
||||
requirements: ['Art. 39 Abs. 1 lit. b DSGVO'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// GOVERNANCE - Governance
|
||||
// ==========================================
|
||||
{
|
||||
id: 'VND-GOV-01',
|
||||
domain: 'GOVERNANCE',
|
||||
title: {
|
||||
de: 'Datenschutzbeauftragter benannt',
|
||||
en: 'Data protection officer appointed',
|
||||
},
|
||||
description: {
|
||||
de: 'Vendor hat DSB benannt (wenn erforderlich)',
|
||||
en: 'Vendor has appointed DPO (if required)',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'DSB benannt und Kontaktdaten verfügbar',
|
||||
en: 'DPO appointed and contact details available',
|
||||
},
|
||||
requirements: ['Art. 37 DSGVO'],
|
||||
isRequired: false,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-GOV-02',
|
||||
domain: 'GOVERNANCE',
|
||||
title: {
|
||||
de: 'Verzeichnis der Verarbeitungstätigkeiten',
|
||||
en: 'Records of processing activities',
|
||||
},
|
||||
description: {
|
||||
de: 'Vendor führt eigenes Verarbeitungsverzeichnis',
|
||||
en: 'Vendor maintains own processing records',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Verzeichnis nach Art. 30 Abs. 2 DSGVO vorhanden',
|
||||
en: 'Records according to Art. 30(2) GDPR available',
|
||||
},
|
||||
requirements: ['Art. 30 Abs. 2 DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
{
|
||||
id: 'VND-GOV-03',
|
||||
domain: 'GOVERNANCE',
|
||||
title: {
|
||||
de: 'Unterstützung bei DSFA',
|
||||
en: 'Support for DPIA',
|
||||
},
|
||||
description: {
|
||||
de: 'Vendor unterstützt bei Datenschutz-Folgenabschätzung',
|
||||
en: 'Vendor supports data protection impact assessment',
|
||||
},
|
||||
passCriteria: {
|
||||
de: 'Unterstützungspflicht bei DSFA vertraglich vereinbart',
|
||||
en: 'Support obligation for DPIA contractually agreed',
|
||||
},
|
||||
requirements: ['Art. 28 Abs. 3 lit. f DSGVO'],
|
||||
isRequired: true,
|
||||
defaultFrequency: 'ANNUAL',
|
||||
},
|
||||
]
|
||||
|
||||
// ==========================================
|
||||
// HELPER FUNCTIONS
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Get all controls
|
||||
*/
|
||||
export function getAllControls(): Control[] {
|
||||
return CONTROLS_LIBRARY
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controls by domain
|
||||
*/
|
||||
export function getControlsByDomain(domain: ControlDomain): Control[] {
|
||||
return CONTROLS_LIBRARY.filter((c) => c.domain === domain)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get control by ID
|
||||
*/
|
||||
export function getControlById(id: string): Control | undefined {
|
||||
return CONTROLS_LIBRARY.find((c) => c.id === id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required controls
|
||||
*/
|
||||
export function getRequiredControls(): Control[] {
|
||||
return CONTROLS_LIBRARY.filter((c) => c.isRequired)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controls by frequency
|
||||
*/
|
||||
export function getControlsByFrequency(frequency: ReviewFrequency): Control[] {
|
||||
return CONTROLS_LIBRARY.filter((c) => c.defaultFrequency === frequency)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controls applicable to vendors
|
||||
*/
|
||||
export function getVendorControls(): Control[] {
|
||||
return CONTROLS_LIBRARY.filter((c) =>
|
||||
['TRANSFER', 'AUDIT', 'DELETION', 'INCIDENT', 'SUBPROCESSOR', 'TOM', 'CONTRACT', 'DATA_SUBJECT', 'SECURITY', 'GOVERNANCE'].includes(c.domain)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controls applicable to processing activities
|
||||
*/
|
||||
export function getProcessingActivityControls(): Control[] {
|
||||
return CONTROLS_LIBRARY.filter((c) =>
|
||||
['TOM', 'DATA_SUBJECT', 'GOVERNANCE', 'SECURITY'].includes(c.domain)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Group controls by domain
|
||||
*/
|
||||
export function getControlsGroupedByDomain(): Map<ControlDomain, Control[]> {
|
||||
const grouped = new Map<ControlDomain, Control[]>()
|
||||
|
||||
for (const control of CONTROLS_LIBRARY) {
|
||||
const existing = grouped.get(control.domain) || []
|
||||
grouped.set(control.domain, [...existing, control])
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain metadata
|
||||
*/
|
||||
export function getControlDomainMeta(domain: ControlDomain): LocalizedText {
|
||||
const meta: Record<ControlDomain, LocalizedText> = {
|
||||
TRANSFER: { de: 'Drittlandtransfer', en: 'Third Country Transfer' },
|
||||
AUDIT: { de: 'Audit & Prüfung', en: 'Audit & Review' },
|
||||
DELETION: { de: 'Löschung', en: 'Deletion' },
|
||||
INCIDENT: { de: 'Incident Response', en: 'Incident Response' },
|
||||
SUBPROCESSOR: { de: 'Unterauftragnehmer', en: 'Sub-Processors' },
|
||||
TOM: { de: 'Technische/Org. Maßnahmen', en: 'Technical/Org. Measures' },
|
||||
CONTRACT: { de: 'Vertragliche Grundlagen', en: 'Contractual Basics' },
|
||||
DATA_SUBJECT: { de: 'Betroffenenrechte', en: 'Data Subject Rights' },
|
||||
SECURITY: { de: 'Sicherheit', en: 'Security' },
|
||||
GOVERNANCE: { de: 'Governance', en: 'Governance' },
|
||||
}
|
||||
|
||||
return meta[domain]
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate control coverage
|
||||
*/
|
||||
export function calculateControlCoverage(
|
||||
controlIds: string[],
|
||||
domain?: ControlDomain
|
||||
): { covered: number; total: number; percentage: number } {
|
||||
const targetControls = domain
|
||||
? getControlsByDomain(domain)
|
||||
: getRequiredControls()
|
||||
|
||||
const covered = targetControls.filter((c) => controlIds.includes(c.id)).length
|
||||
|
||||
return {
|
||||
covered,
|
||||
total: targetControls.length,
|
||||
percentage: targetControls.length > 0 ? Math.round((covered / targetControls.length) * 100) : 0,
|
||||
}
|
||||
}
|
||||
6
admin-v2/lib/sdk/vendor-compliance/risk/index.ts
Normal file
6
admin-v2/lib/sdk/vendor-compliance/risk/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Risk & Controls exports
|
||||
*/
|
||||
|
||||
export * from './calculator'
|
||||
export * from './controls-library'
|
||||
Reference in New Issue
Block a user