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 <noreply@anthropic.com>
This commit is contained in:
@@ -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()...)
|
||||
|
||||
302
ai-compliance-sdk/internal/iace/hazard_library_ai_fw.go
Normal file
302
ai-compliance-sdk/internal/iace/hazard_library_ai_fw.go
Normal file
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
309
ai-compliance-sdk/internal/iace/store_components.go
Normal file
309
ai-compliance-sdk/internal/iace/store_components.go
Normal file
@@ -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
|
||||
}
|
||||
321
ai-compliance-sdk/internal/iace/store_evidence.go
Normal file
321
ai-compliance-sdk/internal/iace/store_evidence.go
Normal file
@@ -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
|
||||
}
|
||||
172
ai-compliance-sdk/internal/iace/store_hazard_library.go
Normal file
172
ai-compliance-sdk/internal/iace/store_hazard_library.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user