refactor(go): split training/store, ucca/rules, ucca_handlers, document_export under 500 LOC

Each of the four oversized files (training/store.go 1569 LOC, ucca/rules.go 1231 LOC,
ucca_handlers.go 1135 LOC, document_export.go 1101 LOC) is split by logical group
into same-package files, all under the 500-line hard cap. Zero behavior changes,
no renamed exported symbols. Also fixed pre-existing hazard_library split (missing
functions and duplicate UUID keys from a prior session).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-19 09:29:54 +02:00
parent 3f306fb6f0
commit 9f96061631
36 changed files with 9416 additions and 9365 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,161 @@
package iace
import (
"archive/zip"
"bytes"
"encoding/json"
"fmt"
"strings"
"time"
)
// ExportDOCX generates a minimal DOCX file containing the CE technical file sections
func (e *DocumentExporter) ExportDOCX(
project *Project,
sections []TechFileSection,
) ([]byte, error) {
if project == nil {
return nil, fmt.Errorf("project must not be nil")
}
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
contentTypes := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>`
if err := addZipEntry(zw, "[Content_Types].xml", contentTypes); err != nil {
return nil, fmt.Errorf("failed to write [Content_Types].xml: %w", err)
}
rels := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>`
if err := addZipEntry(zw, "_rels/.rels", rels); err != nil {
return nil, fmt.Errorf("failed to write _rels/.rels: %w", err)
}
docRels := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
</Relationships>`
if err := addZipEntry(zw, "word/_rels/document.xml.rels", docRels); err != nil {
return nil, fmt.Errorf("failed to write word/_rels/document.xml.rels: %w", err)
}
docXML := e.buildDocumentXML(project, sections)
if err := addZipEntry(zw, "word/document.xml", docXML); err != nil {
return nil, fmt.Errorf("failed to write word/document.xml: %w", err)
}
if err := zw.Close(); err != nil {
return nil, fmt.Errorf("failed to close ZIP: %w", err)
}
return buf.Bytes(), nil
}
func (e *DocumentExporter) buildDocumentXML(project *Project, sections []TechFileSection) string {
var body strings.Builder
body.WriteString(docxHeading(fmt.Sprintf("CE-Akte: %s", project.MachineName), 1))
metaLines := []string{
fmt.Sprintf("Hersteller: %s", project.Manufacturer),
fmt.Sprintf("Maschinentyp: %s", project.MachineType),
}
if project.CEMarkingTarget != "" {
metaLines = append(metaLines, fmt.Sprintf("CE-Kennzeichnungsziel: %s", project.CEMarkingTarget))
}
metaLines = append(metaLines,
fmt.Sprintf("Status: %s", project.Status),
fmt.Sprintf("Datum: %s", time.Now().Format("02.01.2006")),
)
for _, line := range metaLines {
body.WriteString(docxParagraph(line, false))
}
if project.Description != "" {
body.WriteString(docxParagraph("", false))
body.WriteString(docxParagraph(project.Description, true))
}
for _, section := range sections {
body.WriteString(docxHeading(section.Title, 2))
body.WriteString(docxParagraph(
fmt.Sprintf("Typ: %s | Status: %s | Version: %d",
section.SectionType, string(section.Status), section.Version),
true,
))
if section.Content != "" {
for _, line := range strings.Split(section.Content, "\n") {
body.WriteString(docxParagraph(line, false))
}
} else {
body.WriteString(docxParagraph("(Kein Inhalt vorhanden)", true))
}
}
body.WriteString(docxParagraph("", false))
body.WriteString(docxParagraph(
fmt.Sprintf("Generiert am %s mit BreakPilot AI Compliance SDK",
time.Now().Format("02.01.2006 15:04")),
true,
))
return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"
xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
mc:Ignorable="w14 wp14">
<w:body>
%s
</w:body>
</w:document>`, body.String())
}
// ExportJSON returns a JSON representation of the project export data
func (e *DocumentExporter) ExportJSON(
project *Project,
sections []TechFileSection,
hazards []Hazard,
assessments []RiskAssessment,
mitigations []Mitigation,
classifications []RegulatoryClassification,
) ([]byte, error) {
if project == nil {
return nil, fmt.Errorf("project must not be nil")
}
payload := map[string]interface{}{
"project": project,
"sections": sections,
"hazards": hazards,
"assessments": assessments,
"mitigations": mitigations,
"classifications": classifications,
"exported_at": time.Now().UTC().Format(time.RFC3339),
"format_version": "1.0",
}
data, err := json.MarshalIndent(payload, "", " ")
if err != nil {
return nil, fmt.Errorf("failed to marshal JSON: %w", err)
}
return data, nil
}

View File

@@ -0,0 +1,261 @@
package iace
import (
"fmt"
"github.com/xuri/excelize/v2"
)
// ExportExcel generates an XLSX workbook with project data across multiple sheets
func (e *DocumentExporter) ExportExcel(
project *Project,
sections []TechFileSection,
hazards []Hazard,
assessments []RiskAssessment,
mitigations []Mitigation,
) ([]byte, error) {
if project == nil {
return nil, fmt.Errorf("project must not be nil")
}
f := excelize.NewFile()
defer f.Close()
overviewSheet := "Uebersicht"
f.SetSheetName("Sheet1", overviewSheet)
e.xlsxOverview(f, overviewSheet, project)
hazardSheet := "Gefaehrdungsprotokoll"
f.NewSheet(hazardSheet)
e.xlsxHazardLog(f, hazardSheet, hazards, assessments)
mitigationSheet := "Massnahmen"
f.NewSheet(mitigationSheet)
e.xlsxMitigations(f, mitigationSheet, mitigations)
matrixSheet := "Risikomatrix"
f.NewSheet(matrixSheet)
e.xlsxRiskMatrix(f, matrixSheet, assessments)
sectionSheet := "Sektionen"
f.NewSheet(sectionSheet)
e.xlsxSections(f, sectionSheet, sections)
buf, err := f.WriteToBuffer()
if err != nil {
return nil, fmt.Errorf("failed to write Excel: %w", err)
}
return buf.Bytes(), nil
}
func (e *DocumentExporter) xlsxOverview(f *excelize.File, sheet string, project *Project) {
headerStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true, Size: 11},
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"D9E1F2"}},
})
f.SetColWidth(sheet, "A", "A", 30)
f.SetColWidth(sheet, "B", "B", 50)
rows := [][]string{
{"Eigenschaft", "Wert"},
{"Maschinenname", project.MachineName},
{"Maschinentyp", project.MachineType},
{"Hersteller", project.Manufacturer},
{"Beschreibung", project.Description},
{"CE-Kennzeichnungsziel", project.CEMarkingTarget},
{"Projektstatus", string(project.Status)},
{"Vollstaendigkeits-Score", fmt.Sprintf("%.1f%%", project.CompletenessScore*100)},
{"Erstellt am", project.CreatedAt.Format("02.01.2006 15:04")},
{"Aktualisiert am", project.UpdatedAt.Format("02.01.2006 15:04")},
}
for i, row := range rows {
rowNum := i + 1
f.SetCellValue(sheet, cellRef("A", rowNum), row[0])
f.SetCellValue(sheet, cellRef("B", rowNum), row[1])
if i == 0 {
f.SetCellStyle(sheet, cellRef("A", rowNum), cellRef("B", rowNum), headerStyle)
}
}
}
func (e *DocumentExporter) xlsxHazardLog(f *excelize.File, sheet string, hazards []Hazard, assessments []RiskAssessment) {
headerStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
Alignment: &excelize.Alignment{Horizontal: "center"},
})
headers := []string{"Nr", "Name", "Kategorie", "Beschreibung", "S", "E", "P", "A",
"Inherent Risk", "C_eff", "Residual Risk", "Risk Level", "Akzeptabel"}
colWidths := map[string]float64{
"A": 6, "B": 25, "C": 20, "D": 35, "E": 8, "F": 8, "G": 8, "H": 8,
"I": 14, "J": 10, "K": 14, "L": 18, "M": 12,
}
for col, w := range colWidths {
f.SetColWidth(sheet, col, col, w)
}
cols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"}
for i, h := range headers {
f.SetCellValue(sheet, cellRef(cols[i], 1), h)
}
f.SetCellStyle(sheet, "A1", cellRef(cols[len(cols)-1], 1), headerStyle)
assessMap := buildAssessmentMap(assessments)
for i, hazard := range hazards {
row := i + 2
a := assessMap[hazard.ID.String()]
f.SetCellValue(sheet, cellRef("A", row), i+1)
f.SetCellValue(sheet, cellRef("B", row), hazard.Name)
f.SetCellValue(sheet, cellRef("C", row), hazard.Category)
f.SetCellValue(sheet, cellRef("D", row), hazard.Description)
if a != nil {
f.SetCellValue(sheet, cellRef("E", row), a.Severity)
f.SetCellValue(sheet, cellRef("F", row), a.Exposure)
f.SetCellValue(sheet, cellRef("G", row), a.Probability)
f.SetCellValue(sheet, cellRef("H", row), a.Avoidance)
f.SetCellValue(sheet, cellRef("I", row), fmt.Sprintf("%.1f", a.InherentRisk))
f.SetCellValue(sheet, cellRef("J", row), fmt.Sprintf("%.2f", a.CEff))
f.SetCellValue(sheet, cellRef("K", row), fmt.Sprintf("%.1f", a.ResidualRisk))
f.SetCellValue(sheet, cellRef("L", row), riskLevelLabel(a.RiskLevel))
acceptStr := "Nein"
if a.IsAcceptable {
acceptStr = "Ja"
}
f.SetCellValue(sheet, cellRef("M", row), acceptStr)
r, g, b := riskLevelColor(a.RiskLevel)
style, _ := f.NewStyle(&excelize.Style{
Fill: excelize.Fill{
Type: "pattern",
Pattern: 1,
Color: []string{rgbHex(r, g, b)},
},
Alignment: &excelize.Alignment{Horizontal: "center"},
})
f.SetCellStyle(sheet, cellRef("L", row), cellRef("L", row), style)
}
}
}
func (e *DocumentExporter) xlsxMitigations(f *excelize.File, sheet string, mitigations []Mitigation) {
headerStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
Alignment: &excelize.Alignment{Horizontal: "center"},
})
headers := []string{"Nr", "Name", "Typ", "Beschreibung", "Status", "Verifikationsmethode", "Ergebnis"}
cols := []string{"A", "B", "C", "D", "E", "F", "G"}
f.SetColWidth(sheet, "A", "A", 6)
f.SetColWidth(sheet, "B", "B", 25)
f.SetColWidth(sheet, "C", "C", 15)
f.SetColWidth(sheet, "D", "D", 35)
f.SetColWidth(sheet, "E", "E", 15)
f.SetColWidth(sheet, "F", "F", 22)
f.SetColWidth(sheet, "G", "G", 25)
for i, h := range headers {
f.SetCellValue(sheet, cellRef(cols[i], 1), h)
}
f.SetCellStyle(sheet, "A1", cellRef(cols[len(cols)-1], 1), headerStyle)
for i, m := range mitigations {
row := i + 2
f.SetCellValue(sheet, cellRef("A", row), i+1)
f.SetCellValue(sheet, cellRef("B", row), m.Name)
f.SetCellValue(sheet, cellRef("C", row), reductionTypeLabel(m.ReductionType))
f.SetCellValue(sheet, cellRef("D", row), m.Description)
f.SetCellValue(sheet, cellRef("E", row), mitigationStatusLabel(m.Status))
f.SetCellValue(sheet, cellRef("F", row), string(m.VerificationMethod))
f.SetCellValue(sheet, cellRef("G", row), m.VerificationResult)
}
}
func (e *DocumentExporter) xlsxRiskMatrix(f *excelize.File, sheet string, assessments []RiskAssessment) {
headerStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
Alignment: &excelize.Alignment{Horizontal: "center"},
})
f.SetColWidth(sheet, "A", "A", 25)
f.SetColWidth(sheet, "B", "B", 12)
f.SetCellValue(sheet, "A1", "Risikostufe")
f.SetCellValue(sheet, "B1", "Anzahl")
f.SetCellStyle(sheet, "A1", "B1", headerStyle)
counts := countByRiskLevel(assessments)
levels := []RiskLevel{
RiskLevelNotAcceptable,
RiskLevelVeryHigh,
RiskLevelCritical,
RiskLevelHigh,
RiskLevelMedium,
RiskLevelLow,
RiskLevelNegligible,
}
row := 2
for _, level := range levels {
count := counts[level]
f.SetCellValue(sheet, cellRef("A", row), riskLevelLabel(level))
f.SetCellValue(sheet, cellRef("B", row), count)
r, g, b := riskLevelColor(level)
style, _ := f.NewStyle(&excelize.Style{
Fill: excelize.Fill{
Type: "pattern",
Pattern: 1,
Color: []string{rgbHex(r, g, b)},
},
})
f.SetCellStyle(sheet, cellRef("A", row), cellRef("B", row), style)
row++
}
totalStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true},
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"D9E1F2"}},
})
f.SetCellValue(sheet, cellRef("A", row), "Gesamt")
f.SetCellValue(sheet, cellRef("B", row), len(assessments))
f.SetCellStyle(sheet, cellRef("A", row), cellRef("B", row), totalStyle)
}
func (e *DocumentExporter) xlsxSections(f *excelize.File, sheet string, sections []TechFileSection) {
headerStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true, Size: 10, Color: "FFFFFF"},
Fill: excelize.Fill{Type: "pattern", Pattern: 1, Color: []string{"4472C4"}},
Alignment: &excelize.Alignment{Horizontal: "center"},
})
f.SetColWidth(sheet, "A", "A", 25)
f.SetColWidth(sheet, "B", "B", 40)
f.SetColWidth(sheet, "C", "C", 15)
headers := []string{"Sektion", "Titel", "Status"}
cols := []string{"A", "B", "C"}
for i, h := range headers {
f.SetCellValue(sheet, cellRef(cols[i], 1), h)
}
f.SetCellStyle(sheet, "A1", cellRef(cols[len(cols)-1], 1), headerStyle)
for i, s := range sections {
row := i + 2
f.SetCellValue(sheet, cellRef("A", row), s.SectionType)
f.SetCellValue(sheet, cellRef("B", row), s.Title)
f.SetCellValue(sheet, cellRef("C", row), string(s.Status))
}
}

View File

@@ -0,0 +1,198 @@
package iace
import (
"archive/zip"
"bytes"
"encoding/xml"
"fmt"
)
// buildAssessmentMap builds a map from hazardID string to the latest RiskAssessment.
func buildAssessmentMap(assessments []RiskAssessment) map[string]*RiskAssessment {
m := make(map[string]*RiskAssessment)
for i := range assessments {
a := &assessments[i]
key := a.HazardID.String()
if existing, ok := m[key]; !ok || a.Version > existing.Version {
m[key] = a
}
}
return m
}
// riskLevelColor returns RGB values for PDF fill color based on risk level.
func riskLevelColor(level RiskLevel) (r, g, b int) {
switch level {
case RiskLevelNotAcceptable:
return 180, 0, 0
case RiskLevelVeryHigh:
return 220, 40, 40
case RiskLevelCritical:
return 255, 80, 80
case RiskLevelHigh:
return 255, 165, 80
case RiskLevelMedium:
return 255, 230, 100
case RiskLevelLow:
return 180, 230, 140
case RiskLevelNegligible:
return 140, 210, 140
default:
return 240, 240, 240
}
}
// riskLevelLabel returns a German display label for a risk level.
func riskLevelLabel(level RiskLevel) string {
switch level {
case RiskLevelNotAcceptable:
return "Nicht akzeptabel"
case RiskLevelVeryHigh:
return "Sehr hoch"
case RiskLevelCritical:
return "Kritisch"
case RiskLevelHigh:
return "Hoch"
case RiskLevelMedium:
return "Mittel"
case RiskLevelLow:
return "Niedrig"
case RiskLevelNegligible:
return "Vernachlaessigbar"
default:
return string(level)
}
}
// reductionTypeLabel returns a German label for a reduction type.
func reductionTypeLabel(rt ReductionType) string {
switch rt {
case ReductionTypeDesign:
return "Konstruktiv"
case ReductionTypeProtective:
return "Schutzmassnahme"
case ReductionTypeInformation:
return "Information"
default:
return string(rt)
}
}
// mitigationStatusLabel returns a German label for a mitigation status.
func mitigationStatusLabel(status MitigationStatus) string {
switch status {
case MitigationStatusPlanned:
return "Geplant"
case MitigationStatusImplemented:
return "Umgesetzt"
case MitigationStatusVerified:
return "Verifiziert"
case MitigationStatusRejected:
return "Abgelehnt"
default:
return string(status)
}
}
// regulationLabel returns a German label for a regulation type.
func regulationLabel(reg RegulationType) string {
switch reg {
case RegulationNIS2:
return "NIS-2 Richtlinie"
case RegulationAIAct:
return "EU AI Act"
case RegulationCRA:
return "Cyber Resilience Act"
case RegulationMachineryRegulation:
return "EU Maschinenverordnung 2023/1230"
default:
return string(reg)
}
}
// escapeXML escapes special XML characters in text content.
func escapeXML(s string) string {
var buf bytes.Buffer
if err := xml.EscapeText(&buf, []byte(s)); err != nil {
return s
}
return buf.String()
}
// countByRiskLevel counts assessments per risk level.
func countByRiskLevel(assessments []RiskAssessment) map[RiskLevel]int {
counts := make(map[RiskLevel]int)
for _, a := range assessments {
counts[a.RiskLevel]++
}
return counts
}
// pdfTruncate truncates a string for PDF cell display.
func pdfTruncate(s string, maxLen int) string {
runes := []rune(s)
if len(runes) <= maxLen {
return s
}
if maxLen <= 3 {
return string(runes[:maxLen])
}
return string(runes[:maxLen-3]) + "..."
}
// cellRef builds an Excel cell reference like "A1", "B12".
func cellRef(col string, row int) string {
return fmt.Sprintf("%s%d", col, row)
}
// rgbHex converts RGB values to a hex color string (without #).
func rgbHex(r, g, b int) string {
return fmt.Sprintf("%02X%02X%02X", r, g, b)
}
// addZipEntry writes a text file into a zip archive.
func addZipEntry(zw *zip.Writer, name, content string) error {
w, err := zw.Create(name)
if err != nil {
return err
}
_, err = w.Write([]byte(content))
return err
}
// docxHeading builds a DOCX paragraph with a heading style.
func docxHeading(text string, level int) string {
sizes := map[int]int{1: 64, 2: 52, 3: 44}
sz, ok := sizes[level]
if !ok {
sz = 44
}
escaped := escapeXML(text)
return fmt.Sprintf(` <w:p>
<w:pPr>
<w:pStyle w:val="Heading%d"/>
<w:spacing w:after="200"/>
</w:pPr>
<w:r>
<w:rPr><w:b/><w:sz w:val="%d"/><w:szCs w:val="%d"/></w:rPr>
<w:t xml:space="preserve">%s</w:t>
</w:r>
</w:p>
`, level, sz, sz, escaped)
}
// docxParagraph builds a DOCX paragraph, optionally italic.
func docxParagraph(text string, italic bool) string {
escaped := escapeXML(text)
rpr := ""
if italic {
rpr = "<w:rPr><w:i/></w:rPr>"
}
return fmt.Sprintf(` <w:p>
<w:r>
%s
<w:t xml:space="preserve">%s</w:t>
</w:r>
</w:p>
`, rpr, escaped)
}

View File

