// ============================================================================= // TOM Module - Compliance Check Engine // Prueft Technische und Organisatorische Massnahmen auf Vollstaendigkeit, // Konsistenz und DSGVO-Konformitaet (Art. 32 DSGVO) // // Check functions live in tom-compliance-checks.ts (barrel split). // ============================================================================= import type { TOMGeneratorState } from './tom-generator/types' import { resetIssueCounter, checkMissingResponsible, checkOverdueReview, checkMissingEvidence, checkStaleNotImplemented, checkIncompleteCategory, checkNoEncryption, checkNoPseudonymization, checkMissingAvailability, checkNoReviewProcess, checkUncoveredSDMGoal, checkHighRiskWithoutMeasures, } from './tom-compliance-checks' // ============================================================================= // TYPES // ============================================================================= export type TOMComplianceIssueSeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' export type TOMComplianceIssueType = | 'MISSING_RESPONSIBLE' | 'OVERDUE_REVIEW' | 'MISSING_EVIDENCE' | 'INCOMPLETE_CATEGORY' | 'NO_ENCRYPTION_MEASURES' | 'NO_PSEUDONYMIZATION' | 'MISSING_AVAILABILITY' | 'NO_REVIEW_PROCESS' | 'UNCOVERED_SDM_GOAL' | 'HIGH_RISK_WITHOUT_MEASURES' | 'STALE_NOT_IMPLEMENTED' export interface TOMComplianceIssue { id: string controlId: string controlName: string type: TOMComplianceIssueType severity: TOMComplianceIssueSeverity title: string description: string recommendation: string } export interface TOMComplianceCheckResult { issues: TOMComplianceIssue[] score: number // 0-100 stats: { total: number passed: number failed: number bySeverity: Record } } // ============================================================================= // CONSTANTS // ============================================================================= export const TOM_SEVERITY_LABELS_DE: Record = { CRITICAL: 'Kritisch', HIGH: 'Hoch', MEDIUM: 'Mittel', LOW: 'Niedrig', } export const TOM_SEVERITY_COLORS: Record = { CRITICAL: '#dc2626', HIGH: '#ea580c', MEDIUM: '#d97706', LOW: '#6b7280', } // ============================================================================= // MAIN COMPLIANCE CHECK // ============================================================================= /** * Fuehrt einen vollstaendigen Compliance-Check ueber alle TOMs durch. * * @param state - Der vollstaendige TOMGeneratorState * @returns TOMComplianceCheckResult mit Issues, Score und Statistiken */ export function runTOMComplianceCheck(state: TOMGeneratorState): TOMComplianceCheckResult { // Reset counter for deterministic IDs within a single check run resetIssueCounter() const issues: TOMComplianceIssue[] = [] // Filter to applicable TOMs only (REQUIRED or RECOMMENDED, exclude NOT_APPLICABLE) const applicableTOMs = state.derivedTOMs.filter( (tom) => tom.applicability === 'REQUIRED' || tom.applicability === 'RECOMMENDED' ) // Run per-TOM checks (1-3, 11) on each applicable TOM for (const tom of applicableTOMs) { const perTomChecks = [ checkMissingResponsible(tom), checkOverdueReview(tom), checkMissingEvidence(tom), checkStaleNotImplemented(tom, state), ] for (const issue of perTomChecks) { if (issue !== null) { issues.push(issue) } } } // Run aggregate checks (4-10) issues.push(...checkIncompleteCategory(applicableTOMs)) const aggregateChecks = [ checkNoEncryption(applicableTOMs), checkNoPseudonymization(applicableTOMs, state.dataProfile), checkMissingAvailability(applicableTOMs, state), checkNoReviewProcess(applicableTOMs), checkHighRiskWithoutMeasures(applicableTOMs, state.riskProfile), ] for (const issue of aggregateChecks) { if (issue !== null) { issues.push(issue) } } issues.push(...checkUncoveredSDMGoal(applicableTOMs)) // Calculate score const bySeverity: Record = { 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 TOM const failedControlIds = new Set( issues.filter((i) => !i.controlId.startsWith('SDM-') && i.controlId !== 'RISK-PROFILE').map((i) => i.controlId) ) const totalTOMs = applicableTOMs.length const failedCount = failedControlIds.size const passedCount = Math.max(0, totalTOMs - failedCount) return { issues, score, stats: { total: totalTOMs, passed: passedCount, failed: failedCount, bySeverity, }, } }