feat: CE × Compliance Crossover Engine
Automatische Erkennung von DSGVO/AI Act/CRA/NIS2/Data Act Implikationen bei CE-Gefaehrdungen. 50 Trigger-Mappings auf Hazard-Patterns → Compliance-Module mit Modul-Links. - compliance_triggers.go: 50 Pattern→Regulation Mappings - compliance_crossover.go: Engine die Projekt-Hazards gegen Trigger prueft - iace_handler_compliance.go: GET /compliance-triggers API - ComplianceAlerts.tsx: Frontend Alert-Panel auf Projekt-Uebersicht Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/iace"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// CE x Compliance Crossover Engine
|
||||
// ============================================================================
|
||||
|
||||
// GetComplianceTriggers handles GET /projects/:id/compliance-triggers.
|
||||
// It analyses the project's hazards and component patterns to determine
|
||||
// which DSGVO, AI Act, CRA, NIS2, and EU Data Act obligations are triggered.
|
||||
// The response includes deduplicated triggers sorted by severity, plus boolean
|
||||
// summary flags (dsfa_required, ai_act_relevant, cra_relevant, etc.).
|
||||
func (h *IACEHandler) GetComplianceTriggers(c *gin.Context) {
|
||||
projectID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
|
||||
// Verify project exists
|
||||
project, err := h.store.GetProject(c.Request.Context(), projectID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if project == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "project not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch all hazards for this project
|
||||
hazards, err := h.store.ListHazards(c.Request.Context(), projectID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load hazards: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Also run pattern matching with component tags to catch tag-based triggers.
|
||||
// Collect tags from the project's components (reuse the norms handler logic).
|
||||
componentTags := collectComponentTags(h, c, projectID)
|
||||
|
||||
// Get all patterns from the pattern library
|
||||
allPatterns := iace.AllPatterns()
|
||||
|
||||
// Additionally derive extra fired patterns by re-matching component tags
|
||||
// against the pattern engine. This ensures patterns that are not yet
|
||||
// applied as hazards still contribute their compliance triggers.
|
||||
engine := iace.NewPatternEngine()
|
||||
matchInput := iace.MatchInput{
|
||||
CustomTags: componentTags,
|
||||
}
|
||||
matchResult := engine.Match(matchInput)
|
||||
|
||||
// Merge matched pattern IDs into a pseudo-hazard list so the crossover
|
||||
// engine picks them up. We create lightweight Hazard structs with the
|
||||
// pattern ID embedded in the Description field.
|
||||
mergedHazards := make([]iace.Hazard, len(hazards))
|
||||
copy(mergedHazards, hazards)
|
||||
for _, pm := range matchResult.MatchedPatterns {
|
||||
mergedHazards = append(mergedHazards, iace.Hazard{
|
||||
Name: pm.PatternName,
|
||||
Description: "Pattern " + pm.PatternID,
|
||||
Category: firstOrEmpty(pm.HazardCats),
|
||||
})
|
||||
}
|
||||
|
||||
// Run the crossover engine
|
||||
summary := iace.GetProjectComplianceTriggers(mergedHazards, allPatterns)
|
||||
|
||||
c.JSON(http.StatusOK, summary)
|
||||
}
|
||||
|
||||
// firstOrEmpty returns the first element of a string slice or "".
|
||||
func firstOrEmpty(ss []string) string {
|
||||
if len(ss) > 0 {
|
||||
return ss[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -422,6 +422,9 @@ func registerIACERoutes(v1 *gin.RouterGroup, h *handlers.IACEHandler) {
|
||||
iaceRoutes.GET("/production-lines/:lid/dashboard", h.GetProductionLineDashboard)
|
||||
iaceRoutes.POST("/production-lines/:lid/stations", h.AddStationToLine)
|
||||
iaceRoutes.DELETE("/production-lines/:lid/stations/:sid", h.RemoveStationFromLine)
|
||||
|
||||
// CE x Compliance Crossover
|
||||
iaceRoutes.GET("/projects/:id/compliance-triggers", h.GetComplianceTriggers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
package iace
|
||||
|
||||
import "sort"
|
||||
|
||||
// GetProjectComplianceTriggers analyses a project's hazards and the full
|
||||
// pattern library to determine which DSGVO/AI Act/CRA/NIS2/Data Act
|
||||
// obligations are triggered. It returns a deduplicated, severity-sorted
|
||||
// summary with boolean flags for each regulation family.
|
||||
func GetProjectComplianceTriggers(hazards []Hazard, patterns []HazardPattern) *ComplianceTriggerSummary {
|
||||
triggerMap := GetComplianceTriggerMap()
|
||||
|
||||
// Build set of pattern IDs present in the pattern library for quick lookup
|
||||
patternByID := make(map[string]HazardPattern, len(patterns))
|
||||
for _, p := range patterns {
|
||||
patternByID[p.ID] = p
|
||||
}
|
||||
|
||||
// Collect all fired pattern IDs from the project's hazards.
|
||||
// Hazards created from pattern matching store the source pattern ID
|
||||
// in their Description ("Pattern HPXXX") or Name field.
|
||||
firedPatterns := make(map[string]bool)
|
||||
for _, h := range hazards {
|
||||
extractPatternIDs(h.Description, firedPatterns)
|
||||
extractPatternIDs(h.Name, firedPatterns)
|
||||
extractPatternIDs(h.Scenario, firedPatterns)
|
||||
}
|
||||
|
||||
// Also check each pattern against the hazard categories present
|
||||
// in the project — if a pattern generates a category that exists
|
||||
// among the hazards, consider the pattern relevant.
|
||||
hazardCats := make(map[string]bool)
|
||||
for _, h := range hazards {
|
||||
if h.Category != "" {
|
||||
hazardCats[h.Category] = true
|
||||
}
|
||||
}
|
||||
for _, p := range patterns {
|
||||
for _, cat := range p.GeneratedHazardCats {
|
||||
if hazardCats[cat] {
|
||||
firedPatterns[p.ID] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect tag-level information from hazard metadata
|
||||
tags := collectHazardTags(hazards)
|
||||
tagTriggers := GetTagBasedTriggers(tags)
|
||||
|
||||
// Gather all triggers from fired patterns
|
||||
var results []TriggerResult
|
||||
seenRegulation := make(map[string]bool)
|
||||
|
||||
for pid := range firedPatterns {
|
||||
triggers, ok := triggerMap[pid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, t := range triggers {
|
||||
key := t.Regulation + "|" + t.Module
|
||||
if seenRegulation[key] {
|
||||
continue
|
||||
}
|
||||
seenRegulation[key] = true
|
||||
|
||||
// Try to find a hazard name for context
|
||||
hName := findHazardNameForPattern(pid, hazards)
|
||||
results = append(results, TriggerResult{
|
||||
HazardID: "",
|
||||
HazardName: hName,
|
||||
PatternID: pid,
|
||||
Trigger: t,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Append tag-based triggers (deduplicated against pattern triggers)
|
||||
for _, t := range tagTriggers {
|
||||
key := t.Regulation + "|" + t.Module
|
||||
if seenRegulation[key] {
|
||||
continue
|
||||
}
|
||||
seenRegulation[key] = true
|
||||
results = append(results, TriggerResult{
|
||||
HazardID: "",
|
||||
HazardName: "Tag-basiert",
|
||||
PatternID: "",
|
||||
Trigger: t,
|
||||
})
|
||||
}
|
||||
|
||||
// Sort by severity: high > medium > low
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return severityRank(results[i].Trigger.Severity) > severityRank(results[j].Trigger.Severity)
|
||||
})
|
||||
|
||||
// Build boolean summary flags
|
||||
summary := buildSummaryFlags(results)
|
||||
|
||||
return &ComplianceTriggerSummary{
|
||||
Triggers: results,
|
||||
Total: len(results),
|
||||
Summary: summary,
|
||||
}
|
||||
}
|
||||
|
||||
// AllPatterns returns every hazard pattern from all pattern sources.
|
||||
// This mirrors the aggregation in NewPatternEngine but returns just the slice.
|
||||
func AllPatterns() []HazardPattern {
|
||||
p := GetBuiltinHazardPatterns()
|
||||
p = append(p, GetExtendedHazardPatterns()...)
|
||||
p = append(p, GetPressHazardPatterns()...)
|
||||
p = append(p, GetCobotHazardPatterns()...)
|
||||
p = append(p, GetOperationalHazardPatterns()...)
|
||||
p = append(p, GetDGUVExtendedPatterns()...)
|
||||
p = append(p, GetExtendedHazardPatterns2()...)
|
||||
p = append(p, GetElevatorPatterns()...)
|
||||
p = append(p, GetAGVAgriPatterns()...)
|
||||
p = append(p, GetFoodProcessingPatterns()...)
|
||||
p = append(p, GetPackagingPatterns()...)
|
||||
p = append(p, GetLaserPatterns()...)
|
||||
p = append(p, GetMedicalDevicePatterns()...)
|
||||
p = append(p, GetPressureEquipmentPatterns()...)
|
||||
p = append(p, GetConstructionPatterns()...)
|
||||
p = append(p, GetForestryConveyorPatterns()...)
|
||||
p = append(p, GetPlasticsMetalPatterns()...)
|
||||
p = append(p, GetWeldingGlassTextilePatterns()...)
|
||||
p = append(p, GetSpecificMachinePatterns()...)
|
||||
p = append(p, GetSpecificMachinePatterns2()...)
|
||||
p = append(p, GetCyberExtendedPatterns()...)
|
||||
p = append(p, GetCyberExtendedPatterns2()...)
|
||||
p = append(p, GetCyberExtendedPatterns3()...)
|
||||
p = append(p, GetWorkshopPatterns()...)
|
||||
p = append(p, GetMaintenanceExtPatterns()...)
|
||||
p = append(p, GetFinalPatternsA()...)
|
||||
p = append(p, GetFinalPatternsB()...)
|
||||
p = append(p, GetFinalPatternsC()...)
|
||||
p = append(p, GetFinalPatternsD()...)
|
||||
return p
|
||||
}
|
||||
|
||||
// extractPatternIDs scans a text for "HP" followed by digits and adds
|
||||
// any found pattern IDs to the set.
|
||||
func extractPatternIDs(text string, set map[string]bool) {
|
||||
for i := 0; i < len(text)-2; i++ {
|
||||
if text[i] == 'H' && text[i+1] == 'P' && i+2 < len(text) && text[i+2] >= '0' && text[i+2] <= '9' {
|
||||
end := i + 2
|
||||
for end < len(text) && text[end] >= '0' && text[end] <= '9' {
|
||||
end++
|
||||
}
|
||||
set[text[i:end]] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findHazardNameForPattern returns the name of the first hazard whose
|
||||
// description/name/scenario mentions the given pattern ID.
|
||||
func findHazardNameForPattern(pid string, hazards []Hazard) string {
|
||||
for _, h := range hazards {
|
||||
if containsPatternID(h.Description, pid) || containsPatternID(h.Name, pid) || containsPatternID(h.Scenario, pid) {
|
||||
return h.Name
|
||||
}
|
||||
}
|
||||
if len(hazards) > 0 {
|
||||
return hazards[0].Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// containsPatternID checks whether text contains the exact pattern ID token.
|
||||
func containsPatternID(text, pid string) bool {
|
||||
idx := 0
|
||||
for idx <= len(text)-len(pid) {
|
||||
if text[idx:idx+len(pid)] == pid {
|
||||
// Ensure it is not a substring of a longer ID
|
||||
after := idx + len(pid)
|
||||
if after >= len(text) || text[after] < '0' || text[after] > '9' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
idx++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// collectHazardTags extracts tag-like signals from hazard fields.
|
||||
func collectHazardTags(hazards []Hazard) []string {
|
||||
tagSet := make(map[string]bool)
|
||||
for _, h := range hazards {
|
||||
// Infer tags from hazard category names
|
||||
switch h.Category {
|
||||
case "software", "steuerung", "steuerungsfehler":
|
||||
tagSet["has_software"] = true
|
||||
tagSet["programmable"] = true
|
||||
case "cyber", "cybersicherheit", "netzwerk":
|
||||
tagSet["is_networked"] = true
|
||||
tagSet["has_software"] = true
|
||||
case "ki", "kuenstliche_intelligenz", "ai_ml":
|
||||
tagSet["has_ai"] = true
|
||||
tagSet["has_software"] = true
|
||||
case "sensorik", "sensor":
|
||||
tagSet["sensor_part"] = true
|
||||
}
|
||||
}
|
||||
tags := make([]string, 0, len(tagSet))
|
||||
for t := range tagSet {
|
||||
tags = append(tags, t)
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
// severityRank maps severity strings to sort-order integers.
|
||||
func severityRank(s string) int {
|
||||
switch s {
|
||||
case "high":
|
||||
return 3
|
||||
case "medium":
|
||||
return 2
|
||||
case "low":
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// buildSummaryFlags derives boolean flags from the collected trigger results.
|
||||
func buildSummaryFlags(results []TriggerResult) map[string]bool {
|
||||
summary := map[string]bool{
|
||||
"dsfa_required": false,
|
||||
"ai_act_relevant": false,
|
||||
"cra_relevant": false,
|
||||
"nis2_relevant": false,
|
||||
"data_act_relevant": false,
|
||||
}
|
||||
for _, r := range results {
|
||||
reg := r.Trigger.Regulation
|
||||
if len(reg) >= 4 && reg[:4] == "DSGV" {
|
||||
summary["dsfa_required"] = true
|
||||
}
|
||||
if len(reg) >= 6 && reg[:6] == "AI Act" {
|
||||
summary["ai_act_relevant"] = true
|
||||
}
|
||||
if len(reg) >= 3 && reg[:3] == "CRA" {
|
||||
summary["cra_relevant"] = true
|
||||
}
|
||||
if len(reg) >= 4 && reg[:4] == "NIS2" {
|
||||
summary["nis2_relevant"] = true
|
||||
}
|
||||
if len(reg) >= 11 && reg[:11] == "EU Data Act" {
|
||||
summary["data_act_relevant"] = true
|
||||
}
|
||||
}
|
||||
return summary
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
package iace
|
||||
|
||||
// ComplianceTrigger maps a CE hazard pattern to a regulatory requirement.
|
||||
// When a pattern fires for a project, the corresponding triggers tell
|
||||
// the user which DSGVO/AI Act/CRA/NIS2/Data Act obligations apply and
|
||||
// which SDK module they should visit.
|
||||
type ComplianceTrigger struct {
|
||||
Regulation string `json:"regulation"` // e.g. "DSGVO Art. 35"
|
||||
TriggerCondDE string `json:"trigger_cond_de"` // Why this triggers (German)
|
||||
Severity string `json:"severity"` // "high", "medium", "low"
|
||||
Module string `json:"module"` // SDK module key
|
||||
ModuleLink string `json:"module_link"` // Frontend route
|
||||
ActionDE string `json:"action_de"` // Recommended action (German)
|
||||
RAGQuery string `json:"rag_query"` // Search query for RAG enrichment
|
||||
}
|
||||
|
||||
// TriggerResult pairs a fired pattern with one of its compliance triggers.
|
||||
type TriggerResult struct {
|
||||
HazardID string `json:"hazard_id"`
|
||||
HazardName string `json:"hazard_name"`
|
||||
PatternID string `json:"pattern_id"`
|
||||
Trigger ComplianceTrigger `json:"trigger"`
|
||||
}
|
||||
|
||||
// ComplianceTriggerSummary is the top-level response for the crossover engine.
|
||||
type ComplianceTriggerSummary struct {
|
||||
Triggers []TriggerResult `json:"triggers"`
|
||||
Total int `json:"total"`
|
||||
Summary map[string]bool `json:"summary"` // dsfa_required, ai_act_relevant, etc.
|
||||
}
|
||||
|
||||
// GetComplianceTriggerMap returns pattern-ID-keyed compliance triggers.
|
||||
// Each entry lists the regulatory obligations that a fired pattern implies.
|
||||
func GetComplianceTriggerMap() map[string][]ComplianceTrigger {
|
||||
m := make(map[string][]ComplianceTrigger)
|
||||
|
||||
// --- Cobot / camera / biometric patterns ---
|
||||
m["HP059"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "DSGVO Art. 35",
|
||||
TriggerCondDE: "Kamera-Personenerkennung verarbeitet biometrische Daten",
|
||||
Severity: "high",
|
||||
Module: "dsfa",
|
||||
ModuleLink: "/sdk/dsfa",
|
||||
ActionDE: "Datenschutz-Folgenabschaetzung fuer Kamera-System durchfuehren",
|
||||
RAGQuery: "DSFA biometrische Daten Kameraerkennung",
|
||||
},
|
||||
{
|
||||
Regulation: "AI Act Art. 6",
|
||||
TriggerCondDE: "Autonome Sicherheitsentscheidung durch KI-System",
|
||||
Severity: "high",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Hochrisiko-KI-Einstufung pruefen und dokumentieren",
|
||||
RAGQuery: "AI Act Hochrisiko autonome Sicherheitsentscheidung",
|
||||
},
|
||||
}
|
||||
m["HP060"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "DSGVO Art. 35",
|
||||
TriggerCondDE: "Werkzeug-Tracking erfordert Personenerkennung",
|
||||
Severity: "high",
|
||||
Module: "dsfa",
|
||||
ModuleLink: "/sdk/dsfa",
|
||||
ActionDE: "DSFA fuer Werkzeug-Tracking mit Personenerkennung erstellen",
|
||||
RAGQuery: "DSFA Personenerkennung Werkzeug-Tracking",
|
||||
},
|
||||
}
|
||||
|
||||
// --- AI/ML safety-critical patterns ---
|
||||
m["HP040"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "AI Act Art. 6",
|
||||
TriggerCondDE: "KI trifft sicherheitsrelevante Entscheidung",
|
||||
Severity: "high",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Hochrisiko-Klassifizierung und Konformitaetsbewertung einleiten",
|
||||
RAGQuery: "AI Act Art 6 Hochrisiko Sicherheitsentscheidung",
|
||||
},
|
||||
{
|
||||
Regulation: "AI Act Art. 9",
|
||||
TriggerCondDE: "Risikomanagement fuer Hochrisiko-KI erforderlich",
|
||||
Severity: "high",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Risikomanagementsystem nach Art. 9 AI Act aufsetzen",
|
||||
RAGQuery: "AI Act Art 9 Risikomanagementsystem Hochrisiko",
|
||||
},
|
||||
}
|
||||
m["HP041"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "AI Act Art. 14",
|
||||
TriggerCondDE: "Menschliche Aufsicht ueber KI-System erforderlich",
|
||||
Severity: "high",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Human-Oversight-Mechanismus implementieren und dokumentieren",
|
||||
RAGQuery: "AI Act Art 14 menschliche Aufsicht Human Oversight",
|
||||
},
|
||||
}
|
||||
m["HP042"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "AI Act Art. 6",
|
||||
TriggerCondDE: "Bias in sicherheitsrelevanter KI moeglich",
|
||||
Severity: "high",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Bias-Analyse und Datenqualitaetspruefung durchfuehren",
|
||||
RAGQuery: "AI Act Bias Diskriminierung Sicherheits-KI",
|
||||
},
|
||||
}
|
||||
m["HP043"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "AI Act Art. 11",
|
||||
TriggerCondDE: "Technische Dokumentation fuer KI-System erforderlich",
|
||||
Severity: "medium",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Technische Dokumentation nach Anhang IV AI Act erstellen",
|
||||
RAGQuery: "AI Act Art 11 technische Dokumentation Anhang IV",
|
||||
},
|
||||
}
|
||||
m["HP044"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "AI Act Art. 13",
|
||||
TriggerCondDE: "Transparenz-Anforderungen fuer KI-System",
|
||||
Severity: "medium",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Transparenzhinweise und Nutzerdokumentation bereitstellen",
|
||||
RAGQuery: "AI Act Art 13 Transparenz KI Nutzerinformation",
|
||||
},
|
||||
}
|
||||
|
||||
// --- Cyber Resilience Act (software/firmware) ---
|
||||
m["HP033"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "CRA Art. 10",
|
||||
TriggerCondDE: "Schwachstellenmanagement fuer Software-Komponente",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "Vulnerability-Management-Prozess nach CRA einrichten",
|
||||
RAGQuery: "CRA Art 10 Schwachstellenmanagement Software",
|
||||
},
|
||||
{
|
||||
Regulation: "CRA Art. 13",
|
||||
TriggerCondDE: "Sicherheitsupdates muessen bereitgestellt werden",
|
||||
Severity: "medium",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "Update-Strategie und Patch-Management dokumentieren",
|
||||
RAGQuery: "CRA Art 13 Sicherheitsupdates Patch-Management",
|
||||
},
|
||||
}
|
||||
m["HP158"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "CRA Art. 10",
|
||||
TriggerCondDE: "Schwachstelle in Firmware erfordert Vulnerability-Handling",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "Schwachstellenmeldung und Patch-Prozess nach CRA etablieren",
|
||||
RAGQuery: "CRA Art 10 Firmware Schwachstelle Meldepflicht",
|
||||
},
|
||||
{
|
||||
Regulation: "CRA Art. 11",
|
||||
TriggerCondDE: "Meldepflicht bei bekannter Schwachstelle",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "Meldeprozess an ENISA/BSI fuer Schwachstellen einrichten",
|
||||
RAGQuery: "CRA Art 11 Meldepflicht ENISA Schwachstelle",
|
||||
},
|
||||
}
|
||||
m["HP159"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "CRA Art. 10",
|
||||
TriggerCondDE: "Datenintegritaet der Software muss sichergestellt sein",
|
||||
Severity: "medium",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "Integritaetsschutz fuer Software-Artefakte implementieren",
|
||||
RAGQuery: "CRA Art 10 Datenintegritaet Software Signierung",
|
||||
},
|
||||
}
|
||||
m["HP160"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "NIS2 Art. 21",
|
||||
TriggerCondDE: "Cybersicherheits-Risikomanagement erforderlich",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "Cybersicherheits-Risikomanagement nach NIS2 Art. 21 aufsetzen",
|
||||
RAGQuery: "NIS2 Art 21 Cybersicherheit Risikomanagement",
|
||||
},
|
||||
{
|
||||
Regulation: "CRA Anhang I",
|
||||
TriggerCondDE: "Wesentliche Cybersicherheits-Anforderungen nach CRA",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "CRA Anhang I Checkliste fuer Produkt-Cybersicherheit abarbeiten",
|
||||
RAGQuery: "CRA Anhang I wesentliche Anforderungen Cybersicherheit",
|
||||
},
|
||||
}
|
||||
|
||||
// --- Logging/monitoring patterns ---
|
||||
m["HP131"] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "DSGVO Art. 6",
|
||||
TriggerCondDE: "Rechtsgrundlage fuer Protokollierung personenbez. Daten",
|
||||
Severity: "medium",
|
||||
Module: "dsfa",
|
||||
ModuleLink: "/sdk/dsfa",
|
||||
ActionDE: "Rechtsgrundlage fuer Protokollierung pruefen und dokumentieren",
|
||||
RAGQuery: "DSGVO Art 6 Rechtsgrundlage Protokollierung Logging",
|
||||
},
|
||||
}
|
||||
|
||||
// --- AGV / movement profile patterns (HP199-HP213) ---
|
||||
agvIDs := genPatternRange("HP", 199, 213)
|
||||
for _, pid := range agvIDs {
|
||||
m[pid] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "DSGVO Art. 35",
|
||||
TriggerCondDE: "AGV-Bewegungsprofile koennen Rueckschluesse auf Personen erlauben",
|
||||
Severity: "high",
|
||||
Module: "dsfa",
|
||||
ModuleLink: "/sdk/dsfa",
|
||||
ActionDE: "DSFA fuer AGV-Bewegungsdaten erstellen",
|
||||
RAGQuery: "DSFA Bewegungsprofile AGV Personenbezug",
|
||||
},
|
||||
{
|
||||
Regulation: "EU Data Act Art. 3",
|
||||
TriggerCondDE: "Maschinendaten-Zugangsrecht fuer Nutzer nach Data Act",
|
||||
Severity: "medium",
|
||||
Module: "vendor-compliance",
|
||||
ModuleLink: "/sdk/vendor-compliance",
|
||||
ActionDE: "Datenzugangsrechte nach EU Data Act fuer Maschinendaten pruefen",
|
||||
RAGQuery: "EU Data Act Art 3 Maschinendaten Zugangsrecht",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cyber-security patterns HP800-HP814 ---
|
||||
cyberIDs1 := genPatternRange("HP", 800, 814)
|
||||
for _, pid := range cyberIDs1 {
|
||||
m[pid] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "NIS2 Art. 21",
|
||||
TriggerCondDE: "Cybersicherheits-Risikomanagement fuer vernetzte Komponente",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "NIS2 Cybersicherheits-Massnahmen pruefen und dokumentieren",
|
||||
RAGQuery: "NIS2 Art 21 Cybersicherheit vernetzte Maschine",
|
||||
},
|
||||
{
|
||||
Regulation: "CRA Art. 10",
|
||||
TriggerCondDE: "Schwachstellenmanagement fuer vernetzte Komponente",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "CRA-konforme Schwachstellenbehandlung einrichten",
|
||||
RAGQuery: "CRA Art 10 Schwachstellenmanagement vernetzte Maschine",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cyber-security patterns HP815-HP829 ---
|
||||
cyberIDs2 := genPatternRange("HP", 815, 829)
|
||||
for _, pid := range cyberIDs2 {
|
||||
m[pid] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "NIS2 Art. 21",
|
||||
TriggerCondDE: "Netzwerk-Sicherheitsmassnahmen nach NIS2",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "NIS2-Sicherheitskonzept fuer Netzwerkkomponenten erstellen",
|
||||
RAGQuery: "NIS2 Art 21 Netzwerk Sicherheit Massnahmen",
|
||||
},
|
||||
{
|
||||
Regulation: "CRA Art. 10",
|
||||
TriggerCondDE: "CRA-Anforderungen fuer Software mit Netzwerkzugang",
|
||||
Severity: "medium",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "CRA-Konformitaet fuer Netzwerk-Software sicherstellen",
|
||||
RAGQuery: "CRA Software Netzwerkzugang Sicherheitsanforderungen",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// --- AI/ML-specific cyber patterns HP830-HP844 ---
|
||||
aiCyberIDs := genPatternRange("HP", 830, 844)
|
||||
for _, pid := range aiCyberIDs {
|
||||
m[pid] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "AI Act Art. 6",
|
||||
TriggerCondDE: "KI/ML-System in sicherheitsrelevantem Kontext",
|
||||
Severity: "high",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "Hochrisiko-Einstufung und AI-Act-Konformitaet pruefen",
|
||||
RAGQuery: "AI Act Hochrisiko KI ML sicherheitsrelevant",
|
||||
},
|
||||
{
|
||||
Regulation: "DSGVO Art. 22",
|
||||
TriggerCondDE: "Automatisierte Entscheidungsfindung durch KI moeglich",
|
||||
Severity: "high",
|
||||
Module: "dsfa",
|
||||
ModuleLink: "/sdk/dsfa",
|
||||
ActionDE: "Automatisierte Einzelentscheidung nach Art. 22 DSGVO pruefen",
|
||||
RAGQuery: "DSGVO Art 22 automatisierte Entscheidung KI Profiling",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// --- NIS2 network/HMI patterns HP845-HP864 ---
|
||||
nis2IDs := genPatternRange("HP", 845, 864)
|
||||
for _, pid := range nis2IDs {
|
||||
m[pid] = []ComplianceTrigger{
|
||||
{
|
||||
Regulation: "NIS2 Art. 21",
|
||||
TriggerCondDE: "Netzwerk-/HMI-Komponente erfordert NIS2-Massnahmen",
|
||||
Severity: "high",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "NIS2-Sicherheitsanforderungen fuer HMI/Netzwerk umsetzen",
|
||||
RAGQuery: "NIS2 Art 21 HMI Netzwerk Sicherheit",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// GetTagBasedTriggers returns compliance triggers that fire based on
|
||||
// component tag combinations rather than specific pattern IDs.
|
||||
func GetTagBasedTriggers(tags []string) []ComplianceTrigger {
|
||||
tagSet := make(map[string]bool, len(tags))
|
||||
for _, t := range tags {
|
||||
tagSet[t] = true
|
||||
}
|
||||
|
||||
var triggers []ComplianceTrigger
|
||||
|
||||
// has_software + programmable → CRA Art. 10
|
||||
if tagSet["has_software"] && tagSet["programmable"] {
|
||||
triggers = append(triggers, ComplianceTrigger{
|
||||
Regulation: "CRA Art. 10",
|
||||
TriggerCondDE: "Programmierbare Software-Komponente erfordert CRA-Konformitaet",
|
||||
Severity: "medium",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "CRA-Anforderungen fuer programmierbare Software pruefen",
|
||||
RAGQuery: "CRA Art 10 programmierbare Software Sicherheit",
|
||||
})
|
||||
}
|
||||
|
||||
// sensor_part + has_software → EU Data Act Art. 3
|
||||
if tagSet["sensor_part"] && tagSet["has_software"] {
|
||||
triggers = append(triggers, ComplianceTrigger{
|
||||
Regulation: "EU Data Act Art. 3",
|
||||
TriggerCondDE: "Sensor mit Software erzeugt Maschinendaten — Zugangsrecht nach Data Act",
|
||||
Severity: "medium",
|
||||
Module: "vendor-compliance",
|
||||
ModuleLink: "/sdk/vendor-compliance",
|
||||
ActionDE: "Datenzugangsrechte fuer Sensor-/Maschinendaten nach Data Act pruefen",
|
||||
RAGQuery: "EU Data Act Art 3 Sensordaten Maschinendaten Zugang",
|
||||
})
|
||||
}
|
||||
|
||||
// has_ai → AI Act Art. 6 (generic)
|
||||
if tagSet["has_ai"] {
|
||||
triggers = append(triggers, ComplianceTrigger{
|
||||
Regulation: "AI Act Art. 6",
|
||||
TriggerCondDE: "KI-Komponente erkannt — Hochrisiko-Einstufung pruefen",
|
||||
Severity: "high",
|
||||
Module: "ai-act",
|
||||
ModuleLink: "/sdk/ai-act",
|
||||
ActionDE: "AI-Act-Klassifizierung fuer KI-Komponente durchfuehren",
|
||||
RAGQuery: "AI Act Art 6 Klassifizierung KI-System Hochrisiko",
|
||||
})
|
||||
}
|
||||
|
||||
// is_networked → NIS2 Art. 21
|
||||
if tagSet["is_networked"] {
|
||||
triggers = append(triggers, ComplianceTrigger{
|
||||
Regulation: "NIS2 Art. 21",
|
||||
TriggerCondDE: "Vernetzte Komponente unterliegt NIS2-Sicherheitspflichten",
|
||||
Severity: "medium",
|
||||
Module: "cyber",
|
||||
ModuleLink: "/sdk/security-backlog",
|
||||
ActionDE: "NIS2-Anforderungen fuer vernetzte Infrastruktur bewerten",
|
||||
RAGQuery: "NIS2 Art 21 vernetzte Infrastruktur Pflichten",
|
||||
})
|
||||
}
|
||||
|
||||
return triggers
|
||||
}
|
||||
|
||||
// genPatternRange generates pattern IDs like "HP800", "HP801", ..., "HP814".
|
||||
func genPatternRange(prefix string, from, to int) []string {
|
||||
ids := make([]string, 0, to-from+1)
|
||||
for i := from; i <= to; i++ {
|
||||
ids = append(ids, prefix+padInt(i))
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// padInt formats an integer with leading zeros to 3 digits minimum.
|
||||
func padInt(n int) string {
|
||||
if n < 10 {
|
||||
return "00" + triggerItoa(n)
|
||||
}
|
||||
if n < 100 {
|
||||
return "0" + triggerItoa(n)
|
||||
}
|
||||
return triggerItoa(n)
|
||||
}
|
||||
|
||||
// triggerItoa converts a non-negative integer to a string without importing strconv.
|
||||
func triggerItoa(n int) string {
|
||||
if n == 0 {
|
||||
return "0"
|
||||
}
|
||||
var buf [20]byte
|
||||
pos := len(buf)
|
||||
for n > 0 {
|
||||
pos--
|
||||
buf[pos] = byte('0' + n%10)
|
||||
n /= 10
|
||||
}
|
||||
return string(buf[pos:])
|
||||
}
|
||||
Reference in New Issue
Block a user