@@ -0,0 +1,313 @@
package iace
import (
"fmt"
"time"
"github.com/jung-kurt/gofpdf"
)
func (e *DocumentExporter) pdfCoverPage(pdf *gofpdf.Fpdf, project *Project) {
pdf.Ln(60)
pdf.SetFont("Helvetica", "B", 28)
pdf.SetTextColor(0, 0, 0)
pdf.CellFormat(0, 15, "CE-Technische Akte", "", 1, "C", false, 0, "")
pdf.Ln(5)
pdf.SetFont("Helvetica", "B", 22)
pdf.CellFormat(0, 12, project.MachineName, "", 1, "C", false, 0, "")
pdf.Ln(15)
pdf.SetFont("Helvetica", "", 12)
coverItems := []struct {
label string
value string
}{
{"Hersteller", project.Manufacturer},
{"Maschinentyp", project.MachineType},
{"CE-Kennzeichnungsziel", project.CEMarkingTarget},
{"Projektstatus", string(project.Status)},
{"Datum", time.Now().Format("02.01.2006")},
}
for _, item := range coverItems {
if item.value == "" {
continue
}
pdf.SetFont("Helvetica", "B", 12)
pdf.CellFormat(60, 8, item.label+":", "", 0, "R", false, 0, "")
pdf.SetFont("Helvetica", "", 12)
pdf.CellFormat(5, 8, "", "", 0, "", false, 0, "")
pdf.CellFormat(0, 8, item.value, "", 1, "L", false, 0, "")
}
if project.Description != "" {
pdf.Ln(15)
pdf.SetFont("Helvetica", "I", 10)
pdf.MultiCell(0, 5, project.Description, "", "C", false)
}
}
func (e *DocumentExporter) pdfTableOfContents(pdf *gofpdf.Fpdf, sections []TechFileSection) {
pdf.SetFont("Helvetica", "B", 16)
pdf.SetTextColor(50, 50, 50)
pdf.CellFormat(0, 10, "Inhaltsverzeichnis", "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
pdf.SetDrawColor(200, 200, 200)
pdf.Line(10, pdf.GetY(), 200, pdf.GetY())
pdf.Ln(8)
pdf.SetFont("Helvetica", "", 11)
fixedEntries := []string{
"Gefaehrdungsprotokoll",
"Risikomatrix-Zusammenfassung",
"Massnahmen-Uebersicht",
}
pageEstimate := 3
for i, section := range sections {
pdf.CellFormat(10, 7, fmt.Sprintf("%d.", i+1), "", 0, "R", false, 0, "")
pdf.CellFormat(5, 7, "", "", 0, "", false, 0, "")
pdf.CellFormat(130, 7, section.Title, "", 0, "L", false, 0, "")
pdf.CellFormat(0, 7, fmt.Sprintf("~%d", pageEstimate+i), "", 1, "R", false, 0, "")
}
startPage := pageEstimate + len(sections)
for i, entry := range fixedEntries {
idx := len(sections) + i + 1
pdf.CellFormat(10, 7, fmt.Sprintf("%d.", idx), "", 0, "R", false, 0, "")
pdf.CellFormat(5, 7, "", "", 0, "", false, 0, "")
pdf.CellFormat(130, 7, entry, "", 0, "L", false, 0, "")
pdf.CellFormat(0, 7, fmt.Sprintf("~%d", startPage+i), "", 1, "R", false, 0, "")
}
}
func (e *DocumentExporter) pdfSection(pdf *gofpdf.Fpdf, section TechFileSection) {
pdf.SetFont("Helvetica", "B", 14)
pdf.SetTextColor(50, 50, 50)
pdf.CellFormat(0, 10, section.Title, "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
pdf.SetFont("Helvetica", "I", 9)
pdf.SetTextColor(100, 100, 100)
pdf.CellFormat(0, 5,
fmt.Sprintf("Typ: %s | Status: %s | Version: %d",
section.SectionType, string(section.Status), section.Version),
"", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
pdf.SetDrawColor(200, 200, 200)
pdf.Line(10, pdf.GetY(), 200, pdf.GetY())
pdf.Ln(5)
pdf.SetFont("Helvetica", "", 10)
if section.Content != "" {
pdf.MultiCell(0, 5, section.Content, "", "L", false)
} else {
pdf.SetFont("Helvetica", "I", 10)
pdf.SetTextColor(150, 150, 150)
pdf.CellFormat(0, 7, "(Kein Inhalt vorhanden)", "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
}
}
func (e *DocumentExporter) pdfHazardLog(pdf *gofpdf.Fpdf, hazards []Hazard, assessments []RiskAssessment) {
pdf.SetFont("Helvetica", "B", 14)
pdf.SetTextColor(50, 50, 50)
pdf.CellFormat(0, 10, "Gefaehrdungsprotokoll", "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
pdf.SetDrawColor(200, 200, 200)
pdf.Line(10, pdf.GetY(), 200, pdf.GetY())
pdf.Ln(5)
if len(hazards) == 0 {
pdf.SetFont("Helvetica", "I", 10)
pdf.CellFormat(0, 7, "(Keine Gefaehrdungen erfasst)", "", 1, "L", false, 0, "")
return
}
assessMap := buildAssessmentMap(assessments)
colWidths := []float64{10, 40, 30, 12, 12, 12, 30, 20}
headers := []string{"Nr", "Name", "Kategorie", "S", "E", "P", "Risiko", "OK"}
pdf.SetFont("Helvetica", "B", 9)
pdf.SetFillColor(240, 240, 240)
for i, h := range headers {
pdf.CellFormat(colWidths[i], 7, h, "1", 0, "C", true, 0, "")
}
pdf.Ln(-1)
pdf.SetFont("Helvetica", "", 8)
for i, hazard := range hazards {
if pdf.GetY() > 265 {
pdf.AddPage()
pdf.SetFont("Helvetica", "B", 9)
pdf.SetFillColor(240, 240, 240)
for j, h := range headers {
pdf.CellFormat(colWidths[j], 7, h, "1", 0, "C", true, 0, "")
}
pdf.Ln(-1)
pdf.SetFont("Helvetica", "", 8)
}
a := assessMap[hazard.ID.String()]
sev, exp, prob := "", "", ""
riskLabel := "-"
acceptable := "-"
var rl RiskLevel
if a != nil {
sev = fmt.Sprintf("%d", a.Severity)
exp = fmt.Sprintf("%d", a.Exposure)
prob = fmt.Sprintf("%d", a.Probability)
rl = a.RiskLevel
riskLabel = riskLevelLabel(rl)
if a.IsAcceptable {
acceptable = "Ja"
} else {
acceptable = "Nein"
}
}
r, g, b := riskLevelColor(rl)
pdf.SetFillColor(r, g, b)
fill := rl != ""
pdf.CellFormat(colWidths[0], 6, fmt.Sprintf("%d", i+1), "1", 0, "C", fill, 0, "")
pdf.CellFormat(colWidths[1], 6, pdfTruncate(hazard.Name, 22), "1", 0, "L", fill, 0, "")
pdf.CellFormat(colWidths[2], 6, pdfTruncate(hazard.Category, 16), "1", 0, "L", fill, 0, "")
pdf.CellFormat(colWidths[3], 6, sev, "1", 0, "C", fill, 0, "")
pdf.CellFormat(colWidths[4], 6, exp, "1", 0, "C", fill, 0, "")
pdf.CellFormat(colWidths[5], 6, prob, "1", 0, "C", fill, 0, "")
pdf.CellFormat(colWidths[6], 6, riskLabel, "1", 0, "C", fill, 0, "")
pdf.CellFormat(colWidths[7], 6, acceptable, "1", 0, "C", fill, 0, "")
pdf.Ln(-1)
}
}
func (e *DocumentExporter) pdfRiskMatrixSummary(pdf *gofpdf.Fpdf, assessments []RiskAssessment) {
pdf.Ln(10)
pdf.SetFont("Helvetica", "B", 14)
pdf.SetTextColor(50, 50, 50)
pdf.CellFormat(0, 10, "Risikomatrix-Zusammenfassung", "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
pdf.SetDrawColor(200, 200, 200)
pdf.Line(10, pdf.GetY(), 200, pdf.GetY())
pdf.Ln(5)
counts := countByRiskLevel(assessments)
levels := []RiskLevel{
RiskLevelNotAcceptable,
RiskLevelVeryHigh,
RiskLevelCritical,
RiskLevelHigh,
RiskLevelMedium,
RiskLevelLow,
RiskLevelNegligible,
}
pdf.SetFont("Helvetica", "B", 9)
pdf.SetFillColor(240, 240, 240)
pdf.CellFormat(60, 7, "Risikostufe", "1", 0, "L", true, 0, "")
pdf.CellFormat(30, 7, "Anzahl", "1", 0, "C", true, 0, "")
pdf.Ln(-1)
pdf.SetFont("Helvetica", "", 9)
for _, level := range levels {
count := counts[level]
if count == 0 {
continue
}
r, g, b := riskLevelColor(level)
pdf.SetFillColor(r, g, b)
pdf.CellFormat(60, 6, riskLevelLabel(level), "1", 0, "L", true, 0, "")
pdf.CellFormat(30, 6, fmt.Sprintf("%d", count), "1", 0, "C", true, 0, "")
pdf.Ln(-1)
}
pdf.SetFont("Helvetica", "B", 9)
pdf.SetFillColor(240, 240, 240)
pdf.CellFormat(60, 7, "Gesamt", "1", 0, "L", true, 0, "")
pdf.CellFormat(30, 7, fmt.Sprintf("%d", len(assessments)), "1", 0, "C", true, 0, "")
pdf.Ln(-1)
}
func (e *DocumentExporter) pdfMitigationsTable(pdf *gofpdf.Fpdf, mitigations []Mitigation) {
pdf.SetFont("Helvetica", "B", 14)
pdf.SetTextColor(50, 50, 50)
pdf.CellFormat(0, 10, "Massnahmen-Uebersicht", "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
pdf.SetDrawColor(200, 200, 200)
pdf.Line(10, pdf.GetY(), 200, pdf.GetY())
pdf.Ln(5)
if len(mitigations) == 0 {
pdf.SetFont("Helvetica", "I", 10)
pdf.CellFormat(0, 7, "(Keine Massnahmen erfasst)", "", 1, "L", false, 0, "")
return
}
colWidths := []float64{10, 45, 30, 30, 40}
headers := []string{"Nr", "Name", "Typ", "Status", "Verifikation"}
pdf.SetFont("Helvetica", "B", 9)
pdf.SetFillColor(240, 240, 240)
for i, h := range headers {
pdf.CellFormat(colWidths[i], 7, h, "1", 0, "C", true, 0, "")
}
pdf.Ln(-1)
pdf.SetFont("Helvetica", "", 8)
for i, m := range mitigations {
if pdf.GetY() > 265 {
pdf.AddPage()
pdf.SetFont("Helvetica", "B", 9)
pdf.SetFillColor(240, 240, 240)
for j, h := range headers {
pdf.CellFormat(colWidths[j], 7, h, "1", 0, "C", true, 0, "")
}
pdf.Ln(-1)
pdf.SetFont("Helvetica", "", 8)
}
pdf.CellFormat(colWidths[0], 6, fmt.Sprintf("%d", i+1), "1", 0, "C", false, 0, "")
pdf.CellFormat(colWidths[1], 6, pdfTruncate(m.Name, 25), "1", 0, "L", false, 0, "")
pdf.CellFormat(colWidths[2], 6, reductionTypeLabel(m.ReductionType), "1", 0, "C", false, 0, "")
pdf.CellFormat(colWidths[3], 6, mitigationStatusLabel(m.Status), "1", 0, "C", false, 0, "")
pdf.CellFormat(colWidths[4], 6, pdfTruncate(string(m.VerificationMethod), 22), "1", 0, "L", false, 0, "")
pdf.Ln(-1)
}
}
func (e *DocumentExporter) pdfClassifications(pdf *gofpdf.Fpdf, classifications []RegulatoryClassification) {
pdf.SetFont("Helvetica", "B", 14)
pdf.SetTextColor(50, 50, 50)
pdf.CellFormat(0, 10, "Regulatorische Klassifizierungen", "", 1, "L", false, 0, "")
pdf.SetTextColor(0, 0, 0)
pdf.SetDrawColor(200, 200, 200)
pdf.Line(10, pdf.GetY(), 200, pdf.GetY())
pdf.Ln(5)
for _, c := range classifications {
pdf.SetFont("Helvetica", "B", 11)
pdf.CellFormat(0, 7, regulationLabel(c.Regulation), "", 1, "L", false, 0, "")
pdf.SetFont("Helvetica", "", 10)
pdf.CellFormat(50, 6, "Klassifizierung:", "", 0, "L", false, 0, "")
pdf.CellFormat(0, 6, c.ClassificationResult, "", 1, "L", false, 0, "")
pdf.CellFormat(50, 6, "Risikostufe:", "", 0, "L", false, 0, "")
pdf.CellFormat(0, 6, riskLevelLabel(c.RiskLevel), "", 1, "L", false, 0, "")
if c.Reasoning != "" {
pdf.CellFormat(50, 6, "Begruendung:", "", 0, "L", false, 0, "")
pdf.MultiCell(0, 5, c.Reasoning, "", "L", false)
}
pdf.Ln(5)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,580 @@
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.
func builtinHazardsAISW() []HazardLibraryEntry {
now := time.Now()
return []HazardLibraryEntry{
// ====================================================================
// Category: false_classification (4 entries)
// ====================================================================
{
ID: hazardUUID("false_classification", 1),
Category: "false_classification",
Name: "Falsche Bauteil-Klassifikation durch KI",
Description: "Das KI-Modell klassifiziert ein Bauteil fehlerhaft, was zu falscher Weiterverarbeitung oder Montage fuehren kann.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"ai_model", "sensor"},
RegulationReferences: []string{"EU AI Act Art. 9", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Redundante Pruefung", "Konfidenz-Schwellwert"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("false_classification", 2),
Category: "false_classification",
Name: "Falsche Qualitaetsentscheidung (IO/NIO)",
Description: "Fehlerhafte IO/NIO-Entscheidung durch das KI-System fuehrt dazu, dass defekte Teile als gut bewertet oder gute Teile verworfen werden.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"ai_model", "software"},
RegulationReferences: []string{"EU AI Act Art. 9", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Human-in-the-Loop", "Stichproben-Gegenpruefung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("false_classification", 3),
Category: "false_classification",
Name: "Fehlklassifikation bei Grenzwertfaellen",
Description: "Bauteile nahe an Toleranzgrenzen werden systematisch falsch klassifiziert, da das Modell in Grenzwertbereichen unsicher agiert.",
DefaultSeverity: 3,
DefaultProbability: 4,
ApplicableComponentTypes: []string{"ai_model"},
RegulationReferences: []string{"EU AI Act Art. 9", "ISO 13849"},
SuggestedMitigations: mustMarshalJSON([]string{"Erweitertes Training", "Grauzone-Eskalation"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("false_classification", 4),
Category: "false_classification",
Name: "Verwechslung von Bauteiltypen",
Description: "Unterschiedliche Bauteiltypen werden vom KI-Modell verwechselt, was zu falscher Montage oder Verarbeitung fuehrt.",
DefaultSeverity: 4,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"ai_model", "sensor"},
RegulationReferences: []string{"EU AI Act Art. 9", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Barcode-Gegenpruefung", "Doppelte Sensorik"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: timing_error (3 entries)
// ====================================================================
{
ID: hazardUUID("timing_error", 1),
Category: "timing_error",
Name: "Verzoegerte KI-Reaktion in Echtzeitsystem",
Description: "Die KI-Inferenz dauert laenger als die zulaessige Echtzeitfrist, was zu verspaeteten Sicherheitsreaktionen fuehrt.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"software", "ai_model"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62443"},
SuggestedMitigations: mustMarshalJSON([]string{"Watchdog-Timer", "Fallback-Steuerung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("timing_error", 2),
Category: "timing_error",
Name: "Echtzeit-Verletzung Safety-Loop",
Description: "Der sicherheitsgerichtete Regelkreis kann die geforderten Zykluszeiten nicht einhalten, wodurch Sicherheitsfunktionen versagen koennen.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"ISO 13849", "IEC 61508", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Deterministische Ausfuehrung", "WCET-Analyse"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("timing_error", 3),
Category: "timing_error",
Name: "Timing-Jitter bei Netzwerkkommunikation",
Description: "Schwankende Netzwerklatenzen fuehren zu unvorhersehbaren Verzoegerungen in der Datenuebertragung sicherheitsrelevanter Signale.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"network", "software"},
RegulationReferences: []string{"IEC 62443", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"TSN-Netzwerk", "Pufferung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: data_poisoning (2 entries)
// ====================================================================
{
ID: hazardUUID("data_poisoning", 1),
Category: "data_poisoning",
Name: "Manipulierte Trainingsdaten",
Description: "Trainingsdaten werden absichtlich oder unbeabsichtigt manipuliert, wodurch das Modell systematisch fehlerhafte Entscheidungen trifft.",
DefaultSeverity: 4,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"ai_model"},
RegulationReferences: []string{"EU AI Act Art. 10", "CRA"},
SuggestedMitigations: mustMarshalJSON([]string{"Daten-Validierung", "Anomalie-Erkennung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("data_poisoning", 2),
Category: "data_poisoning",
Name: "Adversarial Input Angriff",
Description: "Gezielte Manipulation von Eingabedaten (z.B. Bilder, Sensorsignale), um das KI-Modell zu taeuschen und Fehlentscheidungen auszuloesen.",
DefaultSeverity: 4,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"ai_model", "sensor"},
RegulationReferences: []string{"EU AI Act Art. 15", "CRA", "IEC 62443"},
SuggestedMitigations: mustMarshalJSON([]string{"Input-Validation", "Adversarial Training"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: model_drift (3 entries)
// ====================================================================
{
ID: hazardUUID("model_drift", 1),
Category: "model_drift",
Name: "Performance-Degradation durch Concept Drift",
Description: "Die statistische Verteilung der Eingabedaten aendert sich ueber die Zeit, wodurch die Modellgenauigkeit schleichend abnimmt.",
DefaultSeverity: 3,
DefaultProbability: 4,
ApplicableComponentTypes: []string{"ai_model"},
RegulationReferences: []string{"EU AI Act Art. 9", "EU AI Act Art. 72"},
SuggestedMitigations: mustMarshalJSON([]string{"Monitoring-Dashboard", "Automatisches Retraining"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("model_drift", 2),
Category: "model_drift",
Name: "Data Drift durch veraenderte Umgebung",
Description: "Aenderungen in der physischen Umgebung (Beleuchtung, Temperatur, Material) fuehren zu veraenderten Sensordaten und Modellfehlern.",
DefaultSeverity: 3,
DefaultProbability: 4,
ApplicableComponentTypes: []string{"ai_model", "sensor"},
RegulationReferences: []string{"EU AI Act Art. 9", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Statistische Ueberwachung", "Sensor-Kalibrierung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("model_drift", 3),
Category: "model_drift",
Name: "Schleichende Modell-Verschlechterung",
Description: "Ohne aktives Monitoring verschlechtert sich die Modellqualitaet ueber Wochen oder Monate unbemerkt.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"ai_model"},
RegulationReferences: []string{"EU AI Act Art. 9", "EU AI Act Art. 72"},
SuggestedMitigations: mustMarshalJSON([]string{"Regelmaessige Evaluierung", "A/B-Testing"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: sensor_spoofing (3 entries)
// ====================================================================
{
ID: hazardUUID("sensor_spoofing", 1),
Category: "sensor_spoofing",
Name: "Kamera-Manipulation / Abdeckung",
Description: "Kamerasensoren werden absichtlich oder unbeabsichtigt abgedeckt oder manipuliert, sodass das System auf Basis falscher Bilddaten agiert.",
DefaultSeverity: 4,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"sensor"},
RegulationReferences: []string{"IEC 62443", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Plausibilitaetspruefung", "Mehrfach-Sensorik"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("sensor_spoofing", 2),
Category: "sensor_spoofing",
Name: "Sensor-Signal-Injection",
Description: "Einspeisung gefaelschter Signale in die Sensorleitungen oder Schnittstellen, um das System gezielt zu manipulieren.",
DefaultSeverity: 5,
DefaultProbability: 1,
ApplicableComponentTypes: []string{"sensor", "network"},
RegulationReferences: []string{"IEC 62443", "CRA"},
SuggestedMitigations: mustMarshalJSON([]string{"Signalverschluesselung", "Anomalie-Erkennung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("sensor_spoofing", 3),
Category: "sensor_spoofing",
Name: "Umgebungsbasierte Sensor-Taeuschung",
Description: "Natuerliche oder kuenstliche Umgebungsveraenderungen (Licht, Staub, Vibration) fuehren zu fehlerhaften Sensorwerten.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"sensor"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"},
SuggestedMitigations: mustMarshalJSON([]string{"Sensor-Fusion", "Redundanz"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: communication_failure (3 entries)
// ====================================================================
{
ID: hazardUUID("communication_failure", 1),
Category: "communication_failure",
Name: "Feldbus-Ausfall",
Description: "Ausfall des industriellen Feldbusses (z.B. PROFINET, EtherCAT) fuehrt zum Verlust der Kommunikation zwischen Steuerung und Aktorik.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"network", "controller"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62443"},
SuggestedMitigations: mustMarshalJSON([]string{"Redundanter Bus", "Safe-State-Transition"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("communication_failure", 2),
Category: "communication_failure",
Name: "Cloud-Verbindungsverlust",
Description: "Die Verbindung zur Cloud-Infrastruktur bricht ab, wodurch cloud-abhaengige Funktionen (z.B. Modell-Updates, Monitoring) nicht verfuegbar sind.",
DefaultSeverity: 3,
DefaultProbability: 4,
ApplicableComponentTypes: []string{"network", "software"},
RegulationReferences: []string{"CRA", "EU AI Act Art. 15"},
SuggestedMitigations: mustMarshalJSON([]string{"Offline-Faehigkeit", "Edge-Computing"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("communication_failure", 3),
Category: "communication_failure",
Name: "Netzwerk-Latenz-Spitzen",
Description: "Unkontrollierte Latenzspitzen im Netzwerk fuehren zu Timeouts und verspaeteter Datenlieferung an sicherheitsrelevante Systeme.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"network"},
RegulationReferences: []string{"IEC 62443", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"QoS-Konfiguration", "Timeout-Handling"}),
IsBuiltin: true,
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,
},
}
}

View File

@@ -0,0 +1,217 @@
package iace
import "time"
// builtinHazardsISO12100ElectricalThermal returns ISO 12100 electrical hazard
// entries (indices 7-10) and thermal hazard entries (indices 5-8).
func builtinHazardsISO12100ElectricalThermal() []HazardLibraryEntry {
now := time.Now()
return []HazardLibraryEntry{
// ====================================================================
// Category: electrical_hazard (indices 7-10, 4 entries)
// ====================================================================
{
ID: hazardUUID("electrical_hazard", 7),
Category: "electrical_hazard",
SubCategory: "lichtbogen",
Name: "Lichtbogengefahr bei Schalthandlungen",
Description: "Beim Schalten unter Last kann ein Lichtbogen entstehen, der zu Verbrennungen und Augenschaeden fuehrt.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"electrical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Lichtbogenschutzkleidung (PSA)", "Fernbediente Schaltgeraete"}),
TypicalCauses: []string{"Schalten unter Last", "Verschmutzte Kontakte", "Fehlbedienung bei Wartung"},
TypicalHarm: "Verbrennungen durch Lichtbogen, Augenschaeden, Druckwelle",
RelevantLifecyclePhases: []string{"maintenance", "fault_finding"},
RecommendedMeasuresDesign: []string{"Lasttrennschalter mit Lichtbogenkammer", "Beruerungs­sichere Klemmleisten"},
RecommendedMeasuresTechnical: []string{"Lichtbogen-Erkennungssystem", "Fernausloesemoeglich­keit"},
RecommendedMeasuresInformation: []string{"PSA-Pflicht bei Schalthandlungen", "Schaltbefugnisregelung"},
SuggestedEvidence: []string{"Lichtbogenberechnung", "PSA-Ausstattungsnachweis"},
RelatedKeywords: []string{"Lichtbogen", "Schalthandlung", "Arc Flash"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 8),
Category: "electrical_hazard",
SubCategory: "ueberstrom",
Name: "Ueberstrom durch Kurzschluss",
Description: "Ein Kurzschluss kann zu extrem hohen Stroemen fuehren, die Leitungen ueberhitzen, Braende ausloesen und Bauteile zerstoeren.",
DefaultSeverity: 4,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"electrical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Selektive Absicherung mit Schmelzsicherungen", "Kurzschlussberechnung und Abschaltzeit­nachweis"}),
TypicalCauses: []string{"Beschaedigte Leitungsisolierung", "Feuchtigkeitseintritt", "Fehlerhafte Verdrahtung"},
TypicalHarm: "Brandgefahr, Zerstoerung elektrischer Betriebsmittel",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance", "installation"},
RecommendedMeasuresDesign: []string{"Kurzschlussfeste Dimensionierung der Leitungen", "Selektive Schutzkoordination"},
RecommendedMeasuresTechnical: []string{"Leitungsschutzschalter", "Fehlerstrom-Schutzeinrichtung"},
RecommendedMeasuresInformation: []string{"Stromlaufplan aktuell halten", "Prueffristen fuer elektrische Anlage"},
SuggestedEvidence: []string{"Kurzschlussberechnung", "Pruefprotokoll nach DGUV V3"},
RelatedKeywords: []string{"Kurzschluss", "Ueberstrom", "Leitungsschutz"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 9),
Category: "electrical_hazard",
SubCategory: "erdungsfehler",
Name: "Erdungsfehler im Schutzleitersystem",
Description: "Ein unterbrochener oder fehlerhafter Schutzleiter verhindert die sichere Ableitung von Fehlerstroemen und macht Gehaeuse spannungsfuehrend.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"electrical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Regelmaessige Schutzleiterpruefung", "Fehlerstrom-Schutzschalter als Zusatzmassnahme"}),
TypicalCauses: []string{"Lose Schutzleiterklemme", "Korrosion an Erdungspunkten", "Vergessener Schutzleiteranschluss nach Wartung"},
TypicalHarm: "Elektrischer Schlag bei Beruehrung des Maschinengehaeuses",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance", "installation"},
RecommendedMeasuresDesign: []string{"Redundante Schutzleiteranschluesse", "Schutzleiter-Monitoring"},
RecommendedMeasuresTechnical: []string{"RCD-Schutzschalter 30 mA", "Isolationsueberwachung"},
RecommendedMeasuresInformation: []string{"Pruefplaketten an Schutzleiterpunkten", "Prueffrist 12 Monate"},
SuggestedEvidence: []string{"Schutzleitermessung", "Pruefprotokoll DGUV V3"},
RelatedKeywords: []string{"Schutzleiter", "Erdung", "Fehlerstrom"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 10),
Category: "electrical_hazard",
SubCategory: "isolationsversagen",
Name: "Isolationsversagen in Hochspannungsbereich",
Description: "Alterung, Verschmutzung oder mechanische Beschaedigung der Isolierung in Hochspannungsbereichen kann zu Spannungsueberschlaegen und Koerperdurchstroemung fuehren.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"electrical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Isolationswiderstandsmessung", "Spannungsfeste Einhausung"}),
TypicalCauses: []string{"Alterung der Isolierstoffe", "Mechanische Beschaedigung", "Verschmutzung und Feuchtigkeit"},
TypicalHarm: "Toedlicher Stromschlag, Verbrennungen durch Spannungsueberschlag",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Verstaerkte Isolierung in kritischen Bereichen", "Luftstrecken und Kriechstrecken einhalten"},
RecommendedMeasuresTechnical: []string{"Isolationsueberwachungsgeraet", "Verriegelter Zugang zum Hochspannungsbereich"},
RecommendedMeasuresInformation: []string{"Hochspannungswarnung", "Zutrittsregelung fuer Elektrofachkraefte"},
SuggestedEvidence: []string{"Isolationsmessprotokoll", "Pruefbericht Hochspannungsbereich"},
RelatedKeywords: []string{"Isolation", "Hochspannung", "Durchschlag"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: thermal_hazard (indices 5-8, 4 entries)
// ====================================================================
{
ID: hazardUUID("thermal_hazard", 5),
Category: "thermal_hazard",
SubCategory: "kaeltekontakt",
Name: "Kontakt mit kalten Oberflaechen (Kryotechnik)",
Description: "In kryotechnischen Anlagen oder Kuehlsystemen koennen extrem kalte Oberflaechen bei Beruehrung Kaelteverbrennungen verursachen.",
DefaultSeverity: 4,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Isolierung kalter Oberflaechen", "Kaelteschutzhandschuhe"}),
TypicalCauses: []string{"Fehlende Isolierung an Kryoleitungen", "Beruehrung tiefgekuehlter Bauteile", "Defekte Kaelteisolierung"},
TypicalHarm: "Kaelteverbrennungen an Haenden und Fingern",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Isolierung aller kalten Oberflaechen im Zugriffsbereich", "Abstandshalter zu Kryoleitungen"},
RecommendedMeasuresTechnical: []string{"Temperaturwarnung bei kritischen Oberflaechentemperaturen"},
RecommendedMeasuresInformation: []string{"Warnhinweis Kaeltegefahr", "PSA-Pflicht Kaelteschutz"},
SuggestedEvidence: []string{"Oberflaechentemperaturmessung", "Risikobeurteilung"},
RelatedKeywords: []string{"Kryotechnik", "Kaelte", "Kaelteverbrennung"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("thermal_hazard", 6),
Category: "thermal_hazard",
SubCategory: "waermestrahlung",
Name: "Waermestrahlung von Hochtemperaturprozessen",
Description: "Oefen, Giessereianlagen oder Waermebehandlungsprozesse emittieren intensive Waermestrahlung, die auch ohne direkten Kontakt zu Verbrennungen fuehren kann.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Waermeschutzschilder", "Hitzeschutzkleidung"}),
TypicalCauses: []string{"Offene Ofentuer bei Beschickung", "Fehlende Abschirmung", "Langzeitexposition in der Naehe von Waermequellen"},
TypicalHarm: "Hautverbrennungen durch Waermestrahlung, Hitzschlag",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Waermedaemmung und Strahlungsschilde", "Automatische Beschickung statt manueller"},
RecommendedMeasuresTechnical: []string{"Waermestrahlung-Sensor mit Warnung", "Luftschleier vor Ofenoeeffnungen"},
RecommendedMeasuresInformation: []string{"Maximalaufenthaltsdauer festlegen", "Hitzeschutz-PSA vorschreiben"},
SuggestedEvidence: []string{"Waermestrahlungsmessung am Arbeitsplatz", "Risikobeurteilung"},
RelatedKeywords: []string{"Waermestrahlung", "Ofen", "Hitzeschutz"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("thermal_hazard", 7),
Category: "thermal_hazard",
SubCategory: "brandgefahr",
Name: "Brandgefahr durch ueberhitzte Antriebe",
Description: "Ueberlastete oder schlecht gekuehlte Elektromotoren und Antriebe koennen sich so stark erhitzen, dass umgebende Materialien entzuendet werden.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"actuator", "electrical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Temperatursensor am Motor", "Thermischer Motorschutz"}),
TypicalCauses: []string{"Dauerbetrieb ueber Nennlast", "Blockierter Kuehlluftstrom", "Defektes Motorlager erhoecht Reibung"},
TypicalHarm: "Brand mit Sachschaeden und Personengefaehrdung durch Rauchentwicklung",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Thermische Motorschutzdimensionierung", "Brandschottung um Antriebsbereich"},
RecommendedMeasuresTechnical: []string{"PTC-Temperaturfuehler im Motor", "Rauchmelder im Antriebsbereich"},
RecommendedMeasuresInformation: []string{"Wartungsintervalle fuer Kuehlluftwege", "Brandschutzordnung"},
SuggestedEvidence: []string{"Temperaturmessung unter Last", "Brandschutzkonzept"},
RelatedKeywords: []string{"Motorueberhitzung", "Brand", "Thermischer Schutz"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("thermal_hazard", 8),
Category: "thermal_hazard",
SubCategory: "heisse_fluessigkeiten",
Name: "Verbrennungsgefahr durch heisse Fluessigkeiten",
Description: "Heisse Prozessfluessigkeiten, Kuehlmittel oder Dampf koennen bei Leckage oder beim Oeffnen von Verschluessen Verbruehungen verursachen.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Druckentlastung vor dem Oeffnen", "Spritzschutz an Leitungsverbindungen"}),
TypicalCauses: []string{"Oeffnen von Verschluessen unter Druck", "Schlauchbruch bei heissem Medium", "Spritzer beim Nachfuellen"},
TypicalHarm: "Verbruehungen an Haut und Augen",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Druckentlastungsventil vor Verschluss", "Isolierte Leitungsfuehrung"},
RecommendedMeasuresTechnical: []string{"Temperaturanzeige an kritischen Punkten", "Auffangwannen unter Leitungsverbindungen"},
RecommendedMeasuresInformation: []string{"Warnhinweis heisse Fluessigkeit", "Abkuehlprozedur in Betriebsanweisung"},
SuggestedEvidence: []string{"Temperaturmessung am Austritt", "Risikobeurteilung"},
RelatedKeywords: []string{"Verbruehung", "Heisse Fluessigkeit", "Dampf"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
}
}

View File

@@ -0,0 +1,416 @@
package iace
import "time"
// builtinHazardsISO12100Env returns ISO 12100 noise, vibration, ergonomic
// and material/environmental hazard entries.
func builtinHazardsISO12100Env() []HazardLibraryEntry {
now := time.Now()
return []HazardLibraryEntry{
// Category: ergonomic (indices 1-8, 8 entries)
// ====================================================================
{
ID: hazardUUID("ergonomic", 1),
Category: "ergonomic",
SubCategory: "fehlbedienung",
Name: "Fehlbedienung durch unguenstige Anordnung von Bedienelementen",
Description: "Ungluecklich platzierte oder schlecht beschriftete Bedienelemente erhoehen das Risiko von Fehlbedienungen, die sicherheitskritische Maschinenbewegungen ausloesen.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"controller", "sensor"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Ergonomische Anordnung nach ISO 9355", "Eindeutige Beschriftung und Farbcodierung"}),
TypicalCauses: []string{"Nicht-intuitive Anordnung der Schalter", "Fehlende oder unlesbare Beschriftung", "Zu geringer Abstand zwischen Bedienelementen"},
TypicalHarm: "Verletzungen durch unbeabsichtigte Maschinenaktionen nach Fehlbedienung",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Bedienelemente nach ISO 9355 anordnen", "Farbcodierung und Symbolik nach IEC 60073"},
RecommendedMeasuresTechnical: []string{"Bestaetigung fuer kritische Aktionen", "Abgedeckte Schalter fuer Gefahrenfunktionen"},
RecommendedMeasuresInformation: []string{"Bedienerhandbuch mit Bilddarstellungen", "Schulung der Bediener"},
SuggestedEvidence: []string{"Usability-Test des Bedienfeldes", "Risikobeurteilung"},
RelatedKeywords: []string{"Bedienelemente", "Fehlbedienung", "Ergonomie"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("ergonomic", 2),
Category: "ergonomic",
SubCategory: "zwangshaltung",
Name: "Zwangshaltung bei Beschickungsvorgaengen",
Description: "Unglueckliche Koerperhaltungen beim manuellen Beladen oder Entnehmen von Werkstuecken fuehren zu muskuloskeletalen Beschwerden.",
DefaultSeverity: 3,
DefaultProbability: 4,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Hoehenverstellbare Beschickungsoeffnung", "Automatische Materialzufuhr"}),
TypicalCauses: []string{"Beschickungsoeffnung in unguenstiger Hoehe", "Grosse Greifentfernung", "Wiederholte Drehbewegungen des Rumpfes"},
TypicalHarm: "Rueckenbeschwerden, Schulter-Arm-Syndrom, chronische Gelenkschmerzen",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Beschickungshoehe zwischen 60 und 120 cm", "Kurze Greifwege"},
RecommendedMeasuresTechnical: []string{"Hoehenverstellbare Arbeitstische", "Hebehilfen und Manipulatoren"},
RecommendedMeasuresInformation: []string{"Ergonomie-Schulung", "Hinweise zur richtigen Koerperhaltung"},
SuggestedEvidence: []string{"Ergonomische Arbeitsplatzanalyse", "Gefaehrdungsbeurteilung"},
RelatedKeywords: []string{"Zwangshaltung", "Beschickung", "Muskel-Skelett"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("ergonomic", 3),
Category: "ergonomic",
SubCategory: "manuelle_handhabung",
Name: "Koerperliche Ueberforderung durch manuelle Handhabung",
Description: "Schwere Lasten muessen manuell gehoben, getragen oder verschoben werden, was zu akuten Verletzungen oder chronischen Schaeden fuehrt.",
DefaultSeverity: 3,
DefaultProbability: 4,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Technische Hebehilfen bereitstellen", "Gewichtsgrenze fuer manuelles Heben festlegen"}),
TypicalCauses: []string{"Fehlende Hebehilfen", "Zu schwere Einzelteile", "Haeufiges Heben ueber Schulterhoe­he"},
TypicalHarm: "Bandscheibenvorfall, Rueckenverletzungen, Ueberanstrengung",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance", "setup"},
RecommendedMeasuresDesign: []string{"Bauteile unter 15 kg fuer manuelles Handling", "Hebevorrichtungen integrieren"},
RecommendedMeasuresTechnical: []string{"Kran oder Hebezeug am Arbeitsplatz", "Vakuumheber fuer Platten"},
RecommendedMeasuresInformation: []string{"Hebebelastungstabelle aushangen", "Unterweisung in Hebetechnik"},
SuggestedEvidence: []string{"Lastenhandhabungsbeurteilung", "Risikobeurteilung"},
RelatedKeywords: []string{"Heben", "Lastenhandhabung", "Ueberanstrengung"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("ergonomic", 4),
Category: "ergonomic",
SubCategory: "verwechslung",
Name: "Verwechslungsgefahr bei gleichartigen Bedienelementen",
Description: "Baugleiche, nicht unterscheidbare Taster oder Schalter koennen verwechselt werden, was zu unbeabsichtigten und gefaehrlichen Maschinenaktionen fuehrt.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 4,
ApplicableComponentTypes: []string{"controller", "sensor"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Unterschiedliche Formen und Farben fuer verschiedene Funktionen", "Beschriftung in Klartext"}),
TypicalCauses: []string{"Identische Tasterform fuer unterschiedliche Funktionen", "Fehlende Beschriftung", "Schlechte Beleuchtung am Bedienfeld"},
TypicalHarm: "Unbeabsichtigte Maschinenaktionen mit Verletzungsgefahr",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Form- und Farbcodierung nach IEC 60073", "Raeumliche Gruppierung nach Funktionsbereichen"},
RecommendedMeasuresTechnical: []string{"Beleuchtetes Bedienfeld", "Haptisch unterscheidbare Taster"},
RecommendedMeasuresInformation: []string{"Bedienfeldplan am Arbeitsplatz aushangen", "Einweisung neuer Bediener"},
SuggestedEvidence: []string{"Usability-Bewertung", "Risikobeurteilung"},
RelatedKeywords: []string{"Verwechslung", "Taster", "Bedienpanel"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("ergonomic", 5),
Category: "ergonomic",
SubCategory: "sichtbehinderung",
Name: "Sichtbehinderung des Gefahrenbereichs vom Bedienplatz",
Description: "Vom Bedienplatz aus ist der Gefahrenbereich nicht vollstaendig einsehbar, sodass der Bediener Personen im Gefahrenbereich nicht erkennen kann.",
DefaultSeverity: 5,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"controller", "sensor", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Kameraueberwa­chung des Gefahrenbereichs", "Sicherheits-Laserscanner"}),
TypicalCauses: []string{"Verdeckte Sicht durch Maschinenteile", "Bedienplatz zu weit vom Arbeitsbereich", "Fehlende Spiegel oder Kameras"},
TypicalHarm: "Schwere Verletzungen von Personen im nicht einsehbaren Gefahrenbereich",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Bedienplatz mit direkter Sicht auf Gefahrenbereich", "Transparente Schutzeinrichtungen"},
RecommendedMeasuresTechnical: []string{"Kamerasystem mit Monitor am Bedienplatz", "Sicherheits-Laserscanner"},
RecommendedMeasuresInformation: []string{"Hinweis auf toten Winkel", "Anlaufwarnung vor Maschinenbewegung"},
SuggestedEvidence: []string{"Sichtfeldanalyse vom Bedienplatz", "Risikobeurteilung"},
RelatedKeywords: []string{"Sichtfeld", "Toter Winkel", "Bedienplatz"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("ergonomic", 6),
Category: "ergonomic",
SubCategory: "griffgestaltung",
Name: "Unergonomische Griffgestaltung von Handwerkzeugen",
Description: "Schlecht geformte Griffe an handgefuehrten Werkzeugen und Hebeln fuehren zu Ermuedung, verminderter Griffkraft und erhoehtem Unfallrisiko.",
DefaultSeverity: 3,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 4,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Ergonomische Griffformen nach EN 894", "Rutschfeste Griffoberflaechen"}),
TypicalCauses: []string{"Zu duenner oder zu dicker Griff", "Glatte Griffoberflaeche", "Scharfe Kanten am Griff"},
TypicalHarm: "Sehnenscheidenentzuendung, Abrutschen mit Folgeverletzung",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Griffdurchmesser 30-45 mm", "Anatomisch geformte Griffe"},
RecommendedMeasuresTechnical: []string{"Rutschfeste Beschichtung", "Handgelenkschlaufe an Handwerkzeugen"},
RecommendedMeasuresInformation: []string{"Auswahl ergonomischer Werkzeuge dokumentieren", "Arbeitsmedizinische Beratung"},
SuggestedEvidence: []string{"Ergonomische Werkzeugbewertung", "Risikobeurteilung"},
RelatedKeywords: []string{"Griff", "Handwerkzeug", "Ergonomie"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("ergonomic", 7),
Category: "ergonomic",
SubCategory: "monotonie",
Name: "Monotone Taetigkeit fuehrt zu Aufmerksamkeitsverlust",
Description: "Langandauernde, sich wiederholende Taetigkeiten ohne Abwechslung vermindern die Aufmerksamkeit des Bedieners und erhoehen die Unfallgefahr.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Aufgabenwechsel organisieren", "Regelmaessige Pausen einplanen"}),
TypicalCauses: []string{"Gleichfoermige Wiederholtaetigkeit", "Fehlende Pausenregelung", "Mangelnde Aufgabenvielfalt"},
TypicalHarm: "Unfaelle durch Unaufmerksamkeit, verspaetete Reaktion auf Gefahren",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Automatisierung monotoner Teilaufgaben", "Arbeitsplatzrotation ermoeglichen"},
RecommendedMeasuresTechnical: []string{"Aufmerksamkeitserkennung", "Regelmaessige Warnmeldungen"},
RecommendedMeasuresInformation: []string{"Pausenplan erstellen", "Schulung zur Ermuedungserkennung"},
SuggestedEvidence: []string{"Arbeitsplatzanalyse", "Gefaehrdungsbeurteilung psychische Belastung"},
RelatedKeywords: []string{"Monotonie", "Aufmerksamkeit", "Ermuedung"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("ergonomic", 8),
Category: "ergonomic",
SubCategory: "wartungszugang",
Name: "Ungenuegender Zugang fuer Wartungsarbeiten",
Description: "Enge oder schwer zugaengliche Wartungsbereiche zwingen das Personal zu gefaehrlichen Koerperhaltungen und erhoehen das Risiko von Verletzungen.",
DefaultSeverity: 3,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Wartungsoeffnungen ausreichend dimensionieren", "Wartungsbuehnen und Podeste vorsehen"}),
TypicalCauses: []string{"Zu kleine Wartungsoeffnungen", "Fehlende Arbeitsbuehnen in der Hoehe", "Wartungspunkte hinter schwer zu demontierenden Verkleidungen"},
TypicalHarm: "Quetschungen in engen Raeumen, Absturz von erhoehten Wartungspositionen",
RelevantLifecyclePhases: []string{"maintenance", "fault_finding"},
RecommendedMeasuresDesign: []string{"Mindestmasse fuer Zugangsoeeffnungen nach ISO 14122", "Wartungspunkte leicht zugaenglich anordnen"},
RecommendedMeasuresTechnical: []string{"Feste Buehnen und Leitern mit Absturzsicherung", "Schnellverschluesse statt Schraubverbindungen an Wartungsklappen"},
RecommendedMeasuresInformation: []string{"Wartungszugangsskizze in Betriebsanleitung", "Hinweis auf PSA-Pflicht bei Arbeiten in der Hoehe"},
SuggestedEvidence: []string{"Zugaenglichkeitspruefung", "Risikobeurteilung"},
RelatedKeywords: []string{"Wartungszugang", "Zugaenglichkeit", "Wartungsbuehne"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: material_environmental (indices 1-8, 8 entries)
// ====================================================================
{
ID: hazardUUID("material_environmental", 1),
Category: "material_environmental",
SubCategory: "staubexplosion",
Name: "Staubexplosion durch Feinpartikel",
Description: "Feine brennbare Staube koennen in Verbindung mit einer Zuendquelle explosionsartig reagieren und schwere Zerstoerungen verursachen.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"ATEX-konforme Ausfuehrung", "Absauganlage mit Funkenerkennung"}),
TypicalCauses: []string{"Unzureichende Absaugung", "Staubansammlung in Hohlraeumen", "Elektrische oder mechanische Zuendquelle"},
TypicalHarm: "Explosion mit toedlichen Verletzungen, schwere Verbrennungen, Gebaeudezersto­erung",
RelevantLifecyclePhases: []string{"normal_operation", "cleaning"},
RecommendedMeasuresDesign: []string{"ATEX-konforme Konstruktion", "Druckentlastungsflaechen vorsehen"},
RecommendedMeasuresTechnical: []string{"Absaugung mit Funkenloeschanlage", "Explosionsunterdrueckung"},
RecommendedMeasuresInformation: []string{"Ex-Zoneneinteilung kennzeichnen", "Reinigungsplan fuer Staubablagerungen"},
SuggestedEvidence: []string{"Explosionsschutz-Dokument", "ATEX-Konformitaetserklaerung"},
RelatedKeywords: []string{"Staubexplosion", "ATEX", "Feinstaub"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("material_environmental", 2),
Category: "material_environmental",
SubCategory: "rauch_gas",
Name: "Rauch- und Gasfreisetzung bei Laserschneiden",
Description: "Beim Laserschneiden entstehen gesundheitsschaedliche Rauche und Gase aus dem verdampften Material, die die Atemwege schaedigen.",
DefaultSeverity: 4,
DefaultProbability: 4,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Absaugung direkt am Bearbeitungspunkt", "Filteranlage mit Aktivkohlestufe"}),
TypicalCauses: []string{"Fehlende Absaugung", "Verarbeitung kunststoffbeschichteter Materialien", "Undichte Maschineneinhausung"},
TypicalHarm: "Atemwegserkrankungen, Reizung von Augen und Schleimhaeuten",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Geschlossener Bearbeitungsraum mit Absaugung", "Materialauswahl ohne toxische Beschichtungen"},
RecommendedMeasuresTechnical: []string{"Punktabsaugung mit HEPA-Filter", "Raumluft-Monitoring"},
RecommendedMeasuresInformation: []string{"Materialfreigabeliste pflegen", "Atemschutz-PSA bei Sondermaterialien"},
SuggestedEvidence: []string{"Arbeitsplatzmessung Gefahrstoffe", "Risikobeurteilung"},
RelatedKeywords: []string{"Laserschneiden", "Rauch", "Gefahrstoff"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("material_environmental", 3),
Category: "material_environmental",
SubCategory: "kuehlschmierstoff",
Name: "Daempfe aus Kuehlschmierstoffen",
Description: "Beim Einsatz von Kuehlschmierstoffen entstehen Aerosole und Daempfe, die bei Langzeitexposition zu Haut- und Atemwegserkrankungen fuehren.",
DefaultSeverity: 3,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Oelunebelabscheider an der Maschine", "Hautschutzplan fuer Bediener"}),
TypicalCauses: []string{"Offene Bearbeitungszonen ohne Abschirmung", "Ueberaltertes Kuehlschmiermittel", "Zu hoher Kuehlmitteldurchsatz"},
TypicalHarm: "Oelakne, Atemwegssensibilisierung, allergische Hautreaktionen",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Geschlossener Bearbeitungsraum", "Minimalmengenschmierung statt Volumenoelstrom"},
RecommendedMeasuresTechnical: []string{"Oelunebelabscheider", "KSS-Konzentrations- und pH-Ueberwachung"},
RecommendedMeasuresInformation: []string{"Hautschutz- und Hygieneplan", "KSS-Pflegeanweisung"},
SuggestedEvidence: []string{"Arbeitsplatz-Gefahrstoffmessung", "Hautschutzplan"},
RelatedKeywords: []string{"Kuehlschmierstoff", "KSS", "Aerosol"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("material_environmental", 4),
Category: "material_environmental",
SubCategory: "prozessmedien",
Name: "Chemische Exposition durch Prozessmedien",
Description: "Chemisch aggressive Prozessmedien wie Saeuren, Laugen oder Loesemittel koennen bei Hautkontakt oder Einatmen schwere Gesundheitsschaeden verursachen.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"other", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Geschlossene Mediumfuehrung", "Chemikalienschutz-PSA"}),
TypicalCauses: []string{"Offene Behaelter mit Gefahrstoffen", "Spritzer beim Nachfuellen", "Leckage an Dichtungen"},
TypicalHarm: "Veraetzungen, Atemwegsschaedigung, Vergiftung",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance", "cleaning"},
RecommendedMeasuresDesign: []string{"Geschlossene Kreislaeufe", "Korrosionsbestaendige Materialien"},
RecommendedMeasuresTechnical: []string{"Notdusche und Augenspueler", "Gaswarnanlage"},
RecommendedMeasuresInformation: []string{"Sicherheitsdatenblaetter am Arbeitsplatz", "Gefahrstoffunterweisung"},
SuggestedEvidence: []string{"Gefahrstoffverzeichnis", "Arbeitsplatzmessung"},
RelatedKeywords: []string{"Gefahrstoff", "Chemikalie", "Prozessmedium"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("material_environmental", 5),
Category: "material_environmental",
SubCategory: "dichtungsversagen",
Name: "Leckage von Gefahrstoffen bei Dichtungsversagen",
Description: "Versagende Dichtungen an Rohrleitungen oder Behaeltern setzen Gefahrstoffe frei und gefaehrden Personal und Umwelt.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Doppelwandige Behaelter mit Leckagedetektion", "Auffangwannen unter allen Gefahrstoffbereichen"}),
TypicalCauses: []string{"Alterung der Dichtungsmaterialien", "Chemische Unvertraeglichkeit", "Thermische Ueberbelastung der Dichtung"},
TypicalHarm: "Gefahrstofffreisetzung mit Vergiftung, Hautschaedigung, Umweltschaden",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Doppelwandige Leitungen", "Chemisch bestaendige Dichtungswerkstoffe"},
RecommendedMeasuresTechnical: []string{"Leckagesensoren", "Auffangwannen mit Fassungsvermoegen 110%"},
RecommendedMeasuresInformation: []string{"Dichtungswechselintervalle festlegen", "Notfallplan Gefahrstoffaustritt"},
SuggestedEvidence: []string{"Dichtheitspruefprotokoll", "Gefahrstoff-Notfallplan"},
RelatedKeywords: []string{"Leckage", "Dichtung", "Gefahrstoff"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("material_environmental", 6),
Category: "material_environmental",
SubCategory: "biologische_kontamination",
Name: "Biologische Kontamination in Lebensmittelmaschinen",
Description: "In Maschinen der Lebensmittelindustrie koennen sich Mikroorganismen in schwer zu reinigenden Bereichen ansiedeln und das Produkt kontaminieren.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Hygienic-Design-Konstruktion", "CIP-Reinigungssystem"}),
TypicalCauses: []string{"Totraeume und Hinterschneidungen in der Konstruktion", "Unzureichende Reinigung", "Poroese Oberflaechen"},
TypicalHarm: "Lebensmittelkontamination mit Gesundheitsge­faehrdung der Verbraucher",
RelevantLifecyclePhases: []string{"normal_operation", "cleaning", "maintenance"},
RecommendedMeasuresDesign: []string{"Hygienic Design nach EHEDG-Richtlinien", "Selbstentleerende Konstruktion"},
RecommendedMeasuresTechnical: []string{"CIP-Reinigungsanlage", "Oberflaechenguete Ra kleiner 0.8 Mikrometer"},
RecommendedMeasuresInformation: []string{"Reinigungs- und Desinfektionsplan", "Hygieneschulung des Personals"},
SuggestedEvidence: []string{"EHEDG-Zertifikat", "Mikrobiologische Abklatschproben"},
RelatedKeywords: []string{"Hygiene", "Kontamination", "Lebensmittelsicherheit"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("material_environmental", 7),
Category: "material_environmental",
SubCategory: "elektrostatik",
Name: "Statische Aufladung in staubhaltiger Umgebung",
Description: "In staubhaltiger oder explosionsfaehiger Atmosphaere kann eine elektrostatische Entladung als Zuendquelle wirken und eine Explosion oder einen Brand ausloesen.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "electrical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Erdung aller leitfaehigen Teile", "Ableitfaehige Bodenbeschichtung"}),
TypicalCauses: []string{"Nicht geerdete Maschinenteile", "Reibung von Kunststoffbaendern", "Niedrige Luftfeuchtigkeit"},
TypicalHarm: "Zuendung explosionsfaehiger Atmosphaere, Explosion oder Brand",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Leitfaehige Materialien verwenden", "Erdungskonzept fuer alle Komponenten"},
RecommendedMeasuresTechnical: []string{"Ionisatoren zur Ladungsneutralisation", "Erdungsueberwachung"},
RecommendedMeasuresInformation: []string{"ESD-Hinweisschilder", "Schulung zur elektrostatischen Gefaehrdung"},
SuggestedEvidence: []string{"Erdungsmessung", "Explosionsschutz-Dokument"},
RelatedKeywords: []string{"Elektrostatik", "ESD", "Zuendquelle"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("material_environmental", 8),
Category: "material_environmental",
SubCategory: "uv_strahlung",
Name: "UV-Strahlung bei Schweiss- oder Haerteprozessen",
Description: "Schweissvorgaenge und UV-Haerteprozesse emittieren ultraviolette Strahlung, die Augen und Haut schwer schaedigen kann.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "electrical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Abschirmung des UV-Bereichs", "Schweissschutzschirm und Schutzkleidung"}),
TypicalCauses: []string{"Fehlende Abschirmung der UV-Quelle", "Reflexion an glaenzenden Oberflaechen", "Aufenthalt im Strahlungsbereich ohne Schutz"},
TypicalHarm: "Verblitzen der Augen, Hautverbrennungen, erhoehtes Hautkrebsrisiko bei Langzeitexposition",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Vollstaendige Einhausung der UV-Quelle", "UV-absorbierende Schutzscheiben"},
RecommendedMeasuresTechnical: []string{"Schweissvorhaenge um den Arbeitsbereich", "UV-Sensor mit Abschaltung"},
RecommendedMeasuresInformation: []string{"Warnhinweis UV-Strahlung", "PSA-Pflicht: Schweissschutzhelm und Schutzkleidung"},
SuggestedEvidence: []string{"UV-Strahlungsmessung", "Risikobeurteilung"},
RelatedKeywords: []string{"UV-Strahlung", "Schweissen", "Strahlenschutz"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
}
}

View File

@@ -0,0 +1,362 @@
package iace
import "time"
// builtinHazardsISO12100Mechanical returns ISO 12100 mechanical hazard
// entries (indices 7-20) per Maschinenverordnung 2023/1230 and ISO 12100.
func builtinHazardsISO12100Mechanical() []HazardLibraryEntry {
now := time.Now()
return []HazardLibraryEntry{
// ====================================================================
{
ID: hazardUUID("mechanical_hazard", 7),
Category: "mechanical_hazard",
SubCategory: "quetschgefahr",
Name: "Quetschgefahr durch gegenlaeufige Walzen",
Description: "Zwischen gegenlaeufig rotierenden Walzen entsteht ein Einzugspunkt, an dem Koerperteile oder Kleidung eingezogen und gequetscht werden koennen.",
DefaultSeverity: 5,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Feststehende trennende Schutzeinrichtung am Walzeneinlauf", "Zweihandbedienung bei manueller Beschickung"}),
TypicalCauses: []string{"Fehlende Schutzabdeckung am Einzugspunkt", "Manuelle Materialzufuehrung ohne Hilfsmittel", "Wartung bei laufender Maschine"},
TypicalHarm: "Quetschverletzungen an Fingern, Haenden oder Armen bis hin zu Amputationen",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance", "setup"},
RecommendedMeasuresDesign: []string{"Mindestabstand zwischen Walzen groesser als 25 mm oder kleiner als 5 mm", "Einzugspunkt ausserhalb der Reichweite positionieren"},
RecommendedMeasuresTechnical: []string{"Schutzgitter mit Sicherheitsverriegelung", "Lichtschranke vor dem Einzugsbereich"},
RecommendedMeasuresInformation: []string{"Warnschilder am Einzugspunkt", "Betriebsanweisung zur sicheren Beschickung"},
SuggestedEvidence: []string{"Pruefbericht der Schutzeinrichtung", "Risikobeurteilung nach ISO 12100"},
RelatedKeywords: []string{"Walzen", "Einzugspunkt", "Quetschstelle"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 8),
Category: "mechanical_hazard",
SubCategory: "schergefahr",
Name: "Schergefahr an beweglichen Maschinenteilen",
Description: "Durch gegeneinander bewegte Maschinenteile entstehen Scherstellen, die zu schweren Schnitt- und Trennverletzungen fuehren koennen.",
DefaultSeverity: 5,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Trennende Schutzeinrichtung an der Scherstelle", "Sicherheitsabstand nach ISO 13857"}),
TypicalCauses: []string{"Unzureichender Sicherheitsabstand", "Fehlende Schutzverkleidung", "Eingriff waehrend des Betriebs"},
TypicalHarm: "Schnitt- und Trennverletzungen an Fingern und Haenden",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Sicherheitsabstaende nach ISO 13857 einhalten", "Scherstellen konstruktiv vermeiden"},
RecommendedMeasuresTechnical: []string{"Verriegelte Schutzhauben", "Not-Halt in unmittelbarer Naehe"},
RecommendedMeasuresInformation: []string{"Gefahrenhinweis an Scherstellen", "Schulung der Bediener"},
SuggestedEvidence: []string{"Abstandsmessung gemaess ISO 13857", "Risikobeurteilung"},
RelatedKeywords: []string{"Scherstelle", "Gegenlaeufig", "Schneidgefahr"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 9),
Category: "mechanical_hazard",
SubCategory: "schneidgefahr",
Name: "Schneidgefahr durch rotierende Werkzeuge",
Description: "Rotierende Schneidwerkzeuge wie Fraeser, Saegeblaetter oder Messer koennen bei Kontakt schwere Schnittverletzungen verursachen.",
DefaultSeverity: 5,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Vollstaendige Einhausung des Werkzeugs", "Automatische Werkzeugbremse bei Schutztueroeffnung"}),
TypicalCauses: []string{"Offene Schutzhaube waehrend des Betriebs", "Nachlauf des Werkzeugs nach Abschaltung", "Werkzeugbruch"},
TypicalHarm: "Tiefe Schnittwunden bis hin zu Gliedmassentrennung",
RelevantLifecyclePhases: []string{"normal_operation", "setup", "maintenance"},
RecommendedMeasuresDesign: []string{"Vollstaendige Einhausung mit Verriegelung", "Werkzeugbremse mit kurzer Nachlaufzeit"},
RecommendedMeasuresTechnical: []string{"Verriegelte Schutzhaube mit Zuhaltung", "Drehzahlueberwachung"},
RecommendedMeasuresInformation: []string{"Warnhinweis zur Nachlaufzeit", "Betriebsanweisung zum Werkzeugwechsel"},
SuggestedEvidence: []string{"Nachlaufzeitmessung", "Pruefbericht Schutzeinrichtung"},
RelatedKeywords: []string{"Fraeser", "Saegeblatt", "Schneidwerkzeug"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 10),
Category: "mechanical_hazard",
SubCategory: "einzugsgefahr",
Name: "Einzugsgefahr durch Foerderbaender",
Description: "An Umlenkrollen und Antriebstrommeln von Foerderbaendern bestehen Einzugsstellen, die Koerperteile oder Kleidung erfassen koennen.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Schutzverkleidung an Umlenkrollen", "Not-Halt-Reissleine entlang des Foerderbands"}),
TypicalCauses: []string{"Fehlende Abdeckung an Umlenkpunkten", "Reinigung bei laufendem Band", "Lose Kleidung des Personals"},
TypicalHarm: "Einzugsverletzungen an Armen und Haenden, Quetschungen",
RelevantLifecyclePhases: []string{"normal_operation", "cleaning", "maintenance"},
RecommendedMeasuresDesign: []string{"Umlenkrollen mit Schutzverkleidung", "Unterflur-Foerderung wo moeglich"},
RecommendedMeasuresTechnical: []string{"Not-Halt-Reissleine", "Bandschieflauf-Erkennung"},
RecommendedMeasuresInformation: []string{"Kleidervorschrift fuer Bedienpersonal", "Sicherheitsunterweisung"},
SuggestedEvidence: []string{"Pruefbericht der Schutzeinrichtungen", "Risikobeurteilung"},
RelatedKeywords: []string{"Foerderband", "Umlenkrolle", "Einzugsstelle"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 11),
Category: "mechanical_hazard",
SubCategory: "erfassungsgefahr",
Name: "Erfassungsgefahr durch rotierende Wellen",
Description: "Freiliegende rotierende Wellen, Kupplungen oder Zapfen koennen Kleidung oder Haare erfassen und Personen in die Drehbewegung hineinziehen.",
DefaultSeverity: 5,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Vollstaendige Verkleidung rotierender Wellen", "Drehmomentbegrenzung"}),
TypicalCauses: []string{"Fehlende Wellenabdeckung", "Lose Kleidungsstuecke", "Wartung bei laufender Welle"},
TypicalHarm: "Erfassungsverletzungen mit Knochenbruechen, Skalpierungen oder toedlichem Ausgang",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Wellen vollstaendig einhausen", "Kupplungen mit Schutzhuelsen"},
RecommendedMeasuresTechnical: []string{"Verriegelte Schutzabdeckung", "Stillstandsueberwachung fuer Wartungszugang"},
RecommendedMeasuresInformation: []string{"Kleiderordnung ohne lose Teile", "Warnschilder an Wellenabdeckungen"},
SuggestedEvidence: []string{"Inspektionsbericht Wellenabdeckungen", "Risikobeurteilung"},
RelatedKeywords: []string{"Welle", "Kupplung", "Erfassung"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 12),
Category: "mechanical_hazard",
SubCategory: "stossgefahr",
Name: "Stossgefahr durch pneumatische/hydraulische Zylinder",
Description: "Schnell ausfahrende Pneumatik- oder Hydraulikzylinder koennen Personen stossen oder einklemmen, insbesondere bei unerwartetem Anlauf.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"actuator", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Geschwindigkeitsbegrenzung durch Drosselventile", "Schutzeinrichtung im Bewegungsbereich"}),
TypicalCauses: []string{"Fehlende Endlagendaempfung", "Unerwarteter Druckaufbau", "Aufenthalt im Bewegungsbereich"},
TypicalHarm: "Prellungen, Knochenbrueche, Einklemmverletzungen",
RelevantLifecyclePhases: []string{"normal_operation", "setup", "maintenance"},
RecommendedMeasuresDesign: []string{"Endlagendaempfung vorsehen", "Zylindergeschwindigkeit begrenzen"},
RecommendedMeasuresTechnical: []string{"Lichtvorhang im Bewegungsbereich", "Druckspeicher-Entlastungsventil"},
RecommendedMeasuresInformation: []string{"Kennzeichnung des Bewegungsbereichs", "Betriebsanweisung"},
SuggestedEvidence: []string{"Geschwindigkeitsmessung", "Risikobeurteilung"},
RelatedKeywords: []string{"Zylinder", "Pneumatik", "Stossgefahr"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 13),
Category: "mechanical_hazard",
SubCategory: "herabfallende_teile",
Name: "Herabfallende Teile aus Werkstueckhalterung",
Description: "Unzureichend gesicherte Werkstuecke oder Werkzeuge koennen sich aus der Halterung loesen und herabfallen.",
DefaultSeverity: 4,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Spannkraftueberwachung der Halterung", "Schutzdach ueber dem Bedienerbereich"}),
TypicalCauses: []string{"Unzureichende Spannkraft", "Vibration lockert die Halterung", "Falsches Werkstueck-Spannmittel"},
TypicalHarm: "Kopfverletzungen, Prellungen, Quetschungen durch herabfallende Teile",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Spannkraftueberwachung mit Abschaltung", "Auffangvorrichtung unter Werkstueck"},
RecommendedMeasuresTechnical: []string{"Sensor zur Spannkraftueberwachung", "Schutzhaube"},
RecommendedMeasuresInformation: []string{"Pruefanweisung vor Bearbeitungsstart", "Schutzhelmpflicht im Gefahrenbereich"},
SuggestedEvidence: []string{"Pruefprotokoll Spannmittel", "Risikobeurteilung"},
RelatedKeywords: []string{"Werkstueck", "Spannmittel", "Herabfallen"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 14),
Category: "mechanical_hazard",
SubCategory: "wegschleudern",
Name: "Wegschleudern von Bruchstuecken bei Werkzeugversagen",
Description: "Bei Werkzeugbruch koennen Bruchstuecke mit hoher Geschwindigkeit weggeschleudert werden und Personen im Umfeld verletzen.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Splitterschutzscheibe aus Polycarbonat", "Regelmae­ssige Werkzeuginspektion"}),
TypicalCauses: []string{"Werkzeugverschleiss", "Ueberschreitung der zulaessigen Drehzahl", "Materialfehler im Werkzeug"},
TypicalHarm: "Durchdringende Verletzungen durch Bruchstuecke, Augenverletzungen",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Splitterschutz in der Einhausung", "Drehzahlbegrenzung des Werkzeugs"},
RecommendedMeasuresTechnical: []string{"Unwuchtueberwachung", "Brucherkennungssensor"},
RecommendedMeasuresInformation: []string{"Maximaldrehzahl am Werkzeug kennzeichnen", "Schutzbrillenpflicht"},
SuggestedEvidence: []string{"Bersttest der Einhausung", "Werkzeuginspektionsprotokoll"},
RelatedKeywords: []string{"Werkzeugbruch", "Splitter", "Schleudern"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 15),
Category: "mechanical_hazard",
SubCategory: "instabilitaet",
Name: "Instabilitaet der Maschine durch fehlendes Fundament",
Description: "Eine unzureichend verankerte oder falsch aufgestellte Maschine kann kippen oder sich verschieben, insbesondere bei dynamischen Kraeften.",
DefaultSeverity: 4,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Fundamentberechnung und Verankerung", "Standsicherheitsnachweis"}),
TypicalCauses: []string{"Fehlende Bodenverankerung", "Ungeeigneter Untergrund", "Erhoehte dynamische Lasten"},
TypicalHarm: "Quetschverletzungen durch kippende Maschine, Sachschaeden",
RelevantLifecyclePhases: []string{"installation", "normal_operation", "transport"},
RecommendedMeasuresDesign: []string{"Niedriger Schwerpunkt der Maschine", "Befestigungspunkte im Maschinenrahmen"},
RecommendedMeasuresTechnical: []string{"Bodenverankerung mit Schwerlastduebeln", "Nivellierelemente mit Kippsicherung"},
RecommendedMeasuresInformation: []string{"Aufstellanleitung mit Fundamentplan", "Hinweis auf maximale Bodenbelastung"},
SuggestedEvidence: []string{"Standsicherheitsnachweis", "Fundamentplan"},
RelatedKeywords: []string{"Fundament", "Standsicherheit", "Kippen"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 16),
Category: "mechanical_hazard",
SubCategory: "wiederanlauf",
Name: "Unkontrollierter Wiederanlauf nach Energieunterbruch",
Description: "Nach einem Stromausfall oder Druckabfall kann die Maschine unkontrolliert wieder anlaufen und Personen im Gefahrenbereich verletzen.",
DefaultSeverity: 5,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical", "controller", "electrical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Wiederanlaufsperre nach Energierueckkehr", "Quittierungspflichtiger Neustart"}),
TypicalCauses: []string{"Fehlende Wiederanlaufsperre", "Stromausfall mit anschliessendem automatischem Neustart", "Druckaufbau nach Leckagereparatur"},
TypicalHarm: "Verletzungen durch unerwartete Maschinenbewegung bei Wiederanlauf",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance", "fault_finding"},
RecommendedMeasuresDesign: []string{"Wiederanlaufsperre in der Steuerung", "Energiespeicher sicher entladen"},
RecommendedMeasuresTechnical: []string{"Schaltschuetz mit Selbsthaltung", "Druckschalter mit Ruecksetzbedingung"},
RecommendedMeasuresInformation: []string{"Hinweis auf Wiederanlaufverhalten", "Verfahrensanweisung nach Energieausfall"},
SuggestedEvidence: []string{"Funktionstest Wiederanlaufsperre", "Risikobeurteilung"},
RelatedKeywords: []string{"Wiederanlauf", "Stromausfall", "Anlaufsperre"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 17),
Category: "mechanical_hazard",
SubCategory: "reibungsgefahr",
Name: "Reibungsgefahr an rauen Oberflaechen",
Description: "Raue, scharfkantige oder gratbehaftete Maschinenoberlaechen koennen bei Kontakt zu Hautabschuerfungen und Schnittverletzungen fuehren.",
DefaultSeverity: 3,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 4,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Entgraten aller zugaenglichen Kanten", "Schutzhandschuhe fuer Bedienpersonal"}),
TypicalCauses: []string{"Nicht entgratete Schnittkanten", "Korrosionsraue Oberflaechen", "Verschleissbedingter Materialabtrag"},
TypicalHarm: "Hautabschuerfungen, Schnittverletzungen an Haenden und Armen",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance", "setup"},
RecommendedMeasuresDesign: []string{"Kanten brechen oder abrunden", "Glatte Oberflaechen an Kontaktstellen"},
RecommendedMeasuresTechnical: []string{"Kantenschutzprofile anbringen"},
RecommendedMeasuresInformation: []string{"Hinweis auf scharfe Kanten", "Handschuhpflicht in der Betriebsanweisung"},
SuggestedEvidence: []string{"Oberflaechenpruefung", "Risikobeurteilung"},
RelatedKeywords: []string{"Grat", "Scharfkantig", "Oberflaeche"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 18),
Category: "mechanical_hazard",
SubCategory: "hochdruckstrahl",
Name: "Fluessigkeitshochdruckstrahl",
Description: "Hochdruckstrahlen aus Hydraulik-, Kuehl- oder Reinigungssystemen koennen Haut durchdringen und schwere Gewebeschaeden verursachen.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Abschirmung von Hochdruckleitungen", "Regelmae­ssige Leitungsinspektion"}),
TypicalCauses: []string{"Leitungsbruch unter Hochdruck", "Undichte Verschraubungen", "Alterung von Schlauchleitungen"},
TypicalHarm: "Hochdruckinjektionsverletzungen, Gewebsnekrose",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Schlauchbruchsicherungen einbauen", "Leitungen ausserhalb des Aufenthaltsbereichs verlegen"},
RecommendedMeasuresTechnical: []string{"Druckabschaltung bei Leitungsbruch", "Schutzblechverkleidung"},
RecommendedMeasuresInformation: []string{"Warnhinweis an Hochdruckleitungen", "Prueffristen fuer Schlauchleitungen"},
SuggestedEvidence: []string{"Druckpruefprotokoll", "Inspektionsbericht Schlauchleitungen"},
RelatedKeywords: []string{"Hochdruck", "Hydraulikleitung", "Injection"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 19),
Category: "mechanical_hazard",
SubCategory: "federelemente",
Name: "Gefahr durch federgespannte Elemente",
Description: "Unter Spannung stehende Federn oder elastische Elemente koennen bei unkontrolliertem Loesen Teile wegschleudern oder Personen verletzen.",
DefaultSeverity: 4,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Gesicherte Federentspannung vor Demontage", "Warnung bei vorgespannten Elementen"}),
TypicalCauses: []string{"Demontage ohne vorherige Entspannung", "Materialermuedung der Feder", "Fehlende Kennzeichnung vorgespannter Elemente"},
TypicalHarm: "Verletzungen durch wegschleudernde Federelemente, Prellungen",
RelevantLifecyclePhases: []string{"maintenance", "decommissioning"},
RecommendedMeasuresDesign: []string{"Sichere Entspannungsmoeglichkeit vorsehen", "Federn mit Bruchsicherung"},
RecommendedMeasuresTechnical: []string{"Spezialwerkzeug zur Federentspannung"},
RecommendedMeasuresInformation: []string{"Kennzeichnung vorgespannter Elemente", "Wartungsanweisung mit Entspannungsprozedur"},
SuggestedEvidence: []string{"Wartungsanweisung", "Risikobeurteilung"},
RelatedKeywords: []string{"Feder", "Vorspannung", "Energiespeicher"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 20),
Category: "mechanical_hazard",
SubCategory: "schutztor",
Name: "Quetschgefahr im Schliessbereich von Schutztoren",
Description: "Automatisch schliessende Schutztore und -tueren koennen Personen im Schliessbereich einklemmen oder quetschen.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "actuator", "sensor"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Schliess­kantensicherung mit Kontaktleiste", "Lichtschranke im Schliessbereich"}),
TypicalCauses: []string{"Fehlende Schliesskantensicherung", "Defekter Sensor", "Person im Schliessbereich nicht erkannt"},
TypicalHarm: "Quetschverletzungen an Koerper oder Gliedmassen",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Schliess­kraftbegrenzung", "Reversierautomatik bei Hindernis"},
RecommendedMeasuresTechnical: []string{"Kontaktleiste an der Schliesskante", "Lichtschranke im Durchgangsbereich"},
RecommendedMeasuresInformation: []string{"Warnhinweis am Schutztor", "Automatik-Betrieb kennzeichnen"},
SuggestedEvidence: []string{"Schliesskraftmessung", "Funktionstest Reversierautomatik"},
RelatedKeywords: []string{"Schutztor", "Schliesskante", "Einklemmen"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
}
}

View File

@@ -0,0 +1,417 @@
package iace
import "time"
// builtinHazardsISO12100Pneumatic returns ISO 12100 pneumatic/hydraulic
// and noise/vibration hazard entries per Maschinenverordnung 2023/1230.
func builtinHazardsISO12100Pneumatic() []HazardLibraryEntry {
now := time.Now()
return []HazardLibraryEntry{
// ====================================================================
// Category: pneumatic_hydraulic (indices 1-10, 10 entries)
// ====================================================================
{
ID: hazardUUID("pneumatic_hydraulic", 1),
Category: "pneumatic_hydraulic",
SubCategory: "druckverlust",
Name: "Unkontrollierter Druckverlust in pneumatischem System",
Description: "Ein ploetzlicher Druckabfall im Pneumatiksystem kann zum Versagen von Halte- und Klemmfunktionen fuehren, wodurch Werkstuecke herabfallen oder Achsen absacken.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"actuator", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Rueckschlagventile in Haltezylinderleitungen", "Druckueberwachung mit sicherer Abschaltung"}),
TypicalCauses: []string{"Kompressorausfall", "Leckage in der Versorgungsleitung", "Fehlerhaftes Druckregelventil"},
TypicalHarm: "Quetschverletzungen durch absackende Achsen oder herabfallende Werkstuecke",
RelevantLifecyclePhases: []string{"normal_operation", "fault_finding"},
RecommendedMeasuresDesign: []string{"Mechanische Haltebremsen als Rueckfallebene", "Rueckschlagventile in sicherheitsrelevanten Leitungen"},
RecommendedMeasuresTechnical: []string{"Druckwaechter mit sicherer Reaktion", "Druckspeicher fuer Notbetrieb"},
RecommendedMeasuresInformation: []string{"Warnung bei Druckabfall", "Verfahrensanweisung fuer Druckausfall"},
SuggestedEvidence: []string{"Druckabfalltest", "Risikobeurteilung"},
RelatedKeywords: []string{"Druckverlust", "Pneumatik", "Haltefunktion"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 2),
Category: "pneumatic_hydraulic",
SubCategory: "druckfreisetzung",
Name: "Ploetzliche Druckfreisetzung bei Leitungsbruch",
Description: "Ein Bersten oder Abreissen einer Druckleitung setzt schlagartig Energie frei, wobei Medien und Leitungsbruchstuecke weggeschleudert werden.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Schlauchbruchsicherungen", "Druckfeste Leitungsverlegung"}),
TypicalCauses: []string{"Materialermuedung der Leitung", "Ueberdruckbetrieb", "Mechanische Beschaedigung der Leitung"},
TypicalHarm: "Verletzungen durch weggeschleuderte Leitungsteile und austretende Druckmedien",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Berstdruckfest dimensionierte Leitungen", "Leitungen in Schutzrohren verlegen"},
RecommendedMeasuresTechnical: []string{"Durchflussbegrenzer nach Druckquelle", "Schlauchbruchventile"},
RecommendedMeasuresInformation: []string{"Prueffristen fuer Druckleitungen", "Warnhinweis an Hochdruckbereichen"},
SuggestedEvidence: []string{"Druckpruefprotokoll", "Inspektionsbericht Leitungen"},
RelatedKeywords: []string{"Leitungsbruch", "Druckfreisetzung", "Bersten"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 3),
Category: "pneumatic_hydraulic",
SubCategory: "schlauchpeitschen",
Name: "Schlauchpeitschen durch Berstversagen",
Description: "Ein unter Druck stehender Schlauch kann bei Versagen unkontrolliert umherschlagen und Personen im Umfeld treffen.",
DefaultSeverity: 4,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Fangseile an Schlauchleitungen", "Schlauchbruchventile"}),
TypicalCauses: []string{"Alterung des Schlauchmaterials", "Knicke in der Schlauchfuehrung", "Falsche Schlauchtype fuer das Medium"},
TypicalHarm: "Peitschenverletzungen, Prellungen, Augenverletzungen",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Fangseile oder Ketten an allen Schlauchleitungen", "Festverrohrung statt Schlauch wo moeglich"},
RecommendedMeasuresTechnical: []string{"Schlauchbruchventil am Anschluss"},
RecommendedMeasuresInformation: []string{"Tauschintervalle fuer Schlauchleitungen", "Kennzeichnung mit Herstelldatum"},
SuggestedEvidence: []string{"Schlauchleitungspruefprotokoll", "Risikobeurteilung"},
RelatedKeywords: []string{"Schlauch", "Peitschen", "Fangseil"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 4),
Category: "pneumatic_hydraulic",
SubCategory: "druckspeicherenergie",
Name: "Unerwartete Bewegung durch Druckspeicherrestenergie",
Description: "Nach dem Abschalten der Maschine kann in Druckspeichern verbliebene Energie unerwartete Bewegungen von Zylindern oder Aktoren verursachen.",
DefaultSeverity: 5,
DefaultProbability: 3,
DefaultExposure: 2,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"actuator", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Automatische Druckspeicher-Entladung bei Abschaltung", "Sperrventile vor Aktoren"}),
TypicalCauses: []string{"Nicht entladener Druckspeicher", "Fehlendes Entlastungsventil", "Wartungszugriff ohne Druckfreischaltung"},
TypicalHarm: "Quetsch- und Stossverletzungen durch unerwartete Zylinderbewegung",
RelevantLifecyclePhases: []string{"maintenance", "fault_finding", "decommissioning"},
RecommendedMeasuresDesign: []string{"Automatische Speicherentladung bei Hauptschalter-Aus", "Manuelles Entlastungsventil mit Druckanzeige"},
RecommendedMeasuresTechnical: []string{"Druckmanometer am Speicher", "Verriegeltes Entlastungsventil"},
RecommendedMeasuresInformation: []string{"Warnschild Druckspeicher", "LOTO-Verfahren fuer Druckspeicher"},
SuggestedEvidence: []string{"Funktionstest Speicherentladung", "Risikobeurteilung"},
RelatedKeywords: []string{"Druckspeicher", "Restenergie", "Speicherentladung"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 5),
Category: "pneumatic_hydraulic",
SubCategory: "oelkontamination",
Name: "Kontamination von Hydraulikoel durch Partikel",
Description: "Verunreinigungen im Hydraulikoel fuehren zu erhoehtem Verschleiss an Ventilen und Dichtungen, was Leckagen und Funktionsversagen ausloest.",
DefaultSeverity: 3,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 4,
ApplicableComponentTypes: []string{"actuator", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Feinfilterung des Hydraulikoels", "Regelmaessige Oelanalyse"}),
TypicalCauses: []string{"Verschleisspartikel im System", "Verschmutzte Nachfuellung", "Defekte Filterelemente"},
TypicalHarm: "Maschinenausfall mit Folgeverletzungen durch ploetzliches Versagen hydraulischer Funktionen",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Mehrfachfiltration mit Bypass-Anzeige", "Geschlossener Nachfuellkreislauf"},
RecommendedMeasuresTechnical: []string{"Online-Partikelzaehler", "Differenzdruckanzeige am Filter"},
RecommendedMeasuresInformation: []string{"Oelwechselintervalle festlegen", "Sauberkeitsvorgaben fuer Nachfuellung"},
SuggestedEvidence: []string{"Oelanalysebericht", "Filterwechselprotokoll"},
RelatedKeywords: []string{"Hydraulikoel", "Kontamination", "Filtration"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 6),
Category: "pneumatic_hydraulic",
SubCategory: "leckage",
Name: "Leckage an Hochdruckverbindungen",
Description: "Undichte Verschraubungen oder Dichtungen an Hochdruckverbindungen fuehren zu Medienaustritt, Rutschgefahr und moeglichen Hochdruckinjektionsverletzungen.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Leckagefreie Verschraubungen verwenden", "Auffangwannen unter Verbindungsstellen"}),
TypicalCauses: []string{"Vibrationsbedingte Lockerung", "Alterung der Dichtungen", "Falsches Anzugsmoment"},
TypicalHarm: "Rutschverletzungen, Hochdruckinjektion bei feinem Oelstrahl",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Verschraubungen mit Sicherungsmitteln", "Leckage-Auffangvorrichtungen"},
RecommendedMeasuresTechnical: []string{"Fuellstandsueberwachung im Tank", "Leckagesensor"},
RecommendedMeasuresInformation: []string{"Sichtpruefung in Wartungsplan aufnehmen", "Hinweis auf Injektionsgefahr"},
SuggestedEvidence: []string{"Leckagepruefprotokoll", "Risikobeurteilung"},
RelatedKeywords: []string{"Leckage", "Verschraubung", "Hochdruck"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 7),
Category: "pneumatic_hydraulic",
SubCategory: "kavitation",
Name: "Kavitation in Hydraulikpumpe",
Description: "Dampfblasenbildung und deren Implosion in der Hydraulikpumpe fuehren zu Materialabtrag, Leistungsverlust und ploetzlichem Pumpenversagen.",
DefaultSeverity: 3,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 4,
ApplicableComponentTypes: []string{"actuator", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Korrekte Saughoehe einhalten", "Saugleitungsdimensionierung pruefen"}),
TypicalCauses: []string{"Zu kleine Saugleitung", "Verstopfter Saugfilter", "Zu hohe Oelviskositaet bei Kaelte"},
TypicalHarm: "Maschinenausfall durch Pumpenversagen mit moeglichen Folgeverletzungen",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Saugleitung grosszuegig dimensionieren", "Ueberdruck-Zulaufsystem"},
RecommendedMeasuresTechnical: []string{"Vakuumanzeige an der Saugseite", "Temperaturueberwachung des Oels"},
RecommendedMeasuresInformation: []string{"Vorwaermverfahren bei Kaeltestart", "Wartungsintervall Saugfilter"},
SuggestedEvidence: []string{"Saugdruckmessung", "Pumpeninspektionsbericht"},
RelatedKeywords: []string{"Kavitation", "Hydraulikpumpe", "Saugleitung"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 8),
Category: "pneumatic_hydraulic",
SubCategory: "ueberdruckversagen",
Name: "Ueberdruckversagen durch defektes Druckbegrenzungsventil",
Description: "Ein klemmendes oder falsch eingestelltes Druckbegrenzungsventil laesst den Systemdruck unkontrolliert ansteigen, was zum Bersten von Komponenten fuehren kann.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"actuator", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Redundantes Druckbegrenzungsventil", "Druckschalter mit Abschaltung"}),
TypicalCauses: []string{"Verschmutztes Druckbegrenzungsventil", "Falsche Einstellung nach Wartung", "Ermuedung der Ventilfeder"},
TypicalHarm: "Bersten von Leitungen und Gehaeusen mit Splitterwurf, Hochdruckinjektionsverletzungen",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Redundante Druckbegrenzung", "Berstscheibe als letzte Sicherung"},
RecommendedMeasuresTechnical: []string{"Druckschalter mit sicherer Pumpenabschaltung", "Manometer mit Schleppzeiger"},
RecommendedMeasuresInformation: []string{"Pruefintervall Druckbegrenzungsventil", "Einstellprotokoll nach Wartung"},
SuggestedEvidence: []string{"Ventilpruefprotokoll", "Druckverlaufsmessung"},
RelatedKeywords: []string{"Ueberdruck", "Druckbegrenzungsventil", "Bersten"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 9),
Category: "pneumatic_hydraulic",
SubCategory: "ventilversagen",
Name: "Unkontrollierte Zylinderbewegung bei Ventilversagen",
Description: "Bei Ausfall oder Fehlfunktion eines Wegeventils kann ein Zylinder unkontrolliert ein- oder ausfahren und Personen im Bewegungsbereich verletzen.",
DefaultSeverity: 5,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 2,
ApplicableComponentTypes: []string{"actuator", "controller"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Redundante Ventile fuer sicherheitskritische Achsen", "Lasthalteventile an Vertikalachsen"}),
TypicalCauses: []string{"Elektromagnetausfall am Ventil", "Ventilschieber klemmt", "Kontamination blockiert Ventilsitz"},
TypicalHarm: "Quetsch- und Stossverletzungen durch unkontrollierte Zylinderbewegung",
RelevantLifecyclePhases: []string{"normal_operation", "fault_finding"},
RecommendedMeasuresDesign: []string{"Redundante Ventilanordnung mit Ueberwachung", "Lasthalteventile fuer schwerkraftbelastete Achsen"},
RecommendedMeasuresTechnical: []string{"Positionsueberwachung am Zylinder", "Ventil-Stellungsueberwachung"},
RecommendedMeasuresInformation: []string{"Fehlermeldung bei Ventildiskrepanz", "Notfallprozedur bei Ventilversagen"},
SuggestedEvidence: []string{"Funktionstest Redundanz", "FMEA Ventilschaltung"},
RelatedKeywords: []string{"Wegeventil", "Zylinderversagen", "Ventilausfall"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("pneumatic_hydraulic", 10),
Category: "pneumatic_hydraulic",
SubCategory: "viskositaet",
Name: "Temperaturbedingte Viskositaetsaenderung von Hydraulikmedium",
Description: "Extreme Temperaturen veraendern die Viskositaet des Hydraulikoels so stark, dass Ventile und Pumpen nicht mehr zuverlaessig arbeiten und Sicherheitsfunktionen versagen.",
DefaultSeverity: 3,
DefaultProbability: 2,
DefaultExposure: 2,
DefaultAvoidance: 4,
ApplicableComponentTypes: []string{"actuator", "mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Oeltemperierung", "Oelsorte mit breitem Viskositaetsbereich"}),
TypicalCauses: []string{"Kaltstart ohne Vorwaermung", "Ueberhitzung durch mangelnde Kuehlung", "Falsche Oelsorte"},
TypicalHarm: "Funktionsversagen hydraulischer Sicherheitseinrichtungen",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Oelkuehler und Oelheizung vorsehen", "Temperaturbereich der Oelsorte abstimmen"},
RecommendedMeasuresTechnical: []string{"Oeltemperatursensor mit Warnmeldung", "Aufwaermprogramm in der Steuerung"},
RecommendedMeasuresInformation: []string{"Zulaessiger Temperaturbereich in Betriebsanleitung", "Oelwechselvorschrift"},
SuggestedEvidence: []string{"Temperaturverlaufsmessung", "Oeldatenblatt"},
RelatedKeywords: []string{"Viskositaet", "Oeltemperatur", "Hydraulikmedium"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: noise_vibration (indices 1-6, 6 entries)
// ====================================================================
{
ID: hazardUUID("noise_vibration", 1),
Category: "noise_vibration",
SubCategory: "dauerschall",
Name: "Gehoerschaedigung durch Dauerschallpegel",
Description: "Dauerhaft erhoehte Schallpegel am Arbeitsplatz ueber dem Grenzwert fuehren zu irreversiblen Gehoerschaeden bei den Maschinenbedienern.",
DefaultSeverity: 4,
DefaultProbability: 4,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Laermminderung an der Quelle", "Gehoerschutzpflicht ab 85 dB(A)"}),
TypicalCauses: []string{"Nicht gekapselte Antriebe", "Metallische Schlagvorgaenge", "Fehlende Schalldaemmung"},
TypicalHarm: "Laermschwerhoerigkeit, Tinnitus",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Laermarme Antriebe und Getriebe", "Schwingungsdaempfende Lagerung"},
RecommendedMeasuresTechnical: []string{"Schallschutzkapseln", "Schallschutzwaende"},
RecommendedMeasuresInformation: []string{"Laermbereichskennzeichnung", "Gehoerschutzpflicht beschildern"},
SuggestedEvidence: []string{"Laermpegelmessung am Arbeitsplatz", "Laermkataster"},
RelatedKeywords: []string{"Laerm", "Gehoerschutz", "Schallpegel"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("noise_vibration", 2),
Category: "noise_vibration",
SubCategory: "hand_arm_vibration",
Name: "Hand-Arm-Vibrationssyndrom durch vibrierende Werkzeuge",
Description: "Langzeitige Nutzung handgefuehrter vibrierender Werkzeuge kann zu Durchblutungsstoerungen, Nervenschaeden und Gelenkbeschwerden in Haenden und Armen fuehren.",
DefaultSeverity: 4,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Vibrationsgedaempfte Werkzeuge verwenden", "Expositionszeit begrenzen"}),
TypicalCauses: []string{"Ungepufferte Handgriffe", "Verschlissene Werkzeuge mit erhoehter Vibration", "Fehlende Arbeitszeitbegrenzung"},
TypicalHarm: "Weissfingerkrankheit, Karpaltunnelsyndrom, Gelenkarthrose",
RelevantLifecyclePhases: []string{"normal_operation", "maintenance"},
RecommendedMeasuresDesign: []string{"Vibrationsgedaempfte Griffe", "Automatisierung statt Handarbeit"},
RecommendedMeasuresTechnical: []string{"Vibrationsmessung am Werkzeug", "Anti-Vibrationshandschuhe"},
RecommendedMeasuresInformation: []string{"Expositionsdauer dokumentieren", "Arbeitsmedizinische Vorsorge anbieten"},
SuggestedEvidence: []string{"Vibrationsmessung nach ISO 5349", "Expositionsberechnung"},
RelatedKeywords: []string{"Vibration", "Hand-Arm", "HAVS"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("noise_vibration", 3),
Category: "noise_vibration",
SubCategory: "ganzkoerpervibration",
Name: "Ganzkoerpervibration an Bedienplaetzen",
Description: "Vibrationen, die ueber den Sitz oder die Standflaeche auf den gesamten Koerper uebertragen werden, koennen zu Wirbelsaeulenschaeden fuehren.",
DefaultSeverity: 3,
DefaultProbability: 3,
DefaultExposure: 4,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Schwingungsisolierter Fahrersitz", "Vibrationsgedaempfte Stehplattform"}),
TypicalCauses: []string{"Unwucht in rotierenden Teilen", "Unebener Fahrweg", "Fehlende Schwingungsisolierung des Bedienplatzes"},
TypicalHarm: "Bandscheibenschaeden, Rueckenschmerzen, Ermuedung",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Schwingungsisolierte Kabine oder Plattform", "Auswuchten rotierender Massen"},
RecommendedMeasuresTechnical: []string{"Luftgefederter Sitz", "Vibrationsueberwachung mit Grenzwertwarnung"},
RecommendedMeasuresInformation: []string{"Maximalexpositionsdauer festlegen", "Arbeitsmedizinische Vorsorge"},
SuggestedEvidence: []string{"Ganzkoerper-Vibrationsmessung nach ISO 2631", "Expositionsbewertung"},
RelatedKeywords: []string{"Ganzkoerpervibration", "Wirbelsaeule", "Sitzvibrationen"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("noise_vibration", 4),
Category: "noise_vibration",
SubCategory: "impulslaerm",
Name: "Impulslaerm durch Stanz-/Praegevorgaenge",
Description: "Kurzzeitige Schallspitzen bei Stanz-, Praege- oder Nietvorgaengen ueberschreiten den Spitzenschalldruckpegel und schaedigen das Gehoer besonders stark.",
DefaultSeverity: 4,
DefaultProbability: 4,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Schalldaemmende Werkzeugeinhausung", "Impulsschallgedaempfter Gehoerschutz"}),
TypicalCauses: []string{"Metall-auf-Metall-Schlag", "Offene Stanzwerkzeuge", "Fehlende Schalldaemmung"},
TypicalHarm: "Akutes Knalltrauma, irreversible Gehoerschaedigung",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Elastische Werkzeugauflagen", "Geschlossene Werkzeugkammer"},
RecommendedMeasuresTechnical: []string{"Schallschutzkabine um Stanzbereich", "Impulslaermueberwachung"},
RecommendedMeasuresInformation: []string{"Gehoerschutzpflicht-Kennzeichnung", "Schulung zur Impulslaermgefahr"},
SuggestedEvidence: []string{"Spitzenpegelmessung", "Laermgutachten"},
RelatedKeywords: []string{"Impulslaerm", "Stanzen", "Spitzenschallpegel"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("noise_vibration", 5),
Category: "noise_vibration",
SubCategory: "infraschall",
Name: "Infraschall von Grossventilatoren",
Description: "Grosse Ventilatoren und Geblaese erzeugen niederfrequenten Infraschall, der zu Unwohlsein, Konzentrationsstoerungen und Ermuedung fuehren kann.",
DefaultSeverity: 3,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Schwingungsisolierte Aufstellung", "Schalldaempfer in Kanaelen"}),
TypicalCauses: []string{"Grosse Ventilatorschaufeln mit niedriger Drehzahl", "Resonanzen in Luftkanaelen", "Fehlende Schwingungsentkopplung"},
TypicalHarm: "Unwohlsein, Uebelkeit, Konzentrationsstoerungen bei Dauerexposition",
RelevantLifecyclePhases: []string{"normal_operation"},
RecommendedMeasuresDesign: []string{"Schwingungsentkopplung des Ventilators", "Resonanzfreie Kanaldimensionierung"},
RecommendedMeasuresTechnical: []string{"Niederfrequenz-Schalldaempfer", "Infraschall-Messgeraet"},
RecommendedMeasuresInformation: []string{"Aufklaerung ueber Infraschallsymptome", "Expositionshinweise"},
SuggestedEvidence: []string{"Infraschallmessung", "Risikobeurteilung"},
RelatedKeywords: []string{"Infraschall", "Ventilator", "Niederfrequenz"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("noise_vibration", 6),
Category: "noise_vibration",
SubCategory: "resonanz",
Name: "Resonanzschwingungen in Maschinengestell",
Description: "Anregung des Maschinengestells in seiner Eigenfrequenz kann zu unkontrollierten Schwingungen fuehren, die Bauteile ermueden und zum Versagen bringen.",
DefaultSeverity: 4,
DefaultProbability: 2,
DefaultExposure: 3,
DefaultAvoidance: 3,
ApplicableComponentTypes: []string{"mechanical"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I"},
SuggestedMitigations: mustMarshalJSON([]string{"Eigenfrequenzanalyse bei Konstruktion", "Schwingungsdaempfer anbringen"}),
TypicalCauses: []string{"Drehzahl nahe der Eigenfrequenz des Gestells", "Fehlende Daempfungselemente", "Nachtraegliche Massenveraenderungen"},
TypicalHarm: "Materialermuedungsbruch mit Absturz von Bauteilen, Verletzungen durch Bruchstuecke",
RelevantLifecyclePhases: []string{"normal_operation", "setup"},
RecommendedMeasuresDesign: []string{"Eigenfrequenz ausserhalb des Betriebsdrehzahlbereichs legen", "Versteifung des Gestells"},
RecommendedMeasuresTechnical: []string{"Schwingungssensoren mit Grenzwertueberwachung", "Tilger oder Daempfer anbringen"},
RecommendedMeasuresInformation: []string{"Verbotene Drehzahlbereiche kennzeichnen", "Schwingungsueberwachungsanleitung"},
SuggestedEvidence: []string{"Modalanalyse des Gestells", "Schwingungsmessprotokoll"},
RelatedKeywords: []string{"Resonanz", "Eigenfrequenz", "Strukturschwingung"},
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
}
}

View File

@@ -0,0 +1,597 @@
package iace
import "time"
// builtinHazardsMachineSafety returns hazard library entries covering
// mechanical, environmental and machine safety hazards.
func builtinHazardsMachineSafety() []HazardLibraryEntry {
now := time.Now()
return []HazardLibraryEntry{
// Category: mechanical_hazard (6 entries)
// ====================================================================
{
ID: hazardUUID("mechanical_hazard", 1),
Category: "mechanical_hazard",
Name: "Unerwarteter Anlauf nach Spannungsausfall",
Description: "Nach Wiederkehr der Versorgungsspannung laeuft die Maschine unerwartet an, ohne dass eine Startfreigabe durch den Bediener erfolgt ist.",
DefaultSeverity: 5,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"controller", "firmware"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.2.6", "ISO 13849"},
SuggestedMitigations: mustMarshalJSON([]string{"Anlaufschutz", "Anti-Restart-Funktion", "Sicherheitsrelais"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 2),
Category: "mechanical_hazard",
Name: "Restenergie nach Abschalten",
Description: "Gespeicherte kinetische oder potentielle Energie (z.B. Schwungmasse, abgesenktes Werkzeug) wird nach dem Abschalten nicht sicher abgebaut.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"actuator", "controller"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.6", "ISO 13849"},
SuggestedMitigations: mustMarshalJSON([]string{"Energieabbau-Prozedur", "Mechanische Haltevorrichtung", "LOTO-Freischaltung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 3),
Category: "mechanical_hazard",
Name: "Unerwartete Maschinenbewegung",
Description: "Die Maschine fuehrt eine unkontrollierte Bewegung durch (z.B. Antrieb faehrt ohne Kommando los), was Personen im Gefahrenbereich verletzt.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"actuator", "controller", "software"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "IEC 62061"},
SuggestedMitigations: mustMarshalJSON([]string{"Safe Torque Off (STO)", "Geschwindigkeitsueberwachung", "Schutzzaun-Sensorik"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 4),
Category: "mechanical_hazard",
Name: "Teileauswurf durch Fehlfunktion",
Description: "Werkzeuge, Werkstuecke oder Maschinenteile werden bei einer Fehlfunktion unkontrolliert aus der Maschine geschleudert.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"actuator", "controller"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.3.2"},
SuggestedMitigations: mustMarshalJSON([]string{"Trennende Schutzeinrichtung", "Sicherheitsglas", "Spannkraft-Ueberwachung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 5),
Category: "mechanical_hazard",
Name: "Quetschstelle durch fehlende Schutzeinrichtung",
Description: "Zwischen beweglichen und festen Maschinenteilen entstehen Quetschstellen, die bei fehlendem Schutz zu schweren Verletzungen fuehren koennen.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.3.7", "ISO 13857"},
SuggestedMitigations: mustMarshalJSON([]string{"Schutzverkleidung", "Sicherheitsabstaende nach ISO 13857", "Lichtvorhang"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("mechanical_hazard", 6),
Category: "mechanical_hazard",
Name: "Instabile Struktur unter Last",
Description: "Die Maschinenstruktur oder ein Anbauteil versagt unter statischer oder dynamischer Belastung und gefaehrdet Personen in der Naehe.",
DefaultSeverity: 5,
DefaultProbability: 1,
ApplicableComponentTypes: []string{"other"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang I §1.3.1"},
SuggestedMitigations: mustMarshalJSON([]string{"Festigkeitsberechnung", "Ueberlastsicherung", "Regelmaessige Inspektion"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: electrical_hazard (6 entries)
// ====================================================================
{
ID: hazardUUID("electrical_hazard", 1),
Category: "electrical_hazard",
Name: "Elektrischer Schlag an Bedienpanel",
Description: "Bediener kommen mit spannungsfuehrenden Teilen in Beruehrung, z.B. durch defekte Gehaeuseerdung oder fehlerhafte Isolierung.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"hmi", "controller"},
RegulationReferences: []string{"Niederspannungsrichtlinie 2014/35/EU", "IEC 60204-1"},
SuggestedMitigations: mustMarshalJSON([]string{"Schutzkleinspannung (SELV)", "Schutzerdung", "Isolationsmonitoring"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 2),
Category: "electrical_hazard",
Name: "Lichtbogen durch Schaltfehler",
Description: "Ein Schaltfehler erzeugt einen Lichtbogen, der Bediener verletzen, Geraete beschaedigen oder einen Brand ausloesen kann.",
DefaultSeverity: 5,
DefaultProbability: 1,
ApplicableComponentTypes: []string{"controller"},
RegulationReferences: []string{"Niederspannungsrichtlinie 2014/35/EU", "IEC 60204-1"},
SuggestedMitigations: mustMarshalJSON([]string{"Lichtbogenschutz-Schalter", "Kurzschlussschutz", "Geeignete Schaltgeraete"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 3),
Category: "electrical_hazard",
Name: "Kurzschluss durch Isolationsfehler",
Description: "Beschaedigte Kabelisolierungen fuehren zu einem Kurzschluss, der Feuer ausloesen oder Sicherheitsfunktionen ausser Betrieb setzen kann.",
DefaultSeverity: 4,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"network", "controller"},
RegulationReferences: []string{"Niederspannungsrichtlinie 2014/35/EU", "IEC 60204-1"},
SuggestedMitigations: mustMarshalJSON([]string{"Isolationsueberwachung", "Kabelschutz", "Regelmaessige Sichtpruefung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 4),
Category: "electrical_hazard",
Name: "Erdschluss in Steuerkreis",
Description: "Ein Erdschluss im Steuerkreis kann unbeabsichtigte Schaltzustaende ausloesen und Sicherheitsfunktionen beeinflussen.",
DefaultSeverity: 4,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"controller", "network"},
RegulationReferences: []string{"IEC 60204-1", "ISO 13849"},
SuggestedMitigations: mustMarshalJSON([]string{"Erdschluss-Monitoring", "IT-Netz fuer Steuerkreise", "Regelmaessige Pruefung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 5),
Category: "electrical_hazard",
Name: "Gespeicherte Energie in Kondensatoren",
Description: "Nach dem Abschalten verbleiben hohe Spannungen in Kondensatoren (z.B. Frequenzumrichter, USV), was bei Wartungsarbeiten gefaehrlich ist.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"controller"},
RegulationReferences: []string{"Niederspannungsrichtlinie 2014/35/EU", "IEC 60204-1"},
SuggestedMitigations: mustMarshalJSON([]string{"Entladewartezeit", "Automatische Entladeschaltung", "Warnhinweise am Geraet"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("electrical_hazard", 6),
Category: "electrical_hazard",
Name: "Elektromagnetische Kopplung auf Safety-Leitung",
Description: "Hochfrequente Stoerfelder koppeln auf ungeschirmte Safety-Leitungen und erzeugen Falschsignale, die Sicherheitsfunktionen fehl ausloesen.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"network", "sensor"},
RegulationReferences: []string{"EMV-Richtlinie 2014/30/EU", "IEC 62061"},
SuggestedMitigations: mustMarshalJSON([]string{"Geschirmte Kabel", "Raeumliche Trennung", "EMV-Pruefung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: thermal_hazard (4 entries)
// ====================================================================
{
ID: hazardUUID("thermal_hazard", 1),
Category: "thermal_hazard",
Name: "Ueberhitzung der Steuereinheit",
Description: "Die Steuereinheit ueberschreitet die zulaessige Betriebstemperatur durch Lueftungsausfall oder hohe Umgebungstemperatur, was zu Fehlfunktionen fuehrt.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"controller", "firmware"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068"},
SuggestedMitigations: mustMarshalJSON([]string{"Temperaturueberwachung", "Redundante Lueftung", "Thermisches Abschalten"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("thermal_hazard", 2),
Category: "thermal_hazard",
Name: "Brandgefahr durch Leistungselektronik",
Description: "Defekte Leistungshalbleiter oder Kondensatoren in der Leistungselektronik erwaermen sich unkontrolliert und koennen einen Brand ausloesen.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"controller"},
RegulationReferences: []string{"Niederspannungsrichtlinie 2014/35/EU", "IEC 60204-1"},
SuggestedMitigations: mustMarshalJSON([]string{"Thermosicherungen", "Temperatursensoren", "Brandschutzmassnahmen"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("thermal_hazard", 3),
Category: "thermal_hazard",
Name: "Einfrieren bei Tieftemperatur",
Description: "Sehr tiefe Umgebungstemperaturen fuehren zum Einfrieren von Hydraulikleitungen oder Elektronik und damit zum Ausfall von Sicherheitsfunktionen.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"controller", "actuator"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 60068"},
SuggestedMitigations: mustMarshalJSON([]string{"Heizung", "Mindestbetriebstemperatur definieren", "Temperatursensor"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("thermal_hazard", 4),
Category: "thermal_hazard",
Name: "Waermestress an Kabelisolierung",
Description: "Langfristige Einwirkung hoher Temperaturen auf Kabelisolierungen fuehrt zu Alterung und Isolationsversagen mit Kurzschlussrisiko.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"network", "controller"},
RegulationReferences: []string{"IEC 60204-1", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Hitzebestaendige Kabel (z.B. PTFE)", "Kabelverlegung mit Abstand zur Waermequelle", "Regelmaessige Inspektion"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: emc_hazard (5 entries)
// ====================================================================
{
ID: hazardUUID("emc_hazard", 1),
Category: "emc_hazard",
Name: "EMV-Stoerabstrahlung auf Safety-Bus",
Description: "Hohe elektromagnetische Stoerabstrahlung aus benachbarten Geraeten stoert den industriellen Safety-Bus (z.B. PROFIsafe) und erzeugt Kommunikationsfehler.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"network", "controller"},
RegulationReferences: []string{"EMV-Richtlinie 2014/30/EU", "IEC 62061", "IEC 61784-3"},
SuggestedMitigations: mustMarshalJSON([]string{"EMV-gerechte Verkabelung", "Schirmung", "EMC-Pruefung nach EN 55011"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("emc_hazard", 2),
Category: "emc_hazard",
Name: "Unbeabsichtigte elektromagnetische Abstrahlung",
Description: "Die Maschine selbst strahlt starke EM-Felder ab, die andere sicherheitsrelevante Einrichtungen in der Naehe stoeren.",
DefaultSeverity: 2,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"controller", "actuator"},
RegulationReferences: []string{"EMV-Richtlinie 2014/30/EU"},
SuggestedMitigations: mustMarshalJSON([]string{"EMV-Filter", "Gehaeuseabschirmung", "CE-Zulassung Frequenzumrichter"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("emc_hazard", 3),
Category: "emc_hazard",
Name: "Frequenzumrichter-Stoerung auf Steuerleitung",
Description: "Der Frequenzumrichter erzeugt hochfrequente Stoerungen, die auf benachbarte Steuerleitungen koppeln und falsche Signale erzeugen.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"actuator", "network"},
RegulationReferences: []string{"EMV-Richtlinie 2014/30/EU", "IEC 60204-1"},
SuggestedMitigations: mustMarshalJSON([]string{"Motorfilter", "Kabeltrennabstand", "Separate Kabelkanaele"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("emc_hazard", 4),
Category: "emc_hazard",
Name: "ESD-Schaden an Elektronik",
Description: "Elektrostatische Entladung bei Wartung oder Austausch beschaedigt empfindliche Elektronikbauteile, was zu latenten Fehlfunktionen fuehrt.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"controller", "firmware"},
RegulationReferences: []string{"IEC 61000-4-2", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"ESD-Schulung", "ESD-Schutzausruestung", "ESD-gerechte Verpackung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("emc_hazard", 5),
Category: "emc_hazard",
Name: "HF-Stoerung des Sicherheitssensors",
Description: "Hochfrequenz-Stoerquellen (z.B. Schweissgeraete, Mobiltelefone) beeinflussen die Funktion von Sicherheitssensoren (Lichtvorhang, Scanner).",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"sensor"},
RegulationReferences: []string{"EMV-Richtlinie 2014/30/EU", "IEC 61496"},
SuggestedMitigations: mustMarshalJSON([]string{"EMV-zertifizierte Sicherheitssensoren", "HF-Quellen trennen", "Gegensprechanlagenverbot in Gefahrenzone"}),
IsBuiltin: true,
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,
},
}
}

View File

@@ -0,0 +1,578 @@
package iace
import "time"
// builtinHazardsSoftwareHMI returns extended hazard library entries covering
// software faults, HMI errors, configuration errors, logging/audit failures,
// and integration errors.
func builtinHazardsSoftwareHMI() []HazardLibraryEntry {
now := time.Now()
return []HazardLibraryEntry{
// ====================================================================
// Category: software_fault (10 entries)
// ====================================================================
{
ID: hazardUUID("software_fault", 1),
Category: "software_fault",
Name: "Race Condition in Sicherheitsfunktion",
Description: "Zwei Tasks greifen ohne Synchronisation auf gemeinsame Ressourcen zu, was zu unvorhersehbarem Verhalten in sicherheitsrelevanten Funktionen fuehren kann.",
DefaultSeverity: 5,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"EU 2023/1230 Anhang I §1.2", "IEC 62304", "IEC 61508"},
SuggestedMitigations: mustMarshalJSON([]string{"Mutex/Semaphor", "RTOS-Task-Prioritaeten", "WCET-Analyse"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 2),
Category: "software_fault",
Name: "Stack Overflow in Echtzeit-Task",
Description: "Ein rekursiver Aufruf oder grosse lokale Variablen fuehren zum Stack-Ueberlauf, was Safety-Tasks zum Absturz bringt.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"IEC 62304", "IEC 61508"},
SuggestedMitigations: mustMarshalJSON([]string{"Stack-Groessen-Analyse", "Stack-Guard", "Statische Code-Analyse"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 3),
Category: "software_fault",
Name: "Integer Overflow in Sicherheitsberechnung",
Description: "Arithmetischer Ueberlauf bei der Berechnung sicherheitskritischer Grenzwerte fuehrt zu falschen Ergebnissen und unkontrolliertem Verhalten.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"IEC 62304", "MISRA-C", "IEC 61508"},
SuggestedMitigations: mustMarshalJSON([]string{"Datentyp-Pruefung", "Overflow-Detection", "MISRA-C-Analyse"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 4),
Category: "software_fault",
Name: "Deadlock zwischen Safety-Tasks",
Description: "Gegenseitige Sperrung von Tasks durch zyklische Ressourcenabhaengigkeiten verhindert die Ausfuehrung sicherheitsrelevanter Funktionen.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"IEC 62304", "IEC 61508"},
SuggestedMitigations: mustMarshalJSON([]string{"Ressourcen-Hierarchie", "Watchdog", "Deadlock-Analyse"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 5),
Category: "software_fault",
Name: "Memory Leak im Langzeitbetrieb",
Description: "Nicht freigegebener Heap-Speicher akkumuliert sich ueber Zeit, bis das System abstuerzt und Sicherheitsfunktionen nicht mehr verfuegbar sind.",
DefaultSeverity: 3,
DefaultProbability: 4,
ApplicableComponentTypes: []string{"software"},
RegulationReferences: []string{"IEC 62304", "IEC 61508"},
SuggestedMitigations: mustMarshalJSON([]string{"Memory-Profiling", "Valgrind", "Statisches Speichermanagement"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 6),
Category: "software_fault",
Name: "Null-Pointer-Dereferenz in Safety-Code",
Description: "Zugriff auf einen Null-Zeiger fuehrt zu einem undefinierten Systemzustand oder Absturz des sicherheitsrelevanten Software-Moduls.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"IEC 62304", "MISRA-C"},
SuggestedMitigations: mustMarshalJSON([]string{"Null-Check vor Zugriff", "Statische Analyse", "Defensiv-Programmierung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 7),
Category: "software_fault",
Name: "Unbehandelte Ausnahme in Safety-Code",
Description: "Eine nicht abgefangene Ausnahme bricht die Ausfuehrung des sicherheitsrelevanten Codes ab und hinterlaesst das System in einem undefinierten Zustand.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"software"},
RegulationReferences: []string{"IEC 62304", "IEC 61508"},
SuggestedMitigations: mustMarshalJSON([]string{"Globaler Exception-Handler", "Exception-Safety-Analyse", "Fail-Safe-Rueckfall"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 8),
Category: "software_fault",
Name: "Korrupte Konfigurationsdaten",
Description: "Beschaedigte oder unvollstaendige Konfigurationsdaten werden ohne Validierung geladen und fuehren zu falschem Systemverhalten.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"IEC 62304", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Konfig-Validierung", "CRC-Pruefung", "Fallback-Konfiguration"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 9),
Category: "software_fault",
Name: "Division durch Null in Regelkreis",
Description: "Ein Divisor im sicherheitsrelevanten Regelkreis erreicht den Wert Null, was zu einem Laufzeitfehler oder undefiniertem Ergebnis fuehrt.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"IEC 62304", "IEC 61508", "MISRA-C"},
SuggestedMitigations: mustMarshalJSON([]string{"Vorbedingungspruefung", "Statische Analyse", "Defensiv-Programmierung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("software_fault", 10),
Category: "software_fault",
Name: "Falscher Safety-Parameter durch Software-Bug",
Description: "Ein Software-Fehler setzt einen sicherheitsrelevanten Parameter auf einen falschen Wert, ohne dass eine Plausibilitaetspruefung dies erkennt.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"software", "firmware"},
RegulationReferences: []string{"IEC 62304", "IEC 61508", "Maschinenverordnung 2023/1230"},
SuggestedMitigations: mustMarshalJSON([]string{"Parametervalidierung", "Redundante Speicherung", "Diversitaere Pruefung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
// ====================================================================
// Category: hmi_error (8 entries)
// ====================================================================
{
ID: hazardUUID("hmi_error", 1),
Category: "hmi_error",
Name: "Falsche Einheitendarstellung",
Description: "Das HMI zeigt Werte in einer falschen Masseinheit an (z.B. mm statt inch), was zu Fehlbedienung und Maschinenfehlern fuehren kann.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"hmi", "software"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang III", "EN ISO 9241"},
SuggestedMitigations: mustMarshalJSON([]string{"Einheiten-Label im UI", "Lokalisierungstests", "Einheiten-Konvertierungspruefung"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("hmi_error", 2),
Category: "hmi_error",
Name: "Fehlender oder stummer Sicherheitsalarm",
Description: "Ein kritisches Sicherheitsereignis wird dem Bediener nicht oder nicht rechtzeitig angezeigt, weil die Alarmfunktion deaktiviert oder fehlerhaft ist.",
DefaultSeverity: 5,
DefaultProbability: 2,
ApplicableComponentTypes: []string{"hmi", "software"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849", "EN ISO 9241"},
SuggestedMitigations: mustMarshalJSON([]string{"Alarmtest im Rahmen der Inbetriebnahme", "Akustischer Backup-Alarm", "Alarmverwaltungssystem"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("hmi_error", 3),
Category: "hmi_error",
Name: "Sprachfehler in Bedienoberflaeche",
Description: "Fehlerhafte oder mehrdeutige Bezeichnungen in der Benutzersprache fuehren zu Fehlbedienung sicherheitsrelevanter Funktionen.",
DefaultSeverity: 3,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"hmi"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang III"},
SuggestedMitigations: mustMarshalJSON([]string{"Usability-Test", "Lokalisierungs-Review", "Mehrsprachige Dokumentation"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("hmi_error", 4),
Category: "hmi_error",
Name: "Fehlende Eingabevalidierung im HMI",
Description: "Das HMI akzeptiert ausserhalb des gueltigen Bereichs liegende Eingaben ohne Warnung und leitet sie an die Steuerung weiter.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"hmi", "software"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "IEC 62304"},
SuggestedMitigations: mustMarshalJSON([]string{"Grenzwertpruefung", "Eingabemaske mit Bereichen", "Warnung bei Grenzwertnaehe"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("hmi_error", 5),
Category: "hmi_error",
Name: "Defekter Statusindikator",
Description: "Ein LED, Anzeigeelement oder Softwaresymbol zeigt einen falschen Systemstatus an und verleitet den Bediener zu falschen Annahmen.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"hmi"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang III"},
SuggestedMitigations: mustMarshalJSON([]string{"Regelmaessige HMI-Tests", "Selbsttest beim Einschalten", "Redundante Statusanzeige"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("hmi_error", 6),
Category: "hmi_error",
Name: "Quittierung ohne Ursachenbehebung",
Description: "Der Bediener kann einen Sicherheitsalarm quittieren, ohne die zugrundeliegende Ursache behoben zu haben, was das Risiko wiederkehrender Ereignisse erhoet.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"hmi", "software"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"},
SuggestedMitigations: mustMarshalJSON([]string{"Ursachen-Checkliste vor Quittierung", "Pflicht-Ursachen-Eingabe", "Audit-Log der Quittierungen"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("hmi_error", 7),
Category: "hmi_error",
Name: "Veraltete Anzeige durch Caching-Fehler",
Description: "Die HMI-Anzeige wird nicht aktualisiert und zeigt veraltete Sensorwerte oder Zustaende an, was zu Fehlentscheidungen fuehrt.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"hmi", "software"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230 Anhang III"},
SuggestedMitigations: mustMarshalJSON([]string{"Timestamp-Anzeige", "Refresh-Watchdog", "Verbindungsstatus-Indikator"}),
IsBuiltin: true,
TenantID: nil,
CreatedAt: now,
},
{
ID: hazardUUID("hmi_error", 8),
Category: "hmi_error",
Name: "Fehlende Betriebsart-Kennzeichnung",
Description: "Die aktive Betriebsart (Automatik, Einrichten, Wartung) ist im HMI nicht eindeutig sichtbar, was zu unerwarteten Maschinenbewegungen fuehren kann.",
DefaultSeverity: 4,
DefaultProbability: 3,
ApplicableComponentTypes: []string{"hmi", "software"},
RegulationReferences: []string{"Maschinenverordnung 2023/1230", "ISO 13849"},
SuggestedMitigations: mustMarshalJSON([]string{"Dauerhafte Betriebsart-Anzeige", "Farbliche Kennzeichnung", "Bestaetigung bei Modewechsel"}),
IsBuiltin: true,
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,
},
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,383 @@
package iace
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// ============================================================================
// Tech File Section Operations
// ============================================================================
// CreateTechFileSection creates a new section in the technical file
func (s *Store) CreateTechFileSection(ctx context.Context, projectID uuid.UUID, sectionType, title, content string) (*TechFileSection, error) {
tf := &TechFileSection{
ID: uuid.New(),
ProjectID: projectID,
SectionType: sectionType,
Title: title,
Content: content,
Version: 1,
Status: TechFileSectionStatusDraft,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
_, err := s.pool.Exec(ctx, `
INSERT INTO iace_tech_file_sections (
id, project_id, section_type, title, content,
version, status, approved_by, approved_at, metadata,
created_at, updated_at
) VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8, $9, $10,
$11, $12
)
`,
tf.ID, tf.ProjectID, tf.SectionType, tf.Title, tf.Content,
tf.Version, string(tf.Status), uuid.Nil, nil, nil,
tf.CreatedAt, tf.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("create tech file section: %w", err)
}
return tf, nil
}
// UpdateTechFileSection updates the content of a tech file section and bumps version
func (s *Store) UpdateTechFileSection(ctx context.Context, id uuid.UUID, content string) error {
_, err := s.pool.Exec(ctx, `
UPDATE iace_tech_file_sections SET
content = $2,
version = version + 1,
status = $3,
updated_at = NOW()
WHERE id = $1
`, id, content, string(TechFileSectionStatusDraft))
if err != nil {
return fmt.Errorf("update tech file section: %w", err)
}
return nil
}
// ApproveTechFileSection marks a tech file section as approved
func (s *Store) ApproveTechFileSection(ctx context.Context, id uuid.UUID, approvedBy string) error {
now := time.Now().UTC()
approvedByUUID, err := uuid.Parse(approvedBy)
if err != nil {
return fmt.Errorf("invalid approved_by UUID: %w", err)
}
_, err = s.pool.Exec(ctx, `
UPDATE iace_tech_file_sections SET
status = $2,
approved_by = $3,
approved_at = $4,
updated_at = $4
WHERE id = $1
`, id, string(TechFileSectionStatusApproved), approvedByUUID, now)
if err != nil {
return fmt.Errorf("approve tech file section: %w", err)
}
return nil
}
// ListTechFileSections lists all tech file sections for a project
func (s *Store) ListTechFileSections(ctx context.Context, projectID uuid.UUID) ([]TechFileSection, error) {
rows, err := s.pool.Query(ctx, `
SELECT
id, project_id, section_type, title, content,
version, status, approved_by, approved_at, metadata,
created_at, updated_at
FROM iace_tech_file_sections WHERE project_id = $1
ORDER BY section_type ASC, created_at ASC
`, projectID)
if err != nil {
return nil, fmt.Errorf("list tech file sections: %w", err)
}
defer rows.Close()
var sections []TechFileSection
for rows.Next() {
var tf TechFileSection
var status string
var metadata []byte
err := rows.Scan(
&tf.ID, &tf.ProjectID, &tf.SectionType, &tf.Title, &tf.Content,
&tf.Version, &status, &tf.ApprovedBy, &tf.ApprovedAt, &metadata,
&tf.CreatedAt, &tf.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("list tech file sections scan: %w", err)
}
tf.Status = TechFileSectionStatus(status)
json.Unmarshal(metadata, &tf.Metadata)
sections = append(sections, tf)
}
return sections, nil
}
// ============================================================================
// Monitoring Event Operations
// ============================================================================
// CreateMonitoringEvent creates a new post-market monitoring event
func (s *Store) CreateMonitoringEvent(ctx context.Context, req CreateMonitoringEventRequest) (*MonitoringEvent, error) {
me := &MonitoringEvent{
ID: uuid.New(),
ProjectID: req.ProjectID,
EventType: req.EventType,
Title: req.Title,
Description: req.Description,
Severity: req.Severity,
Status: "open",
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
_, err := s.pool.Exec(ctx, `
INSERT INTO iace_monitoring_events (
id, project_id, event_type, title, description,
severity, impact_assessment, status,
resolved_at, resolved_by, metadata,
created_at, updated_at
) VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8,
$9, $10, $11,
$12, $13
)
`,
me.ID, me.ProjectID, string(me.EventType), me.Title, me.Description,
me.Severity, "", me.Status,
nil, uuid.Nil, nil,
me.CreatedAt, me.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("create monitoring event: %w", err)
}
return me, nil
}
// ListMonitoringEvents lists all monitoring events for a project
func (s *Store) ListMonitoringEvents(ctx context.Context, projectID uuid.UUID) ([]MonitoringEvent, error) {
rows, err := s.pool.Query(ctx, `
SELECT
id, project_id, event_type, title, description,
severity, impact_assessment, status,
resolved_at, resolved_by, metadata,
created_at, updated_at
FROM iace_monitoring_events WHERE project_id = $1
ORDER BY created_at DESC
`, projectID)
if err != nil {
return nil, fmt.Errorf("list monitoring events: %w", err)
}
defer rows.Close()
var events []MonitoringEvent
for rows.Next() {
var me MonitoringEvent
var eventType string
var metadata []byte
err := rows.Scan(
&me.ID, &me.ProjectID, &eventType, &me.Title, &me.Description,
&me.Severity, &me.ImpactAssessment, &me.Status,
&me.ResolvedAt, &me.ResolvedBy, &metadata,
&me.CreatedAt, &me.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("list monitoring events scan: %w", err)
}
me.EventType = MonitoringEventType(eventType)
json.Unmarshal(metadata, &me.Metadata)
events = append(events, me)
}
return events, nil
}
// UpdateMonitoringEvent updates a monitoring event with a dynamic set of fields
func (s *Store) UpdateMonitoringEvent(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*MonitoringEvent, error) {
if len(updates) == 0 {
return s.getMonitoringEvent(ctx, id)
}
query := "UPDATE iace_monitoring_events SET updated_at = NOW()"
args := []interface{}{id}
argIdx := 2
for key, val := range updates {
switch key {
case "title", "description", "severity", "impact_assessment", "status":
query += fmt.Sprintf(", %s = $%d", key, argIdx)
args = append(args, val)
argIdx++
case "event_type":
query += fmt.Sprintf(", event_type = $%d", argIdx)
args = append(args, val)
argIdx++
case "resolved_at":
query += fmt.Sprintf(", resolved_at = $%d", argIdx)
args = append(args, val)
argIdx++
case "resolved_by":
query += fmt.Sprintf(", resolved_by = $%d", argIdx)
args = append(args, val)
argIdx++
case "metadata":
metaJSON, _ := json.Marshal(val)
query += fmt.Sprintf(", metadata = $%d", argIdx)
args = append(args, metaJSON)
argIdx++
}
}
query += " WHERE id = $1"
_, err := s.pool.Exec(ctx, query, args...)
if err != nil {
return nil, fmt.Errorf("update monitoring event: %w", err)
}
return s.getMonitoringEvent(ctx, id)
}
// getMonitoringEvent is a helper to fetch a single monitoring event by ID
func (s *Store) getMonitoringEvent(ctx context.Context, id uuid.UUID) (*MonitoringEvent, error) {
var me MonitoringEvent
var eventType string
var metadata []byte
err := s.pool.QueryRow(ctx, `
SELECT
id, project_id, event_type, title, description,
severity, impact_assessment, status,
resolved_at, resolved_by, metadata,
created_at, updated_at
FROM iace_monitoring_events WHERE id = $1
`, id).Scan(
&me.ID, &me.ProjectID, &eventType, &me.Title, &me.Description,
&me.Severity, &me.ImpactAssessment, &me.Status,
&me.ResolvedAt, &me.ResolvedBy, &metadata,
&me.CreatedAt, &me.UpdatedAt,
)
if err == pgx.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get monitoring event: %w", err)
}
me.EventType = MonitoringEventType(eventType)
json.Unmarshal(metadata, &me.Metadata)
return &me, nil
}
// ============================================================================
// Audit Trail Operations
// ============================================================================
// AddAuditEntry adds an immutable audit trail entry
func (s *Store) AddAuditEntry(ctx context.Context, projectID uuid.UUID, entityType string, entityID uuid.UUID, action AuditAction, userID string, oldValues, newValues json.RawMessage) error {
id := uuid.New()
now := time.Now().UTC()
userUUID, err := uuid.Parse(userID)
if err != nil {
return fmt.Errorf("invalid user_id UUID: %w", err)
}
// Compute a simple hash for integrity: sha256(entityType + entityID + action + timestamp)
hashInput := fmt.Sprintf("%s:%s:%s:%s:%s", projectID, entityType, entityID, string(action), now.Format(time.RFC3339Nano))
// Use a simple deterministic hash representation
hash := fmt.Sprintf("%x", hashInput)
_, err = s.pool.Exec(ctx, `
INSERT INTO iace_audit_trail (
id, project_id, entity_type, entity_id,
action, user_id, old_values, new_values,
hash, created_at
) VALUES (
$1, $2, $3, $4,
$5, $6, $7, $8,
$9, $10
)
`,
id, projectID, entityType, entityID,
string(action), userUUID, oldValues, newValues,
hash, now,
)
if err != nil {
return fmt.Errorf("add audit entry: %w", err)
}
return nil
}
// ListAuditTrail lists all audit trail entries for a project, newest first
func (s *Store) ListAuditTrail(ctx context.Context, projectID uuid.UUID) ([]AuditTrailEntry, error) {
rows, err := s.pool.Query(ctx, `
SELECT
id, project_id, entity_type, entity_id,
action, user_id, old_values, new_values,
hash, created_at
FROM iace_audit_trail WHERE project_id = $1
ORDER BY created_at DESC
`, projectID)
if err != nil {
return nil, fmt.Errorf("list audit trail: %w", err)
}
defer rows.Close()
var entries []AuditTrailEntry
for rows.Next() {
var e AuditTrailEntry
var action string
err := rows.Scan(
&e.ID, &e.ProjectID, &e.EntityType, &e.EntityID,
&action, &e.UserID, &e.OldValues, &e.NewValues,
&e.Hash, &e.CreatedAt,
)
if err != nil {
return nil, fmt.Errorf("list audit trail scan: %w", err)
}
e.Action = AuditAction(action)
entries = append(entries, e)
}
return entries, nil
}
// HasAuditEntryForType checks if an audit trail entry exists for the given entity type within a project.
func (s *Store) HasAuditEntryForType(ctx context.Context, projectID uuid.UUID, entityType string) (bool, error) {
var exists bool
err := s.pool.QueryRow(ctx, `
SELECT EXISTS(
SELECT 1 FROM iace_audit_trail
WHERE project_id = $1 AND entity_type = $2
)
`, projectID, entityType).Scan(&exists)
if err != nil {
return false, fmt.Errorf("has audit entry: %w", err)
}
return exists, nil
}

View File

@@ -0,0 +1,555 @@
package iace
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// ============================================================================
// Hazard CRUD Operations
// ============================================================================
// CreateHazard creates a new hazard within a project
func (s *Store) CreateHazard(ctx context.Context, req CreateHazardRequest) (*Hazard, error) {
h := &Hazard{
ID: uuid.New(),
ProjectID: req.ProjectID,
ComponentID: req.ComponentID,
LibraryHazardID: req.LibraryHazardID,
Name: req.Name,
Description: req.Description,
Scenario: req.Scenario,
Category: req.Category,
SubCategory: req.SubCategory,
Status: HazardStatusIdentified,
MachineModule: req.MachineModule,
Function: req.Function,
LifecyclePhase: req.LifecyclePhase,
HazardousZone: req.HazardousZone,
TriggerEvent: req.TriggerEvent,
AffectedPerson: req.AffectedPerson,
PossibleHarm: req.PossibleHarm,
ReviewStatus: ReviewStatusDraft,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
_, err := s.pool.Exec(ctx, `
INSERT INTO iace_hazards (
id, project_id, component_id, library_hazard_id,
name, description, scenario, category, sub_category, status,
machine_module, function, lifecycle_phase, hazardous_zone,
trigger_event, affected_person, possible_harm, review_status,
created_at, updated_at
) VALUES (
$1, $2, $3, $4,
$5, $6, $7, $8, $9, $10,
$11, $12, $13, $14,
$15, $16, $17, $18,
$19, $20
)
`,
h.ID, h.ProjectID, h.ComponentID, h.LibraryHazardID,
h.Name, h.Description, h.Scenario, h.Category, h.SubCategory, string(h.Status),
h.MachineModule, h.Function, h.LifecyclePhase, h.HazardousZone,
h.TriggerEvent, h.AffectedPerson, h.PossibleHarm, string(h.ReviewStatus),
h.CreatedAt, h.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("create hazard: %w", err)
}
return h, nil
}
// GetHazard retrieves a hazard by ID
func (s *Store) GetHazard(ctx context.Context, id uuid.UUID) (*Hazard, error) {
var h Hazard
var status, reviewStatus string
err := s.pool.QueryRow(ctx, `
SELECT
id, project_id, component_id, library_hazard_id,
name, description, scenario, category, sub_category, status,
machine_module, function, lifecycle_phase, hazardous_zone,
trigger_event, affected_person, possible_harm, review_status,
created_at, updated_at
FROM iace_hazards WHERE id = $1
`, id).Scan(
&h.ID, &h.ProjectID, &h.ComponentID, &h.LibraryHazardID,
&h.Name, &h.Description, &h.Scenario, &h.Category, &h.SubCategory, &status,
&h.MachineModule, &h.Function, &h.LifecyclePhase, &h.HazardousZone,
&h.TriggerEvent, &h.AffectedPerson, &h.PossibleHarm, &reviewStatus,
&h.CreatedAt, &h.UpdatedAt,
)
if err == pgx.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get hazard: %w", err)
}
h.Status = HazardStatus(status)
h.ReviewStatus = ReviewStatus(reviewStatus)
return &h, nil
}
// ListHazards lists all hazards for a project
func (s *Store) ListHazards(ctx context.Context, projectID uuid.UUID) ([]Hazard, error) {
rows, err := s.pool.Query(ctx, `
SELECT
id, project_id, component_id, library_hazard_id,
name, description, scenario, category, sub_category, status,
machine_module, function, lifecycle_phase, hazardous_zone,
trigger_event, affected_person, possible_harm, review_status,
created_at, updated_at
FROM iace_hazards WHERE project_id = $1
ORDER BY created_at ASC
`, projectID)
if err != nil {
return nil, fmt.Errorf("list hazards: %w", err)
}
defer rows.Close()
var hazards []Hazard
for rows.Next() {
var h Hazard
var status, reviewStatus string
err := rows.Scan(
&h.ID, &h.ProjectID, &h.ComponentID, &h.LibraryHazardID,
&h.Name, &h.Description, &h.Scenario, &h.Category, &h.SubCategory, &status,
&h.MachineModule, &h.Function, &h.LifecyclePhase, &h.HazardousZone,
&h.TriggerEvent, &h.AffectedPerson, &h.PossibleHarm, &reviewStatus,
&h.CreatedAt, &h.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("list hazards scan: %w", err)
}
h.Status = HazardStatus(status)
h.ReviewStatus = ReviewStatus(reviewStatus)
hazards = append(hazards, h)
}
return hazards, nil
}
// UpdateHazard updates a hazard with a dynamic set of fields
func (s *Store) UpdateHazard(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*Hazard, error) {
if len(updates) == 0 {
return s.GetHazard(ctx, id)
}
query := "UPDATE iace_hazards SET updated_at = NOW()"
args := []interface{}{id}
argIdx := 2
allowedFields := map[string]bool{
"name": true, "description": true, "scenario": true, "category": true,
"sub_category": true, "status": true, "component_id": true,
"machine_module": true, "function": true, "lifecycle_phase": true,
"hazardous_zone": true, "trigger_event": true, "affected_person": true,
"possible_harm": true, "review_status": true,
}
for key, val := range updates {
if allowedFields[key] {
query += fmt.Sprintf(", %s = $%d", key, 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 hazard: %w", err)
}
return s.GetHazard(ctx, id)
}
// ============================================================================
// Risk Assessment Operations
// ============================================================================
// CreateRiskAssessment creates a new risk assessment for a hazard
func (s *Store) CreateRiskAssessment(ctx context.Context, assessment *RiskAssessment) error {
if assessment.ID == uuid.Nil {
assessment.ID = uuid.New()
}
if assessment.CreatedAt.IsZero() {
assessment.CreatedAt = time.Now().UTC()
}
_, err := s.pool.Exec(ctx, `
INSERT INTO iace_risk_assessments (
id, hazard_id, version, assessment_type,
severity, exposure, probability,
inherent_risk, control_maturity, control_coverage,
test_evidence_strength, c_eff, residual_risk,
risk_level, is_acceptable, acceptance_justification,
assessed_by, created_at
) VALUES (
$1, $2, $3, $4,
$5, $6, $7,
$8, $9, $10,
$11, $12, $13,
$14, $15, $16,
$17, $18
)
`,
assessment.ID, assessment.HazardID, assessment.Version, string(assessment.AssessmentType),
assessment.Severity, assessment.Exposure, assessment.Probability,
assessment.InherentRisk, assessment.ControlMaturity, assessment.ControlCoverage,
assessment.TestEvidenceStrength, assessment.CEff, assessment.ResidualRisk,
string(assessment.RiskLevel), assessment.IsAcceptable, assessment.AcceptanceJustification,
assessment.AssessedBy, assessment.CreatedAt,
)
if err != nil {
return fmt.Errorf("create risk assessment: %w", err)
}
return nil
}
// GetLatestAssessment retrieves the most recent risk assessment for a hazard
func (s *Store) GetLatestAssessment(ctx context.Context, hazardID uuid.UUID) (*RiskAssessment, error) {
var a RiskAssessment
var assessmentType, riskLevel string
err := s.pool.QueryRow(ctx, `
SELECT
id, hazard_id, version, assessment_type,
severity, exposure, probability,
inherent_risk, control_maturity, control_coverage,
test_evidence_strength, c_eff, residual_risk,
risk_level, is_acceptable, acceptance_justification,
assessed_by, created_at
FROM iace_risk_assessments
WHERE hazard_id = $1
ORDER BY version DESC, created_at DESC
LIMIT 1
`, hazardID).Scan(
&a.ID, &a.HazardID, &a.Version, &assessmentType,
&a.Severity, &a.Exposure, &a.Probability,
&a.InherentRisk, &a.ControlMaturity, &a.ControlCoverage,
&a.TestEvidenceStrength, &a.CEff, &a.ResidualRisk,
&riskLevel, &a.IsAcceptable, &a.AcceptanceJustification,
&a.AssessedBy, &a.CreatedAt,
)
if err == pgx.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get latest assessment: %w", err)
}
a.AssessmentType = AssessmentType(assessmentType)
a.RiskLevel = RiskLevel(riskLevel)
return &a, nil
}
// ListAssessments lists all risk assessments for a hazard, newest first
func (s *Store) ListAssessments(ctx context.Context, hazardID uuid.UUID) ([]RiskAssessment, error) {
rows, err := s.pool.Query(ctx, `
SELECT
id, hazard_id, version, assessment_type,
severity, exposure, probability,
inherent_risk, control_maturity, control_coverage,
test_evidence_strength, c_eff, residual_risk,
risk_level, is_acceptable, acceptance_justification,
assessed_by, created_at
FROM iace_risk_assessments
WHERE hazard_id = $1
ORDER BY version DESC, created_at DESC
`, hazardID)
if err != nil {
return nil, fmt.Errorf("list assessments: %w", err)
}
defer rows.Close()
var assessments []RiskAssessment
for rows.Next() {
var a RiskAssessment
var assessmentType, riskLevel string
err := rows.Scan(
&a.ID, &a.HazardID, &a.Version, &assessmentType,
&a.Severity, &a.Exposure, &a.Probability,
&a.InherentRisk, &a.ControlMaturity, &a.ControlCoverage,
&a.TestEvidenceStrength, &a.CEff, &a.ResidualRisk,
&riskLevel, &a.IsAcceptable, &a.AcceptanceJustification,
&a.AssessedBy, &a.CreatedAt,
)
if err != nil {
return nil, fmt.Errorf("list assessments scan: %w", err)
}
a.AssessmentType = AssessmentType(assessmentType)
a.RiskLevel = RiskLevel(riskLevel)
assessments = append(assessments, a)
}
return assessments, nil
}
// ============================================================================
// Risk Summary (Aggregated View)
// ============================================================================
// GetRiskSummary computes an aggregated risk overview for a project
func (s *Store) GetRiskSummary(ctx context.Context, projectID uuid.UUID) (*RiskSummaryResponse, error) {
// Get all hazards for the project
hazards, err := s.ListHazards(ctx, projectID)
if err != nil {
return nil, fmt.Errorf("get risk summary - list hazards: %w", err)
}
summary := &RiskSummaryResponse{
TotalHazards: len(hazards),
AllAcceptable: true,
}
if len(hazards) == 0 {
summary.OverallRiskLevel = RiskLevelNegligible
return summary, nil
}
highestRisk := RiskLevelNegligible
for _, h := range hazards {
latest, err := s.GetLatestAssessment(ctx, h.ID)
if err != nil {
return nil, fmt.Errorf("get risk summary - get assessment for hazard %s: %w", h.ID, err)
}
if latest == nil {
// Hazard without assessment counts as unassessed; consider it not acceptable
summary.AllAcceptable = false
continue
}
switch latest.RiskLevel {
case RiskLevelNotAcceptable:
summary.NotAcceptable++
case RiskLevelVeryHigh:
summary.VeryHigh++
case RiskLevelCritical:
summary.Critical++
case RiskLevelHigh:
summary.High++
case RiskLevelMedium:
summary.Medium++
case RiskLevelLow:
summary.Low++
case RiskLevelNegligible:
summary.Negligible++
}
if !latest.IsAcceptable {
summary.AllAcceptable = false
}
// Track highest risk level
if riskLevelSeverity(latest.RiskLevel) > riskLevelSeverity(highestRisk) {
highestRisk = latest.RiskLevel
}
}
summary.OverallRiskLevel = highestRisk
return summary, nil
}
// riskLevelSeverity returns a numeric severity for risk level comparison
func riskLevelSeverity(rl RiskLevel) int {
switch rl {
case RiskLevelNotAcceptable:
return 7
case RiskLevelVeryHigh:
return 6
case RiskLevelCritical:
return 5
case RiskLevelHigh:
return 4
case RiskLevelMedium:
return 3
case RiskLevelLow:
return 2
case RiskLevelNegligible:
return 1
default:
return 0
}
}
// ============================================================================
// 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, &regulationReferences,
&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, &regulationReferences,
&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
}

View File

@@ -0,0 +1,506 @@
package iace
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// ============================================================================
// Mitigation CRUD Operations
// ============================================================================
// CreateMitigation creates a new mitigation measure for a hazard
func (s *Store) CreateMitigation(ctx context.Context, req CreateMitigationRequest) (*Mitigation, error) {
m := &Mitigation{
ID: uuid.New(),
HazardID: req.HazardID,
ReductionType: req.ReductionType,
Name: req.Name,
Description: req.Description,
Status: MitigationStatusPlanned,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
_, err := s.pool.Exec(ctx, `
INSERT INTO iace_mitigations (
id, hazard_id, reduction_type, name, description,
status, verification_method, verification_result,
verified_at, verified_by,
created_at, updated_at
) VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8,
$9, $10,
$11, $12
)
`,
m.ID, m.HazardID, string(m.ReductionType), m.Name, m.Description,
string(m.Status), "", "",
nil, uuid.Nil,
m.CreatedAt, m.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("create mitigation: %w", err)
}
return m, nil
}
// UpdateMitigation updates a mitigation with a dynamic set of fields
func (s *Store) UpdateMitigation(ctx context.Context, id uuid.UUID, updates map[string]interface{}) (*Mitigation, error) {
if len(updates) == 0 {
return s.getMitigation(ctx, id)
}
query := "UPDATE iace_mitigations SET updated_at = NOW()"
args := []interface{}{id}
argIdx := 2
for key, val := range updates {
switch key {
case "name", "description", "verification_result":
query += fmt.Sprintf(", %s = $%d", key, argIdx)
args = append(args, val)
argIdx++
case "status":
query += fmt.Sprintf(", status = $%d", argIdx)
args = append(args, val)
argIdx++
case "reduction_type":
query += fmt.Sprintf(", reduction_type = $%d", argIdx)
args = append(args, val)
argIdx++
case "verification_method":
query += fmt.Sprintf(", verification_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 mitigation: %w", err)
}
return s.getMitigation(ctx, id)
}
// VerifyMitigation marks a mitigation as verified
func (s *Store) VerifyMitigation(ctx context.Context, id uuid.UUID, verificationResult string, verifiedBy string) error {
now := time.Now().UTC()
verifiedByUUID, err := uuid.Parse(verifiedBy)
if err != nil {
return fmt.Errorf("invalid verified_by UUID: %w", err)
}
_, err = s.pool.Exec(ctx, `
UPDATE iace_mitigations SET
status = $2,
verification_result = $3,
verified_at = $4,
verified_by = $5,
updated_at = $4
WHERE id = $1
`, id, string(MitigationStatusVerified), verificationResult, now, verifiedByUUID)
if err != nil {
return fmt.Errorf("verify mitigation: %w", err)
}
return nil
}
// ListMitigations lists all mitigations for a hazard
func (s *Store) ListMitigations(ctx context.Context, hazardID uuid.UUID) ([]Mitigation, error) {
rows, err := s.pool.Query(ctx, `
SELECT
id, hazard_id, reduction_type, name, description,
status, verification_method, verification_result,
verified_at, verified_by,
created_at, updated_at
FROM iace_mitigations WHERE hazard_id = $1
ORDER BY created_at ASC
`, hazardID)
if err != nil {
return nil, fmt.Errorf("list mitigations: %w", err)
}
defer rows.Close()
var mitigations []Mitigation
for rows.Next() {
var m Mitigation
var reductionType, status, verificationMethod string
err := rows.Scan(
&m.ID, &m.HazardID, &reductionType, &m.Name, &m.Description,
&status, &verificationMethod, &m.VerificationResult,
&m.VerifiedAt, &m.VerifiedBy,
&m.CreatedAt, &m.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("list mitigations scan: %w", err)
}
m.ReductionType = ReductionType(reductionType)
m.Status = MitigationStatus(status)
m.VerificationMethod = VerificationMethod(verificationMethod)
mitigations = append(mitigations, m)
}
return mitigations, nil
}
// GetMitigation fetches a single mitigation by ID.
func (s *Store) GetMitigation(ctx context.Context, id uuid.UUID) (*Mitigation, error) {
return s.getMitigation(ctx, id)
}
// getMitigation is a helper to fetch a single mitigation by ID
func (s *Store) getMitigation(ctx context.Context, id uuid.UUID) (*Mitigation, error) {
var m Mitigation
var reductionType, status, verificationMethod string
err := s.pool.QueryRow(ctx, `
SELECT
id, hazard_id, reduction_type, name, description,
status, verification_method, verification_result,
verified_at, verified_by,
created_at, updated_at
FROM iace_mitigations WHERE id = $1
`, id).Scan(
&m.ID, &m.HazardID, &reductionType, &m.Name, &m.Description,
&status, &verificationMethod, &m.VerificationResult,
&m.VerifiedAt, &m.VerifiedBy,
&m.CreatedAt, &m.UpdatedAt,
)
if err == pgx.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get mitigation: %w", err)
}
m.ReductionType = ReductionType(reductionType)
m.Status = MitigationStatus(status)
m.VerificationMethod = VerificationMethod(verificationMethod)
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
}

View File

@@ -0,0 +1,529 @@
package iace
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// ============================================================================
// Project CRUD Operations
// ============================================================================
// CreateProject creates a new IACE project
func (s *Store) CreateProject(ctx context.Context, tenantID uuid.UUID, req CreateProjectRequest) (*Project, error) {
project := &Project{
ID: uuid.New(),
TenantID: tenantID,
MachineName: req.MachineName,
MachineType: req.MachineType,
Manufacturer: req.Manufacturer,
Description: req.Description,
NarrativeText: req.NarrativeText,
Status: ProjectStatusDraft,
CEMarkingTarget: req.CEMarkingTarget,
Metadata: req.Metadata,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
_, err := s.pool.Exec(ctx, `
INSERT INTO iace_projects (
id, tenant_id, machine_name, machine_type, manufacturer,
description, narrative_text, status, ce_marking_target,
completeness_score, risk_summary, triggered_regulations, metadata,
created_at, updated_at, archived_at
) VALUES (
$1, $2, $3, $4, $5,
$6, $7, $8, $9,
$10, $11, $12, $13,
$14, $15, $16
)
`,
project.ID, project.TenantID, project.MachineName, project.MachineType, project.Manufacturer,
project.Description, project.NarrativeText, string(project.Status), project.CEMarkingTarget,
project.CompletenessScore, nil, project.TriggeredRegulations, project.Metadata,
project.CreatedAt, project.UpdatedAt, project.ArchivedAt,
)
if err != nil {
return nil, fmt.Errorf("create project: %w", err)
}
return project, nil
}
// GetProject retrieves a project by ID
func (s *Store) GetProject(ctx context.Context, id uuid.UUID) (*Project, error) {
var p Project
var status string
var riskSummary, triggeredRegulations, metadata []byte
err := s.pool.QueryRow(ctx, `
SELECT
id, tenant_id, machine_name, machine_type, manufacturer,
description, narrative_text, status, ce_marking_target,
completeness_score, risk_summary, triggered_regulations, metadata,
created_at, updated_at, archived_at
FROM iace_projects WHERE id = $1
`, id).Scan(
&p.ID, &p.TenantID, &p.MachineName, &p.MachineType, &p.Manufacturer,
&p.Description, &p.NarrativeText, &status, &p.CEMarkingTarget,
&p.CompletenessScore, &riskSummary, &triggeredRegulations, &metadata,
&p.CreatedAt, &p.UpdatedAt, &p.ArchivedAt,
)
if err == pgx.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("get project: %w", err)
}
p.Status = ProjectStatus(status)
json.Unmarshal(riskSummary, &p.RiskSummary)
json.Unmarshal(triggeredRegulations, &p.TriggeredRegulations)
json.Unmarshal(metadata, &p.Metadata)
return &p, nil
}
// ListProjects lists all projects for a tenant
func (s *Store) ListProjects(ctx context.Context, tenantID uuid.UUID) ([]Project, error) {
rows, err := s.pool.Query(ctx, `
SELECT
id, tenant_id, machine_name, machine_type, manufacturer,
description, narrative_text, status, ce_marking_target,
completeness_score, risk_summary, triggered_regulations, metadata,
created_at, updated_at, archived_at
FROM iace_projects WHERE tenant_id = $1
ORDER BY created_at DESC
`, tenantID)
if err != nil {
return nil, fmt.Errorf("list projects: %w", err)
}
defer rows.Close()
var projects []Project
for rows.Next() {
var p Project
var status string
var riskSummary, triggeredRegulations, metadata []byte
err := rows.Scan(
&p.ID, &p.TenantID, &p.MachineName, &p.MachineType, &p.Manufacturer,
&p.Description, &p.NarrativeText, &status, &p.CEMarkingTarget,
&p.CompletenessScore, &riskSummary, &triggeredRegulations, &metadata,
&p.CreatedAt, &p.UpdatedAt, &p.ArchivedAt,
)
if err != nil {
return nil, fmt.Errorf("list projects scan: %w", err)
}
p.Status = ProjectStatus(status)
json.Unmarshal(riskSummary, &p.RiskSummary)
json.Unmarshal(triggeredRegulations, &p.TriggeredRegulations)
json.Unmarshal(metadata, &p.Metadata)
projects = append(projects, p)
}
return projects, nil
}
// UpdateProject updates an existing project's mutable fields
func (s *Store) UpdateProject(ctx context.Context, id uuid.UUID, req UpdateProjectRequest) (*Project, error) {
// Fetch current project first
project, err := s.GetProject(ctx, id)
if err != nil {
return nil, err
}
if project == nil {
return nil, nil
}
// Apply partial updates
if req.MachineName != nil {
project.MachineName = *req.MachineName
}
if req.MachineType != nil {
project.MachineType = *req.MachineType
}
if req.Manufacturer != nil {
project.Manufacturer = *req.Manufacturer
}
if req.Description != nil {
project.Description = *req.Description
}
if req.NarrativeText != nil {
project.NarrativeText = *req.NarrativeText
}
if req.CEMarkingTarget != nil {
project.CEMarkingTarget = *req.CEMarkingTarget
}
if req.Metadata != nil {
project.Metadata = *req.Metadata
}
project.UpdatedAt = time.Now().UTC()
_, err = s.pool.Exec(ctx, `
UPDATE iace_projects SET
machine_name = $2, machine_type = $3, manufacturer = $4,
description = $5, narrative_text = $6, ce_marking_target = $7,
metadata = $8, updated_at = $9
WHERE id = $1
`,
id, project.MachineName, project.MachineType, project.Manufacturer,
project.Description, project.NarrativeText, project.CEMarkingTarget,
project.Metadata, project.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("update project: %w", err)
}
return project, nil
}
// ArchiveProject sets the archived_at timestamp and status for a project
func (s *Store) ArchiveProject(ctx context.Context, id uuid.UUID) error {
now := time.Now().UTC()
_, err := s.pool.Exec(ctx, `
UPDATE iace_projects SET
status = $2, archived_at = $3, updated_at = $3
WHERE id = $1
`, id, string(ProjectStatusArchived), now)
if err != nil {
return fmt.Errorf("archive project: %w", err)
}
return nil
}
// UpdateProjectStatus updates the lifecycle status of a project
func (s *Store) UpdateProjectStatus(ctx context.Context, id uuid.UUID, status ProjectStatus) error {
_, err := s.pool.Exec(ctx, `
UPDATE iace_projects SET status = $2, updated_at = NOW()
WHERE id = $1
`, id, string(status))
if err != nil {
return fmt.Errorf("update project status: %w", err)
}
return nil
}
// UpdateProjectCompleteness updates the completeness score and risk summary
func (s *Store) UpdateProjectCompleteness(ctx context.Context, id uuid.UUID, score float64, riskSummary map[string]int) error {
riskSummaryJSON, err := json.Marshal(riskSummary)
if err != nil {
return fmt.Errorf("marshal risk summary: %w", err)
}
_, err = s.pool.Exec(ctx, `
UPDATE iace_projects SET
completeness_score = $2, risk_summary = $3, updated_at = NOW()
WHERE id = $1
`, id, score, riskSummaryJSON)
if err != nil {
return fmt.Errorf("update project completeness: %w", err)
}
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, &reg, &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, &reg, &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
}