feat(ai-sdk): keep cyber/AI hazards out of the traditional CE hazard log
CI / detect-changes (push) Successful in 5s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Successful in 5s
CI / validate-canonical-controls (push) Successful in 2s
CI / loc-budget (push) Successful in 16s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Successful in 57s
CI / iace-gt-coverage (push) Successful in 18s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / detect-changes (push) Successful in 5s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Successful in 5s
CI / validate-canonical-controls (push) Successful in 2s
CI / loc-budget (push) Successful in 16s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Successful in 57s
CI / iace-gt-coverage (push) Successful in 18s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
InitializeProject created hazards for every matched pattern, so native cybersecurity/AI topics (unauthorized access, firmware manipulation, missing SBOM, ...) mixed into the ISO 12100 hazard log. Route the security categories (frontend groups I. Cyber/Netzwerk + J. KI) to the CRA module instead — generically for EVERY project, enforced centrally in InitializeProject. The split is by the nature of the hazard, not the component: functional-safety control faults stay in CE (software faults, lost safety functions, config errors, bus failures, botched updates) — they are random/systematic faults, not attacks, and feed the CRA safety-function bridge. This holds whether the controller is a bought-in CE-marked PLC or the manufacturer's own control. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -211,6 +211,13 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, cat := range mp.HazardCats {
|
for _, cat := range mp.HazardCats {
|
||||||
|
// Native cyber/AI categories (frontend groups I+J) belong to the
|
||||||
|
// CRA module, not the traditional CE (ISO 12100) hazard log.
|
||||||
|
// Enforced centrally here so it holds for EVERY project.
|
||||||
|
if isCyberSecurityCategory(cat) {
|
||||||
|
fmt.Printf("CYBER-SKIP: cat=%s pattern=%s — routed to CRA module\n", cat, mp.PatternID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
maxForCat := categoryHazardCap(cat, len(comps))
|
maxForCat := categoryHazardCap(cat, len(comps))
|
||||||
if catCount[cat] >= maxForCat {
|
if catCount[cat] >= maxForCat {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
// Safety/Security separation for the IACE hazard log.
|
||||||
|
//
|
||||||
|
// The traditional CE risk assessment (Maschinenrichtlinie / EN ISO 12100) and
|
||||||
|
// the cybersecurity assessment (Cyber Resilience Act) are two distinct steps.
|
||||||
|
// IACE owns the traditional, physical + functional-safety hazards; the CRA
|
||||||
|
// module (/sdk/iace/{id}/cra) owns the native cyber/AI topics and re-examines
|
||||||
|
// which safety functions a cyber attack can re-open (see iace-safety-bridge).
|
||||||
|
//
|
||||||
|
// The split is by the NATURE of the hazard, not by the component: a control
|
||||||
|
// fault, bus failure or botched update is FUNCTIONAL safety (random/systematic
|
||||||
|
// fault) and stays in CE — independent of whether the controller is a bought-in
|
||||||
|
// CE-marked PLC or the manufacturer's own embedded control. Only the security
|
||||||
|
// PROPERTIES against malicious actors (access control, firmware/update
|
||||||
|
// integrity, SBOM, vulnerability handling, default passwords) are CRA.
|
||||||
|
//
|
||||||
|
// Functional-safety control categories (software_control, software_fault,
|
||||||
|
// safety_function_failure, configuration_error, communication_failure,
|
||||||
|
// update_failure, sensor_fault, …) therefore intentionally STAY in IACE — they
|
||||||
|
// are the safety functions whose loss the CRA bridge re-examines.
|
||||||
|
//
|
||||||
|
// Enforced centrally in InitializeProject so it holds for EVERY project.
|
||||||
|
var nativeCyberSecurityCategories = map[string]bool{
|
||||||
|
// I. Cyber / Netzwerk — security against malicious actors
|
||||||
|
"unauthorized_access": true,
|
||||||
|
"firmware_corruption": true,
|
||||||
|
"cyber_resilience": true,
|
||||||
|
"logging_audit_failure": true,
|
||||||
|
"cyber_network": true,
|
||||||
|
"sensor_spoofing": true,
|
||||||
|
// J. KI-spezifisch
|
||||||
|
"ai_specific": true,
|
||||||
|
"ai_misclassification": true,
|
||||||
|
"false_classification": true,
|
||||||
|
"model_drift": true,
|
||||||
|
"data_poisoning": true,
|
||||||
|
"unintended_bias": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// isCyberSecurityCategory reports whether a hazard category is a native cyber/AI
|
||||||
|
// topic that belongs to the CRA module rather than the traditional CE hazard log.
|
||||||
|
func isCyberSecurityCategory(category string) bool {
|
||||||
|
return nativeCyberSecurityCategories[category]
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIsCyberSecurityCategory_RoutedToCRA(t *testing.T) {
|
||||||
|
cyber := []string{
|
||||||
|
"unauthorized_access", "firmware_corruption", "cyber_resilience",
|
||||||
|
"logging_audit_failure", "cyber_network", "sensor_spoofing",
|
||||||
|
"ai_specific", "ai_misclassification", "false_classification",
|
||||||
|
"model_drift", "data_poisoning", "unintended_bias",
|
||||||
|
}
|
||||||
|
for _, c := range cyber {
|
||||||
|
if !isCyberSecurityCategory(c) {
|
||||||
|
t.Errorf("category %q must be routed to the CRA module, not the traditional IACE log", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsCyberSecurityCategory_StaysInIACE(t *testing.T) {
|
||||||
|
// Physical + functional-safety categories must remain in the traditional CE
|
||||||
|
// hazard log. communication_failure (bus failure -> loss of control) and
|
||||||
|
// update_failure (botched update -> lost safety function) are FUNCTIONAL
|
||||||
|
// faults, not attacks, so they stay too.
|
||||||
|
keep := []string{
|
||||||
|
"mechanical_hazard", "electrical_hazard", "thermal_hazard",
|
||||||
|
"pneumatic_hydraulic", "noise_vibration", "ergonomic_hazard",
|
||||||
|
"material_environmental", "chemical_risk", "fire_explosion",
|
||||||
|
"software_control", "software_fault", "safety_function_failure",
|
||||||
|
"configuration_error", "sensor_fault", "hmi_error",
|
||||||
|
"communication_failure", "update_failure",
|
||||||
|
}
|
||||||
|
for _, c := range keep {
|
||||||
|
if isCyberSecurityCategory(c) {
|
||||||
|
t.Errorf("category %q must stay in the traditional IACE log, not be routed to CRA", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user