feat(iace): sync IACE frontend, API routes, and scope engine updates from breakpilot-pwa
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 35s
CI / test-python-backend-compliance (push) Successful in 29s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 21s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 35s
CI / test-python-backend-compliance (push) Successful in 29s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 21s
- Add IACE project pages (classification, evidence, hazards, mitigations, monitoring, tech-file, verification) - Add IACE API catch-all route - Update compliance-scope-engine with IACE AI Act product triggers - Update compliance-scope-types, navigation, roles, and sidebar for IACE - Update company-profile page
This commit is contained in:
@@ -13,6 +13,7 @@ import type {
|
||||
ScopeDocumentType,
|
||||
DocumentScopeRequirement,
|
||||
} from './compliance-scope-types'
|
||||
import type { CompanyProfile, MachineBuilderProfile } from './types'
|
||||
import {
|
||||
getDepthLevelNumeric,
|
||||
depthLevelFromNumeric,
|
||||
@@ -786,6 +787,176 @@ export const HARD_TRIGGER_RULES: HardTriggerRule[] = [
|
||||
legalReference: 'Art. 39 Abs. 1 lit. b DSGVO',
|
||||
description: 'Fehlende Schulungen zum Datenschutz',
|
||||
},
|
||||
|
||||
// ========== J: IACE — AI Act Produkt-Triggers (3 rules) ==========
|
||||
{
|
||||
id: 'HT-J01',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.containsAI',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act Annex I + EU Maschinenverordnung 2023/1230',
|
||||
description: 'KI mit Sicherheitsfunktion in Maschine → AI Act High-Risk',
|
||||
combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-J02',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.containsAI',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act + EU Maschinenverordnung 2023/1230',
|
||||
description: 'Autonome KI in Maschine → AI Act + Maschinenverordnung',
|
||||
combineWithMachineBuilder: { field: 'autonomousBehavior', value: true },
|
||||
riskWeight: 8,
|
||||
},
|
||||
{
|
||||
id: 'HT-J03',
|
||||
category: 'iace_ai_act_product',
|
||||
questionId: 'machineBuilder.hasSafetyFunction',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['VVT', 'TOM'],
|
||||
legalReference: 'EU AI Act Annex III',
|
||||
description: 'KI-Bildverarbeitung mit Sicherheitsbezug',
|
||||
combineWithMachineBuilder: { field: 'aiIntegrationType', includes: 'vision' },
|
||||
riskWeight: 8,
|
||||
},
|
||||
|
||||
// ========== K: IACE — CRA Triggers (3 rules) ==========
|
||||
{
|
||||
id: 'HT-K01',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.isNetworked',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Cyber Resilience Act (CRA)',
|
||||
description: 'Vernetztes Produkt → Cyber Resilience Act',
|
||||
riskWeight: 6,
|
||||
},
|
||||
{
|
||||
id: 'HT-K02',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.hasRemoteAccess',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'CRA + NIS2 Art. 21',
|
||||
description: 'Remote-Zugriff → CRA + NIS2 Supply Chain',
|
||||
riskWeight: 7,
|
||||
},
|
||||
{
|
||||
id: 'HT-K03',
|
||||
category: 'iace_cra',
|
||||
questionId: 'machineBuilder.hasOTAUpdates',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'CRA Art. 10 - Patch Management',
|
||||
description: 'OTA-Updates → CRA Patch Management Pflicht',
|
||||
riskWeight: 7,
|
||||
},
|
||||
|
||||
// ========== L: IACE — NIS2 indirekt (2 rules) ==========
|
||||
{
|
||||
id: 'HT-L01',
|
||||
category: 'iace_nis2_indirect',
|
||||
questionId: 'machineBuilder.criticalSectorClients',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'NIS2 Art. 21 - Supply Chain',
|
||||
description: 'Lieferant an KRITIS → NIS2 Supply Chain Anforderungen',
|
||||
riskWeight: 7,
|
||||
},
|
||||
{
|
||||
id: 'HT-L02',
|
||||
category: 'iace_nis2_indirect',
|
||||
questionId: 'machineBuilder.oemClients',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'NIS2 + EU Maschinenverordnung',
|
||||
description: 'OEM-Zulieferer → Compliance-Nachweispflicht',
|
||||
riskWeight: 5,
|
||||
},
|
||||
|
||||
// ========== M: IACE — Maschinenverordnung Triggers (4 rules) ==========
|
||||
{
|
||||
id: 'HT-M01',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.containsSoftware',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230 Anhang III',
|
||||
description: 'Software als Sicherheitskomponente → Maschinenverordnung',
|
||||
combineWithMachineBuilder: { field: 'hasSafetyFunction', value: true },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-M02',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.ceMarkingRequired',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230',
|
||||
description: 'CE-Kennzeichnung erforderlich',
|
||||
riskWeight: 6,
|
||||
},
|
||||
{
|
||||
id: 'HT-M03',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.ceMarkingRequired',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L3',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: [],
|
||||
legalReference: 'EU Maschinenverordnung 2023/1230 Art. 10',
|
||||
description: 'CE ohne bestehende Risikobeurteilung → Dringend!',
|
||||
combineWithMachineBuilder: { field: 'hasRiskAssessment', value: false },
|
||||
riskWeight: 9,
|
||||
},
|
||||
{
|
||||
id: 'HT-M04',
|
||||
category: 'iace_machinery_regulation',
|
||||
questionId: 'machineBuilder.containsFirmware',
|
||||
condition: 'EQUALS',
|
||||
conditionValue: true,
|
||||
minimumLevel: 'L2',
|
||||
requiresDSFA: false,
|
||||
mandatoryDocuments: ['TOM'],
|
||||
legalReference: 'EU Maschinenverordnung + CRA',
|
||||
description: 'Firmware mit Remote-Update → Change Management Pflicht',
|
||||
combineWithMachineBuilder: { field: 'hasOTAUpdates', value: true },
|
||||
riskWeight: 7,
|
||||
},
|
||||
]
|
||||
|
||||
// ============================================================================
|
||||
@@ -795,15 +966,16 @@ export const HARD_TRIGGER_RULES: HardTriggerRule[] = [
|
||||
export class ComplianceScopeEngine {
|
||||
/**
|
||||
* Haupteinstiegspunkt: Evaluiert alle Profiling-Antworten und produziert eine ScopeDecision
|
||||
* Optional: companyProfile fuer machineBuilder-basierte IACE Triggers
|
||||
*/
|
||||
evaluate(answers: ScopeProfilingAnswer[]): ScopeDecision {
|
||||
evaluate(answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null): ScopeDecision {
|
||||
const decision = createEmptyScopeDecision()
|
||||
|
||||
// 1. Scores berechnen
|
||||
decision.scores = this.calculateScores(answers)
|
||||
|
||||
// 2. Hard Triggers prüfen
|
||||
decision.triggeredHardTriggers = this.evaluateHardTriggers(answers)
|
||||
// 2. Hard Triggers prüfen (inkl. IACE machineBuilder Triggers)
|
||||
decision.triggeredHardTriggers = this.evaluateHardTriggers(answers, companyProfile)
|
||||
|
||||
// 3. Finales Level bestimmen
|
||||
decision.determinedLevel = this.determineLevel(
|
||||
@@ -934,13 +1106,14 @@ export class ComplianceScopeEngine {
|
||||
|
||||
/**
|
||||
* Evaluiert Hard Trigger Rules
|
||||
* Optional: companyProfile fuer machineBuilder-basierte IACE Triggers
|
||||
*/
|
||||
evaluateHardTriggers(answers: ScopeProfilingAnswer[]): TriggeredHardTrigger[] {
|
||||
evaluateHardTriggers(answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null): TriggeredHardTrigger[] {
|
||||
const triggered: TriggeredHardTrigger[] = []
|
||||
const answerMap = new Map(answers.map((a) => [a.questionId, a.answerValue]))
|
||||
|
||||
for (const rule of HARD_TRIGGER_RULES) {
|
||||
const isTriggered = this.checkTriggerCondition(rule, answerMap, answers)
|
||||
const isTriggered = this.checkTriggerCondition(rule, answerMap, answers, companyProfile)
|
||||
|
||||
if (isTriggered) {
|
||||
triggered.push({
|
||||
@@ -958,14 +1131,61 @@ export class ComplianceScopeEngine {
|
||||
return triggered
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest einen Wert aus dem MachineBuilderProfile anhand eines Feldnamens
|
||||
*/
|
||||
private getMachineBuilderValue(mb: MachineBuilderProfile, field: string): unknown {
|
||||
return (mb as Record<string, unknown>)[field]
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob eine Trigger-Regel erfüllt ist
|
||||
*/
|
||||
private checkTriggerCondition(
|
||||
rule: HardTriggerRule,
|
||||
answerMap: Map<string, any>,
|
||||
answers: ScopeProfilingAnswer[]
|
||||
answers: ScopeProfilingAnswer[],
|
||||
companyProfile?: CompanyProfile | null,
|
||||
): boolean {
|
||||
// IACE machineBuilder-basierte Triggers
|
||||
if (rule.questionId.startsWith('machineBuilder.')) {
|
||||
const mb = companyProfile?.machineBuilder
|
||||
if (!mb) return false
|
||||
|
||||
const fieldName = rule.questionId.replace('machineBuilder.', '')
|
||||
const fieldValue = this.getMachineBuilderValue(mb, fieldName)
|
||||
if (fieldValue === undefined) return false
|
||||
|
||||
let baseCondition = false
|
||||
switch (rule.condition) {
|
||||
case 'EQUALS':
|
||||
baseCondition = fieldValue === rule.conditionValue
|
||||
break
|
||||
case 'CONTAINS':
|
||||
if (Array.isArray(fieldValue)) {
|
||||
baseCondition = fieldValue.includes(rule.conditionValue)
|
||||
}
|
||||
break
|
||||
default:
|
||||
baseCondition = fieldValue === rule.conditionValue
|
||||
}
|
||||
|
||||
if (!baseCondition) return false
|
||||
|
||||
// combineWithMachineBuilder: additional AND condition on another MB field
|
||||
const combine = (rule as any).combineWithMachineBuilder
|
||||
if (combine) {
|
||||
const combineVal = this.getMachineBuilderValue(mb, combine.field)
|
||||
if (combine.value !== undefined && combineVal !== combine.value) return false
|
||||
if (combine.includes !== undefined) {
|
||||
if (!Array.isArray(combineVal) || !combineVal.includes(combine.includes)) return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Standard answer-based triggers
|
||||
const answerValue = answerMap.get(rule.questionId)
|
||||
if (answerValue === undefined) return false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user