feat(iace): Sprint 4A — Residual Risk Modeling (Suppression Engine)
RiskReduction Struct + automatische Risk Trajectory:
- RiskReduction{SeverityDelta, ExposureDelta, ProbabilityDelta} auf ProtectiveMeasureEntry
- CalculateRiskTrajectory() in engine.go: berechnet schrittweise Risikoreduktion
entlang ISO 12100 Hierarchie (design → protection → information)
- Kumulative Deltas pro Stufe, Clamp auf Minimum 1
- RiskTrajectoryStep mit Stage, S/E/P, Score, Level, IsAcceptable
101 Massnahmen mit RiskReduction-Profilen versehen:
- Design/Geometry (M001-M010): S-1, E-1 (Gefahrstelle eliminiert)
- Design/Force (M011-M022): S-2 (Energie/Kraft reduziert)
- Design/Control (M039-M050): P-2 (sichere Steuerung)
- Protection/Guards (M061-M072): E-2 (Zugang verhindert)
- Protection/Electro (M073-M079): E-1, P-1 (Erkennung)
- Protection/Safety (M105-M113): P-2 (sichere SPS)
- Protection/Monitoring (M114-M120): P-1 (Frueerkennung)
- Protection/Cyber (M121-M130): P-1
- Information/Training (M161-M168): P-1
- Information/PPE (M169-M175): S-1
8 neue Tests: NoMeasures, DesignReduce, FullHierarchy, ClampMin1,
OnlyProtection, WithoutReduction, MandatoryAsProtective, LibraryCount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -123,6 +123,73 @@ func (e *RiskEngine) CalculateResidualRisk(severity, exposure, probability int,
|
||||
return inherent * (1 - cEff)
|
||||
}
|
||||
|
||||
// RiskTrajectoryStep represents one step in the risk reduction trajectory.
|
||||
type RiskTrajectoryStep struct {
|
||||
Stage string `json:"stage"` // "inherent", "after_design", "after_protection", "after_information"
|
||||
Severity int `json:"severity"`
|
||||
Exposure int `json:"exposure"`
|
||||
Probability int `json:"probability"`
|
||||
RiskScore float64 `json:"risk_score"`
|
||||
RiskLevel string `json:"risk_level"`
|
||||
IsAcceptable bool `json:"is_acceptable"`
|
||||
}
|
||||
|
||||
// CalculateRiskTrajectory computes the step-by-step risk reduction when measures
|
||||
// are applied in ISO 12100 hierarchy order: design → protection → information.
|
||||
// Each measure's RiskReduction deltas are cumulated per stage.
|
||||
// Parameters are clamped to minimum 1 after each stage.
|
||||
func (e *RiskEngine) CalculateRiskTrajectory(
|
||||
severity, exposure, probability int,
|
||||
measures []ProtectiveMeasureEntry,
|
||||
) []RiskTrajectoryStep {
|
||||
s, ex, p := clamp(severity, 1, 5), clamp(exposure, 1, 5), clamp(probability, 1, 5)
|
||||
|
||||
// Inherent risk (no measures)
|
||||
steps := []RiskTrajectoryStep{{
|
||||
Stage: "inherent", Severity: s, Exposure: ex, Probability: p,
|
||||
RiskScore: float64(s * ex * p),
|
||||
RiskLevel: string(e.DetermineRiskLevel(float64(s * ex * p))),
|
||||
}}
|
||||
|
||||
// Group measures by reduction type in hierarchy order
|
||||
stages := []struct {
|
||||
name string
|
||||
rtype string
|
||||
}{
|
||||
{"after_design", "design"},
|
||||
{"after_protection", "protection"},
|
||||
{"after_protection", "protective"}, // MandatoryNorm measures use "protective"
|
||||
{"after_information", "information"},
|
||||
}
|
||||
|
||||
for _, stage := range stages {
|
||||
dS, dE, dP := 0, 0, 0
|
||||
for _, m := range measures {
|
||||
if m.ReductionType != stage.rtype || m.RiskReduction == nil {
|
||||
continue
|
||||
}
|
||||
dS += m.RiskReduction.SeverityDelta
|
||||
dE += m.RiskReduction.ExposureDelta
|
||||
dP += m.RiskReduction.ProbabilityDelta
|
||||
}
|
||||
if dS == 0 && dE == 0 && dP == 0 {
|
||||
continue
|
||||
}
|
||||
s = clamp(s+dS, 1, 5)
|
||||
ex = clamp(ex+dE, 1, 5)
|
||||
p = clamp(p+dP, 1, 5)
|
||||
score := float64(s * ex * p)
|
||||
level := e.DetermineRiskLevel(score)
|
||||
steps = append(steps, RiskTrajectoryStep{
|
||||
Stage: stage.name, Severity: s, Exposure: ex, Probability: p,
|
||||
RiskScore: score, RiskLevel: string(level),
|
||||
IsAcceptable: score < 15,
|
||||
})
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
||||
|
||||
// DetermineRiskLevel classifies the residual risk into a RiskLevel category.
|
||||
//
|
||||
// Thresholds:
|
||||
|
||||
Reference in New Issue
Block a user