Files
breakpilot-compliance/ai-compliance-sdk/internal/ucca/v2_loader.go
Benjamin Admin 717c31547a
Some checks failed
Build + Deploy / build-backend-compliance (push) Successful in 2m43s
Build + Deploy / build-admin-compliance (push) Successful in 1m46s
Build + Deploy / build-ai-sdk (push) Successful in 47s
Build + Deploy / build-developer-portal (push) Successful in 1m0s
Build + Deploy / build-tts (push) Successful in 1m14s
Build + Deploy / build-document-crawler (push) Successful in 37s
Build + Deploy / build-dsms-gateway (push) Successful in 20s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 19s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m35s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 42s
CI / test-python-backend (push) Successful in 42s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 27s
CI / validate-canonical-controls (push) Successful in 23s
Build + Deploy / trigger-orca (push) Failing after 2h32m34s
feat: Regulatory News Dashboard — proaktive Compliance-Alerts
Zeigt anstehende regulatorische Fristen im Dashboard an, abgeleitet
aus den bestehenden Obligation v2 JSON-Dateien. Keine neue DB-Tabelle.

Erster News-Eintrag: Widerrufsbutton-Pflicht ab 19.06.2026
(EU-RL 2023/2673, §356a BGB) — eigener Text, keine externe Quelle.

Features:
- Go Service: scannt Obligations nach Fristen, berechnet Urgency
- API: GET /sdk/v1/regulatory-news mit Countdown + Farbcodierung
- Dashboard: RegulatoryNewsFeed Sektion mit Countdown-Badges
- Vorlage: news-Feld in v2 JSON fuer zukuenftige regulatorische Updates
- 11 Tests (Sortierung, Urgency, Deadline-Parsing, Real-File-Test)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 17:43:19 +02:00

245 lines
8.1 KiB
Go

