From a83056b5e7ca5a5185e34848ed5956e9f5494081 Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:35:02 +0200 Subject: [PATCH] refactor(go/iace): split hazard_library and store into focused files under 500 LOC All oversized iace files now comply with the 500-line hard cap: - hazard_library_ai_sw.go split into ai_sw (false_classification..communication) and ai_fw (unauthorized_access..update_failure) - hazard_library_software_hmi.go split into software_hmi (software_fault+hmi) and config_integration (configuration_error+logging+integration) - hazard_library_machine_safety.go split to keep mechanical/electrical/thermal/emc, safety_functions extracted into hazard_library_safety_functions.go - store_hazards.go split: hazard library queries moved to store_hazard_library.go - store_projects.go split: component and classification ops to store_components.go - store_mitigations.go split: evidence/verification/ref-data to store_evidence.go - hazard_library.go GetBuiltinHazardLibrary() updated to call all sub-functions - All iace tests pass (go test ./internal/iace/...) Co-Authored-By: Claude Sonnet 4.6 --- .../internal/iace/hazard_library.go | 3 + .../internal/iace/hazard_library_ai_fw.go | 302 ++++++++++++++++ .../internal/iace/hazard_library_ai_sw.go | 298 +--------------- .../iace/hazard_library_config_integration.go | 317 +++++++++++++++++ .../iace/hazard_library_machine_safety.go | 278 --------------- .../iace/hazard_library_safety_functions.go | 288 ++++++++++++++++ .../iace/hazard_library_software_hmi.go | 311 +---------------- .../internal/iace/store_components.go | 309 +++++++++++++++++ .../internal/iace/store_evidence.go | 321 ++++++++++++++++++ .../internal/iace/store_hazard_library.go | 172 ++++++++++ .../internal/iace/store_hazards.go | 162 --------- .../internal/iace/store_mitigations.go | 310 ----------------- .../internal/iace/store_projects.go | 296 ---------------- 13 files changed, 1717 insertions(+), 1650 deletions(-) create mode 100644 ai-compliance-sdk/internal/iace/hazard_library_ai_fw.go create mode 100644 ai-compliance-sdk/internal/iace/hazard_library_config_integration.go create mode 100644 ai-compliance-sdk/internal/iace/hazard_library_safety_functions.go create mode 100644 ai-compliance-sdk/internal/iace/store_components.go create mode 100644 ai-compliance-sdk/internal/iace/store_evidence.go create mode 100644 ai-compliance-sdk/internal/iace/store_hazard_library.go diff --git a/ai-compliance-sdk/internal/iace/hazard_library.go b/ai-compliance-sdk/internal/iace/hazard_library.go index 65b3ef9..93118f4 100644 --- a/ai-compliance-sdk/internal/iace/hazard_library.go +++ b/ai-compliance-sdk/internal/iace/hazard_library.go @@ -33,8 +33,11 @@ func mustMarshalJSON(v interface{}) json.RawMessage { func GetBuiltinHazardLibrary() []HazardLibraryEntry { var all []HazardLibraryEntry all = append(all, builtinHazardsAISW()...) + all = append(all, builtinHazardsAIFW()...) all = append(all, builtinHazardsSoftwareHMI()...) + all = append(all, builtinHazardsConfigIntegration()...) all = append(all, builtinHazardsMachineSafety()...) + all = append(all, builtinHazardsSafetyFunctions()...) all = append(all, builtinHazardsISO12100Mechanical()...) all = append(all, builtinHazardsISO12100ElectricalThermal()...) all = append(all, builtinHazardsISO12100Pneumatic()...) diff --git a/ai-compliance-sdk/internal/iace/hazard_library_ai_fw.go b/ai-compliance-sdk/internal/iace/hazard_library_ai_fw.go new file mode 100644 index 0000000..dce8019 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/hazard_library_ai_fw.go @@ -0,0 +1,302 @@ +package iace + +import "time" + +// builtinHazardsAIFW returns hazard library entries for AI/firmware categories: +// unauthorized_access, firmware_corruption, safety_boundary_violation, +// mode_confusion, unintended_bias, update_failure. +func builtinHazardsAIFW() []HazardLibraryEntry { + now := time.Now() + + return []HazardLibraryEntry{ + // ==================================================================== + // Category: unauthorized_access (4 entries) + // ==================================================================== + { + ID: hazardUUID("unauthorized_access", 1), + Category: "unauthorized_access", + Name: "Unautorisierter Remote-Zugriff", + Description: "Ein Angreifer erlangt ueber das Netzwerk Zugriff auf die Maschinensteuerung und kann sicherheitsrelevante Parameter aendern.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"network", "software"}, + RegulationReferences: []string{"IEC 62443", "CRA", "EU AI Act Art. 15"}, + SuggestedMitigations: mustMarshalJSON([]string{"VPN", "MFA", "Netzwerksegmentierung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("unauthorized_access", 2), + Category: "unauthorized_access", + Name: "Konfigurations-Manipulation", + Description: "Sicherheitsrelevante Konfigurationsparameter werden unautorisiert geaendert, z.B. Grenzwerte, Schwellwerte oder Betriebsmodi.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"IEC 62443", "CRA", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Zugriffskontrolle", "Audit-Log"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("unauthorized_access", 3), + Category: "unauthorized_access", + Name: "Privilege Escalation", + Description: "Ein Benutzer oder Prozess erlangt hoehere Berechtigungen als vorgesehen und kann sicherheitskritische Aktionen ausfuehren.", + DefaultSeverity: 5, + DefaultProbability: 1, + ApplicableComponentTypes: []string{"software"}, + RegulationReferences: []string{"IEC 62443", "CRA"}, + SuggestedMitigations: mustMarshalJSON([]string{"RBAC", "Least Privilege"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("unauthorized_access", 4), + Category: "unauthorized_access", + Name: "Supply-Chain-Angriff auf Komponente", + Description: "Eine kompromittierte Softwarekomponente oder Firmware wird ueber die Lieferkette eingeschleust und enthaelt Schadcode oder Backdoors.", + DefaultSeverity: 5, + DefaultProbability: 1, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"CRA", "IEC 62443", "EU AI Act Art. 15"}, + SuggestedMitigations: mustMarshalJSON([]string{"SBOM", "Signaturpruefung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: firmware_corruption (3 entries) + // ==================================================================== + { + ID: hazardUUID("firmware_corruption", 1), + Category: "firmware_corruption", + Name: "Update-Abbruch mit inkonsistentem Zustand", + Description: "Ein Firmware-Update wird unterbrochen (z.B. Stromausfall), wodurch das System in einem inkonsistenten und potenziell unsicheren Zustand verbleibt.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"firmware"}, + RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"A/B-Partitioning", "Rollback"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("firmware_corruption", 2), + Category: "firmware_corruption", + Name: "Rollback-Fehler auf alte Version", + Description: "Ein Rollback auf eine aeltere Firmware-Version schlaegt fehl oder fuehrt zu Inkompatibilitaeten mit der aktuellen Hardware-/Softwarekonfiguration.", + DefaultSeverity: 4, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"firmware"}, + RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Versionsmanagement", "Kompatibilitaetspruefung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("firmware_corruption", 3), + Category: "firmware_corruption", + Name: "Boot-Chain-Angriff", + Description: "Die Bootsequenz wird manipuliert, um unsignierte oder kompromittierte Firmware auszufuehren, was die gesamte Sicherheitsarchitektur untergaebt.", + DefaultSeverity: 5, + DefaultProbability: 1, + ApplicableComponentTypes: []string{"firmware"}, + RegulationReferences: []string{"CRA", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Secure Boot", "TPM"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: safety_boundary_violation (4 entries) + // ==================================================================== + { + ID: hazardUUID("safety_boundary_violation", 1), + Category: "safety_boundary_violation", + Name: "Kraft-/Drehmoment-Ueberschreitung", + Description: "Aktorische Systeme ueberschreiten die zulaessigen Kraft- oder Drehmomentwerte, was zu Verletzungen oder Maschinenschaeden fuehren kann.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"controller", "actuator"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62061"}, + SuggestedMitigations: mustMarshalJSON([]string{"Hardware-Limiter", "SIL-Ueberwachung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_boundary_violation", 2), + Category: "safety_boundary_violation", + Name: "Geschwindigkeitsueberschreitung Roboter", + Description: "Ein Industrieroboter ueberschreitet die zulaessige Geschwindigkeit, insbesondere bei Mensch-Roboter-Kollaboration.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"controller", "software"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "ISO 10218"}, + SuggestedMitigations: mustMarshalJSON([]string{"Safe Speed Monitoring", "Lichtgitter"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_boundary_violation", 3), + Category: "safety_boundary_violation", + Name: "Versagen des Safe-State", + Description: "Das System kann im Fehlerfall keinen sicheren Zustand einnehmen, da die Sicherheitssteuerung selbst versagt.", + DefaultSeverity: 5, + DefaultProbability: 1, + ApplicableComponentTypes: []string{"controller", "software", "firmware"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62061"}, + SuggestedMitigations: mustMarshalJSON([]string{"Redundante Sicherheitssteuerung", "Diverse Programmierung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_boundary_violation", 4), + Category: "safety_boundary_violation", + Name: "Arbeitsraum-Verletzung", + Description: "Ein Roboter oder Aktor verlaesst seinen definierten Arbeitsraum und dringt in den Schutzbereich von Personen ein.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"controller", "sensor"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "ISO 10218"}, + SuggestedMitigations: mustMarshalJSON([]string{"Sichere Achsueberwachung", "Schutzzaun-Sensorik"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: mode_confusion (3 entries) + // ==================================================================== + { + ID: hazardUUID("mode_confusion", 1), + Category: "mode_confusion", + Name: "Falsche Betriebsart aktiv", + Description: "Das System befindet sich in einer unbeabsichtigten Betriebsart (z.B. Automatik statt Einrichtbetrieb), was zu unerwarteten Maschinenbewegungen fuehrt.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"hmi", "software"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Betriebsart-Anzeige", "Schluesselschalter"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("mode_confusion", 2), + Category: "mode_confusion", + Name: "Wartung/Normal-Verwechslung", + Description: "Das System wird im Normalbetrieb gewartet oder der Wartungsmodus wird nicht korrekt verlassen, was zu gefaehrlichen Situationen fuehrt.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"hmi", "software"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Zugangskontrolle", "Sicherheitsverriegelung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("mode_confusion", 3), + Category: "mode_confusion", + Name: "Automatik-Eingriff waehrend Handbetrieb", + Description: "Das System wechselt waehrend des Handbetriebs unerwartet in den Automatikbetrieb, wodurch eine Person im Gefahrenbereich verletzt werden kann.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "controller"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Exklusive Betriebsarten", "Zustimmtaster"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: unintended_bias (2 entries) + // ==================================================================== + { + ID: hazardUUID("unintended_bias", 1), + Category: "unintended_bias", + Name: "Diskriminierende KI-Entscheidung", + Description: "Das KI-Modell trifft systematisch diskriminierende Entscheidungen, z.B. bei der Qualitaetsbewertung bestimmter Produktchargen oder Lieferanten.", + DefaultSeverity: 3, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"ai_model"}, + RegulationReferences: []string{"EU AI Act Art. 10", "EU AI Act Art. 71"}, + SuggestedMitigations: mustMarshalJSON([]string{"Bias-Testing", "Fairness-Metriken"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("unintended_bias", 2), + Category: "unintended_bias", + Name: "Verzerrte Trainingsdaten", + Description: "Die Trainingsdaten sind nicht repraesentativ und enthalten systematische Verzerrungen, die zu unfairen oder fehlerhaften Modellergebnissen fuehren.", + DefaultSeverity: 3, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"ai_model"}, + RegulationReferences: []string{"EU AI Act Art. 10", "EU AI Act Art. 71"}, + SuggestedMitigations: mustMarshalJSON([]string{"Datensatz-Audit", "Ausgewogenes Sampling"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: update_failure (3 entries) + // ==================================================================== + { + ID: hazardUUID("update_failure", 1), + Category: "update_failure", + Name: "Unvollstaendiges OTA-Update", + Description: "Ein Over-the-Air-Update wird nur teilweise uebertragen oder angewendet, wodurch das System in einem inkonsistenten Zustand verbleibt.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"firmware", "software"}, + RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Atomare Updates", "Integritaetspruefung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("update_failure", 2), + Category: "update_failure", + Name: "Versionskonflikt nach Update", + Description: "Nach einem Update sind Software- und Firmware-Versionen inkompatibel, was zu Fehlfunktionen oder Ausfaellen fuehrt.", + DefaultSeverity: 3, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Kompatibilitaetsmatrix", "Staging-Tests"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("update_failure", 3), + Category: "update_failure", + Name: "Unkontrollierter Auto-Update", + Description: "Ein automatisches Update wird ohne Genehmigung oder ausserhalb eines Wartungsfensters eingespielt und stoert den laufenden Betrieb.", + DefaultSeverity: 4, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software"}, + RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Update-Genehmigung", "Wartungsfenster"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + } +} diff --git a/ai-compliance-sdk/internal/iace/hazard_library_ai_sw.go b/ai-compliance-sdk/internal/iace/hazard_library_ai_sw.go index 5e1d7f9..5d31620 100644 --- a/ai-compliance-sdk/internal/iace/hazard_library_ai_sw.go +++ b/ai-compliance-sdk/internal/iace/hazard_library_ai_sw.go @@ -2,11 +2,9 @@ package iace import "time" -// builtinHazardsAISW returns the initial hazard library entries covering -// AI/SW/network-related categories: false_classification, timing_error, -// data_poisoning, model_drift, sensor_spoofing, communication_failure, -// unauthorized_access, firmware_corruption, safety_boundary_violation, -// mode_confusion, unintended_bias, update_failure. +// builtinHazardsAISW returns hazard library entries for AI/software categories: +// false_classification, timing_error, data_poisoning, model_drift, +// sensor_spoofing, communication_failure. func builtinHazardsAISW() []HazardLibraryEntry { now := time.Now() @@ -286,295 +284,5 @@ func builtinHazardsAISW() []HazardLibraryEntry { TenantID: nil, CreatedAt: now, }, - - // ==================================================================== - // Category: unauthorized_access (4 entries) - // ==================================================================== - { - ID: hazardUUID("unauthorized_access", 1), - Category: "unauthorized_access", - Name: "Unautorisierter Remote-Zugriff", - Description: "Ein Angreifer erlangt ueber das Netzwerk Zugriff auf die Maschinensteuerung und kann sicherheitsrelevante Parameter aendern.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"network", "software"}, - RegulationReferences: []string{"IEC 62443", "CRA", "EU AI Act Art. 15"}, - SuggestedMitigations: mustMarshalJSON([]string{"VPN", "MFA", "Netzwerksegmentierung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("unauthorized_access", 2), - Category: "unauthorized_access", - Name: "Konfigurations-Manipulation", - Description: "Sicherheitsrelevante Konfigurationsparameter werden unautorisiert geaendert, z.B. Grenzwerte, Schwellwerte oder Betriebsmodi.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"IEC 62443", "CRA", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Zugriffskontrolle", "Audit-Log"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("unauthorized_access", 3), - Category: "unauthorized_access", - Name: "Privilege Escalation", - Description: "Ein Benutzer oder Prozess erlangt hoehere Berechtigungen als vorgesehen und kann sicherheitskritische Aktionen ausfuehren.", - DefaultSeverity: 5, - DefaultProbability: 1, - ApplicableComponentTypes: []string{"software"}, - RegulationReferences: []string{"IEC 62443", "CRA"}, - SuggestedMitigations: mustMarshalJSON([]string{"RBAC", "Least Privilege"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("unauthorized_access", 4), - Category: "unauthorized_access", - Name: "Supply-Chain-Angriff auf Komponente", - Description: "Eine kompromittierte Softwarekomponente oder Firmware wird ueber die Lieferkette eingeschleust und enthaelt Schadcode oder Backdoors.", - DefaultSeverity: 5, - DefaultProbability: 1, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"CRA", "IEC 62443", "EU AI Act Art. 15"}, - SuggestedMitigations: mustMarshalJSON([]string{"SBOM", "Signaturpruefung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: firmware_corruption (3 entries) - // ==================================================================== - { - ID: hazardUUID("firmware_corruption", 1), - Category: "firmware_corruption", - Name: "Update-Abbruch mit inkonsistentem Zustand", - Description: "Ein Firmware-Update wird unterbrochen (z.B. Stromausfall), wodurch das System in einem inkonsistenten und potenziell unsicheren Zustand verbleibt.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"firmware"}, - RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"A/B-Partitioning", "Rollback"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("firmware_corruption", 2), - Category: "firmware_corruption", - Name: "Rollback-Fehler auf alte Version", - Description: "Ein Rollback auf eine aeltere Firmware-Version schlaegt fehl oder fuehrt zu Inkompatibilitaeten mit der aktuellen Hardware-/Softwarekonfiguration.", - DefaultSeverity: 4, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"firmware"}, - RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Versionsmanagement", "Kompatibilitaetspruefung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("firmware_corruption", 3), - Category: "firmware_corruption", - Name: "Boot-Chain-Angriff", - Description: "Die Bootsequenz wird manipuliert, um unsignierte oder kompromittierte Firmware auszufuehren, was die gesamte Sicherheitsarchitektur untergaebt.", - DefaultSeverity: 5, - DefaultProbability: 1, - ApplicableComponentTypes: []string{"firmware"}, - RegulationReferences: []string{"CRA", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Secure Boot", "TPM"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: safety_boundary_violation (4 entries) - // ==================================================================== - { - ID: hazardUUID("safety_boundary_violation", 1), - Category: "safety_boundary_violation", - Name: "Kraft-/Drehmoment-Ueberschreitung", - Description: "Aktorische Systeme ueberschreiten die zulaessigen Kraft- oder Drehmomentwerte, was zu Verletzungen oder Maschinenschaeden fuehren kann.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"controller", "actuator"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62061"}, - SuggestedMitigations: mustMarshalJSON([]string{"Hardware-Limiter", "SIL-Ueberwachung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_boundary_violation", 2), - Category: "safety_boundary_violation", - Name: "Geschwindigkeitsueberschreitung Roboter", - Description: "Ein Industrieroboter ueberschreitet die zulaessige Geschwindigkeit, insbesondere bei Mensch-Roboter-Kollaboration.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"controller", "software"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "ISO 10218"}, - SuggestedMitigations: mustMarshalJSON([]string{"Safe Speed Monitoring", "Lichtgitter"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_boundary_violation", 3), - Category: "safety_boundary_violation", - Name: "Versagen des Safe-State", - Description: "Das System kann im Fehlerfall keinen sicheren Zustand einnehmen, da die Sicherheitssteuerung selbst versagt.", - DefaultSeverity: 5, - DefaultProbability: 1, - ApplicableComponentTypes: []string{"controller", "software", "firmware"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62061"}, - SuggestedMitigations: mustMarshalJSON([]string{"Redundante Sicherheitssteuerung", "Diverse Programmierung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_boundary_violation", 4), - Category: "safety_boundary_violation", - Name: "Arbeitsraum-Verletzung", - Description: "Ein Roboter oder Aktor verlaesst seinen definierten Arbeitsraum und dringt in den Schutzbereich von Personen ein.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"controller", "sensor"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "ISO 10218"}, - SuggestedMitigations: mustMarshalJSON([]string{"Sichere Achsueberwachung", "Schutzzaun-Sensorik"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: mode_confusion (3 entries) - // ==================================================================== - { - ID: hazardUUID("mode_confusion", 1), - Category: "mode_confusion", - Name: "Falsche Betriebsart aktiv", - Description: "Das System befindet sich in einer unbeabsichtigten Betriebsart (z.B. Automatik statt Einrichtbetrieb), was zu unerwarteten Maschinenbewegungen fuehrt.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"hmi", "software"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Betriebsart-Anzeige", "Schluesselschalter"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("mode_confusion", 2), - Category: "mode_confusion", - Name: "Wartung/Normal-Verwechslung", - Description: "Das System wird im Normalbetrieb gewartet oder der Wartungsmodus wird nicht korrekt verlassen, was zu gefaehrlichen Situationen fuehrt.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"hmi", "software"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Zugangskontrolle", "Sicherheitsverriegelung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("mode_confusion", 3), - Category: "mode_confusion", - Name: "Automatik-Eingriff waehrend Handbetrieb", - Description: "Das System wechselt waehrend des Handbetriebs unerwartet in den Automatikbetrieb, wodurch eine Person im Gefahrenbereich verletzt werden kann.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "controller"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Exklusive Betriebsarten", "Zustimmtaster"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: unintended_bias (2 entries) - // ==================================================================== - { - ID: hazardUUID("unintended_bias", 1), - Category: "unintended_bias", - Name: "Diskriminierende KI-Entscheidung", - Description: "Das KI-Modell trifft systematisch diskriminierende Entscheidungen, z.B. bei der Qualitaetsbewertung bestimmter Produktchargen oder Lieferanten.", - DefaultSeverity: 3, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"ai_model"}, - RegulationReferences: []string{"EU AI Act Art. 10", "EU AI Act Art. 71"}, - SuggestedMitigations: mustMarshalJSON([]string{"Bias-Testing", "Fairness-Metriken"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("unintended_bias", 2), - Category: "unintended_bias", - Name: "Verzerrte Trainingsdaten", - Description: "Die Trainingsdaten sind nicht repraesentativ und enthalten systematische Verzerrungen, die zu unfairen oder fehlerhaften Modellergebnissen fuehren.", - DefaultSeverity: 3, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"ai_model"}, - RegulationReferences: []string{"EU AI Act Art. 10", "EU AI Act Art. 71"}, - SuggestedMitigations: mustMarshalJSON([]string{"Datensatz-Audit", "Ausgewogenes Sampling"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: update_failure (3 entries) - // ==================================================================== - { - ID: hazardUUID("update_failure", 1), - Category: "update_failure", - Name: "Unvollstaendiges OTA-Update", - Description: "Ein Over-the-Air-Update wird nur teilweise uebertragen oder angewendet, wodurch das System in einem inkonsistenten Zustand verbleibt.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"firmware", "software"}, - RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Atomare Updates", "Integritaetspruefung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("update_failure", 2), - Category: "update_failure", - Name: "Versionskonflikt nach Update", - Description: "Nach einem Update sind Software- und Firmware-Versionen inkompatibel, was zu Fehlfunktionen oder Ausfaellen fuehrt.", - DefaultSeverity: 3, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Kompatibilitaetsmatrix", "Staging-Tests"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("update_failure", 3), - Category: "update_failure", - Name: "Unkontrollierter Auto-Update", - Description: "Ein automatisches Update wird ohne Genehmigung oder ausserhalb eines Wartungsfensters eingespielt und stoert den laufenden Betrieb.", - DefaultSeverity: 4, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software"}, - RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Update-Genehmigung", "Wartungsfenster"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, } } diff --git a/ai-compliance-sdk/internal/iace/hazard_library_config_integration.go b/ai-compliance-sdk/internal/iace/hazard_library_config_integration.go new file mode 100644 index 0000000..a10708c --- /dev/null +++ b/ai-compliance-sdk/internal/iace/hazard_library_config_integration.go @@ -0,0 +1,317 @@ +package iace + +import "time" + +// builtinHazardsConfigIntegration returns hazard library entries for +// configuration errors, logging/audit failures, and integration errors. +func builtinHazardsConfigIntegration() []HazardLibraryEntry { + now := time.Now() + + return []HazardLibraryEntry{ + // ==================================================================== + // Category: configuration_error (8 entries) + // ==================================================================== + { + ID: hazardUUID("configuration_error", 1), + Category: "configuration_error", + Name: "Falscher Safety-Parameter bei Inbetriebnahme", + Description: "Beim Einrichten werden sicherheitsrelevante Parameter (z.B. Maximalgeschwindigkeit, Abschaltgrenzen) falsch konfiguriert und nicht verifiziert.", + DefaultSeverity: 5, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "firmware", "controller"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62061"}, + SuggestedMitigations: mustMarshalJSON([]string{"Parameterpruefung nach Inbetriebnahme", "4-Augen-Prinzip", "Parameterprotokoll in technischer Akte"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("configuration_error", 2), + Category: "configuration_error", + Name: "Factory Reset loescht Sicherheitskonfiguration", + Description: "Ein Factory Reset setzt alle Parameter auf Werkseinstellungen zurueck, einschliesslich sicherheitsrelevanter Konfigurationen, ohne Warnung.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"firmware", "software"}, + RegulationReferences: []string{"IEC 62304", "CRA", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Separate Safety-Partition", "Bestaetigung vor Reset", "Safety-Config vor Reset sichern"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("configuration_error", 3), + Category: "configuration_error", + Name: "Fehlerhafte Parameter-Migration bei Update", + Description: "Beim Software-Update werden vorhandene Konfigurationsparameter nicht korrekt in das neue Format migriert, was zu falschen Systemeinstellungen fuehrt.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"IEC 62304", "CRA"}, + SuggestedMitigations: mustMarshalJSON([]string{"Migrations-Skript-Tests", "Konfig-Backup vor Update", "Post-Update-Verifikation"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("configuration_error", 4), + Category: "configuration_error", + Name: "Konflikthafte redundante Einstellungen", + Description: "Widersprüchliche Parameter in verschiedenen Konfigurationsdateien oder -ebenen fuehren zu unvorhersehbarem Systemverhalten.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"IEC 62304", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Konfig-Validierung beim Start", "Einzelne Quelle fuer Safety-Params", "Konsistenzpruefung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("configuration_error", 5), + Category: "configuration_error", + Name: "Hard-coded Credentials in Konfiguration", + Description: "Passwörter oder Schluessel sind fest im Code oder in Konfigurationsdateien hinterlegt und koennen nicht geaendert werden.", + DefaultSeverity: 4, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"CRA", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Secrets-Management", "Kein Hard-Coding", "Credential-Scan im CI"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("configuration_error", 6), + Category: "configuration_error", + Name: "Debug-Modus in Produktionsumgebung aktiv", + Description: "Debug-Schnittstellen oder erhoehte Logging-Level sind in der Produktionsumgebung aktiv und ermoeglichen Angreifern Zugang zu sensiblen Systeminfos.", + DefaultSeverity: 4, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"CRA", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Build-Konfiguration pruefe Debug-Flag", "Produktions-Checkliste", "Debug-Port-Deaktivierung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("configuration_error", 7), + Category: "configuration_error", + Name: "Out-of-Bounds-Eingabe ohne Validierung", + Description: "Nutzereingaben oder Schnittstellendaten werden ohne Bereichspruefung in sicherheitsrelevante Parameter uebernommen.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "hmi"}, + RegulationReferences: []string{"IEC 62304", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Eingabevalidierung", "Bereichsgrenzen definieren", "Sanity-Check"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("configuration_error", 8), + Category: "configuration_error", + Name: "Konfigurationsdatei nicht schreibgeschuetzt", + Description: "Sicherheitsrelevante Konfigurationsdateien koennen von unautorisierten Nutzern oder Prozessen veraendert werden.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "firmware"}, + RegulationReferences: []string{"IEC 62443", "CRA"}, + SuggestedMitigations: mustMarshalJSON([]string{"Dateisystem-Berechtigungen", "Code-Signing fuer Konfig", "Integritaetspruefung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: logging_audit_failure (5 entries) + // ==================================================================== + { + ID: hazardUUID("logging_audit_failure", 1), + Category: "logging_audit_failure", + Name: "Safety-Events nicht protokolliert", + Description: "Sicherheitsrelevante Ereignisse (Alarme, Not-Halt-Betaetigungen, Fehlerzustaende) werden nicht in ein Protokoll geschrieben.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "controller"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62443", "CRA"}, + SuggestedMitigations: mustMarshalJSON([]string{"Pflicht-Logging Safety-Events", "Unveraenderliches Audit-Log", "Log-Integritaetspruefung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("logging_audit_failure", 2), + Category: "logging_audit_failure", + Name: "Log-Manipulation moeglich", + Description: "Authentifizierte Benutzer oder Angreifer koennen Protokolleintraege aendern oder loeschen und so Beweise fuer Sicherheitsvorfaelle vernichten.", + DefaultSeverity: 4, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software"}, + RegulationReferences: []string{"CRA", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Write-Once-Speicher", "Kryptografische Signaturen", "Externes Log-Management"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("logging_audit_failure", 3), + Category: "logging_audit_failure", + Name: "Log-Overflow ueberschreibt alte Eintraege", + Description: "Wenn der Log-Speicher voll ist, werden aeltere Eintraege ohne Warnung ueberschrieben, was eine lueckenlose Rueckverfolgung verhindert.", + DefaultSeverity: 3, + DefaultProbability: 4, + ApplicableComponentTypes: []string{"software", "controller"}, + RegulationReferences: []string{"CRA", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Log-Kapazitaetsalarm", "Externes Log-System", "Zirkulaerpuffer mit Warnschwelle"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("logging_audit_failure", 4), + Category: "logging_audit_failure", + Name: "Fehlende Zeitstempel in Protokolleintraegen", + Description: "Log-Eintraege enthalten keine oder ungenaue Zeitstempel, was die zeitliche Rekonstruktion von Ereignissen bei der Fehlersuche verhindert.", + DefaultSeverity: 3, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "controller"}, + RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"NTP-Synchronisation", "RTC im Geraet", "ISO-8601-Zeitstempel"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("logging_audit_failure", 5), + Category: "logging_audit_failure", + Name: "Audit-Trail loeschbar durch Bediener", + Description: "Der Audit-Trail kann von einem normalen Bediener geloescht werden, was die Nachvollziehbarkeit von Sicherheitsereignissen untergaebt.", + DefaultSeverity: 4, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software"}, + RegulationReferences: []string{"CRA", "IEC 62443", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"RBAC: Nur Admin darf loeschen", "Log-Export vor Loeschung", "Unanderbare Log-Speicherung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: integration_error (8 entries) + // ==================================================================== + { + ID: hazardUUID("integration_error", 1), + Category: "integration_error", + Name: "Datentyp-Mismatch an Schnittstelle", + Description: "Zwei Systeme tauschen Daten ueber eine Schnittstelle aus, die inkompatible Datentypen verwendet, was zu Interpretationsfehlern fuehrt.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "network"}, + RegulationReferences: []string{"IEC 62304", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Schnittstellendefinition (IDL/Protobuf)", "Integrationstests", "Datentypvalidierung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("integration_error", 2), + Category: "integration_error", + Name: "Endianness-Fehler bei Datenuebertragung", + Description: "Big-Endian- und Little-Endian-Systeme kommunizieren ohne Byte-Order-Konvertierung, was zu falsch interpretierten numerischen Werten fuehrt.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "network"}, + RegulationReferences: []string{"IEC 62304", "IEC 62443"}, + SuggestedMitigations: mustMarshalJSON([]string{"Explizite Byte-Order-Definiton", "Integrationstests", "Schnittstellenspezifikation"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("integration_error", 3), + Category: "integration_error", + Name: "Protokoll-Versions-Konflikt", + Description: "Sender und Empfaenger verwenden unterschiedliche Protokollversionen, die nicht rueckwaertskompatibel sind, was zu Paketablehnung oder Fehlinterpretation fuehrt.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "network", "firmware"}, + RegulationReferences: []string{"IEC 62443", "CRA"}, + SuggestedMitigations: mustMarshalJSON([]string{"Versions-Aushandlung beim Verbindungsaufbau", "Backward-Compatibilitaet", "Kompatibilitaets-Matrix"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("integration_error", 4), + Category: "integration_error", + Name: "Timeout nicht behandelt bei Kommunikation", + Description: "Eine Kommunikationsverbindung bricht ab oder antwortet nicht, der Sender erkennt dies nicht und wartet unendlich lang.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "network"}, + RegulationReferences: []string{"IEC 62443", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Timeout-Konfiguration", "Watchdog-Timer", "Fail-Safe bei Verbindungsverlust"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("integration_error", 5), + Category: "integration_error", + Name: "Buffer Overflow an Schnittstelle", + Description: "Eine Schnittstelle akzeptiert Eingaben, die groesser als der zugewiesene Puffer sind, was zu Speicher-Ueberschreibung und Kontrollfluss-Manipulation fuehrt.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "firmware", "network"}, + RegulationReferences: []string{"CRA", "IEC 62443", "IEC 62304"}, + SuggestedMitigations: mustMarshalJSON([]string{"Laengenvalidierung", "Sichere Puffer-Funktionen", "Statische Analyse (z.B. MISRA)"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("integration_error", 6), + Category: "integration_error", + Name: "Fehlender Heartbeat bei Safety-Verbindung", + Description: "Eine Safety-Kommunikationsverbindung sendet keinen periodischen Heartbeat, so dass ein stiller Ausfall (z.B. unterbrochenes Kabel) nicht erkannt wird.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"network", "software"}, + RegulationReferences: []string{"IEC 61784-3", "ISO 13849", "IEC 62061"}, + SuggestedMitigations: mustMarshalJSON([]string{"Heartbeat-Protokoll", "Verbindungsueberwachung", "Safe-State bei Heartbeat-Ausfall"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("integration_error", 7), + Category: "integration_error", + Name: "Falscher Skalierungsfaktor bei Sensordaten", + Description: "Sensordaten werden mit einem falschen Faktor skaliert, was zu signifikant fehlerhaften Messwerten und moeglichen Fehlentscheidungen fuehrt.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"sensor", "software"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62304"}, + SuggestedMitigations: mustMarshalJSON([]string{"Kalibrierungspruefung", "Plausibilitaetstest", "Schnittstellendokumentation"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("integration_error", 8), + Category: "integration_error", + Name: "Einheitenfehler (mm vs. inch)", + Description: "Unterschiedliche Masseinheiten zwischen Systemen fuehren zu fehlerhaften Bewegungsbefehlen oder Werkzeugpositionierungen.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"software", "hmi"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62304"}, + SuggestedMitigations: mustMarshalJSON([]string{"Explizite Einheitendefinition", "Einheitenkonvertierung in der Schnittstelle", "Integrationstests"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + } +} diff --git a/ai-compliance-sdk/internal/iace/hazard_library_machine_safety.go b/ai-compliance-sdk/internal/iace/hazard_library_machine_safety.go index 67fe11b..d567006 100644 --- a/ai-compliance-sdk/internal/iace/hazard_library_machine_safety.go +++ b/ai-compliance-sdk/internal/iace/hazard_library_machine_safety.go @@ -315,283 +315,5 @@ func builtinHazardsMachineSafety() []HazardLibraryEntry { TenantID: nil, CreatedAt: now, }, - - // ==================================================================== - // Category: safety_function_failure (8 entries) - // ==================================================================== - { - ID: hazardUUID("safety_function_failure", 1), - Category: "safety_function_failure", - Name: "Not-Halt trennt Energieversorgung nicht", - Description: "Der Not-Halt-Taster betaetigt die Sicherheitsschalter, die Energiezufuhr wird jedoch nicht vollstaendig unterbrochen, weil das Sicherheitsrelais versagt.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"controller", "actuator"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.2.4", "IEC 60947-5-5", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Regelmaessiger Not-Halt-Test", "Redundantes Sicherheitsrelais", "Selbstueberwachender Sicherheitskreis"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_function_failure", 2), - Category: "safety_function_failure", - Name: "Schutztuer-Monitoring umgangen", - Description: "Das Schutztuer-Positionssignal wird durch einen Fehler oder Manipulation als 'geschlossen' gemeldet, obwohl die Tuer offen ist.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"sensor", "controller"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 14119", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Zwangsöffnender Positionsschalter", "Codierter Sicherheitssensor", "Anti-Tamper-Masssnahmen"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_function_failure", 3), - Category: "safety_function_failure", - Name: "Safe Speed Monitoring fehlt", - Description: "Beim Einrichten im reduzierten Betrieb fehlt eine unabhaengige Geschwindigkeitsueberwachung, so dass der Bediener nicht ausreichend geschuetzt ist.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"controller", "software"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62061", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Sicherheitsumrichter mit SLS", "Unabhaengige Drehzahlmessung", "SIL-2-Geschwindigkeitsueberwachung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_function_failure", 4), - Category: "safety_function_failure", - Name: "STO-Funktion (Safe Torque Off) Fehler", - Description: "Die STO-Sicherheitsfunktion schaltet den Antriebsmoment nicht ab, obwohl die Funktion aktiviert wurde, z.B. durch Fehler im Sicherheits-SPS-Ausgang.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"actuator", "controller"}, - RegulationReferences: []string{"IEC 61800-5-2", "Maschinenverordnung 2023/1230", "IEC 62061"}, - SuggestedMitigations: mustMarshalJSON([]string{"STO-Pruefung bei Inbetriebnahme", "Pruefzyklus im Betrieb", "Zertifizierter Sicherheitsumrichter"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_function_failure", 5), - Category: "safety_function_failure", - Name: "Muting-Missbrauch bei Lichtvorhang", - Description: "Die Muting-Funktion des Lichtvorhangs wird durch Fehler oder Manipulation zu lange oder unkontrolliert aktiviert, was den Schutz aufhebt.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"sensor", "controller"}, - RegulationReferences: []string{"IEC 61496-3", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Zeitbegrenztes Muting", "Muting-Lampe und Alarm", "Protokollierung der Muting-Ereignisse"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_function_failure", 6), - Category: "safety_function_failure", - Name: "Zweihand-Taster durch Gegenstand ueberbrueckt", - Description: "Die Zweihand-Betaetigungseinrichtung wird durch ein eingeklemmtes Objekt permanent aktiviert, was den Bediener aus dem Schutzkonzept loest.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"hmi", "controller"}, - RegulationReferences: []string{"ISO 13851", "Maschinenverordnung 2023/1230", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Anti-Tie-Down-Pruefung", "Typ-III-Zweihand-Taster", "Regelmaessige Funktionskontrolle"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_function_failure", 7), - Category: "safety_function_failure", - Name: "Sicherheitsrelais-Ausfall ohne Erkennung", - Description: "Ein Sicherheitsrelais versagt unentdeckt (z.B. verklebte Kontakte), sodass der Sicherheitskreis nicht mehr auftrennt.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"controller"}, - RegulationReferences: []string{"ISO 13849", "IEC 62061"}, - SuggestedMitigations: mustMarshalJSON([]string{"Selbstueberwachung (zwangsgefuehrt)", "Regelmaessiger Testlauf", "Redundantes Relais"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("safety_function_failure", 8), - Category: "safety_function_failure", - Name: "Logic-Solver-Fehler in Sicherheits-SPS", - Description: "Die Sicherheitssteuerung (Safety-SPS) fuehrt sicherheitsrelevante Logik fehlerhaft aus, z.B. durch Speicherfehler oder Prozessorfehler.", - DefaultSeverity: 5, - DefaultProbability: 1, - ApplicableComponentTypes: []string{"controller", "software"}, - RegulationReferences: []string{"IEC 61511", "IEC 61508", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"SIL-zertifizierte SPS", "Watchdog", "Selbsttest-Routinen (BIST)"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: environmental_hazard (5 entries) - // ==================================================================== - { - ID: hazardUUID("environmental_hazard", 1), - Category: "environmental_hazard", - Name: "Ausfall durch hohe Umgebungstemperatur", - Description: "Hohe Umgebungstemperaturen ueberschreiten die spezifizierten Grenzwerte der Elektronik oder Aktorik und fuehren zu Fehlfunktionen.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"controller", "sensor"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068-2"}, - SuggestedMitigations: mustMarshalJSON([]string{"Betriebstemperatur-Spezifikation einhalten", "Klimaanlagensystem", "Temperatursensor + Abschaltung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("environmental_hazard", 2), - Category: "environmental_hazard", - Name: "Ausfall bei Tieftemperatur", - Description: "Sehr tiefe Temperaturen reduzieren die Viskositaet von Hydraulikfluessigkeiten, beeinflussen Elektronik und fuehren zu mechanischen Ausfaellen.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"actuator", "controller"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068-2"}, - SuggestedMitigations: mustMarshalJSON([]string{"Tieftemperatur-spezifizierte Komponenten", "Heizung im Schaltschrank", "Anlaeufroutine bei Kaeltestart"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("environmental_hazard", 3), - Category: "environmental_hazard", - Name: "Korrosion durch Feuchtigkeit", - Description: "Hohe Luftfeuchtigkeit oder Kondenswasser fuehrt zur Korrosion von Kontakten und Leiterbahnen, was zu Ausfaellen und Isolationsfehlern fuehrt.", - DefaultSeverity: 3, - DefaultProbability: 4, - ApplicableComponentTypes: []string{"controller", "sensor"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60529"}, - SuggestedMitigations: mustMarshalJSON([]string{"IP-Schutz entsprechend der Umgebung", "Belueftung mit Filter", "Regelmaessige Inspektion"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("environmental_hazard", 4), - Category: "environmental_hazard", - Name: "Fehlfunktion durch Vibrationen", - Description: "Mechanische Vibrationen lockern Verbindungen, schuetteln Kontakte auf oder beschaedigen Loetpunkte in Elektronikbaugruppen.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"controller", "sensor"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068-2-6"}, - SuggestedMitigations: mustMarshalJSON([]string{"Vibrationsdaempfung", "Vergossene Elektronik", "Regelmaessige Verbindungskontrolle"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("environmental_hazard", 5), - Category: "environmental_hazard", - Name: "Kontamination durch Staub oder Fluessigkeiten", - Description: "Staub, Metallspaeene oder Kuehlmittel gelangen in das Gehaeuseinnere und fuehren zu Kurzschluessen, Isolationsfehlern oder Kuehlproblemen.", - DefaultSeverity: 3, - DefaultProbability: 4, - ApplicableComponentTypes: []string{"controller", "hmi"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60529"}, - SuggestedMitigations: mustMarshalJSON([]string{"Hohe IP-Schutzklasse", "Dichtungen regelmaessig pruefen", "Ueberdruck im Schaltschrank"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: maintenance_hazard (6 entries) - // ==================================================================== - { - ID: hazardUUID("maintenance_hazard", 1), - Category: "maintenance_hazard", - Name: "Wartung ohne LOTO-Prozedur", - Description: "Wartungsarbeiten werden ohne korrekte Lockout/Tagout-Prozedur durchgefuehrt, sodass die Maschine waehrend der Arbeit anlaufen kann.", - DefaultSeverity: 5, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"controller", "software"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.6.3"}, - SuggestedMitigations: mustMarshalJSON([]string{"LOTO-Funktion in Software", "Schulung", "Prozedur im Betriebshandbuch"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("maintenance_hazard", 2), - Category: "maintenance_hazard", - Name: "Fehlende LOTO-Funktion in Software", - Description: "Die Steuerungssoftware bietet keine Moeglichkeit, die Maschine fuer Wartungsarbeiten sicher zu sperren und zu verriegeln.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "hmi"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.6.3"}, - SuggestedMitigations: mustMarshalJSON([]string{"Software-LOTO implementieren", "Wartungsmodus mit Schluessel", "Energiesperrfunktion"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("maintenance_hazard", 3), - Category: "maintenance_hazard", - Name: "Wartung bei laufender Maschine", - Description: "Wartungsarbeiten werden an betriebener Maschine durchgefuehrt, weil kein erzwungener Wartungsmodus vorhanden ist.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "controller"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Erzwungenes Abschalten fuer Wartungsmodus", "Schluesselschalter", "Schutzmassnahmen im Wartungsmodus"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("maintenance_hazard", 4), - Category: "maintenance_hazard", - Name: "Wartungs-Tool ohne Zugangskontrolle", - Description: "Ein Diagnose- oder Wartungswerkzeug ist ohne Authentifizierung zugaenglich und ermoeglicht die unbeaufsichtigte Aenderung von Sicherheitsparametern.", - DefaultSeverity: 4, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "hmi"}, - RegulationReferences: []string{"IEC 62443", "CRA"}, - SuggestedMitigations: mustMarshalJSON([]string{"Authentifizierung fuer Wartungs-Tools", "Rollenkonzept", "Audit-Log fuer Wartungszugriffe"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("maintenance_hazard", 5), - Category: "maintenance_hazard", - Name: "Unsichere Demontage gefaehrlicher Baugruppen", - Description: "Die Betriebsanleitung beschreibt nicht, wie gefaehrliche Baugruppen (z.B. Hochvolt, gespeicherte Energie) sicher demontiert werden.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"other"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.7.4"}, - SuggestedMitigations: mustMarshalJSON([]string{"Detaillierte Demontageanleitung", "Warnhinweise an Geraet", "Schulung des Wartungspersonals"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("maintenance_hazard", 6), - Category: "maintenance_hazard", - Name: "Wiederanlauf nach Wartung ohne Freigabeprozedur", - Description: "Nach Wartungsarbeiten wird die Maschine ohne formelle Freigabeprozedur wieder in Betrieb genommen, was zu Verletzungen bei noch anwesendem Personal fuehren kann.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "hmi"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.6.3", "ISO 13849"}, - SuggestedMitigations: mustMarshalJSON([]string{"Software-Wiederanlauf-Freigabe", "Gefahrenbereich-Pruefung vor Anlauf", "Akustisches Warnsignal vor Anlauf"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, } } diff --git a/ai-compliance-sdk/internal/iace/hazard_library_safety_functions.go b/ai-compliance-sdk/internal/iace/hazard_library_safety_functions.go new file mode 100644 index 0000000..58b3786 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/hazard_library_safety_functions.go @@ -0,0 +1,288 @@ +package iace + +import "time" + +// builtinHazardsSafetyFunctions returns hazard library entries for +// safety function failures, environmental hazards, and maintenance hazards. +func builtinHazardsSafetyFunctions() []HazardLibraryEntry { + now := time.Now() + return []HazardLibraryEntry{ + // ==================================================================== + // Category: safety_function_failure (8 entries) + // ==================================================================== + { + ID: hazardUUID("safety_function_failure", 1), + Category: "safety_function_failure", + Name: "Not-Halt trennt Energieversorgung nicht", + Description: "Der Not-Halt-Taster betaetigt die Sicherheitsschalter, die Energiezufuhr wird jedoch nicht vollstaendig unterbrochen, weil das Sicherheitsrelais versagt.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"controller", "actuator"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.2.4", "IEC 60947-5-5", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Regelmaessiger Not-Halt-Test", "Redundantes Sicherheitsrelais", "Selbstueberwachender Sicherheitskreis"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_function_failure", 2), + Category: "safety_function_failure", + Name: "Schutztuer-Monitoring umgangen", + Description: "Das Schutztuer-Positionssignal wird durch einen Fehler oder Manipulation als 'geschlossen' gemeldet, obwohl die Tuer offen ist.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"sensor", "controller"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 14119", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Zwangsöffnender Positionsschalter", "Codierter Sicherheitssensor", "Anti-Tamper-Masssnahmen"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_function_failure", 3), + Category: "safety_function_failure", + Name: "Safe Speed Monitoring fehlt", + Description: "Beim Einrichten im reduzierten Betrieb fehlt eine unabhaengige Geschwindigkeitsueberwachung, so dass der Bediener nicht ausreichend geschuetzt ist.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"controller", "software"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62061", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Sicherheitsumrichter mit SLS", "Unabhaengige Drehzahlmessung", "SIL-2-Geschwindigkeitsueberwachung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_function_failure", 4), + Category: "safety_function_failure", + Name: "STO-Funktion (Safe Torque Off) Fehler", + Description: "Die STO-Sicherheitsfunktion schaltet den Antriebsmoment nicht ab, obwohl die Funktion aktiviert wurde, z.B. durch Fehler im Sicherheits-SPS-Ausgang.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"actuator", "controller"}, + RegulationReferences: []string{"IEC 61800-5-2", "Maschinenverordnung 2023/1230", "IEC 62061"}, + SuggestedMitigations: mustMarshalJSON([]string{"STO-Pruefung bei Inbetriebnahme", "Pruefzyklus im Betrieb", "Zertifizierter Sicherheitsumrichter"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_function_failure", 5), + Category: "safety_function_failure", + Name: "Muting-Missbrauch bei Lichtvorhang", + Description: "Die Muting-Funktion des Lichtvorhangs wird durch Fehler oder Manipulation zu lange oder unkontrolliert aktiviert, was den Schutz aufhebt.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"sensor", "controller"}, + RegulationReferences: []string{"IEC 61496-3", "Maschinenverordnung 2023/1230"}, + SuggestedMitigations: mustMarshalJSON([]string{"Zeitbegrenztes Muting", "Muting-Lampe und Alarm", "Protokollierung der Muting-Ereignisse"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_function_failure", 6), + Category: "safety_function_failure", + Name: "Zweihand-Taster durch Gegenstand ueberbrueckt", + Description: "Die Zweihand-Betaetigungseinrichtung wird durch ein eingeklemmtes Objekt permanent aktiviert, was den Bediener aus dem Schutzkonzept loest.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"hmi", "controller"}, + RegulationReferences: []string{"ISO 13851", "Maschinenverordnung 2023/1230", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Anti-Tie-Down-Pruefung", "Typ-III-Zweihand-Taster", "Regelmaessige Funktionskontrolle"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_function_failure", 7), + Category: "safety_function_failure", + Name: "Sicherheitsrelais-Ausfall ohne Erkennung", + Description: "Ein Sicherheitsrelais versagt unentdeckt (z.B. verklebte Kontakte), sodass der Sicherheitskreis nicht mehr auftrennt.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"controller"}, + RegulationReferences: []string{"ISO 13849", "IEC 62061"}, + SuggestedMitigations: mustMarshalJSON([]string{"Selbstueberwachung (zwangsgefuehrt)", "Regelmaessiger Testlauf", "Redundantes Relais"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("safety_function_failure", 8), + Category: "safety_function_failure", + Name: "Logic-Solver-Fehler in Sicherheits-SPS", + Description: "Die Sicherheitssteuerung (Safety-SPS) fuehrt sicherheitsrelevante Logik fehlerhaft aus, z.B. durch Speicherfehler oder Prozessorfehler.", + DefaultSeverity: 5, + DefaultProbability: 1, + ApplicableComponentTypes: []string{"controller", "software"}, + RegulationReferences: []string{"IEC 61511", "IEC 61508", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"SIL-zertifizierte SPS", "Watchdog", "Selbsttest-Routinen (BIST)"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: environmental_hazard (5 entries) + // ==================================================================== + { + ID: hazardUUID("environmental_hazard", 1), + Category: "environmental_hazard", + Name: "Ausfall durch hohe Umgebungstemperatur", + Description: "Hohe Umgebungstemperaturen ueberschreiten die spezifizierten Grenzwerte der Elektronik oder Aktorik und fuehren zu Fehlfunktionen.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"controller", "sensor"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068-2"}, + SuggestedMitigations: mustMarshalJSON([]string{"Betriebstemperatur-Spezifikation einhalten", "Klimaanlagensystem", "Temperatursensor + Abschaltung"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("environmental_hazard", 2), + Category: "environmental_hazard", + Name: "Ausfall bei Tieftemperatur", + Description: "Sehr tiefe Temperaturen reduzieren die Viskositaet von Hydraulikfluessigkeiten, beeinflussen Elektronik und fuehren zu mechanischen Ausfaellen.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"actuator", "controller"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068-2"}, + SuggestedMitigations: mustMarshalJSON([]string{"Tieftemperatur-spezifizierte Komponenten", "Heizung im Schaltschrank", "Anlaeufroutine bei Kaeltestart"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("environmental_hazard", 3), + Category: "environmental_hazard", + Name: "Korrosion durch Feuchtigkeit", + Description: "Hohe Luftfeuchtigkeit oder Kondenswasser fuehrt zur Korrosion von Kontakten und Leiterbahnen, was zu Ausfaellen und Isolationsfehlern fuehrt.", + DefaultSeverity: 3, + DefaultProbability: 4, + ApplicableComponentTypes: []string{"controller", "sensor"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60529"}, + SuggestedMitigations: mustMarshalJSON([]string{"IP-Schutz entsprechend der Umgebung", "Belueftung mit Filter", "Regelmaessige Inspektion"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("environmental_hazard", 4), + Category: "environmental_hazard", + Name: "Fehlfunktion durch Vibrationen", + Description: "Mechanische Vibrationen lockern Verbindungen, schuetteln Kontakte auf oder beschaedigen Loetpunkte in Elektronikbaugruppen.", + DefaultSeverity: 4, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"controller", "sensor"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068-2-6"}, + SuggestedMitigations: mustMarshalJSON([]string{"Vibrationsdaempfung", "Vergossene Elektronik", "Regelmaessige Verbindungskontrolle"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("environmental_hazard", 5), + Category: "environmental_hazard", + Name: "Kontamination durch Staub oder Fluessigkeiten", + Description: "Staub, Metallspaeene oder Kuehlmittel gelangen in das Gehaeuseinnere und fuehren zu Kurzschluessen, Isolationsfehlern oder Kuehlproblemen.", + DefaultSeverity: 3, + DefaultProbability: 4, + ApplicableComponentTypes: []string{"controller", "hmi"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60529"}, + SuggestedMitigations: mustMarshalJSON([]string{"Hohe IP-Schutzklasse", "Dichtungen regelmaessig pruefen", "Ueberdruck im Schaltschrank"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + + // ==================================================================== + // Category: maintenance_hazard (6 entries) + // ==================================================================== + { + ID: hazardUUID("maintenance_hazard", 1), + Category: "maintenance_hazard", + Name: "Wartung ohne LOTO-Prozedur", + Description: "Wartungsarbeiten werden ohne korrekte Lockout/Tagout-Prozedur durchgefuehrt, sodass die Maschine waehrend der Arbeit anlaufen kann.", + DefaultSeverity: 5, + DefaultProbability: 3, + ApplicableComponentTypes: []string{"controller", "software"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.6.3"}, + SuggestedMitigations: mustMarshalJSON([]string{"LOTO-Funktion in Software", "Schulung", "Prozedur im Betriebshandbuch"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("maintenance_hazard", 2), + Category: "maintenance_hazard", + Name: "Fehlende LOTO-Funktion in Software", + Description: "Die Steuerungssoftware bietet keine Moeglichkeit, die Maschine fuer Wartungsarbeiten sicher zu sperren und zu verriegeln.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "hmi"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.6.3"}, + SuggestedMitigations: mustMarshalJSON([]string{"Software-LOTO implementieren", "Wartungsmodus mit Schluessel", "Energiesperrfunktion"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("maintenance_hazard", 3), + Category: "maintenance_hazard", + Name: "Wartung bei laufender Maschine", + Description: "Wartungsarbeiten werden an betriebener Maschine durchgefuehrt, weil kein erzwungener Wartungsmodus vorhanden ist.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "controller"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Erzwungenes Abschalten fuer Wartungsmodus", "Schluesselschalter", "Schutzmassnahmen im Wartungsmodus"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("maintenance_hazard", 4), + Category: "maintenance_hazard", + Name: "Wartungs-Tool ohne Zugangskontrolle", + Description: "Ein Diagnose- oder Wartungswerkzeug ist ohne Authentifizierung zugaenglich und ermoeglicht die unbeaufsichtigte Aenderung von Sicherheitsparametern.", + DefaultSeverity: 4, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "hmi"}, + RegulationReferences: []string{"IEC 62443", "CRA"}, + SuggestedMitigations: mustMarshalJSON([]string{"Authentifizierung fuer Wartungs-Tools", "Rollenkonzept", "Audit-Log fuer Wartungszugriffe"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("maintenance_hazard", 5), + Category: "maintenance_hazard", + Name: "Unsichere Demontage gefaehrlicher Baugruppen", + Description: "Die Betriebsanleitung beschreibt nicht, wie gefaehrliche Baugruppen (z.B. Hochvolt, gespeicherte Energie) sicher demontiert werden.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"other"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.7.4"}, + SuggestedMitigations: mustMarshalJSON([]string{"Detaillierte Demontageanleitung", "Warnhinweise an Geraet", "Schulung des Wartungspersonals"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + { + ID: hazardUUID("maintenance_hazard", 6), + Category: "maintenance_hazard", + Name: "Wiederanlauf nach Wartung ohne Freigabeprozedur", + Description: "Nach Wartungsarbeiten wird die Maschine ohne formelle Freigabeprozedur wieder in Betrieb genommen, was zu Verletzungen bei noch anwesendem Personal fuehren kann.", + DefaultSeverity: 5, + DefaultProbability: 2, + ApplicableComponentTypes: []string{"software", "hmi"}, + RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.6.3", "ISO 13849"}, + SuggestedMitigations: mustMarshalJSON([]string{"Software-Wiederanlauf-Freigabe", "Gefahrenbereich-Pruefung vor Anlauf", "Akustisches Warnsignal vor Anlauf"}), + IsBuiltin: true, + TenantID: nil, + CreatedAt: now, + }, + } +} diff --git a/ai-compliance-sdk/internal/iace/hazard_library_software_hmi.go b/ai-compliance-sdk/internal/iace/hazard_library_software_hmi.go index e6db2b9..b3fd1c9 100644 --- a/ai-compliance-sdk/internal/iace/hazard_library_software_hmi.go +++ b/ai-compliance-sdk/internal/iace/hazard_library_software_hmi.go @@ -2,9 +2,8 @@ package iace import "time" -// builtinHazardsSoftwareHMI returns extended hazard library entries covering -// software faults, HMI errors, configuration errors, logging/audit failures, -// and integration errors. +// builtinHazardsSoftwareHMI returns hazard library entries for +// software faults and HMI errors. func builtinHazardsSoftwareHMI() []HazardLibraryEntry { now := time.Now() @@ -268,311 +267,5 @@ func builtinHazardsSoftwareHMI() []HazardLibraryEntry { TenantID: nil, CreatedAt: now, }, - - // ==================================================================== - // Category: configuration_error (8 entries) - // ==================================================================== - { - ID: hazardUUID("configuration_error", 1), - Category: "configuration_error", - Name: "Falscher Safety-Parameter bei Inbetriebnahme", - Description: "Beim Einrichten werden sicherheitsrelevante Parameter (z.B. Maximalgeschwindigkeit, Abschaltgrenzen) falsch konfiguriert und nicht verifiziert.", - DefaultSeverity: 5, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "firmware", "controller"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62061"}, - SuggestedMitigations: mustMarshalJSON([]string{"Parameterpruefung nach Inbetriebnahme", "4-Augen-Prinzip", "Parameterprotokoll in technischer Akte"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("configuration_error", 2), - Category: "configuration_error", - Name: "Factory Reset loescht Sicherheitskonfiguration", - Description: "Ein Factory Reset setzt alle Parameter auf Werkseinstellungen zurueck, einschliesslich sicherheitsrelevanter Konfigurationen, ohne Warnung.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"firmware", "software"}, - RegulationReferences: []string{"IEC 62304", "CRA", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Separate Safety-Partition", "Bestaetigung vor Reset", "Safety-Config vor Reset sichern"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("configuration_error", 3), - Category: "configuration_error", - Name: "Fehlerhafte Parameter-Migration bei Update", - Description: "Beim Software-Update werden vorhandene Konfigurationsparameter nicht korrekt in das neue Format migriert, was zu falschen Systemeinstellungen fuehrt.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"IEC 62304", "CRA"}, - SuggestedMitigations: mustMarshalJSON([]string{"Migrations-Skript-Tests", "Konfig-Backup vor Update", "Post-Update-Verifikation"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("configuration_error", 4), - Category: "configuration_error", - Name: "Konflikthafte redundante Einstellungen", - Description: "Widersprüchliche Parameter in verschiedenen Konfigurationsdateien oder -ebenen fuehren zu unvorhersehbarem Systemverhalten.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"IEC 62304", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Konfig-Validierung beim Start", "Einzelne Quelle fuer Safety-Params", "Konsistenzpruefung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("configuration_error", 5), - Category: "configuration_error", - Name: "Hard-coded Credentials in Konfiguration", - Description: "Passwörter oder Schluessel sind fest im Code oder in Konfigurationsdateien hinterlegt und koennen nicht geaendert werden.", - DefaultSeverity: 4, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"CRA", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Secrets-Management", "Kein Hard-Coding", "Credential-Scan im CI"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("configuration_error", 6), - Category: "configuration_error", - Name: "Debug-Modus in Produktionsumgebung aktiv", - Description: "Debug-Schnittstellen oder erhoehte Logging-Level sind in der Produktionsumgebung aktiv und ermoeglichen Angreifern Zugang zu sensiblen Systeminfos.", - DefaultSeverity: 4, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"CRA", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Build-Konfiguration pruefe Debug-Flag", "Produktions-Checkliste", "Debug-Port-Deaktivierung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("configuration_error", 7), - Category: "configuration_error", - Name: "Out-of-Bounds-Eingabe ohne Validierung", - Description: "Nutzereingaben oder Schnittstellendaten werden ohne Bereichspruefung in sicherheitsrelevante Parameter uebernommen.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "hmi"}, - RegulationReferences: []string{"IEC 62304", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Eingabevalidierung", "Bereichsgrenzen definieren", "Sanity-Check"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("configuration_error", 8), - Category: "configuration_error", - Name: "Konfigurationsdatei nicht schreibgeschuetzt", - Description: "Sicherheitsrelevante Konfigurationsdateien koennen von unautorisierten Nutzern oder Prozessen veraendert werden.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "firmware"}, - RegulationReferences: []string{"IEC 62443", "CRA"}, - SuggestedMitigations: mustMarshalJSON([]string{"Dateisystem-Berechtigungen", "Code-Signing fuer Konfig", "Integritaetspruefung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: logging_audit_failure (5 entries) - // ==================================================================== - { - ID: hazardUUID("logging_audit_failure", 1), - Category: "logging_audit_failure", - Name: "Safety-Events nicht protokolliert", - Description: "Sicherheitsrelevante Ereignisse (Alarme, Not-Halt-Betaetigungen, Fehlerzustaende) werden nicht in ein Protokoll geschrieben.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "controller"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62443", "CRA"}, - SuggestedMitigations: mustMarshalJSON([]string{"Pflicht-Logging Safety-Events", "Unveraenderliches Audit-Log", "Log-Integritaetspruefung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("logging_audit_failure", 2), - Category: "logging_audit_failure", - Name: "Log-Manipulation moeglich", - Description: "Authentifizierte Benutzer oder Angreifer koennen Protokolleintraege aendern oder loeschen und so Beweise fuer Sicherheitsvorfaelle vernichten.", - DefaultSeverity: 4, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software"}, - RegulationReferences: []string{"CRA", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Write-Once-Speicher", "Kryptografische Signaturen", "Externes Log-Management"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("logging_audit_failure", 3), - Category: "logging_audit_failure", - Name: "Log-Overflow ueberschreibt alte Eintraege", - Description: "Wenn der Log-Speicher voll ist, werden aeltere Eintraege ohne Warnung ueberschrieben, was eine lueckenlose Rueckverfolgung verhindert.", - DefaultSeverity: 3, - DefaultProbability: 4, - ApplicableComponentTypes: []string{"software", "controller"}, - RegulationReferences: []string{"CRA", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Log-Kapazitaetsalarm", "Externes Log-System", "Zirkulaerpuffer mit Warnschwelle"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("logging_audit_failure", 4), - Category: "logging_audit_failure", - Name: "Fehlende Zeitstempel in Protokolleintraegen", - Description: "Log-Eintraege enthalten keine oder ungenaue Zeitstempel, was die zeitliche Rekonstruktion von Ereignissen bei der Fehlersuche verhindert.", - DefaultSeverity: 3, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "controller"}, - RegulationReferences: []string{"CRA", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"NTP-Synchronisation", "RTC im Geraet", "ISO-8601-Zeitstempel"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("logging_audit_failure", 5), - Category: "logging_audit_failure", - Name: "Audit-Trail loeschbar durch Bediener", - Description: "Der Audit-Trail kann von einem normalen Bediener geloescht werden, was die Nachvollziehbarkeit von Sicherheitsereignissen untergaebt.", - DefaultSeverity: 4, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software"}, - RegulationReferences: []string{"CRA", "IEC 62443", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"RBAC: Nur Admin darf loeschen", "Log-Export vor Loeschung", "Unanderbare Log-Speicherung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - - // ==================================================================== - // Category: integration_error (8 entries) - // ==================================================================== - { - ID: hazardUUID("integration_error", 1), - Category: "integration_error", - Name: "Datentyp-Mismatch an Schnittstelle", - Description: "Zwei Systeme tauschen Daten ueber eine Schnittstelle aus, die inkompatible Datentypen verwendet, was zu Interpretationsfehlern fuehrt.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "network"}, - RegulationReferences: []string{"IEC 62304", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Schnittstellendefinition (IDL/Protobuf)", "Integrationstests", "Datentypvalidierung"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("integration_error", 2), - Category: "integration_error", - Name: "Endianness-Fehler bei Datenuebertragung", - Description: "Big-Endian- und Little-Endian-Systeme kommunizieren ohne Byte-Order-Konvertierung, was zu falsch interpretierten numerischen Werten fuehrt.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "network"}, - RegulationReferences: []string{"IEC 62304", "IEC 62443"}, - SuggestedMitigations: mustMarshalJSON([]string{"Explizite Byte-Order-Definiton", "Integrationstests", "Schnittstellenspezifikation"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("integration_error", 3), - Category: "integration_error", - Name: "Protokoll-Versions-Konflikt", - Description: "Sender und Empfaenger verwenden unterschiedliche Protokollversionen, die nicht rueckwaertskompatibel sind, was zu Paketablehnung oder Fehlinterpretation fuehrt.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "network", "firmware"}, - RegulationReferences: []string{"IEC 62443", "CRA"}, - SuggestedMitigations: mustMarshalJSON([]string{"Versions-Aushandlung beim Verbindungsaufbau", "Backward-Compatibilitaet", "Kompatibilitaets-Matrix"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("integration_error", 4), - Category: "integration_error", - Name: "Timeout nicht behandelt bei Kommunikation", - Description: "Eine Kommunikationsverbindung bricht ab oder antwortet nicht, der Sender erkennt dies nicht und wartet unendlich lang.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "network"}, - RegulationReferences: []string{"IEC 62443", "Maschinenverordnung 2023/1230"}, - SuggestedMitigations: mustMarshalJSON([]string{"Timeout-Konfiguration", "Watchdog-Timer", "Fail-Safe bei Verbindungsverlust"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("integration_error", 5), - Category: "integration_error", - Name: "Buffer Overflow an Schnittstelle", - Description: "Eine Schnittstelle akzeptiert Eingaben, die groesser als der zugewiesene Puffer sind, was zu Speicher-Ueberschreibung und Kontrollfluss-Manipulation fuehrt.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"software", "firmware", "network"}, - RegulationReferences: []string{"CRA", "IEC 62443", "IEC 62304"}, - SuggestedMitigations: mustMarshalJSON([]string{"Laengenvalidierung", "Sichere Puffer-Funktionen", "Statische Analyse (z.B. MISRA)"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("integration_error", 6), - Category: "integration_error", - Name: "Fehlender Heartbeat bei Safety-Verbindung", - Description: "Eine Safety-Kommunikationsverbindung sendet keinen periodischen Heartbeat, so dass ein stiller Ausfall (z.B. unterbrochenes Kabel) nicht erkannt wird.", - DefaultSeverity: 5, - DefaultProbability: 2, - ApplicableComponentTypes: []string{"network", "software"}, - RegulationReferences: []string{"IEC 61784-3", "ISO 13849", "IEC 62061"}, - SuggestedMitigations: mustMarshalJSON([]string{"Heartbeat-Protokoll", "Verbindungsueberwachung", "Safe-State bei Heartbeat-Ausfall"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("integration_error", 7), - Category: "integration_error", - Name: "Falscher Skalierungsfaktor bei Sensordaten", - Description: "Sensordaten werden mit einem falschen Faktor skaliert, was zu signifikant fehlerhaften Messwerten und moeglichen Fehlentscheidungen fuehrt.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"sensor", "software"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62304"}, - SuggestedMitigations: mustMarshalJSON([]string{"Kalibrierungspruefung", "Plausibilitaetstest", "Schnittstellendokumentation"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, - { - ID: hazardUUID("integration_error", 8), - Category: "integration_error", - Name: "Einheitenfehler (mm vs. inch)", - Description: "Unterschiedliche Masseinheiten zwischen Systemen fuehren zu fehlerhaften Bewegungsbefehlen oder Werkzeugpositionierungen.", - DefaultSeverity: 4, - DefaultProbability: 3, - ApplicableComponentTypes: []string{"software", "hmi"}, - RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62304"}, - SuggestedMitigations: mustMarshalJSON([]string{"Explizite Einheitendefinition", "Einheitenkonvertierung in der Schnittstelle", "Integrationstests"}), - IsBuiltin: true, - TenantID: nil, - CreatedAt: now, - }, } } diff --git a/ai-compliance-sdk/internal/iace/store_components.go b/ai-compliance-sdk/internal/iace/store_components.go new file mode 100644 index 0000000..afcfcb9 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/store_components.go @@ -0,0 +1,309 @@ +package iace + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5" +) + + +// ============================================================================ +// Component CRUD Operations +// ============================================================================ + +// CreateComponent creates a new component within a project +func (s *Store) CreateComponent(ctx context.Context, req CreateComponentRequest) (*Component, error) { + comp := &Component{ + ID: uuid.New(), + ProjectID: req.ProjectID, + ParentID: req.ParentID, + Name: req.Name, + ComponentType: req.ComponentType, + Version: req.Version, + Description: req.Description, + IsSafetyRelevant: req.IsSafetyRelevant, + IsNetworked: req.IsNetworked, + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), + } + + _, err := s.pool.Exec(ctx, ` + INSERT INTO iace_components ( + id, project_id, parent_id, name, component_type, + version, description, is_safety_relevant, is_networked, + metadata, sort_order, created_at, updated_at + ) VALUES ( + $1, $2, $3, $4, $5, + $6, $7, $8, $9, + $10, $11, $12, $13 + ) + `, + comp.ID, comp.ProjectID, comp.ParentID, comp.Name, string(comp.ComponentType), + comp.Version, comp.Description, comp.IsSafetyRelevant, comp.IsNetworked, + comp.Metadata, comp.SortOrder, comp.CreatedAt, comp.UpdatedAt, + ) + if err != nil { + return nil, fmt.Errorf("create component: %w", err) + } + + return comp, nil +} + +// GetComponent retrieves a component by ID +func (s *Store) GetComponent(ctx context.Context, id uuid.UUID) (*Component, error) { + var c Component + var compType string + var metadata []byte + + err := s.pool.QueryRow(ctx, ` + SELECT + id, project_id, parent_id, name, component_type, + version, description, is_safety_relevant, is_networked, + metadata, sort_order, created_at, updated_at + FROM iace_components WHERE id = $1 + `, id).Scan( + &c.ID, &c.ProjectID, &c.ParentID, &c.Name, &compType, + &c.Version, &c.Description, &c.IsSafetyRelevant, &c.IsNetworked, + &metadata, &c.SortOrder, &c.CreatedAt, &c.UpdatedAt, + ) + if err == pgx.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("get component: %w", err) + } + + c.ComponentType = ComponentType(compType) + json.Unmarshal(metadata, &c.Metadata) + + return &c, nil +} + +// ListComponents lists all components for a project +func (s *Store) ListComponents(ctx context.Context, projectID uuid.UUID) ([]Component, error) { + rows, err := s.pool.Query(ctx, ` + SELECT + id, project_id, parent_id, name, component_type, + version, description, is_safety_relevant, is_networked, + metadata, sort_order, created_at, updated_at + FROM iace_components WHERE project_id = $1 + ORDER BY sort_order ASC, created_at ASC + `, projectID) + if err != nil { + return nil, fmt.Errorf("list components: %w", err) + } + defer rows.Close() + + var components []Component + for rows.Next() { + var c Component + var compType string + var metadata []byte + + err := rows.Scan( + &c.ID, &c.ProjectID, &c.ParentID, &c.Name, &compType, + &c.Version, &c.Description, &c.IsSafetyRelevant, &c.IsNetworked, + &metadata, &c.SortOrder, &c.CreatedAt, &c.UpdatedAt, + ) + if err != nil { + return nil, fmt.Errorf("list components scan: %w", err) + } + + c.ComponentType = ComponentType(compType) + json.Unmarshal(metadata, &c.Metadata) + + components = append(components, c) + } + + return components, nil +} + +// UpdateComponent updates a component with a dynamic set of fields +func (s *Store) UpdateComponent(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*Component, error) { + if len(updates) == 0 { + return s.GetComponent(ctx, id) + } + + query := "UPDATE iace_components SET updated_at = NOW()" + args := []interface{}{id} + argIdx := 2 + + for key, val := range updates { + switch key { + case "name", "version", "description": + query += fmt.Sprintf(", %s = $%d", key, argIdx) + args = append(args, val) + argIdx++ + case "component_type": + query += fmt.Sprintf(", component_type = $%d", argIdx) + args = append(args, val) + argIdx++ + case "is_safety_relevant": + query += fmt.Sprintf(", is_safety_relevant = $%d", argIdx) + args = append(args, val) + argIdx++ + case "is_networked": + query += fmt.Sprintf(", is_networked = $%d", argIdx) + args = append(args, val) + argIdx++ + case "sort_order": + query += fmt.Sprintf(", sort_order = $%d", argIdx) + args = append(args, val) + argIdx++ + case "metadata": + metaJSON, _ := json.Marshal(val) + query += fmt.Sprintf(", metadata = $%d", argIdx) + args = append(args, metaJSON) + argIdx++ + case "parent_id": + query += fmt.Sprintf(", parent_id = $%d", argIdx) + args = append(args, val) + argIdx++ + } + } + + query += " WHERE id = $1" + + _, err := s.pool.Exec(ctx, query, args...) + if err != nil { + return nil, fmt.Errorf("update component: %w", err) + } + + return s.GetComponent(ctx, id) +} + +// DeleteComponent deletes a component by ID +func (s *Store) DeleteComponent(ctx context.Context, id uuid.UUID) error { + _, err := s.pool.Exec(ctx, "DELETE FROM iace_components WHERE id = $1", id) + if err != nil { + return fmt.Errorf("delete component: %w", err) + } + return nil +} + +// ============================================================================ +// Classification Operations +// ============================================================================ + +// UpsertClassification inserts or updates a regulatory classification for a project +func (s *Store) UpsertClassification(ctx context.Context, projectID uuid.UUID, regulation RegulationType, result string, riskLevel string, confidence float64, reasoning string, ragSources, requirements json.RawMessage) (*RegulatoryClassification, error) { + id := uuid.New() + now := time.Now().UTC() + + _, err := s.pool.Exec(ctx, ` + INSERT INTO iace_classifications ( + id, project_id, regulation, classification_result, + risk_level, confidence, reasoning, + rag_sources, requirements, + created_at, updated_at + ) VALUES ( + $1, $2, $3, $4, + $5, $6, $7, + $8, $9, + $10, $11 + ) + ON CONFLICT (project_id, regulation) + DO UPDATE SET + classification_result = EXCLUDED.classification_result, + risk_level = EXCLUDED.risk_level, + confidence = EXCLUDED.confidence, + reasoning = EXCLUDED.reasoning, + rag_sources = EXCLUDED.rag_sources, + requirements = EXCLUDED.requirements, + updated_at = EXCLUDED.updated_at + `, + id, projectID, string(regulation), result, + riskLevel, confidence, reasoning, + ragSources, requirements, + now, now, + ) + if err != nil { + return nil, fmt.Errorf("upsert classification: %w", err) + } + + // Retrieve the upserted row (may have kept the original ID on conflict) + return s.getClassificationByProjectAndRegulation(ctx, projectID, regulation) +} + +// getClassificationByProjectAndRegulation is a helper to fetch a single classification +func (s *Store) getClassificationByProjectAndRegulation(ctx context.Context, projectID uuid.UUID, regulation RegulationType) (*RegulatoryClassification, error) { + var c RegulatoryClassification + var reg, rl string + var ragSources, requirements []byte + + err := s.pool.QueryRow(ctx, ` + SELECT + id, project_id, regulation, classification_result, + risk_level, confidence, reasoning, + rag_sources, requirements, + created_at, updated_at + FROM iace_classifications + WHERE project_id = $1 AND regulation = $2 + `, projectID, string(regulation)).Scan( + &c.ID, &c.ProjectID, ®, &c.ClassificationResult, + &rl, &c.Confidence, &c.Reasoning, + &ragSources, &requirements, + &c.CreatedAt, &c.UpdatedAt, + ) + if err == pgx.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("get classification: %w", err) + } + + c.Regulation = RegulationType(reg) + c.RiskLevel = RiskLevel(rl) + json.Unmarshal(ragSources, &c.RAGSources) + json.Unmarshal(requirements, &c.Requirements) + + return &c, nil +} + +// GetClassifications retrieves all classifications for a project +func (s *Store) GetClassifications(ctx context.Context, projectID uuid.UUID) ([]RegulatoryClassification, error) { + rows, err := s.pool.Query(ctx, ` + SELECT + id, project_id, regulation, classification_result, + risk_level, confidence, reasoning, + rag_sources, requirements, + created_at, updated_at + FROM iace_classifications + WHERE project_id = $1 + ORDER BY regulation ASC + `, projectID) + if err != nil { + return nil, fmt.Errorf("get classifications: %w", err) + } + defer rows.Close() + + var classifications []RegulatoryClassification + for rows.Next() { + var c RegulatoryClassification + var reg, rl string + var ragSources, requirements []byte + + err := rows.Scan( + &c.ID, &c.ProjectID, ®, &c.ClassificationResult, + &rl, &c.Confidence, &c.Reasoning, + &ragSources, &requirements, + &c.CreatedAt, &c.UpdatedAt, + ) + if err != nil { + return nil, fmt.Errorf("get classifications scan: %w", err) + } + + c.Regulation = RegulationType(reg) + c.RiskLevel = RiskLevel(rl) + json.Unmarshal(ragSources, &c.RAGSources) + json.Unmarshal(requirements, &c.Requirements) + + classifications = append(classifications, c) + } + + return classifications, nil +} diff --git a/ai-compliance-sdk/internal/iace/store_evidence.go b/ai-compliance-sdk/internal/iace/store_evidence.go new file mode 100644 index 0000000..e2017b0 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/store_evidence.go @@ -0,0 +1,321 @@ +package iace + +import ( + "context" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5" +) + +// ============================================================================ +// Evidence Operations +// ============================================================================ + +// CreateEvidence creates a new evidence record +func (s *Store) CreateEvidence(ctx context.Context, evidence *Evidence) error { + if evidence.ID == uuid.Nil { + evidence.ID = uuid.New() + } + if evidence.CreatedAt.IsZero() { + evidence.CreatedAt = time.Now().UTC() + } + + _, err := s.pool.Exec(ctx, ` + INSERT INTO iace_evidence ( + id, project_id, mitigation_id, verification_plan_id, + file_name, file_path, file_hash, file_size, mime_type, + description, uploaded_by, created_at + ) VALUES ( + $1, $2, $3, $4, + $5, $6, $7, $8, $9, + $10, $11, $12 + ) + `, + evidence.ID, evidence.ProjectID, evidence.MitigationID, evidence.VerificationPlanID, + evidence.FileName, evidence.FilePath, evidence.FileHash, evidence.FileSize, evidence.MimeType, + evidence.Description, evidence.UploadedBy, evidence.CreatedAt, + ) + if err != nil { + return fmt.Errorf("create evidence: %w", err) + } + + return nil +} + +// ListEvidence lists all evidence for a project +func (s *Store) ListEvidence(ctx context.Context, projectID uuid.UUID) ([]Evidence, error) { + rows, err := s.pool.Query(ctx, ` + SELECT + id, project_id, mitigation_id, verification_plan_id, + file_name, file_path, file_hash, file_size, mime_type, + description, uploaded_by, created_at + FROM iace_evidence WHERE project_id = $1 + ORDER BY created_at DESC + `, projectID) + if err != nil { + return nil, fmt.Errorf("list evidence: %w", err) + } + defer rows.Close() + + var evidence []Evidence + for rows.Next() { + var e Evidence + + err := rows.Scan( + &e.ID, &e.ProjectID, &e.MitigationID, &e.VerificationPlanID, + &e.FileName, &e.FilePath, &e.FileHash, &e.FileSize, &e.MimeType, + &e.Description, &e.UploadedBy, &e.CreatedAt, + ) + if err != nil { + return nil, fmt.Errorf("list evidence scan: %w", err) + } + + evidence = append(evidence, e) + } + + return evidence, nil +} + +// ============================================================================ +// Verification Plan Operations +// ============================================================================ + +// CreateVerificationPlan creates a new verification plan +func (s *Store) CreateVerificationPlan(ctx context.Context, req CreateVerificationPlanRequest) (*VerificationPlan, error) { + vp := &VerificationPlan{ + ID: uuid.New(), + ProjectID: req.ProjectID, + HazardID: req.HazardID, + MitigationID: req.MitigationID, + Title: req.Title, + Description: req.Description, + AcceptanceCriteria: req.AcceptanceCriteria, + Method: req.Method, + Status: "planned", + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), + } + + _, err := s.pool.Exec(ctx, ` + INSERT INTO iace_verification_plans ( + id, project_id, hazard_id, mitigation_id, + title, description, acceptance_criteria, method, + status, result, completed_at, completed_by, + created_at, updated_at + ) VALUES ( + $1, $2, $3, $4, + $5, $6, $7, $8, + $9, $10, $11, $12, + $13, $14 + ) + `, + vp.ID, vp.ProjectID, vp.HazardID, vp.MitigationID, + vp.Title, vp.Description, vp.AcceptanceCriteria, string(vp.Method), + vp.Status, "", nil, uuid.Nil, + vp.CreatedAt, vp.UpdatedAt, + ) + if err != nil { + return nil, fmt.Errorf("create verification plan: %w", err) + } + + return vp, nil +} + +// UpdateVerificationPlan updates a verification plan with a dynamic set of fields +func (s *Store) UpdateVerificationPlan(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*VerificationPlan, error) { + if len(updates) == 0 { + return s.getVerificationPlan(ctx, id) + } + + query := "UPDATE iace_verification_plans SET updated_at = NOW()" + args := []interface{}{id} + argIdx := 2 + + for key, val := range updates { + switch key { + case "title", "description", "acceptance_criteria", "result", "status": + query += fmt.Sprintf(", %s = $%d", key, argIdx) + args = append(args, val) + argIdx++ + case "method": + query += fmt.Sprintf(", method = $%d", argIdx) + args = append(args, val) + argIdx++ + } + } + + query += " WHERE id = $1" + + _, err := s.pool.Exec(ctx, query, args...) + if err != nil { + return nil, fmt.Errorf("update verification plan: %w", err) + } + + return s.getVerificationPlan(ctx, id) +} + +// CompleteVerification marks a verification plan as completed +func (s *Store) CompleteVerification(ctx context.Context, id uuid.UUID, result string, completedBy string) error { + now := time.Now().UTC() + completedByUUID, err := uuid.Parse(completedBy) + if err != nil { + return fmt.Errorf("invalid completed_by UUID: %w", err) + } + + _, err = s.pool.Exec(ctx, ` + UPDATE iace_verification_plans SET + status = 'completed', + result = $2, + completed_at = $3, + completed_by = $4, + updated_at = $3 + WHERE id = $1 + `, id, result, now, completedByUUID) + if err != nil { + return fmt.Errorf("complete verification: %w", err) + } + + return nil +} + +// ListVerificationPlans lists all verification plans for a project +func (s *Store) ListVerificationPlans(ctx context.Context, projectID uuid.UUID) ([]VerificationPlan, error) { + rows, err := s.pool.Query(ctx, ` + SELECT + id, project_id, hazard_id, mitigation_id, + title, description, acceptance_criteria, method, + status, result, completed_at, completed_by, + created_at, updated_at + FROM iace_verification_plans WHERE project_id = $1 + ORDER BY created_at ASC + `, projectID) + if err != nil { + return nil, fmt.Errorf("list verification plans: %w", err) + } + defer rows.Close() + + var plans []VerificationPlan + for rows.Next() { + var vp VerificationPlan + var method string + + err := rows.Scan( + &vp.ID, &vp.ProjectID, &vp.HazardID, &vp.MitigationID, + &vp.Title, &vp.Description, &vp.AcceptanceCriteria, &method, + &vp.Status, &vp.Result, &vp.CompletedAt, &vp.CompletedBy, + &vp.CreatedAt, &vp.UpdatedAt, + ) + if err != nil { + return nil, fmt.Errorf("list verification plans scan: %w", err) + } + + vp.Method = VerificationMethod(method) + plans = append(plans, vp) + } + + return plans, nil +} + +// getVerificationPlan is a helper to fetch a single verification plan by ID +func (s *Store) getVerificationPlan(ctx context.Context, id uuid.UUID) (*VerificationPlan, error) { + var vp VerificationPlan + var method string + + err := s.pool.QueryRow(ctx, ` + SELECT + id, project_id, hazard_id, mitigation_id, + title, description, acceptance_criteria, method, + status, result, completed_at, completed_by, + created_at, updated_at + FROM iace_verification_plans WHERE id = $1 + `, id).Scan( + &vp.ID, &vp.ProjectID, &vp.HazardID, &vp.MitigationID, + &vp.Title, &vp.Description, &vp.AcceptanceCriteria, &method, + &vp.Status, &vp.Result, &vp.CompletedAt, &vp.CompletedBy, + &vp.CreatedAt, &vp.UpdatedAt, + ) + if err == pgx.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("get verification plan: %w", err) + } + + vp.Method = VerificationMethod(method) + return &vp, nil +} + +// ============================================================================ +// Reference Data Operations +// ============================================================================ + +// ListLifecyclePhases returns all 12 lifecycle phases with DE/EN labels +func (s *Store) ListLifecyclePhases(ctx context.Context) ([]LifecyclePhaseInfo, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, label_de, label_en, sort_order + FROM iace_lifecycle_phases + ORDER BY sort_order ASC + `) + if err != nil { + return nil, fmt.Errorf("list lifecycle phases: %w", err) + } + defer rows.Close() + + var phases []LifecyclePhaseInfo + for rows.Next() { + var p LifecyclePhaseInfo + if err := rows.Scan(&p.ID, &p.LabelDE, &p.LabelEN, &p.Sort); err != nil { + return nil, fmt.Errorf("list lifecycle phases scan: %w", err) + } + phases = append(phases, p) + } + return phases, nil +} + +// ListRoles returns all affected person roles from the reference table +func (s *Store) ListRoles(ctx context.Context) ([]RoleInfo, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, label_de, label_en, sort_order + FROM iace_roles + ORDER BY sort_order ASC + `) + if err != nil { + return nil, fmt.Errorf("list roles: %w", err) + } + defer rows.Close() + + var roles []RoleInfo + for rows.Next() { + var r RoleInfo + if err := rows.Scan(&r.ID, &r.LabelDE, &r.LabelEN, &r.Sort); err != nil { + return nil, fmt.Errorf("list roles scan: %w", err) + } + roles = append(roles, r) + } + return roles, nil +} + +// ListEvidenceTypes returns all evidence types from the reference table +func (s *Store) ListEvidenceTypes(ctx context.Context) ([]EvidenceTypeInfo, error) { + rows, err := s.pool.Query(ctx, ` + SELECT id, category, label_de, label_en, sort_order + FROM iace_evidence_types + ORDER BY sort_order ASC + `) + if err != nil { + return nil, fmt.Errorf("list evidence types: %w", err) + } + defer rows.Close() + + var types []EvidenceTypeInfo + for rows.Next() { + var e EvidenceTypeInfo + if err := rows.Scan(&e.ID, &e.Category, &e.LabelDE, &e.LabelEN, &e.Sort); err != nil { + return nil, fmt.Errorf("list evidence types scan: %w", err) + } + types = append(types, e) + } + return types, nil +} diff --git a/ai-compliance-sdk/internal/iace/store_hazard_library.go b/ai-compliance-sdk/internal/iace/store_hazard_library.go new file mode 100644 index 0000000..533b421 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/store_hazard_library.go @@ -0,0 +1,172 @@ +package iace + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5" +) + +// ============================================================================ +// Hazard Library Operations +// ============================================================================ + +// ListHazardLibrary lists hazard library entries, optionally filtered by category and component type +func (s *Store) ListHazardLibrary(ctx context.Context, category string, componentType string) ([]HazardLibraryEntry, error) { + query := ` + SELECT + id, category, COALESCE(sub_category, ''), name, description, + default_severity, default_probability, + COALESCE(default_exposure, 3), COALESCE(default_avoidance, 3), + applicable_component_types, regulation_references, + suggested_mitigations, + COALESCE(typical_causes, '[]'::jsonb), + COALESCE(typical_harm, ''), + COALESCE(relevant_lifecycle_phases, '[]'::jsonb), + COALESCE(recommended_measures_design, '[]'::jsonb), + COALESCE(recommended_measures_technical, '[]'::jsonb), + COALESCE(recommended_measures_information, '[]'::jsonb), + COALESCE(suggested_evidence, '[]'::jsonb), + COALESCE(related_keywords, '[]'::jsonb), + is_builtin, tenant_id, + created_at + FROM iace_hazard_library WHERE 1=1` + + args := []interface{}{} + argIdx := 1 + + if category != "" { + query += fmt.Sprintf(" AND category = $%d", argIdx) + args = append(args, category) + argIdx++ + } + if componentType != "" { + query += fmt.Sprintf(" AND applicable_component_types @> $%d::jsonb", argIdx) + componentTypeJSON, _ := json.Marshal([]string{componentType}) + args = append(args, string(componentTypeJSON)) + argIdx++ + } + + query += " ORDER BY category ASC, name ASC" + + rows, err := s.pool.Query(ctx, query, args...) + if err != nil { + return nil, fmt.Errorf("list hazard library: %w", err) + } + defer rows.Close() + + var entries []HazardLibraryEntry + for rows.Next() { + var e HazardLibraryEntry + var applicableComponentTypes, regulationReferences, suggestedMitigations []byte + var typicalCauses, relevantPhases, measuresDesign, measuresTechnical, measuresInfo, evidence, keywords []byte + + err := rows.Scan( + &e.ID, &e.Category, &e.SubCategory, &e.Name, &e.Description, + &e.DefaultSeverity, &e.DefaultProbability, + &e.DefaultExposure, &e.DefaultAvoidance, + &applicableComponentTypes, ®ulationReferences, + &suggestedMitigations, + &typicalCauses, &e.TypicalHarm, &relevantPhases, + &measuresDesign, &measuresTechnical, &measuresInfo, + &evidence, &keywords, + &e.IsBuiltin, &e.TenantID, + &e.CreatedAt, + ) + if err != nil { + return nil, fmt.Errorf("list hazard library scan: %w", err) + } + + json.Unmarshal(applicableComponentTypes, &e.ApplicableComponentTypes) + json.Unmarshal(regulationReferences, &e.RegulationReferences) + json.Unmarshal(suggestedMitigations, &e.SuggestedMitigations) + json.Unmarshal(typicalCauses, &e.TypicalCauses) + json.Unmarshal(relevantPhases, &e.RelevantLifecyclePhases) + json.Unmarshal(measuresDesign, &e.RecommendedMeasuresDesign) + json.Unmarshal(measuresTechnical, &e.RecommendedMeasuresTechnical) + json.Unmarshal(measuresInfo, &e.RecommendedMeasuresInformation) + json.Unmarshal(evidence, &e.SuggestedEvidence) + json.Unmarshal(keywords, &e.RelatedKeywords) + + if e.ApplicableComponentTypes == nil { + e.ApplicableComponentTypes = []string{} + } + if e.RegulationReferences == nil { + e.RegulationReferences = []string{} + } + + entries = append(entries, e) + } + + return entries, nil +} + +// GetHazardLibraryEntry retrieves a single hazard library entry by ID +func (s *Store) GetHazardLibraryEntry(ctx context.Context, id uuid.UUID) (*HazardLibraryEntry, error) { + var e HazardLibraryEntry + var applicableComponentTypes, regulationReferences, suggestedMitigations []byte + var typicalCauses, relevantLifecyclePhases []byte + var recommendedMeasuresDesign, recommendedMeasuresTechnical, recommendedMeasuresInformation []byte + var suggestedEvidence, relatedKeywords []byte + + err := s.pool.QueryRow(ctx, ` + SELECT + id, category, name, description, + default_severity, default_probability, + applicable_component_types, regulation_references, + suggested_mitigations, is_builtin, tenant_id, + created_at, + COALESCE(sub_category, ''), + COALESCE(default_exposure, 3), + COALESCE(default_avoidance, 3), + COALESCE(typical_causes, '[]'), + COALESCE(typical_harm, ''), + COALESCE(relevant_lifecycle_phases, '[]'), + COALESCE(recommended_measures_design, '[]'), + COALESCE(recommended_measures_technical, '[]'), + COALESCE(recommended_measures_information, '[]'), + COALESCE(suggested_evidence, '[]'), + COALESCE(related_keywords, '[]') + FROM iace_hazard_library WHERE id = $1 + `, id).Scan( + &e.ID, &e.Category, &e.Name, &e.Description, + &e.DefaultSeverity, &e.DefaultProbability, + &applicableComponentTypes, ®ulationReferences, + &suggestedMitigations, &e.IsBuiltin, &e.TenantID, + &e.CreatedAt, + &e.SubCategory, + &e.DefaultExposure, &e.DefaultAvoidance, + &typicalCauses, &e.TypicalHarm, + &relevantLifecyclePhases, + &recommendedMeasuresDesign, &recommendedMeasuresTechnical, &recommendedMeasuresInformation, + &suggestedEvidence, &relatedKeywords, + ) + if err == pgx.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("get hazard library entry: %w", err) + } + + json.Unmarshal(applicableComponentTypes, &e.ApplicableComponentTypes) + json.Unmarshal(regulationReferences, &e.RegulationReferences) + json.Unmarshal(suggestedMitigations, &e.SuggestedMitigations) + json.Unmarshal(typicalCauses, &e.TypicalCauses) + json.Unmarshal(relevantLifecyclePhases, &e.RelevantLifecyclePhases) + json.Unmarshal(recommendedMeasuresDesign, &e.RecommendedMeasuresDesign) + json.Unmarshal(recommendedMeasuresTechnical, &e.RecommendedMeasuresTechnical) + json.Unmarshal(recommendedMeasuresInformation, &e.RecommendedMeasuresInformation) + json.Unmarshal(suggestedEvidence, &e.SuggestedEvidence) + json.Unmarshal(relatedKeywords, &e.RelatedKeywords) + + if e.ApplicableComponentTypes == nil { + e.ApplicableComponentTypes = []string{} + } + if e.RegulationReferences == nil { + e.RegulationReferences = []string{} + } + + return &e, nil +} diff --git a/ai-compliance-sdk/internal/iace/store_hazards.go b/ai-compliance-sdk/internal/iace/store_hazards.go index 15afcd2..bd1ecb0 100644 --- a/ai-compliance-sdk/internal/iace/store_hazards.go +++ b/ai-compliance-sdk/internal/iace/store_hazards.go @@ -2,7 +2,6 @@ package iace import ( "context" - "encoding/json" "fmt" "time" @@ -392,164 +391,3 @@ func riskLevelSeverity(rl RiskLevel) int { } } -// ============================================================================ -// Hazard Library Operations -// ============================================================================ - -// ListHazardLibrary lists hazard library entries, optionally filtered by category and component type -func (s *Store) ListHazardLibrary(ctx context.Context, category string, componentType string) ([]HazardLibraryEntry, error) { - query := ` - SELECT - id, category, COALESCE(sub_category, ''), name, description, - default_severity, default_probability, - COALESCE(default_exposure, 3), COALESCE(default_avoidance, 3), - applicable_component_types, regulation_references, - suggested_mitigations, - COALESCE(typical_causes, '[]'::jsonb), - COALESCE(typical_harm, ''), - COALESCE(relevant_lifecycle_phases, '[]'::jsonb), - COALESCE(recommended_measures_design, '[]'::jsonb), - COALESCE(recommended_measures_technical, '[]'::jsonb), - COALESCE(recommended_measures_information, '[]'::jsonb), - COALESCE(suggested_evidence, '[]'::jsonb), - COALESCE(related_keywords, '[]'::jsonb), - is_builtin, tenant_id, - created_at - FROM iace_hazard_library WHERE 1=1` - - args := []interface{}{} - argIdx := 1 - - if category != "" { - query += fmt.Sprintf(" AND category = $%d", argIdx) - args = append(args, category) - argIdx++ - } - if componentType != "" { - query += fmt.Sprintf(" AND applicable_component_types @> $%d::jsonb", argIdx) - componentTypeJSON, _ := json.Marshal([]string{componentType}) - args = append(args, string(componentTypeJSON)) - argIdx++ - } - - query += " ORDER BY category ASC, name ASC" - - rows, err := s.pool.Query(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("list hazard library: %w", err) - } - defer rows.Close() - - var entries []HazardLibraryEntry - for rows.Next() { - var e HazardLibraryEntry - var applicableComponentTypes, regulationReferences, suggestedMitigations []byte - var typicalCauses, relevantPhases, measuresDesign, measuresTechnical, measuresInfo, evidence, keywords []byte - - err := rows.Scan( - &e.ID, &e.Category, &e.SubCategory, &e.Name, &e.Description, - &e.DefaultSeverity, &e.DefaultProbability, - &e.DefaultExposure, &e.DefaultAvoidance, - &applicableComponentTypes, ®ulationReferences, - &suggestedMitigations, - &typicalCauses, &e.TypicalHarm, &relevantPhases, - &measuresDesign, &measuresTechnical, &measuresInfo, - &evidence, &keywords, - &e.IsBuiltin, &e.TenantID, - &e.CreatedAt, - ) - if err != nil { - return nil, fmt.Errorf("list hazard library scan: %w", err) - } - - json.Unmarshal(applicableComponentTypes, &e.ApplicableComponentTypes) - json.Unmarshal(regulationReferences, &e.RegulationReferences) - json.Unmarshal(suggestedMitigations, &e.SuggestedMitigations) - json.Unmarshal(typicalCauses, &e.TypicalCauses) - json.Unmarshal(relevantPhases, &e.RelevantLifecyclePhases) - json.Unmarshal(measuresDesign, &e.RecommendedMeasuresDesign) - json.Unmarshal(measuresTechnical, &e.RecommendedMeasuresTechnical) - json.Unmarshal(measuresInfo, &e.RecommendedMeasuresInformation) - json.Unmarshal(evidence, &e.SuggestedEvidence) - json.Unmarshal(keywords, &e.RelatedKeywords) - - if e.ApplicableComponentTypes == nil { - e.ApplicableComponentTypes = []string{} - } - if e.RegulationReferences == nil { - e.RegulationReferences = []string{} - } - - entries = append(entries, e) - } - - return entries, nil -} - -// GetHazardLibraryEntry retrieves a single hazard library entry by ID -func (s *Store) GetHazardLibraryEntry(ctx context.Context, id uuid.UUID) (*HazardLibraryEntry, error) { - var e HazardLibraryEntry - var applicableComponentTypes, regulationReferences, suggestedMitigations []byte - var typicalCauses, relevantLifecyclePhases []byte - var recommendedMeasuresDesign, recommendedMeasuresTechnical, recommendedMeasuresInformation []byte - var suggestedEvidence, relatedKeywords []byte - - err := s.pool.QueryRow(ctx, ` - SELECT - id, category, name, description, - default_severity, default_probability, - applicable_component_types, regulation_references, - suggested_mitigations, is_builtin, tenant_id, - created_at, - COALESCE(sub_category, ''), - COALESCE(default_exposure, 3), - COALESCE(default_avoidance, 3), - COALESCE(typical_causes, '[]'), - COALESCE(typical_harm, ''), - COALESCE(relevant_lifecycle_phases, '[]'), - COALESCE(recommended_measures_design, '[]'), - COALESCE(recommended_measures_technical, '[]'), - COALESCE(recommended_measures_information, '[]'), - COALESCE(suggested_evidence, '[]'), - COALESCE(related_keywords, '[]') - FROM iace_hazard_library WHERE id = $1 - `, id).Scan( - &e.ID, &e.Category, &e.Name, &e.Description, - &e.DefaultSeverity, &e.DefaultProbability, - &applicableComponentTypes, ®ulationReferences, - &suggestedMitigations, &e.IsBuiltin, &e.TenantID, - &e.CreatedAt, - &e.SubCategory, - &e.DefaultExposure, &e.DefaultAvoidance, - &typicalCauses, &e.TypicalHarm, - &relevantLifecyclePhases, - &recommendedMeasuresDesign, &recommendedMeasuresTechnical, &recommendedMeasuresInformation, - &suggestedEvidence, &relatedKeywords, - ) - if err == pgx.ErrNoRows { - return nil, nil - } - if err != nil { - return nil, fmt.Errorf("get hazard library entry: %w", err) - } - - json.Unmarshal(applicableComponentTypes, &e.ApplicableComponentTypes) - json.Unmarshal(regulationReferences, &e.RegulationReferences) - json.Unmarshal(suggestedMitigations, &e.SuggestedMitigations) - json.Unmarshal(typicalCauses, &e.TypicalCauses) - json.Unmarshal(relevantLifecyclePhases, &e.RelevantLifecyclePhases) - json.Unmarshal(recommendedMeasuresDesign, &e.RecommendedMeasuresDesign) - json.Unmarshal(recommendedMeasuresTechnical, &e.RecommendedMeasuresTechnical) - json.Unmarshal(recommendedMeasuresInformation, &e.RecommendedMeasuresInformation) - json.Unmarshal(suggestedEvidence, &e.SuggestedEvidence) - json.Unmarshal(relatedKeywords, &e.RelatedKeywords) - - if e.ApplicableComponentTypes == nil { - e.ApplicableComponentTypes = []string{} - } - if e.RegulationReferences == nil { - e.RegulationReferences = []string{} - } - - return &e, nil -} diff --git a/ai-compliance-sdk/internal/iace/store_mitigations.go b/ai-compliance-sdk/internal/iace/store_mitigations.go index 08cb70d..993df4a 100644 --- a/ai-compliance-sdk/internal/iace/store_mitigations.go +++ b/ai-compliance-sdk/internal/iace/store_mitigations.go @@ -194,313 +194,3 @@ func (s *Store) getMitigation(ctx context.Context, id uuid.UUID) (*Mitigation, e return &m, nil } -// ============================================================================ -// Evidence Operations -// ============================================================================ - -// CreateEvidence creates a new evidence record -func (s *Store) CreateEvidence(ctx context.Context, evidence *Evidence) error { - if evidence.ID == uuid.Nil { - evidence.ID = uuid.New() - } - if evidence.CreatedAt.IsZero() { - evidence.CreatedAt = time.Now().UTC() - } - - _, err := s.pool.Exec(ctx, ` - INSERT INTO iace_evidence ( - id, project_id, mitigation_id, verification_plan_id, - file_name, file_path, file_hash, file_size, mime_type, - description, uploaded_by, created_at - ) VALUES ( - $1, $2, $3, $4, - $5, $6, $7, $8, $9, - $10, $11, $12 - ) - `, - evidence.ID, evidence.ProjectID, evidence.MitigationID, evidence.VerificationPlanID, - evidence.FileName, evidence.FilePath, evidence.FileHash, evidence.FileSize, evidence.MimeType, - evidence.Description, evidence.UploadedBy, evidence.CreatedAt, - ) - if err != nil { - return fmt.Errorf("create evidence: %w", err) - } - - return nil -} - -// ListEvidence lists all evidence for a project -func (s *Store) ListEvidence(ctx context.Context, projectID uuid.UUID) ([]Evidence, error) { - rows, err := s.pool.Query(ctx, ` - SELECT - id, project_id, mitigation_id, verification_plan_id, - file_name, file_path, file_hash, file_size, mime_type, - description, uploaded_by, created_at - FROM iace_evidence WHERE project_id = $1 - ORDER BY created_at DESC - `, projectID) - if err != nil { - return nil, fmt.Errorf("list evidence: %w", err) - } - defer rows.Close() - - var evidence []Evidence - for rows.Next() { - var e Evidence - - err := rows.Scan( - &e.ID, &e.ProjectID, &e.MitigationID, &e.VerificationPlanID, - &e.FileName, &e.FilePath, &e.FileHash, &e.FileSize, &e.MimeType, - &e.Description, &e.UploadedBy, &e.CreatedAt, - ) - if err != nil { - return nil, fmt.Errorf("list evidence scan: %w", err) - } - - evidence = append(evidence, e) - } - - return evidence, nil -} - -// ============================================================================ -// Verification Plan Operations -// ============================================================================ - -// CreateVerificationPlan creates a new verification plan -func (s *Store) CreateVerificationPlan(ctx context.Context, req CreateVerificationPlanRequest) (*VerificationPlan, error) { - vp := &VerificationPlan{ - ID: uuid.New(), - ProjectID: req.ProjectID, - HazardID: req.HazardID, - MitigationID: req.MitigationID, - Title: req.Title, - Description: req.Description, - AcceptanceCriteria: req.AcceptanceCriteria, - Method: req.Method, - Status: "planned", - CreatedAt: time.Now().UTC(), - UpdatedAt: time.Now().UTC(), - } - - _, err := s.pool.Exec(ctx, ` - INSERT INTO iace_verification_plans ( - id, project_id, hazard_id, mitigation_id, - title, description, acceptance_criteria, method, - status, result, completed_at, completed_by, - created_at, updated_at - ) VALUES ( - $1, $2, $3, $4, - $5, $6, $7, $8, - $9, $10, $11, $12, - $13, $14 - ) - `, - vp.ID, vp.ProjectID, vp.HazardID, vp.MitigationID, - vp.Title, vp.Description, vp.AcceptanceCriteria, string(vp.Method), - vp.Status, "", nil, uuid.Nil, - vp.CreatedAt, vp.UpdatedAt, - ) - if err != nil { - return nil, fmt.Errorf("create verification plan: %w", err) - } - - return vp, nil -} - -// UpdateVerificationPlan updates a verification plan with a dynamic set of fields -func (s *Store) UpdateVerificationPlan(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*VerificationPlan, error) { - if len(updates) == 0 { - return s.getVerificationPlan(ctx, id) - } - - query := "UPDATE iace_verification_plans SET updated_at = NOW()" - args := []interface{}{id} - argIdx := 2 - - for key, val := range updates { - switch key { - case "title", "description", "acceptance_criteria", "result", "status": - query += fmt.Sprintf(", %s = $%d", key, argIdx) - args = append(args, val) - argIdx++ - case "method": - query += fmt.Sprintf(", method = $%d", argIdx) - args = append(args, val) - argIdx++ - } - } - - query += " WHERE id = $1" - - _, err := s.pool.Exec(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("update verification plan: %w", err) - } - - return s.getVerificationPlan(ctx, id) -} - -// CompleteVerification marks a verification plan as completed -func (s *Store) CompleteVerification(ctx context.Context, id uuid.UUID, result string, completedBy string) error { - now := time.Now().UTC() - completedByUUID, err := uuid.Parse(completedBy) - if err != nil { - return fmt.Errorf("invalid completed_by UUID: %w", err) - } - - _, err = s.pool.Exec(ctx, ` - UPDATE iace_verification_plans SET - status = 'completed', - result = $2, - completed_at = $3, - completed_by = $4, - updated_at = $3 - WHERE id = $1 - `, id, result, now, completedByUUID) - if err != nil { - return fmt.Errorf("complete verification: %w", err) - } - - return nil -} - -// ListVerificationPlans lists all verification plans for a project -func (s *Store) ListVerificationPlans(ctx context.Context, projectID uuid.UUID) ([]VerificationPlan, error) { - rows, err := s.pool.Query(ctx, ` - SELECT - id, project_id, hazard_id, mitigation_id, - title, description, acceptance_criteria, method, - status, result, completed_at, completed_by, - created_at, updated_at - FROM iace_verification_plans WHERE project_id = $1 - ORDER BY created_at ASC - `, projectID) - if err != nil { - return nil, fmt.Errorf("list verification plans: %w", err) - } - defer rows.Close() - - var plans []VerificationPlan - for rows.Next() { - var vp VerificationPlan - var method string - - err := rows.Scan( - &vp.ID, &vp.ProjectID, &vp.HazardID, &vp.MitigationID, - &vp.Title, &vp.Description, &vp.AcceptanceCriteria, &method, - &vp.Status, &vp.Result, &vp.CompletedAt, &vp.CompletedBy, - &vp.CreatedAt, &vp.UpdatedAt, - ) - if err != nil { - return nil, fmt.Errorf("list verification plans scan: %w", err) - } - - vp.Method = VerificationMethod(method) - plans = append(plans, vp) - } - - return plans, nil -} - -// getVerificationPlan is a helper to fetch a single verification plan by ID -func (s *Store) getVerificationPlan(ctx context.Context, id uuid.UUID) (*VerificationPlan, error) { - var vp VerificationPlan - var method string - - err := s.pool.QueryRow(ctx, ` - SELECT - id, project_id, hazard_id, mitigation_id, - title, description, acceptance_criteria, method, - status, result, completed_at, completed_by, - created_at, updated_at - FROM iace_verification_plans WHERE id = $1 - `, id).Scan( - &vp.ID, &vp.ProjectID, &vp.HazardID, &vp.MitigationID, - &vp.Title, &vp.Description, &vp.AcceptanceCriteria, &method, - &vp.Status, &vp.Result, &vp.CompletedAt, &vp.CompletedBy, - &vp.CreatedAt, &vp.UpdatedAt, - ) - if err == pgx.ErrNoRows { - return nil, nil - } - if err != nil { - return nil, fmt.Errorf("get verification plan: %w", err) - } - - vp.Method = VerificationMethod(method) - return &vp, nil -} - -// ============================================================================ -// Reference Data Operations -// ============================================================================ - -// ListLifecyclePhases returns all 12 lifecycle phases with DE/EN labels -func (s *Store) ListLifecyclePhases(ctx context.Context) ([]LifecyclePhaseInfo, error) { - rows, err := s.pool.Query(ctx, ` - SELECT id, label_de, label_en, sort_order - FROM iace_lifecycle_phases - ORDER BY sort_order ASC - `) - if err != nil { - return nil, fmt.Errorf("list lifecycle phases: %w", err) - } - defer rows.Close() - - var phases []LifecyclePhaseInfo - for rows.Next() { - var p LifecyclePhaseInfo - if err := rows.Scan(&p.ID, &p.LabelDE, &p.LabelEN, &p.Sort); err != nil { - return nil, fmt.Errorf("list lifecycle phases scan: %w", err) - } - phases = append(phases, p) - } - return phases, nil -} - -// ListRoles returns all affected person roles from the reference table -func (s *Store) ListRoles(ctx context.Context) ([]RoleInfo, error) { - rows, err := s.pool.Query(ctx, ` - SELECT id, label_de, label_en, sort_order - FROM iace_roles - ORDER BY sort_order ASC - `) - if err != nil { - return nil, fmt.Errorf("list roles: %w", err) - } - defer rows.Close() - - var roles []RoleInfo - for rows.Next() { - var r RoleInfo - if err := rows.Scan(&r.ID, &r.LabelDE, &r.LabelEN, &r.Sort); err != nil { - return nil, fmt.Errorf("list roles scan: %w", err) - } - roles = append(roles, r) - } - return roles, nil -} - -// ListEvidenceTypes returns all evidence types from the reference table -func (s *Store) ListEvidenceTypes(ctx context.Context) ([]EvidenceTypeInfo, error) { - rows, err := s.pool.Query(ctx, ` - SELECT id, category, label_de, label_en, sort_order - FROM iace_evidence_types - ORDER BY sort_order ASC - `) - if err != nil { - return nil, fmt.Errorf("list evidence types: %w", err) - } - defer rows.Close() - - var types []EvidenceTypeInfo - for rows.Next() { - var e EvidenceTypeInfo - if err := rows.Scan(&e.ID, &e.Category, &e.LabelDE, &e.LabelEN, &e.Sort); err != nil { - return nil, fmt.Errorf("list evidence types scan: %w", err) - } - types = append(types, e) - } - return types, nil -} diff --git a/ai-compliance-sdk/internal/iace/store_projects.go b/ai-compliance-sdk/internal/iace/store_projects.go index cd2860c..a2d0712 100644 --- a/ai-compliance-sdk/internal/iace/store_projects.go +++ b/ai-compliance-sdk/internal/iace/store_projects.go @@ -231,299 +231,3 @@ func (s *Store) UpdateProjectCompleteness(ctx context.Context, id uuid.UUID, sco return nil } -// ============================================================================ -// Component CRUD Operations -// ============================================================================ - -// CreateComponent creates a new component within a project -func (s *Store) CreateComponent(ctx context.Context, req CreateComponentRequest) (*Component, error) { - comp := &Component{ - ID: uuid.New(), - ProjectID: req.ProjectID, - ParentID: req.ParentID, - Name: req.Name, - ComponentType: req.ComponentType, - Version: req.Version, - Description: req.Description, - IsSafetyRelevant: req.IsSafetyRelevant, - IsNetworked: req.IsNetworked, - CreatedAt: time.Now().UTC(), - UpdatedAt: time.Now().UTC(), - } - - _, err := s.pool.Exec(ctx, ` - INSERT INTO iace_components ( - id, project_id, parent_id, name, component_type, - version, description, is_safety_relevant, is_networked, - metadata, sort_order, created_at, updated_at - ) VALUES ( - $1, $2, $3, $4, $5, - $6, $7, $8, $9, - $10, $11, $12, $13 - ) - `, - comp.ID, comp.ProjectID, comp.ParentID, comp.Name, string(comp.ComponentType), - comp.Version, comp.Description, comp.IsSafetyRelevant, comp.IsNetworked, - comp.Metadata, comp.SortOrder, comp.CreatedAt, comp.UpdatedAt, - ) - if err != nil { - return nil, fmt.Errorf("create component: %w", err) - } - - return comp, nil -} - -// GetComponent retrieves a component by ID -func (s *Store) GetComponent(ctx context.Context, id uuid.UUID) (*Component, error) { - var c Component - var compType string - var metadata []byte - - err := s.pool.QueryRow(ctx, ` - SELECT - id, project_id, parent_id, name, component_type, - version, description, is_safety_relevant, is_networked, - metadata, sort_order, created_at, updated_at - FROM iace_components WHERE id = $1 - `, id).Scan( - &c.ID, &c.ProjectID, &c.ParentID, &c.Name, &compType, - &c.Version, &c.Description, &c.IsSafetyRelevant, &c.IsNetworked, - &metadata, &c.SortOrder, &c.CreatedAt, &c.UpdatedAt, - ) - if err == pgx.ErrNoRows { - return nil, nil - } - if err != nil { - return nil, fmt.Errorf("get component: %w", err) - } - - c.ComponentType = ComponentType(compType) - json.Unmarshal(metadata, &c.Metadata) - - return &c, nil -} - -// ListComponents lists all components for a project -func (s *Store) ListComponents(ctx context.Context, projectID uuid.UUID) ([]Component, error) { - rows, err := s.pool.Query(ctx, ` - SELECT - id, project_id, parent_id, name, component_type, - version, description, is_safety_relevant, is_networked, - metadata, sort_order, created_at, updated_at - FROM iace_components WHERE project_id = $1 - ORDER BY sort_order ASC, created_at ASC - `, projectID) - if err != nil { - return nil, fmt.Errorf("list components: %w", err) - } - defer rows.Close() - - var components []Component - for rows.Next() { - var c Component - var compType string - var metadata []byte - - err := rows.Scan( - &c.ID, &c.ProjectID, &c.ParentID, &c.Name, &compType, - &c.Version, &c.Description, &c.IsSafetyRelevant, &c.IsNetworked, - &metadata, &c.SortOrder, &c.CreatedAt, &c.UpdatedAt, - ) - if err != nil { - return nil, fmt.Errorf("list components scan: %w", err) - } - - c.ComponentType = ComponentType(compType) - json.Unmarshal(metadata, &c.Metadata) - - components = append(components, c) - } - - return components, nil -} - -// UpdateComponent updates a component with a dynamic set of fields -func (s *Store) UpdateComponent(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*Component, error) { - if len(updates) == 0 { - return s.GetComponent(ctx, id) - } - - query := "UPDATE iace_components SET updated_at = NOW()" - args := []interface{}{id} - argIdx := 2 - - for key, val := range updates { - switch key { - case "name", "version", "description": - query += fmt.Sprintf(", %s = $%d", key, argIdx) - args = append(args, val) - argIdx++ - case "component_type": - query += fmt.Sprintf(", component_type = $%d", argIdx) - args = append(args, val) - argIdx++ - case "is_safety_relevant": - query += fmt.Sprintf(", is_safety_relevant = $%d", argIdx) - args = append(args, val) - argIdx++ - case "is_networked": - query += fmt.Sprintf(", is_networked = $%d", argIdx) - args = append(args, val) - argIdx++ - case "sort_order": - query += fmt.Sprintf(", sort_order = $%d", argIdx) - args = append(args, val) - argIdx++ - case "metadata": - metaJSON, _ := json.Marshal(val) - query += fmt.Sprintf(", metadata = $%d", argIdx) - args = append(args, metaJSON) - argIdx++ - case "parent_id": - query += fmt.Sprintf(", parent_id = $%d", argIdx) - args = append(args, val) - argIdx++ - } - } - - query += " WHERE id = $1" - - _, err := s.pool.Exec(ctx, query, args...) - if err != nil { - return nil, fmt.Errorf("update component: %w", err) - } - - return s.GetComponent(ctx, id) -} - -// DeleteComponent deletes a component by ID -func (s *Store) DeleteComponent(ctx context.Context, id uuid.UUID) error { - _, err := s.pool.Exec(ctx, "DELETE FROM iace_components WHERE id = $1", id) - if err != nil { - return fmt.Errorf("delete component: %w", err) - } - return nil -} - -// ============================================================================ -// Classification Operations -// ============================================================================ - -// UpsertClassification inserts or updates a regulatory classification for a project -func (s *Store) UpsertClassification(ctx context.Context, projectID uuid.UUID, regulation RegulationType, result string, riskLevel string, confidence float64, reasoning string, ragSources, requirements json.RawMessage) (*RegulatoryClassification, error) { - id := uuid.New() - now := time.Now().UTC() - - _, err := s.pool.Exec(ctx, ` - INSERT INTO iace_classifications ( - id, project_id, regulation, classification_result, - risk_level, confidence, reasoning, - rag_sources, requirements, - created_at, updated_at - ) VALUES ( - $1, $2, $3, $4, - $5, $6, $7, - $8, $9, - $10, $11 - ) - ON CONFLICT (project_id, regulation) - DO UPDATE SET - classification_result = EXCLUDED.classification_result, - risk_level = EXCLUDED.risk_level, - confidence = EXCLUDED.confidence, - reasoning = EXCLUDED.reasoning, - rag_sources = EXCLUDED.rag_sources, - requirements = EXCLUDED.requirements, - updated_at = EXCLUDED.updated_at - `, - id, projectID, string(regulation), result, - riskLevel, confidence, reasoning, - ragSources, requirements, - now, now, - ) - if err != nil { - return nil, fmt.Errorf("upsert classification: %w", err) - } - - // Retrieve the upserted row (may have kept the original ID on conflict) - return s.getClassificationByProjectAndRegulation(ctx, projectID, regulation) -} - -// getClassificationByProjectAndRegulation is a helper to fetch a single classification -func (s *Store) getClassificationByProjectAndRegulation(ctx context.Context, projectID uuid.UUID, regulation RegulationType) (*RegulatoryClassification, error) { - var c RegulatoryClassification - var reg, rl string - var ragSources, requirements []byte - - err := s.pool.QueryRow(ctx, ` - SELECT - id, project_id, regulation, classification_result, - risk_level, confidence, reasoning, - rag_sources, requirements, - created_at, updated_at - FROM iace_classifications - WHERE project_id = $1 AND regulation = $2 - `, projectID, string(regulation)).Scan( - &c.ID, &c.ProjectID, ®, &c.ClassificationResult, - &rl, &c.Confidence, &c.Reasoning, - &ragSources, &requirements, - &c.CreatedAt, &c.UpdatedAt, - ) - if err == pgx.ErrNoRows { - return nil, nil - } - if err != nil { - return nil, fmt.Errorf("get classification: %w", err) - } - - c.Regulation = RegulationType(reg) - c.RiskLevel = RiskLevel(rl) - json.Unmarshal(ragSources, &c.RAGSources) - json.Unmarshal(requirements, &c.Requirements) - - return &c, nil -} - -// GetClassifications retrieves all classifications for a project -func (s *Store) GetClassifications(ctx context.Context, projectID uuid.UUID) ([]RegulatoryClassification, error) { - rows, err := s.pool.Query(ctx, ` - SELECT - id, project_id, regulation, classification_result, - risk_level, confidence, reasoning, - rag_sources, requirements, - created_at, updated_at - FROM iace_classifications - WHERE project_id = $1 - ORDER BY regulation ASC - `, projectID) - if err != nil { - return nil, fmt.Errorf("get classifications: %w", err) - } - defer rows.Close() - - var classifications []RegulatoryClassification - for rows.Next() { - var c RegulatoryClassification - var reg, rl string - var ragSources, requirements []byte - - err := rows.Scan( - &c.ID, &c.ProjectID, ®, &c.ClassificationResult, - &rl, &c.Confidence, &c.Reasoning, - &ragSources, &requirements, - &c.CreatedAt, &c.UpdatedAt, - ) - if err != nil { - return nil, fmt.Errorf("get classifications scan: %w", err) - } - - c.Regulation = RegulationType(reg) - c.RiskLevel = RiskLevel(rl) - json.Unmarshal(ragSources, &c.RAGSources) - json.Unmarshal(requirements, &c.Requirements) - - classifications = append(classifications, c) - } - - return classifications, nil -}