Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
734
ai-compliance-sdk/internal/ucca/financial_policy.go
Normal file
734
ai-compliance-sdk/internal/ucca/financial_policy.go
Normal file
@@ -0,0 +1,734 @@
|
||||
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"`
|
||||
}
|
||||
Reference in New Issue
Block a user