package ucca
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
)
// V2Manifest represents the registry of all v2 obligation files
type V2Manifest struct {
SchemaVersion string `json:"schema_version"`
Regulations []V2RegulationEntry `json:"regulations"`
TOMMappingFile string `json:"tom_mapping_file"`
TotalObl int `json:"total_obligations"`
}
// V2RegulationEntry is a single regulation in the manifest
type V2RegulationEntry struct {
ID string `json:"id"`
File string `json:"file"`
Version string `json:"version"`
Count int `json:"count"`
}
// V2RegulationFile is the top-level structure of a v2 regulation JSON file
type V2RegulationFile struct {
Regulation string `json:"regulation"`
Name string `json:"name"`
Description string `json:"description"`
Version string `json:"version"`
EffectiveDate string `json:"effective_date,omitempty"`
Obligations []V2Obligation `json:"obligations"`
Controls []V2Control `json:"controls,omitempty"`
IncidentDL []V2IncidentDL `json:"incident_deadlines,omitempty"`
}
// V2Obligation is the extended obligation structure
type V2Obligation struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
AppliesWhen string `json:"applies_when"`
AppliesWhenCondition *ConditionNode `json:"applies_when_condition,omitempty"`
LegalBasis []V2LegalBasis `json:"legal_basis"`
Sources []V2Source `json:"sources,omitempty"`
Category string `json:"category"`
Responsible string `json:"responsible"`
Deadline *V2Deadline `json:"deadline,omitempty"`
Sanctions *V2Sanctions `json:"sanctions,omitempty"`
Evidence []interface{} `json:"evidence,omitempty"`
Priority string `json:"priority"`
TOMControlIDs []string `json:"tom_control_ids,omitempty"`
BreakpilotFeature string `json:"breakpilot_feature,omitempty"`
ValidFrom string `json:"valid_from,omitempty"`
ValidUntil *string `json:"valid_until"`
Version string `json:"version,omitempty"`
ISO27001Mapping []string `json:"iso27001_mapping,omitempty"`
HowToImplement string `json:"how_to_implement,omitempty"`
News *V2ObligationNews `json:"news,omitempty"`
}
// V2ObligationNews is news metadata for dashboard display.
// Own text referencing legal basis — never copied from external sources.
type V2ObligationNews struct {
Headline string `json:"headline"`
Summary string `json:"summary"`
ActionRequired string `json:"action_required"`
Affected string `json:"affected,omitempty"`
ActionLink string `json:"action_link,omitempty"`
}
// V2LegalBasis is a legal reference in v2 format
type V2LegalBasis struct {
Norm string `json:"norm"`
Article string `json:"article"`
Title string `json:"title,omitempty"`
Erwaegungsgrund string `json:"erwaegungsgrund,omitempty"`
}
// V2Source is an external source reference
type V2Source struct {
Type string `json:"type"`
Ref string `json:"ref"`
}
// V2Deadline is a deadline in v2 format
type V2Deadline struct {
Type string `json:"type"`
Date string `json:"date,omitempty"`
Duration string `json:"duration,omitempty"`
Interval string `json:"interval,omitempty"`
Event string `json:"event,omitempty"`
}
// V2Sanctions is sanctions info in v2 format
type V2Sanctions struct {
MaxFine string `json:"max_fine,omitempty"`
PersonalLiability bool `json:"personal_liability,omitempty"`
CriminalLiability bool `json:"criminal_liability,omitempty"`
Description string `json:"description,omitempty"`
}
// V2Control is a control in v2 format
type V2Control struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Category string `json:"category"`
WhatToDo string `json:"what_to_do,omitempty"`
ISO27001Mapping []string `json:"iso27001_mapping,omitempty"`
Priority string `json:"priority,omitempty"`
}
// V2IncidentDL is an incident deadline in v2 format
type V2IncidentDL struct {
Phase string `json:"phase"`
Deadline string `json:"deadline"`
Content string `json:"content,omitempty"`
Recipient string `json:"recipient,omitempty"`
LegalBasis []V2LegalBasis `json:"legal_basis,omitempty"`
}
// ConditionNode represents a condition tree node for obligation applicability
type ConditionNode struct {
AllOf []ConditionNode `json:"all_of,omitempty"`
AnyOf []ConditionNode `json:"any_of,omitempty"`
Field string `json:"field,omitempty"`
Operator string `json:"operator,omitempty"`
Value interface{} `json:"value,omitempty"`
}
// V2TOMMapping is the bidirectional mapping file
type V2TOMMapping struct {
SchemaVersion string `json:"schema_version"`
ObligationToControl map[string][]string `json:"obligation_to_control"`
ControlToObligation map[string][]string `json:"control_to_obligation"`
}
// getV2BasePath returns the base path for v2 obligation files
func getV2BasePath() string {
// Try relative to the binary
candidates := []string{
"policies/obligations/v2",
"../policies/obligations/v2",
"../../policies/obligations/v2",
}
// Also try relative to the source file
_, filename, _, ok := runtime.Caller(0)
if ok {
srcDir := filepath.Dir(filename)
candidates = append(candidates,
filepath.Join(srcDir, "../../policies/obligations/v2"),
)
}
for _, p := range candidates {
abs, err := filepath.Abs(p)
if err != nil {
continue
}
if info, err := os.Stat(abs); err == nil && info.IsDir() {
return abs
}
}
return "policies/obligations/v2"
}
// LoadV2Manifest loads the v2 manifest file
func LoadV2Manifest() (*V2Manifest, error) {
basePath := getV2BasePath()
manifestPath := filepath.Join(basePath, "_manifest.json")
data, err := os.ReadFile(manifestPath)
if err != nil {
return nil, fmt.Errorf("failed to read v2 manifest: %w", err)
}
var manifest V2Manifest
if err := json.Unmarshal(data, &manifest); err != nil {
return nil, fmt.Errorf("failed to parse v2 manifest: %w", err)
}
return &manifest, nil
}
// LoadV2RegulationFile loads a single v2 regulation JSON file
func LoadV2RegulationFile(filename string) (*V2RegulationFile, error) {
basePath := getV2BasePath()
filePath := filepath.Join(basePath, filename)
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read v2 regulation file %s: %w", filename, err)
}
var regFile V2RegulationFile
if err := json.Unmarshal(data, &regFile); err != nil {
return nil, fmt.Errorf("failed to parse v2 regulation file %s: %w", filename, err)
}
return &regFile, nil
}
// LoadV2TOMMapping loads the bidirectional TOM mapping
func LoadV2TOMMapping() (*V2TOMMapping, error) {
basePath := getV2BasePath()
mappingPath := filepath.Join(basePath, "_tom_mapping.json")
data, err := os.ReadFile(mappingPath)
if err != nil {
return nil, fmt.Errorf("failed to read TOM mapping: %w", err)
}
var mapping V2TOMMapping
if err := json.Unmarshal(data, &mapping); err != nil {
return nil, fmt.Errorf("failed to parse TOM mapping: %w", err)
}
return &mapping, nil
}
// LoadAllV2Regulations loads all v2 regulation files from the manifest
func LoadAllV2Regulations() (map[string]*V2RegulationFile, error) {
manifest, err := LoadV2Manifest()
if err != nil {
return nil, err
}
result := make(map[string]*V2RegulationFile)
for _, entry := range manifest.Regulations {
regFile, err := LoadV2RegulationFile(entry.File)
if err != nil {
fmt.Printf("Warning: Could not load v2 regulation %s: %v\n", entry.ID, err)
continue
}
result[entry.ID] = regFile
}
return result, nil
}