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
@@ -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