feat(cra): readiness derives obligations from Machinery Reg 2023/1230 too

Machine/plant builders are hit by BOTH the CRA and the new Machinery Regulation.
New machinery_reg_cyber.py models its two well-corroborated Annex III cyber-with-
safety essential requirements (1.1.9 protection against corruption, 1.2.1 control-
system safety incl. foreseeable manipulation) in our own words; EU legal text is
freely reusable (Commission Decision 2011/833/EU, source acknowledged), harmonised
standards referenced by identifier only. The readiness check asks "is it
machinery?" and, if so, adds these obligations tagged "Maschinen-VO" alongside the
CRA ones — the combination is visible (regulations list + per-item source badge).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-14 14:26:08 +02:00
parent 2da3e5fdbb
commit b0f78ae9a3
4 changed files with 89 additions and 0 deletions
@@ -20,6 +20,7 @@ from compliance.services.cra_use_case_controls import enrich_findings_with_bread
from compliance.services.cra_component_findings import findings_from_components
from compliance.api.cra_annex_i_data import ANNEX_I_REQUIREMENTS, MEASURES, DEADLINES
from compliance.api.cra_routes import _classify # reuse the deterministic Annex III/IV classifier
from compliance.api.machinery_reg_cyber import MACHINERY_REG_CYBER
from database import SessionLocal
from .tenant_utils import get_tenant_id
@@ -128,6 +129,7 @@ class ReadinessRequest(BaseModel):
has_firmware: Optional[bool] = False
remote_maintenance: Optional[bool] = False # implies connectivity + updates
user_parameter_app: Optional[bool] = False # implies connectivity + updates
is_machinery: Optional[bool] = False # CE machinery -> also Machinery Reg 2023/1230
# CRA Annex I evidence_type -> guideline bucket (Code / Prozess / Dokumentation).
@@ -156,7 +158,9 @@ async def readiness(body: ReadinessRequest):
classification, rationale = _classify(intake)
in_scope = classification != "NOT_IN_SCOPE"
groups = {"code": [], "process": [], "document": []}
regulations = []
if in_scope:
regulations.append("CRA")
for req in ANNEX_I_REQUIREMENTS:
bucket = _GUIDELINE_BUCKET.get(req.get("evidence_type", "process"), "process")
groups[bucket].append({
@@ -164,13 +168,26 @@ async def readiness(body: ReadinessRequest):
"annex_anchor": req["annex_anchor"], "severity": req["severity"],
"effort_days": req.get("effort_days"),
"measures": [{"id": m, "name": MEASURES.get(m, m)} for m in req.get("mapped_measures", [])],
"source": "CRA",
})
# Machine/plant builders are ALSO hit by the new Machinery Regulation's
# cyber-with-safety essential requirements (Annex III) — show the combination.
if body.is_machinery:
regulations.append("Maschinen-VO 2023/1230")
for req in MACHINERY_REG_CYBER:
bucket = _GUIDELINE_BUCKET.get(req.get("evidence_type", "process"), "process")
groups[bucket].append({
"req_id": req["req_id"], "title": req["title"], "category": req["category"],
"annex_anchor": req["annex_anchor"], "severity": req["severity"],
"effort_days": None, "measures": [], "source": "Maschinen-VO",
})
total_effort = sum(r["effort_days"] for g in groups.values() for r in g if r.get("effort_days"))
return {
"in_scope": in_scope,
"classification": classification,
"rationale": rationale,
"conformity_path_hint": _PATH_HINT.get(classification, ""),
"regulations": regulations,
"guideline": groups,
"counts": {k: len(v) for k, v in groups.items()},
"total_effort_days": total_effort,