Files
breakpilot-compliance/ai-compliance-sdk/internal/ucca/obligation_condition_engine.go
Benjamin Admin 38e278ee3c
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 29s
CI / test-python-document-crawler (push) Successful in 20s
CI / test-python-dsms-gateway (push) Successful in 18s
feat(ucca): Pflichtendatenbank v2 (325 Obligations), Trigger-Engine, TOM-Control-Mapping
- 9 Regulation-JSON-Dateien (DSGVO 80, AI Act 60, NIS2 40, BDSG 30, TTDSG 20, DSA 35, Data Act 25, EU-Maschinen 15, DORA 20)
- Condition-Tree-Engine fuer automatische Pflichtenselektion (all_of/any_of, 80+ Field-Paths)
- Generischer JSONRegulationModule-Loader mit YAML-Fallback
- Bidirektionales TOM-Control-Mapping (291 Obligation→Control, 92 Control→Obligation)
- Gap-Analyse-Engine (Compliance-%, Priority Actions, Domain Breakdown)
- ScopeDecision→UnifiedFacts Bridge fuer Auto-Profiling
- 4 neue API-Endpoints (assess-from-scope, tom-controls, gap-analysis, reverse-lookup)
- Frontend: Auto-Profiling Button, Regulation-Filter Chips, TOM-Panel, Gap-Analyse-View
- 18 Unit Tests (Condition Engine, v2 Loader, TOM Mapper)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:51:44 +01:00

301 lines
17 KiB
Go

