feat(cra): kuratierte Maßnahmen-Bibliothek — alle 40 CRA-Anforderungen belegt

- data/measures_curated.json: 24 deduplizierte, standard-gestützte Maßnahmen
  (9 bestehende M540-548 + 15 neue M600-614), Volltext + norm_refs + multi-reg
  covers. Deckt alle 40 CRA-AI-x (vorher nur 17).
- cra_annex_i_data lädt die Bibliothek defensiv: MEASURES=Superset, MEASURE_DETAILS
  (Volltext), mapped_measures aus covers abgeleitet. Fallback = hartkodierte 9.
- Mapper: open_measures tragen jetzt name+description+norm_refs (echte Volltexte).
- useCRA: merge nutzt Backend-Volltexte statt Demo-Lookup.
- Tests: Coverage (40/40) + Volltext im Assessment.

Quelle: extern handkuratiert/recherchiert, hier dedupliziert + gemappt. Maschinen-
VO/NIS2/IEC-Maßnahmen folgen, sobald deren Spine existiert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-16 07:44:13 +02:00
parent 4c206aa332
commit 6c619ecc42
5 changed files with 203 additions and 6 deletions
@@ -258,3 +258,33 @@ SEVERITY_WEIGHT = {
"MEDIUM": 30,
"LOW": 10,
}
# ── Kuratierte Maßnahmen-Bibliothek (Source of Truth: data/measures_curated.json) ──
# Deduplizierte, standard-gestützte Maßnahmen. Beim Import: MEASURES (id->Name) wird
# Superset, MEASURE_DETAILS trägt den Volltext, und jede CRA-Anforderung bekommt
# mapped_measures aus dem `covers`-Feld. Defensiv: bei fehlender/kaputter JSON bleibt
# das hartkodierte 9er-Set + Mapping erhalten.
import json as _json
import os as _os
MEASURE_DETAILS: dict = {}
try:
_MEAS_PATH = _os.path.join(_os.path.dirname(__file__), "..", "data", "measures_curated.json")
with open(_MEAS_PATH, encoding="utf-8") as _fh:
_curated = _json.load(_fh)
MEASURE_DETAILS = {m["id"]: m for m in _curated if m.get("id")}
for _m in _curated:
if _m.get("id") and _m.get("name"):
MEASURES[_m["id"]] = _m["name"]
_cover_map: dict = {}
for _m in _curated:
for _c in _m.get("covers", []):
_cover_map.setdefault(_c, []).append(_m["id"])
for _r in ANNEX_I_REQUIREMENTS:
_ids = _cover_map.get(_r["req_id"])
if _ids:
_r["mapped_measures"] = _ids
except Exception: # noqa: BLE001 — Bibliothek optional; Fallback = hartkodiertes Set
pass
@@ -0,0 +1,121 @@
[
{"id": "M540", "name": "Software Bill of Materials (SBOM) erstellen und mitliefern",
"description": "Für Software, Firmware und relevante Drittkomponenten wird eine SBOM erstellt und gepflegt. Sie enthält mindestens Komponente, Version, Herkunft und Abhängigkeit und wird für das Schwachstellenmanagement genutzt.",
"sub_topic": "supply_chain", "evidence_type": "code", "covers": ["CRA-AI-23", "CRA-AI-33"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-218", "NTIA SBOM"], "alternatives": ["SPDX", "CycloneDX"]},
{"id": "M541", "name": "Signierte Software- und Firmware-Updates mit Rollback-Schutz",
"description": "Updates werden ausschließlich kryptographisch signiert ausgeliefert; die Steuerung prüft die Signatur vor Installation und verweigert unsignierte Pakete. Ein Rollback-Schutz verhindert das Downgraden auf nachweislich verwundbare Versionen.",
"sub_topic": "updates", "evidence_type": "code", "covers": ["CRA-AI-29", "CRA-AI-5", "CRA-AI-28"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "IEC 62443-4-1", "NIST SP 800-53: SI-7"], "alternatives": []},
{"id": "M542", "name": "Initiale Default-Passwörter beim ersten Start erzwungen ändern",
"description": "Die Maschine fordert beim ersten Hochfahren zwingend die Änderung aller werkseitigen Default-Passwörter (Bediener, Wartung, Admin). Default-Credentials werden nicht im Klartext veröffentlicht; eine Wiederherstellung auf Default ist nur über physischen Zugriff möglich.",
"sub_topic": "authentication", "evidence_type": "code", "covers": ["CRA-AI-8", "CRA-AI-9"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "ETSI EN 303 645", "NIST SP 800-53: IA-5"], "alternatives": []},
{"id": "M543", "name": "Coordinated-Vulnerability-Disclosure-Policy veröffentlichen und betreiben",
"description": "Der Hersteller stellt einen klaren Meldeweg für Schwachstellen bereit. Meldungen werden bestätigt, bewertet, priorisiert und bis zur Behebung nachvollziehbar bearbeitet.",
"sub_topic": "vuln_handling", "evidence_type": "document", "covers": ["CRA-AI-35"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "ISO/IEC 29147", "ISO/IEC 30111"], "alternatives": []},
{"id": "M544", "name": "Patch-SLA mit Severity-Tiers dokumentieren und Lifecycle-Support festlegen",
"description": "Für kritische, hohe, mittlere und niedrige Schwachstellen sind Bewertungs- und Behebungsfristen definiert. Supportzeitraum und Updateversorgung pro Produkt sind festgelegt; Abweichungen werden risikobasiert begründet und dokumentiert.",
"sub_topic": "vuln_handling", "evidence_type": "process", "covers": ["CRA-AI-31", "CRA-AI-34", "CRA-AI-39"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "ISO/IEC 30111", "NIST SP 800-53: SI-2"], "alternatives": []},
{"id": "M545", "name": "Secure-by-Default-Konfiguration und Cybersecurity-Hardening-Guide beilegen",
"description": "Das Produkt wird mit sicheren Standardeinstellungen ausgeliefert (keine offenen Debug-Schnittstellen, keine unnötigen Dienste). Die Doku enthält einen gepflegten Hardening-Guide: Netzwerk-Segmentierung, deaktivierbare Dienste, sichere Konfiguration der Komponenten und Benutzerverwaltung.",
"sub_topic": "secure_design", "evidence_type": "hybrid", "covers": ["CRA-AI-1", "CRA-AI-40"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I/II", "IEC 62443-3-3", "ETSI EN 303 645"], "alternatives": []},
{"id": "M546", "name": "Incident-Meldeprozess an ENISA / nationale CSIRT definieren",
"description": "Für aktiv ausgenutzte Schwachstellen und erhebliche Sicherheitsvorfälle ist ein Meldeprozess mit Rollen, Fristen (24h/72h), Freigaben und Kommunikationswegen definiert. Zuständigkeiten für ENISA, CSIRT, Kundeninformation und interne Eskalation sind festgelegt.",
"sub_topic": "vuln_handling", "evidence_type": "process", "covers": ["CRA-AI-36", "CRA-AI-37", "CRA-AI-38"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "ISO/IEC 27035"], "alternatives": []},
{"id": "M547", "name": "Updates über authentisierten Kanal mit Integritätsprüfung",
"description": "Der Update-Kanal ist manipulationssicher: TLS 1.3 mit Zertifikatsprüfung bei Online-Updates, Hash-Prüfung der Update-Datei vor dem Anwenden. Der Update-Prozess ist atomar — bei Abbruch bleibt die alte Version lauffähig.",
"sub_topic": "updates", "evidence_type": "code", "covers": ["CRA-AI-30", "CRA-AI-28", "CRA-AI-6"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "IEC 62443-4-2", "NIST SP 800-53: SI-2"], "alternatives": []},
{"id": "M548", "name": "Sicherheitsbewertung / Penetrationstest vor Inverkehrbringen durchführen",
"description": "Vor Inverkehrbringen werden Architektur, Konfiguration, Schnittstellen, Update-Prozess, Authentisierung und bekannte Schwachstellen bewertet. Je nach Risiko erfolgt zusätzlich ein Penetrationstest oder eine unabhängige technische Prüfung.",
"sub_topic": "ssdlc", "evidence_type": "hybrid", "covers": ["CRA-AI-20"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "IEC 62443-4-1", "OWASP ASVS"], "alternatives": ["Penetrationstest", "Security-Review", "Red-Team-Test"]},
{"id": "M600", "name": "Nicht benötigte Dienste, Ports und Protokolle standardmäßig deaktivieren",
"description": "Auf SPS, HMI, IPC, Gateway und Fernwartung werden nur die für den Einsatzzweck erforderlichen Dienste aktiviert. Nicht benötigte Ports, Konten, Webdienste und Diagnose-Schnittstellen bleiben deaktiviert; Aktivierungen werden dokumentiert.",
"sub_topic": "secure_design", "evidence_type": "code", "covers": ["CRA-AI-2"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "IEC 62443-3-3", "NIST SP 800-53: CM-7", "ETSI EN 303 645"], "alternatives": []},
{"id": "M601", "name": "Sichere Systemarchitektur mit Netzwerkzonen und Conduits umsetzen",
"description": "Produktionsnetz, Fernwartung, Office-IT, Cloud-Anbindung und Safety-Funktionen werden logisch oder physisch getrennt. Sicherheitskritische Funktionen sind nicht direkt aus untrusted Segmenten erreichbar; Kommunikationspfade sind explizit erlaubt und dokumentiert.",
"sub_topic": "secure_design", "evidence_type": "hybrid", "covers": ["CRA-AI-3"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "IEC 62443-3-2", "IEC 62443-3-3", "NIST SP 800-53: SC-7"], "alternatives": []},
{"id": "M602", "name": "Least-Privilege und rollenbasierte Autorisierung erzwingen",
"description": "Benutzer, Dienste und Komponenten erhalten nur die für ihre Aufgabe erforderlichen Rechte. Bediener, Instandhaltung, Hersteller-Service und Admin haben getrennte Rollen; Schreibrechte auf sicherheitsrelevante Parameter sind beschränkt und werden regelmäßig überprüft.",
"sub_topic": "authentication", "evidence_type": "code", "covers": ["CRA-AI-4", "CRA-AI-12"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-53: AC-6", "NIST SP 800-53: AC-2", "IEC 62443-3-3"], "alternatives": []},
{"id": "M603", "name": "Starke Authentifizierung für privilegierte Zugänge einsetzen",
"description": "Administrations-, Wartungs- und Fernwartungszugänge werden durch starke Authentisierung geschützt. Für besonders kritische Zugänge wird ein zweiter Faktor, ein Client-Zertifikat oder ein Hardware-Token verlangt.",
"sub_topic": "authentication", "evidence_type": "code", "covers": ["CRA-AI-7"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-53: IA-2", "OWASP ASVS V2", "ETSI EN 303 645"], "alternatives": ["Client-Zertifikat", "Hardware-Token", "TOTP"]},
{"id": "M604", "name": "Credential- und Schlüsselmanagement etablieren",
"description": "Passwörter, API-Keys, Zertifikate und private Schlüssel werden nicht im Klartext oder Quellcode gespeichert und nicht über Kunden/Maschinen wiederverwendet. Schlüssel werden sicher erzeugt, getrennt verwahrt, rotiert und bei Kompromittierung ersetzt.",
"sub_topic": "authentication", "evidence_type": "hybrid", "covers": ["CRA-AI-9", "CRA-AI-16"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-53: IA-5", "NIST SP 800-57", "IEC 62443-4-2"], "alternatives": ["HSM", "Secure Element", "TPM"]},
{"id": "M605", "name": "Sitzungen automatisch absichern und beenden",
"description": "Authentisierte Sitzungen besitzen eindeutige Session-IDs, begrenzte Lebensdauer und automatische Sperrung bei Inaktivität. Logout, Rollenwechsel oder Passwortänderung machen bestehende Sitzungen ungültig.",
"sub_topic": "authentication", "evidence_type": "code", "covers": ["CRA-AI-10"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "OWASP ASVS V3", "NIST SP 800-53: AC-12"], "alternatives": []},
{"id": "M606", "name": "Brute-Force-Angriffe begrenzen und Anmeldungen protokollieren",
"description": "Wiederholte Fehlanmeldungen führen zu Verzögerungen, temporären Sperren oder zusätzlichen Prüfungen, ohne reguläre Benutzer unangemessen einzuschränken. Anmeldeversuche an HMI, Web, API, Fernwartung und Servicekonten werden protokolliert.",
"sub_topic": "authentication", "evidence_type": "code", "covers": ["CRA-AI-11", "CRA-AI-24"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-53: AC-7", "OWASP ASVS V2"], "alternatives": []},
{"id": "M607", "name": "Vertrauliche Daten im Gerät und in Backups verschlüsseln",
"description": "Personenbezogene, sicherheitsrelevante und betriebskritische Daten werden auf Datenträgern, Speichermedien und Backups kryptographisch geschützt. Schlüsselmaterial wird getrennt von den Daten verwaltet.",
"sub_topic": "cryptography", "evidence_type": "code", "covers": ["CRA-AI-13", "CRA-AI-14"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-53: SC-28", "NIST SP 800-57", "IEC 62443-3-3"], "alternatives": []},
{"id": "M608", "name": "Externe Kommunikation kryptographisch absichern",
"description": "Fernwartung, Cloud-Anbindung, API-Verkehr, Telemetrie und Update-Downloads erfolgen über authentisierte und verschlüsselte Verbindungen. Zertifikate werden geprüft, unsichere Protokolle und schwache Cipher sind deaktiviert.",
"sub_topic": "cryptography", "evidence_type": "code", "covers": ["CRA-AI-15"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-52", "IEC 62443-4-2"], "alternatives": []},
{"id": "M609", "name": "Datenminimierung für Erhebung, Telemetrie und Logs umsetzen",
"description": "Personenbezogene, betriebliche und diagnostische Daten werden auf das notwendige Maß beschränkt. Aufbewahrungsfristen und Löschprozesse sind definiert; nicht mehr benötigte Daten werden entfernt, pseudonymisiert oder anonymisiert.",
"sub_topic": "secure_design", "evidence_type": "process", "covers": ["CRA-AI-17"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "ISO/IEC 27002", "NIST Privacy Framework"], "alternatives": []},
{"id": "M610", "name": "Secure Software Development Lifecycle mit Code Reviews betreiben",
"description": "Bedrohungsanalyse, Sicherheitsanforderungen, sichere Implementierung, Schwachstellenprüfungen und Freigaben sind feste Bestandteile der Entwicklung. Änderungen an Authentisierung, Kryptographie, Updates und Safety-Parametern werden vor Freigabe systematisch reviewt und dokumentiert.",
"sub_topic": "ssdlc", "evidence_type": "process", "covers": ["CRA-AI-18", "CRA-AI-19"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-218", "IEC 62443-4-1", "OWASP SAMM"], "alternatives": []},
{"id": "M611", "name": "Schwachstellen identifizieren und Abhängigkeiten kontinuierlich überwachen",
"description": "Eigenentwicklungen, Bibliotheken, Container und Firmware werden fortlaufend und in CI/CD gegen Schwachstellendatenbanken geprüft. Treffer werden bewertet, priorisiert und ins Patch-Management überführt; kritische Treffer erzeugen Tickets oder blockieren Releases.",
"sub_topic": "supply_chain", "evidence_type": "code", "covers": ["CRA-AI-22", "CRA-AI-32"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-218", "OWASP Dependency-Check", "SLSA"], "alternatives": []},
{"id": "M612", "name": "Supply-Chain-Security: Drittkomponenten vor Integration bewerten",
"description": "Open-Source-Komponenten, Lieferantenbibliotheken, Container und Firmware-Bausteine werden vor Einsatz auf Herkunft, Wartungsstatus, Lizenz, Schwachstellen und Integrität geprüft. Unsichere oder nicht nachvollziehbare Komponenten werden nicht freigegeben.",
"sub_topic": "supply_chain", "evidence_type": "process", "covers": ["CRA-AI-21"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-218", "IEC 62443-4-1"], "alternatives": []},
{"id": "M613", "name": "Sicherheitsereignisse protokollieren, überwachen und auf Anomalien prüfen",
"description": "Anmeldungen, Konfigurations- und Parameteränderungen, Fernwartung und Update-Vorgänge werden nachvollziehbar protokolliert. Ereignisse werden überwacht und auf Auffälligkeiten ausgewertet; kritische Ereignisse lösen definierte Reaktionen aus.",
"sub_topic": "logging", "evidence_type": "process", "covers": ["CRA-AI-24", "CRA-AI-25", "CRA-AI-26"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-53: AU-6", "IEC 62443-3-3"], "alternatives": []},
{"id": "M614", "name": "Log-Integrität, Aufbewahrung und Vorfalldokumentation sicherstellen",
"description": "Sicherheitsprotokolle werden gegen nachträgliche Änderung geschützt und für definierte Zeiträume aufbewahrt. Sicherheitsvorfälle, Ursachenanalysen und Maßnahmen werden nachvollziehbar dokumentiert; Erkenntnisse fließen in Produkt- und Prozessverbesserung.",
"sub_topic": "logging", "evidence_type": "document", "covers": ["CRA-AI-27", "CRA-AI-40"],
"norm_refs": ["Verordnung (EU) 2024/2847 (CRA), Anhang I", "NIST SP 800-53: AU-9", "ISO/IEC 27035"], "alternatives": []}
]
@@ -13,11 +13,21 @@ compliance.api.cra_annex_i_data (pure data, no FastAPI dependency).
from dataclasses import dataclass, field, asdict
from typing import Optional
from compliance.api.cra_annex_i_data import ANNEX_I_REQUIREMENTS, MEASURES, DEADLINES
from compliance.api.cra_annex_i_data import ANNEX_I_REQUIREMENTS, MEASURES, MEASURE_DETAILS, DEADLINES
from compliance.services.cra_security_crosswalk import security_refs_for
from compliance.services.cra_prioritizer import prioritize, OBJECTIVES
from compliance.services.cra_safety_bridge import build_cross_links
def _measure_obj(mid: str) -> dict:
"""Full curated measure (name/description/norm_refs) for the assessment output,
falling back to just the name when only the thin id->name map has it."""
d = MEASURE_DETAILS.get(mid)
if d:
return {"id": mid, "name": d.get("name", ""), "description": d.get("description", ""),
"norm_refs": d.get("norm_refs", [])}
return {"id": mid, "name": MEASURES.get(mid, ""), "description": MEASURES.get(mid, ""), "norm_refs": []}
_REQ_INDEX = {r["req_id"]: r for r in ANNEX_I_REQUIREMENTS}
_SEV_ORDER = {"LOW": 1, "MEDIUM": 2, "HIGH": 3, "CRITICAL": 4}
_SEV_BY_RANK = {v: k for k, v in _SEV_ORDER.items()}
@@ -249,7 +259,7 @@ def assess_findings(findings: list, weights=None, safety_functions=None) -> CRAA
mapped=mapped,
by_risk=by_risk,
requirements_touched=sorted(reqs_touched),
open_measures=[{"id": mid, "description": MEASURES.get(mid, "")} for mid in measure_ids],
open_measures=[_measure_obj(mid) for mid in measure_ids],
unmapped_findings=unmapped,
coverage_pct=round(100.0 * covered / total, 1) if total else 0.0,
quick_wins=[m.finding_id for m in mapped if m.quick_win],
@@ -0,0 +1,34 @@
"""The curated measures library (data/measures_curated.json) must load, cover all
40 CRA Annex I requirements, and surface full text (name + norm_refs) in the
assessment output."""
from compliance.api.cra_annex_i_data import (
ANNEX_I_REQUIREMENTS,
MEASURES,
MEASURE_DETAILS,
)
from compliance.services.cra_finding_mapper import assess_findings_payload
def test_library_loaded():
assert len(MEASURE_DETAILS) >= 24
# MEASURES (id->name) is the superset incl. the original M540-M548.
assert "M540" in MEASURES and "M600" in MEASURES
def test_all_40_requirements_have_a_measure():
uncovered = [r["req_id"] for r in ANNEX_I_REQUIREMENTS if not r.get("mapped_measures")]
assert uncovered == [], f"uncovered: {uncovered}"
def test_mapped_measures_resolve_to_known_ids():
for r in ANNEX_I_REQUIREMENTS:
for mid in r.get("mapped_measures", []):
assert mid in MEASURES, f"{r['req_id']} -> unknown measure {mid}"
def test_assessment_open_measures_carry_full_text():
res = assess_findings_payload({"findings": [{"id": "x", "cwe": "CWE-79", "severity": "high"}]})
assert res["open_measures"], "expected at least one measure"
om = res["open_measures"][0]
assert om.get("name")
assert "norm_refs" in om