/** * TOM Rules Engine — Gap Analysis & Helper Functions * * Singleton instance, convenience functions, and gap analysis logic. */ import { DerivedTOM, EvidenceDocument, GapAnalysisResult, MissingControl, PartialControl, MissingEvidence, RulesEngineResult, RulesEngineEvaluationContext, } from './types' import { getControlById } from './controls/loader' import { TOMRulesEngine } from './rules-evaluator' // ============================================================================= // SINGLETON INSTANCE // ============================================================================= let rulesEngineInstance: TOMRulesEngine | null = null export function getTOMRulesEngine(): TOMRulesEngine { if (!rulesEngineInstance) { rulesEngineInstance = new TOMRulesEngine() } return rulesEngineInstance } // ============================================================================= // GAP ANALYSIS // ============================================================================= export function performGapAnalysis( derivedTOMs: DerivedTOM[], documents: EvidenceDocument[] ): GapAnalysisResult { const missingControls: MissingControl[] = [] const partialControls: PartialControl[] = [] const missingEvidence: MissingEvidence[] = [] const recommendations: string[] = [] let totalScore = 0 let totalWeight = 0 const applicableTOMs = derivedTOMs.filter( (tom) => tom.applicability === 'REQUIRED' || tom.applicability === 'RECOMMENDED' ) for (const tom of applicableTOMs) { const control = getControlById(tom.controlId) if (!control) continue const weight = tom.applicability === 'REQUIRED' ? 3 : 1 totalWeight += weight if (tom.implementationStatus === 'NOT_IMPLEMENTED') { missingControls.push({ controlId: tom.controlId, reason: `${control.name.de} ist nicht implementiert`, priority: control.priority, }) } else if (tom.implementationStatus === 'PARTIAL') { partialControls.push({ controlId: tom.controlId, missingAspects: tom.evidenceGaps, }) totalScore += weight * 0.5 } else { totalScore += weight } const linkedEvidenceIds = tom.linkedEvidence const requiredEvidence = control.evidenceRequirements const providedEvidence = documents.filter((doc) => linkedEvidenceIds.includes(doc.id) ) if (providedEvidence.length < requiredEvidence.length) { const missing = requiredEvidence.filter( (req) => !providedEvidence.some( (doc) => doc.documentType === 'POLICY' || doc.documentType === 'CERTIFICATE' || doc.originalName.toLowerCase().includes(req.toLowerCase()) ) ) if (missing.length > 0) { missingEvidence.push({ controlId: tom.controlId, requiredEvidence: missing, }) } } } const overallScore = totalWeight > 0 ? Math.round((totalScore / totalWeight) * 100) : 0 if (missingControls.length > 0) { const criticalMissing = missingControls.filter((mc) => mc.priority === 'CRITICAL') if (criticalMissing.length > 0) { recommendations.push( `${criticalMissing.length} kritische Kontrollen sind nicht implementiert. Diese sollten priorisiert werden.` ) } } if (partialControls.length > 0) { recommendations.push( `${partialControls.length} Kontrollen sind nur teilweise implementiert. Vervollstaendigen Sie die Implementierung.` ) } if (missingEvidence.length > 0) { recommendations.push( `Fuer ${missingEvidence.length} Kontrollen fehlen Nachweisdokumente. Laden Sie die entsprechenden Dokumente hoch.` ) } if (overallScore >= 80) { recommendations.push( 'Ihr TOM-Compliance-Score ist gut. Fuehren Sie regelmaessige Ueberpruefungen durch.' ) } else if (overallScore >= 50) { recommendations.push( 'Ihr TOM-Compliance-Score erfordert Verbesserungen. Fokussieren Sie sich auf die kritischen Luecken.' ) } else { recommendations.push( 'Ihr TOM-Compliance-Score ist niedrig. Eine systematische Ueberarbeitung der Massnahmen wird empfohlen.' ) } return { overallScore, missingControls, partialControls, missingEvidence, recommendations, generatedAt: new Date(), } } // ============================================================================= // HELPER FUNCTIONS // ============================================================================= export function evaluateControlsForContext( context: RulesEngineEvaluationContext ): RulesEngineResult[] { return getTOMRulesEngine().evaluateControls(context) } export function deriveTOMsForContext( context: RulesEngineEvaluationContext ): DerivedTOM[] { return getTOMRulesEngine().deriveAllTOMs(context) } export function performQuickGapAnalysis( derivedTOMs: DerivedTOM[], documents: EvidenceDocument[] ): GapAnalysisResult { return performGapAnalysis(derivedTOMs, documents) }