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 }, } }