Files
breakpilot-compliance/admin-compliance/lib/sdk/tom-generator/gap-analysis.ts
Sharang Parnerkar 528abc86ab refactor(admin): split 8 oversized lib/ files into focused modules under 500 LOC
Split these files that exceeded the 500-line hard cap:
- privacy-policy.ts (965 LOC) -> sections + renderers
- academy/api.ts (787 LOC) -> courses + mock-data
- whistleblower/api.ts (755 LOC) -> operations + mock-data
- vvt-profiling.ts (659 LOC) -> data + logic
- cookie-banner.ts (595 LOC) -> config + embed
- dsr/types.ts (581 LOC) -> core + api types
- tom-generator/rules-engine.ts (560 LOC) -> evaluator + gap-analysis
- datapoint-helpers.ts (548 LOC) -> generators + validators

Each original file becomes a barrel re-export for backward compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 21:05:59 +02:00

172 lines
4.9 KiB
TypeScript

/**
* 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)
}