package ucca
import (
"encoding/json"
"fmt"
"strings"
)
// ObligationConditionEngine evaluates condition trees against UnifiedFacts
type ObligationConditionEngine struct {
fieldMap map[string]func(*UnifiedFacts) interface{}
}
// NewObligationConditionEngine creates a new condition engine with all field mappings
func NewObligationConditionEngine() *ObligationConditionEngine {
e := &ObligationConditionEngine{}
e.fieldMap = e.buildFieldMap()
return e
}
// Evaluate evaluates a condition node against facts
func (e *ObligationConditionEngine) Evaluate(node *ConditionNode, facts *UnifiedFacts) bool {
if node == nil {
return true // nil condition = always applies
}
// Composite: all_of (AND)
if len(node.AllOf) > 0 {
for _, child := range node.AllOf {
if !e.Evaluate(&child, facts) {
return false
}
}
return true
}
// Composite: any_of (OR)
if len(node.AnyOf) > 0 {
for _, child := range node.AnyOf {
if e.Evaluate(&child, facts) {
return true
}
}
return false
}
// Leaf node: field + operator + value
if node.Field != "" {
return e.evaluateLeaf(node, facts)
}
return true
}
func (e *ObligationConditionEngine) evaluateLeaf(node *ConditionNode, facts *UnifiedFacts) bool {
getter, ok := e.fieldMap[node.Field]
if !ok {
fmt.Printf("Warning: unknown field in condition: %s\n", node.Field)
return false
}
actual := getter(facts)
return e.compare(actual, node.Operator, node.Value)
}
func (e *ObligationConditionEngine) compare(actual interface{}, operator string, expected interface{}) bool {
switch strings.ToUpper(operator) {
case "EQUALS":
return e.equals(actual, expected)
case "NOT_EQUALS":
return !e.equals(actual, expected)
case "GREATER_THAN":
return e.toFloat(actual) > e.toFloat(expected)
case "LESS_THAN":
return e.toFloat(actual) < e.toFloat(expected)
case "GREATER_OR_EQUAL":
return e.toFloat(actual) >= e.toFloat(expected)
case "LESS_OR_EQUAL":
return e.toFloat(actual) <= e.toFloat(expected)
case "IN":
return e.inSlice(actual, expected)
case "NOT_IN":
return !e.inSlice(actual, expected)
case "CONTAINS":
return e.contains(actual, expected)
case "EXISTS":
return actual != nil && actual != "" && actual != false && actual != 0
default:
fmt.Printf("Warning: unknown operator: %s\n", operator)
return false
}
}
func (e *ObligationConditionEngine) equals(a, b interface{}) bool {
// Handle bool comparisons
aBool, aIsBool := e.toBool(a)
bBool, bIsBool := e.toBool(b)
if aIsBool && bIsBool {
return aBool == bBool
}
// Handle numeric comparisons
aFloat, aIsNum := e.toFloatOk(a)
bFloat, bIsNum := e.toFloatOk(b)
if aIsNum && bIsNum {
return aFloat == bFloat
}
// String comparison
return fmt.Sprintf("%v", a) == fmt.Sprintf("%v", b)
}
func (e *ObligationConditionEngine) toBool(v interface{}) (bool, bool) {
switch b := v.(type) {
case bool:
return b, true
}
return false, false
}
func (e *ObligationConditionEngine) toFloat(v interface{}) float64 {
f, _ := e.toFloatOk(v)
return f
}
func (e *ObligationConditionEngine) toFloatOk(v interface{}) (float64, bool) {
switch n := v.(type) {
case int:
return float64(n), true
case int64:
return float64(n), true
case float64:
return n, true
case float32:
return float64(n), true
case json.Number:
f, err := n.Float64()
return f, err == nil
}
return 0, false
}
func (e *ObligationConditionEngine) inSlice(actual, expected interface{}) bool {
actualStr := fmt.Sprintf("%v", actual)
switch v := expected.(type) {
case []interface{}:
for _, item := range v {
if fmt.Sprintf("%v", item) == actualStr {
return true
}
}
case []string:
for _, item := range v {
if item == actualStr {
return true
}
}
}
return false
}
func (e *ObligationConditionEngine) contains(actual, expected interface{}) bool {
// Check if actual (slice) contains expected
switch v := actual.(type) {
case []string:
exp := fmt.Sprintf("%v", expected)
for _, item := range v {
if item == exp {
return true
}
}
case string:
return strings.Contains(v, fmt.Sprintf("%v", expected))
}
return false
}
// buildFieldMap creates the mapping from JSON field paths to Go struct accessors
func (e *ObligationConditionEngine) buildFieldMap() map[string]func(*UnifiedFacts) interface{} {
return map[string]func(*UnifiedFacts) interface{}{
// Organization
"organization.employee_count": func(f *UnifiedFacts) interface{} { return f.Organization.EmployeeCount },
"organization.annual_revenue": func(f *UnifiedFacts) interface{} { return f.Organization.AnnualRevenue },
"organization.country": func(f *UnifiedFacts) interface{} { return f.Organization.Country },
"organization.eu_member": func(f *UnifiedFacts) interface{} { return f.Organization.EUMember },
"organization.is_public_authority": func(f *UnifiedFacts) interface{} { return f.Organization.IsPublicAuthority },
"organization.legal_form": func(f *UnifiedFacts) interface{} { return f.Organization.LegalForm },
"organization.size_category": func(f *UnifiedFacts) interface{} { return f.Organization.CalculateSizeCategory() },
"organization.is_part_of_group": func(f *UnifiedFacts) interface{} { return f.Organization.IsPartOfGroup },
// Data Protection
"data_protection.processes_personal_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesPersonalData },
"data_protection.is_controller": func(f *UnifiedFacts) interface{} { return f.DataProtection.IsController },
"data_protection.is_processor": func(f *UnifiedFacts) interface{} { return f.DataProtection.IsProcessor },
"data_protection.processes_special_categories": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesSpecialCategories },
"data_protection.processes_children_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesMinorData },
"data_protection.processes_minor_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesMinorData },
"data_protection.processes_criminal_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesCriminalData },
"data_protection.large_scale": func(f *UnifiedFacts) interface{} { return f.DataProtection.LargeScaleProcessing },
"data_protection.large_scale_processing": func(f *UnifiedFacts) interface{} { return f.DataProtection.LargeScaleProcessing },
"data_protection.systematic_monitoring": func(f *UnifiedFacts) interface{} { return f.DataProtection.SystematicMonitoring },
"data_protection.uses_automated_decisions": func(f *UnifiedFacts) interface{} { return f.DataProtection.AutomatedDecisionMaking },
"data_protection.automated_decision_making": func(f *UnifiedFacts) interface{} { return f.DataProtection.AutomatedDecisionMaking },
"data_protection.cross_border_transfer": func(f *UnifiedFacts) interface{} { return f.DataProtection.TransfersToThirdCountries },
"data_protection.transfers_to_third_countries": func(f *UnifiedFacts) interface{} { return f.DataProtection.TransfersToThirdCountries },
"data_protection.cross_border_processing": func(f *UnifiedFacts) interface{} { return f.DataProtection.CrossBorderProcessing },
"data_protection.uses_processors": func(f *UnifiedFacts) interface{} { return f.DataProtection.UsesExternalProcessor },
"data_protection.uses_external_processor": func(f *UnifiedFacts) interface{} { return f.DataProtection.UsesExternalProcessor },
"data_protection.high_risk": func(f *UnifiedFacts) interface{} { return f.DataProtection.LargeScaleProcessing || f.DataProtection.SystematicMonitoring || f.DataProtection.ProcessesSpecialCategories },
"data_protection.uses_profiling": func(f *UnifiedFacts) interface{} { return f.DataProtection.Profiling },
"data_protection.profiling": func(f *UnifiedFacts) interface{} { return f.DataProtection.Profiling },
"data_protection.requires_dsb": func(f *UnifiedFacts) interface{} { return f.DataProtection.RequiresDSBByLaw },
"data_protection.needs_dpo": func(f *UnifiedFacts) interface{} { return f.DataProtection.RequiresDSBByLaw },
"data_protection.has_appointed_dsb": func(f *UnifiedFacts) interface{} { return f.DataProtection.HasAppointedDSB },
"data_protection.data_subject_count": func(f *UnifiedFacts) interface{} { return f.DataProtection.DataSubjectCount },
"data_protection.sccs_in_place": func(f *UnifiedFacts) interface{} { return f.DataProtection.SCCsInPlace },
"data_protection.binding_corporate_rules": func(f *UnifiedFacts) interface{} { return f.DataProtection.BindingCorporateRules },
"data_protection.special_categories": func(f *UnifiedFacts) interface{} { return f.DataProtection.SpecialCategories },
"data_protection.processes_employee_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesEmployeeData },
"data_protection.processes_health_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesHealthData },
"data_protection.processes_financial_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesFinancialData },
"data_protection.uses_cookies": func(f *UnifiedFacts) interface{} { return f.DataProtection.UsesCookies },
"data_protection.uses_tracking": func(f *UnifiedFacts) interface{} { return f.DataProtection.UsesTracking },
"data_protection.uses_video_surveillance": func(f *UnifiedFacts) interface{} { return f.DataProtection.UsesVideoSurveillance },
"data_protection.processes_biometric_data": func(f *UnifiedFacts) interface{} { return f.DataProtection.ProcessesBiometricData },
"data_protection.operates_platform": func(f *UnifiedFacts) interface{} { return f.DataProtection.OperatesPlatform },
"data_protection.platform_user_count": func(f *UnifiedFacts) interface{} { return f.DataProtection.PlatformUserCount },
// AI Usage
"ai_usage.uses_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.UsesAI },
"ai_usage.is_ai_provider": func(f *UnifiedFacts) interface{} { return f.AIUsage.IsAIProvider },
"ai_usage.is_ai_deployer": func(f *UnifiedFacts) interface{} { return f.AIUsage.IsAIDeployer },
"ai_usage.is_ai_distributor": func(f *UnifiedFacts) interface{} { return f.AIUsage.IsAIDistributor },
"ai_usage.is_ai_importer": func(f *UnifiedFacts) interface{} { return f.AIUsage.IsAIImporter },
"ai_usage.high_risk_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.HasHighRiskAI },
"ai_usage.has_high_risk_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.HasHighRiskAI },
"ai_usage.limited_risk_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.HasLimitedRiskAI },
"ai_usage.has_limited_risk_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.HasLimitedRiskAI },
"ai_usage.minimal_risk_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.HasMinimalRiskAI },
"ai_usage.is_gpai_provider": func(f *UnifiedFacts) interface{} { return f.AIUsage.UsesGPAI },
"ai_usage.gpai_systemic_risk": func(f *UnifiedFacts) interface{} { return f.AIUsage.GPAIWithSystemicRisk },
"ai_usage.uses_biometric_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.BiometricIdentification },
"ai_usage.biometric_identification": func(f *UnifiedFacts) interface{} { return f.AIUsage.BiometricIdentification },
"ai_usage.uses_emotion_recognition": func(f *UnifiedFacts) interface{} { return f.AIUsage.EmotionRecognition },
"ai_usage.ai_in_education": func(f *UnifiedFacts) interface{} { return f.AIUsage.EducationAccess },
"ai_usage.ai_in_employment": func(f *UnifiedFacts) interface{} { return f.AIUsage.EmploymentDecisions },
"ai_usage.ai_in_critical_infrastructure": func(f *UnifiedFacts) interface{} { return f.AIUsage.CriticalInfrastructure },
"ai_usage.ai_in_law_enforcement": func(f *UnifiedFacts) interface{} { return f.AIUsage.LawEnforcement },
"ai_usage.ai_in_justice": func(f *UnifiedFacts) interface{} { return f.AIUsage.JusticeAdministration },
"ai_usage.uses_generative_ai": func(f *UnifiedFacts) interface{} { return f.AIUsage.AIInteractsWithNaturalPersons },
"ai_usage.uses_deepfakes": func(f *UnifiedFacts) interface{} { return f.AIUsage.GeneratesDeepfakes },
"ai_usage.ai_makes_decisions": func(f *UnifiedFacts) interface{} { return f.DataProtection.AutomatedDecisionMaking },
"ai_usage.ai_interacts_with_persons": func(f *UnifiedFacts) interface{} { return f.AIUsage.AIInteractsWithNaturalPersons },
// Sector
"sector.primary_sector": func(f *UnifiedFacts) interface{} { return f.Sector.PrimarySector },
"sector.is_kritis": func(f *UnifiedFacts) interface{} { return f.Sector.IsKRITIS },
"sector.nis2_applicable": func(f *UnifiedFacts) interface{} { return f.Sector.PrimarySector != "" && f.Sector.PrimarySector != "other" },
"sector.nis2_classification": func(f *UnifiedFacts) interface{} { return f.Sector.NIS2Classification },
"sector.is_annex_i": func(f *UnifiedFacts) interface{} { return f.Sector.IsAnnexI },
"sector.is_annex_ii": func(f *UnifiedFacts) interface{} { return f.Sector.IsAnnexII },
"sector.provides_dns": func(f *UnifiedFacts) interface{} { return containsString(f.Sector.SpecialServices, "dns") },
"sector.provides_cloud": func(f *UnifiedFacts) interface{} { return containsString(f.Sector.SpecialServices, "cloud") },
"sector.provides_cdn": func(f *UnifiedFacts) interface{} { return containsString(f.Sector.SpecialServices, "cdn") },
"sector.provides_data_center": func(f *UnifiedFacts) interface{} { return containsString(f.Sector.SpecialServices, "datacenter") },
"sector.provides_managed_services": func(f *UnifiedFacts) interface{} { return containsString(f.Sector.SpecialServices, "msp") || containsString(f.Sector.SpecialServices, "mssp") },
"sector.is_financial_institution": func(f *UnifiedFacts) interface{} { return f.Sector.IsFinancialInstitution },
"sector.is_healthcare_provider": func(f *UnifiedFacts) interface{} { return f.Sector.IsHealthcareProvider },
// IT Security
"it_security.has_isms": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasISMS },
"it_security.iso27001_certified": func(f *UnifiedFacts) interface{} { return f.ITSecurity.ISO27001Certified },
"it_security.has_incident_process": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasIncidentProcess },
"it_security.has_mfa": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasMFA },
"it_security.has_encryption": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasEncryption },
"it_security.has_backup": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasBackup },
"it_security.has_bcm": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasBCM },
"it_security.has_siem": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasSecurityMonitoring },
"it_security.has_network_segmentation": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasNetworkSegmentation },
"it_security.has_vulnerability_mgmt": func(f *UnifiedFacts) interface{} { return f.ITSecurity.HasVulnerabilityMgmt },
// Financial
"financial.dora_applies": func(f *UnifiedFacts) interface{} { return f.Financial.DORAApplies },
"financial.is_regulated": func(f *UnifiedFacts) interface{} { return f.Financial.IsRegulated },
"financial.has_critical_ict": func(f *UnifiedFacts) interface{} { return f.Financial.HasCriticalICT },
"financial.ict_outsourced": func(f *UnifiedFacts) interface{} { return f.Financial.ICTOutsourced },
"financial.concentration_risk": func(f *UnifiedFacts) interface{} { return f.Financial.ConcentrationRisk },
// Supply Chain
"supply_chain.has_risk_management": func(f *UnifiedFacts) interface{} { return f.SupplyChain.HasSupplyChainRiskMgmt },
"supply_chain.supplier_count": func(f *UnifiedFacts) interface{} { return f.SupplyChain.SupplierCount },
// Personnel
"personnel.has_ciso": func(f *UnifiedFacts) interface{} { return f.Personnel.HasCISO },
"personnel.has_dpo": func(f *UnifiedFacts) interface{} { return f.Personnel.HasDPO },
"personnel.has_ai_competence": func(f *UnifiedFacts) interface{} { return f.Personnel.HasAICompetence },
"personnel.has_ai_governance": func(f *UnifiedFacts) interface{} { return f.Personnel.HasAIGovernance },
"personnel.has_compliance_officer": func(f *UnifiedFacts) interface{} { return f.Personnel.HasComplianceOfficer },
}
}