package ucca import ( "fmt" "os" "path/filepath" "strings" "gopkg.in/yaml.v3" ) // ============================================================================ // Financial Regulations Policy Engine // ============================================================================ // // This engine evaluates financial use-cases against DORA, MaRisk, and BAIT rules. // It extends the base PolicyEngine with financial-specific logic. // // Key regulations: // - DORA (Digital Operational Resilience Act) - EU 2022/2554 // - MaRisk (Mindestanforderungen an das Risikomanagement) - BaFin // - BAIT (Bankaufsichtliche Anforderungen an die IT) - BaFin // // ============================================================================ // DefaultFinancialPolicyPath is the default location for the financial policy file var DefaultFinancialPolicyPath = "policies/financial_regulations_policy.yaml" // FinancialPolicyConfig represents the financial regulations policy structure type FinancialPolicyConfig struct { Metadata FinancialPolicyMetadata `yaml:"metadata"` ApplicableDomains []string `yaml:"applicable_domains"` FactsSchema map[string]interface{} `yaml:"facts_schema"` Controls map[string]FinancialControlDef `yaml:"controls"` Gaps map[string]FinancialGapDef `yaml:"gaps"` StopLines map[string]FinancialStopLine `yaml:"stop_lines"` Rules []FinancialRuleDef `yaml:"rules"` EscalationTriggers []FinancialEscalationTrigger `yaml:"escalation_triggers"` } // FinancialPolicyMetadata contains policy header information type FinancialPolicyMetadata struct { Version string `yaml:"version"` EffectiveDate string `yaml:"effective_date"` Author string `yaml:"author"` Jurisdiction string `yaml:"jurisdiction"` Regulations []FinancialRegulationInfo `yaml:"regulations"` } // FinancialRegulationInfo describes a regulation type FinancialRegulationInfo struct { Name string `yaml:"name"` FullName string `yaml:"full_name"` Reference string `yaml:"reference,omitempty"` Authority string `yaml:"authority,omitempty"` Version string `yaml:"version,omitempty"` Effective string `yaml:"effective,omitempty"` } // FinancialControlDef represents a control specific to financial regulations type FinancialControlDef struct { ID string `yaml:"id"` Title string `yaml:"title"` Category string `yaml:"category"` DORARef string `yaml:"dora_ref,omitempty"` MaRiskRef string `yaml:"marisk_ref,omitempty"` BAITRef string `yaml:"bait_ref,omitempty"` MiFIDRef string `yaml:"mifid_ref,omitempty"` GwGRef string `yaml:"gwg_ref,omitempty"` Description string `yaml:"description"` WhenApplicable []string `yaml:"when_applicable,omitempty"` WhatToDo string `yaml:"what_to_do"` EvidenceNeeded []string `yaml:"evidence_needed,omitempty"` Effort string `yaml:"effort"` } // FinancialGapDef represents a compliance gap type FinancialGapDef struct { ID string `yaml:"id"` Title string `yaml:"title"` Description string `yaml:"description"` Severity string `yaml:"severity"` Escalation string `yaml:"escalation,omitempty"` When []string `yaml:"when,omitempty"` Controls []string `yaml:"controls,omitempty"` LegalRefs []string `yaml:"legal_refs,omitempty"` } // FinancialStopLine represents a hard blocker type FinancialStopLine struct { ID string `yaml:"id"` Title string `yaml:"title"` Description string `yaml:"description"` Outcome string `yaml:"outcome"` When []string `yaml:"when,omitempty"` Message string `yaml:"message"` } // FinancialRuleDef represents a rule from the financial policy type FinancialRuleDef struct { ID string `yaml:"id"` Category string `yaml:"category"` Title string `yaml:"title"` Description string `yaml:"description"` Condition FinancialConditionDef `yaml:"condition"` Effect FinancialEffectDef `yaml:"effect"` Severity string `yaml:"severity"` DORARef string `yaml:"dora_ref,omitempty"` MaRiskRef string `yaml:"marisk_ref,omitempty"` BAITRef string `yaml:"bait_ref,omitempty"` MiFIDRef string `yaml:"mifid_ref,omitempty"` GwGRef string `yaml:"gwg_ref,omitempty"` Rationale string `yaml:"rationale"` } // FinancialConditionDef represents a rule condition type FinancialConditionDef struct { Field string `yaml:"field,omitempty"` Operator string `yaml:"operator,omitempty"` Value interface{} `yaml:"value,omitempty"` AllOf []FinancialConditionDef `yaml:"all_of,omitempty"` AnyOf []FinancialConditionDef `yaml:"any_of,omitempty"` } // FinancialEffectDef represents the effect when a rule triggers type FinancialEffectDef struct { Feasibility string `yaml:"feasibility,omitempty"` ControlsAdd []string `yaml:"controls_add,omitempty"` RiskAdd int `yaml:"risk_add,omitempty"` Escalation bool `yaml:"escalation,omitempty"` } // FinancialEscalationTrigger defines when to escalate type FinancialEscalationTrigger struct { ID string `yaml:"id"` Trigger []string `yaml:"trigger,omitempty"` Level string `yaml:"level"` Reason string `yaml:"reason"` } // ============================================================================ // Financial Policy Engine Implementation // ============================================================================ // FinancialPolicyEngine evaluates intakes against financial regulations type FinancialPolicyEngine struct { config *FinancialPolicyConfig } // NewFinancialPolicyEngine creates a new financial policy engine func NewFinancialPolicyEngine() (*FinancialPolicyEngine, error) { searchPaths := []string{ DefaultFinancialPolicyPath, filepath.Join(".", "policies", "financial_regulations_policy.yaml"), filepath.Join("..", "policies", "financial_regulations_policy.yaml"), filepath.Join("..", "..", "policies", "financial_regulations_policy.yaml"), "/app/policies/financial_regulations_policy.yaml", } var data []byte var err error for _, path := range searchPaths { data, err = os.ReadFile(path) if err == nil { break } } if err != nil { return nil, fmt.Errorf("failed to load financial policy from any known location: %w", err) } var config FinancialPolicyConfig if err := yaml.Unmarshal(data, &config); err != nil { return nil, fmt.Errorf("failed to parse financial policy YAML: %w", err) } return &FinancialPolicyEngine{config: &config}, nil } // NewFinancialPolicyEngineFromPath loads policy from a specific file path func NewFinancialPolicyEngineFromPath(path string) (*FinancialPolicyEngine, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read financial policy file: %w", err) } var config FinancialPolicyConfig if err := yaml.Unmarshal(data, &config); err != nil { return nil, fmt.Errorf("failed to parse financial policy YAML: %w", err) } return &FinancialPolicyEngine{config: &config}, nil } // GetPolicyVersion returns the financial policy version func (e *FinancialPolicyEngine) GetPolicyVersion() string { return e.config.Metadata.Version } // IsApplicable checks if the financial policy applies to the given intake func (e *FinancialPolicyEngine) IsApplicable(intake *UseCaseIntake) bool { // Check if domain is in applicable domains domain := strings.ToLower(string(intake.Domain)) for _, d := range e.config.ApplicableDomains { if domain == d { return true } } return false } // Evaluate runs financial regulation rules against the intake func (e *FinancialPolicyEngine) Evaluate(intake *UseCaseIntake) *FinancialAssessmentResult { result := &FinancialAssessmentResult{ IsApplicable: e.IsApplicable(intake), Feasibility: FeasibilityYES, RiskScore: 0, TriggeredRules: []FinancialTriggeredRule{}, RequiredControls: []FinancialRequiredControl{}, IdentifiedGaps: []FinancialIdentifiedGap{}, StopLinesHit: []FinancialStopLineHit{}, EscalationLevel: "", PolicyVersion: e.config.Metadata.Version, } // If not applicable, return early if !result.IsApplicable { return result } // Check if financial context is provided if intake.FinancialContext == nil { result.MissingContext = true return result } hasBlock := false controlSet := make(map[string]bool) needsEscalation := "" // Evaluate each rule for _, rule := range e.config.Rules { if e.evaluateCondition(&rule.Condition, intake) { triggered := FinancialTriggeredRule{ Code: rule.ID, Category: rule.Category, Title: rule.Title, Description: rule.Description, Severity: parseSeverity(rule.Severity), ScoreDelta: rule.Effect.RiskAdd, Rationale: rule.Rationale, } // Add regulation references if rule.DORARef != "" { triggered.DORARef = rule.DORARef } if rule.MaRiskRef != "" { triggered.MaRiskRef = rule.MaRiskRef } if rule.BAITRef != "" { triggered.BAITRef = rule.BAITRef } if rule.MiFIDRef != "" { triggered.MiFIDRef = rule.MiFIDRef } result.TriggeredRules = append(result.TriggeredRules, triggered) result.RiskScore += rule.Effect.RiskAdd // Track severity if parseSeverity(rule.Severity) == SeverityBLOCK { hasBlock = true } // Override feasibility if specified if rule.Effect.Feasibility != "" { switch rule.Effect.Feasibility { case "NO": result.Feasibility = FeasibilityNO case "CONDITIONAL": if result.Feasibility != FeasibilityNO { result.Feasibility = FeasibilityCONDITIONAL } } } // Collect controls for _, ctrlID := range rule.Effect.ControlsAdd { if !controlSet[ctrlID] { controlSet[ctrlID] = true if ctrl, ok := e.config.Controls[ctrlID]; ok { result.RequiredControls = append(result.RequiredControls, FinancialRequiredControl{ ID: ctrl.ID, Title: ctrl.Title, Category: ctrl.Category, Description: ctrl.Description, WhatToDo: ctrl.WhatToDo, EvidenceNeeded: ctrl.EvidenceNeeded, Effort: ctrl.Effort, DORARef: ctrl.DORARef, MaRiskRef: ctrl.MaRiskRef, BAITRef: ctrl.BAITRef, }) } } } // Track escalation if rule.Effect.Escalation { needsEscalation = e.determineEscalationLevel(intake) } } } // Check stop lines for _, stopLine := range e.config.StopLines { if e.evaluateStopLineConditions(stopLine.When, intake) { result.StopLinesHit = append(result.StopLinesHit, FinancialStopLineHit{ ID: stopLine.ID, Title: stopLine.Title, Message: stopLine.Message, Outcome: stopLine.Outcome, }) result.Feasibility = FeasibilityNO hasBlock = true } } // Check gaps for _, gap := range e.config.Gaps { if e.evaluateGapConditions(gap.When, intake) { result.IdentifiedGaps = append(result.IdentifiedGaps, FinancialIdentifiedGap{ ID: gap.ID, Title: gap.Title, Description: gap.Description, Severity: parseSeverity(gap.Severity), Controls: gap.Controls, LegalRefs: gap.LegalRefs, }) if gap.Escalation != "" && needsEscalation == "" { needsEscalation = gap.Escalation } } } // Set final feasibility if hasBlock { result.Feasibility = FeasibilityNO } // Set escalation level result.EscalationLevel = needsEscalation // Generate summary result.Summary = e.generateSummary(result) return result } // evaluateCondition evaluates a condition against the intake func (e *FinancialPolicyEngine) evaluateCondition(cond *FinancialConditionDef, intake *UseCaseIntake) bool { // Handle composite all_of if len(cond.AllOf) > 0 { for _, subCond := range cond.AllOf { if !e.evaluateCondition(&subCond, intake) { return false } } return true } // Handle composite any_of if len(cond.AnyOf) > 0 { for _, subCond := range cond.AnyOf { if e.evaluateCondition(&subCond, intake) { return true } } return false } // Handle simple field condition if cond.Field != "" { return e.evaluateFieldCondition(cond.Field, cond.Operator, cond.Value, intake) } return false } // evaluateFieldCondition evaluates a single field comparison func (e *FinancialPolicyEngine) evaluateFieldCondition(field, operator string, value interface{}, intake *UseCaseIntake) bool { fieldValue := e.getFieldValue(field, intake) if fieldValue == nil { return false } switch operator { case "equals": return e.compareEquals(fieldValue, value) case "not_equals": return !e.compareEquals(fieldValue, value) case "in": return e.compareIn(fieldValue, value) default: return false } } // getFieldValue extracts a field value from the intake func (e *FinancialPolicyEngine) getFieldValue(field string, intake *UseCaseIntake) interface{} { parts := strings.Split(field, ".") if len(parts) == 0 { return nil } switch parts[0] { case "domain": return strings.ToLower(string(intake.Domain)) case "financial_entity": if len(parts) < 2 || intake.FinancialContext == nil { return nil } return e.getFinancialEntityValue(parts[1], intake.FinancialContext) case "ict_service": if len(parts) < 2 || intake.FinancialContext == nil { return nil } return e.getICTServiceValue(parts[1], intake.FinancialContext) case "ai_application": if len(parts) < 2 || intake.FinancialContext == nil { return nil } return e.getAIApplicationValue(parts[1], intake.FinancialContext) case "model_usage": if len(parts) < 2 { return nil } switch parts[1] { case "training": return intake.ModelUsage.Training case "finetune": return intake.ModelUsage.Finetune case "rag": return intake.ModelUsage.RAG } } return nil } func (e *FinancialPolicyEngine) getFinancialEntityValue(field string, ctx *FinancialContext) interface{} { switch field { case "type": return string(ctx.FinancialEntity.Type) case "regulated": return ctx.FinancialEntity.Regulated case "size_category": return string(ctx.FinancialEntity.SizeCategory) } return nil } func (e *FinancialPolicyEngine) getICTServiceValue(field string, ctx *FinancialContext) interface{} { switch field { case "is_critical": return ctx.ICTService.IsCritical case "is_outsourced": return ctx.ICTService.IsOutsourced case "provider_location": return string(ctx.ICTService.ProviderLocation) case "concentration_risk": return ctx.ICTService.ConcentrationRisk } return nil } func (e *FinancialPolicyEngine) getAIApplicationValue(field string, ctx *FinancialContext) interface{} { switch field { case "affects_customer_decisions": return ctx.AIApplication.AffectsCustomerDecisions case "algorithmic_trading": return ctx.AIApplication.AlgorithmicTrading case "risk_assessment": return ctx.AIApplication.RiskAssessment case "aml_kyc": return ctx.AIApplication.AMLKYC case "model_validation_done": return ctx.AIApplication.ModelValidationDone } return nil } // compareEquals compares two values for equality func (e *FinancialPolicyEngine) compareEquals(fieldValue, expected interface{}) bool { if bv, ok := fieldValue.(bool); ok { if eb, ok := expected.(bool); ok { return bv == eb } } if sv, ok := fieldValue.(string); ok { if es, ok := expected.(string); ok { return strings.EqualFold(sv, es) } } return false } // compareIn checks if fieldValue is in a list func (e *FinancialPolicyEngine) compareIn(fieldValue, expected interface{}) bool { list, ok := expected.([]interface{}) if !ok { return false } sv, ok := fieldValue.(string) if !ok { return false } for _, item := range list { if is, ok := item.(string); ok && strings.EqualFold(is, sv) { return true } } return false } // evaluateStopLineConditions evaluates stop line conditions func (e *FinancialPolicyEngine) evaluateStopLineConditions(conditions []string, intake *UseCaseIntake) bool { if intake.FinancialContext == nil { return false } for _, cond := range conditions { if !e.parseAndEvaluateSimpleCondition(cond, intake) { return false } } return len(conditions) > 0 } // evaluateGapConditions evaluates gap conditions func (e *FinancialPolicyEngine) evaluateGapConditions(conditions []string, intake *UseCaseIntake) bool { if intake.FinancialContext == nil { return false } for _, cond := range conditions { if !e.parseAndEvaluateSimpleCondition(cond, intake) { return false } } return len(conditions) > 0 } // parseAndEvaluateSimpleCondition parses "field == value" style conditions func (e *FinancialPolicyEngine) parseAndEvaluateSimpleCondition(condition string, intake *UseCaseIntake) bool { // Parse "field == value" or "field != value" if strings.Contains(condition, "==") { parts := strings.SplitN(condition, "==", 2) if len(parts) != 2 { return false } field := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) fieldVal := e.getFieldValue(field, intake) if fieldVal == nil { return false } // Handle boolean values if value == "true" { if bv, ok := fieldVal.(bool); ok { return bv } } else if value == "false" { if bv, ok := fieldVal.(bool); ok { return !bv } } // Handle string values if sv, ok := fieldVal.(string); ok { return strings.EqualFold(sv, value) } } return false } // determineEscalationLevel determines the appropriate escalation level func (e *FinancialPolicyEngine) determineEscalationLevel(intake *UseCaseIntake) string { if intake.FinancialContext == nil { return "" } ctx := intake.FinancialContext // E3: Highest level for critical cases if ctx.AIApplication.AlgorithmicTrading { return "E3" } if ctx.ICTService.IsCritical && ctx.ICTService.IsOutsourced { return "E3" } // E2: Medium level if ctx.AIApplication.RiskAssessment || ctx.AIApplication.AffectsCustomerDecisions { return "E2" } return "E1" } // generateSummary creates a human-readable summary func (e *FinancialPolicyEngine) generateSummary(result *FinancialAssessmentResult) string { var parts []string switch result.Feasibility { case FeasibilityYES: parts = append(parts, "Der Use Case ist aus regulatorischer Sicht (DORA/MaRisk/BAIT) grundsätzlich umsetzbar.") case FeasibilityCONDITIONAL: parts = append(parts, "Der Use Case ist unter Einhaltung der Finanzregulierungen bedingt umsetzbar.") case FeasibilityNO: parts = append(parts, "Der Use Case ist ohne weitere Maßnahmen regulatorisch nicht zulässig.") } if len(result.StopLinesHit) > 0 { parts = append(parts, fmt.Sprintf("%d kritische Stop-Lines wurden ausgelöst.", len(result.StopLinesHit))) } if len(result.IdentifiedGaps) > 0 { parts = append(parts, fmt.Sprintf("%d Compliance-Lücken wurden identifiziert.", len(result.IdentifiedGaps))) } if len(result.RequiredControls) > 0 { parts = append(parts, fmt.Sprintf("%d regulatorische Kontrollen sind erforderlich.", len(result.RequiredControls))) } if result.EscalationLevel != "" { parts = append(parts, fmt.Sprintf("Eskalation auf Stufe %s empfohlen.", result.EscalationLevel)) } return strings.Join(parts, " ") } // GetAllControls returns all controls in the financial policy func (e *FinancialPolicyEngine) GetAllControls() map[string]FinancialControlDef { return e.config.Controls } // GetAllGaps returns all gaps in the financial policy func (e *FinancialPolicyEngine) GetAllGaps() map[string]FinancialGapDef { return e.config.Gaps } // GetAllStopLines returns all stop lines in the financial policy func (e *FinancialPolicyEngine) GetAllStopLines() map[string]FinancialStopLine { return e.config.StopLines } // GetApplicableDomains returns domains where financial regulations apply func (e *FinancialPolicyEngine) GetApplicableDomains() []string { return e.config.ApplicableDomains } // ============================================================================ // Financial Assessment Result Types // ============================================================================ // FinancialAssessmentResult represents the result of financial regulation evaluation type FinancialAssessmentResult struct { IsApplicable bool `json:"is_applicable"` MissingContext bool `json:"missing_context,omitempty"` Feasibility Feasibility `json:"feasibility"` RiskScore int `json:"risk_score"` TriggeredRules []FinancialTriggeredRule `json:"triggered_rules"` RequiredControls []FinancialRequiredControl `json:"required_controls"` IdentifiedGaps []FinancialIdentifiedGap `json:"identified_gaps"` StopLinesHit []FinancialStopLineHit `json:"stop_lines_hit"` EscalationLevel string `json:"escalation_level,omitempty"` Summary string `json:"summary"` PolicyVersion string `json:"policy_version"` } // FinancialTriggeredRule represents a triggered financial regulation rule type FinancialTriggeredRule struct { Code string `json:"code"` Category string `json:"category"` Title string `json:"title"` Description string `json:"description"` Severity Severity `json:"severity"` ScoreDelta int `json:"score_delta"` DORARef string `json:"dora_ref,omitempty"` MaRiskRef string `json:"marisk_ref,omitempty"` BAITRef string `json:"bait_ref,omitempty"` MiFIDRef string `json:"mifid_ref,omitempty"` Rationale string `json:"rationale"` } // FinancialRequiredControl represents a required control type FinancialRequiredControl struct { ID string `json:"id"` Title string `json:"title"` Category string `json:"category"` Description string `json:"description"` WhatToDo string `json:"what_to_do"` EvidenceNeeded []string `json:"evidence_needed,omitempty"` Effort string `json:"effort"` DORARef string `json:"dora_ref,omitempty"` MaRiskRef string `json:"marisk_ref,omitempty"` BAITRef string `json:"bait_ref,omitempty"` } // FinancialIdentifiedGap represents an identified compliance gap type FinancialIdentifiedGap struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description"` Severity Severity `json:"severity"` Controls []string `json:"controls,omitempty"` LegalRefs []string `json:"legal_refs,omitempty"` } // FinancialStopLineHit represents a hit stop line type FinancialStopLineHit struct { ID string `json:"id"` Title string `json:"title"` Message string `json:"message"` Outcome string `json:"outcome"` }