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:
@@ -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,
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
"""Machinery Regulation (EU) 2023/1230 — the NEW cyber-with-safety essential
|
||||
requirements (Annex III). Applies from 20 Jan 2027 and, for the first time,
|
||||
puts cybersecurity-affecting-safety into the CE machinery framework — the
|
||||
counterpart to the CRA for machine/plant builders.
|
||||
|
||||
Own-words summaries of the regulation text (EU legal texts are freely reusable
|
||||
incl. commercial with source acknowledgement, Commission Decision 2011/833/EU).
|
||||
We do NOT reproduce verbatim, and harmonised standards (prEN 50742, EN ISO 13849,
|
||||
EN ISO 12100) are referenced BY IDENTIFIER ONLY — they are copyrighted (CEN/ISO).
|
||||
|
||||
Scope note: only the two well-corroborated Annex III cyber clauses (1.1.9, 1.2.1)
|
||||
are modelled. Further clauses are intentionally omitted rather than guessed.
|
||||
"""
|
||||
|
||||
SOURCE_REGULATION = "Maschinenverordnung (EU) 2023/1230"
|
||||
|
||||
MACHINERY_REG_CYBER = [
|
||||
{
|
||||
"req_id": "MR-1.1.9",
|
||||
"annex_anchor": "Anhang III, 1.1.9",
|
||||
"title": "Schutz vor Korruption/Manipulation",
|
||||
"category": "Manipulationsschutz",
|
||||
"description": (
|
||||
"Vernetzung oder Fernzugriff darf keine gefaehrliche Situation ausloesen. "
|
||||
"Sicherheitsrelevante Hardware, Software und uebertragene Signale muessen identifiziert und "
|
||||
"gegen versehentliche wie absichtliche Manipulation geschuetzt werden; Eingriffe in "
|
||||
"sicherheitsrelevante Software/Konfiguration muessen als legitim oder unzulaessig "
|
||||
"nachvollziehbar (protokolliert) sein."
|
||||
),
|
||||
"severity": "HIGH",
|
||||
"evidence_type": "hybrid",
|
||||
"norm_references": ["Maschinenverordnung (EU) 2023/1230, Anhang III, 1.1.9", "prEN 50742 (Entwurf)"],
|
||||
"source_regulation": SOURCE_REGULATION,
|
||||
},
|
||||
{
|
||||
"req_id": "MR-1.2.1",
|
||||
"annex_anchor": "Anhang III, 1.2.1",
|
||||
"title": "Sicherheit und Zuverlaessigkeit der Steuerungen",
|
||||
"category": "Steuerungssicherheit",
|
||||
"description": (
|
||||
"Steuerungen sind so auszulegen, dass Fehler, aeussere Einfluesse (auch Funkstoerungen), "
|
||||
"Bedienfehler und vernuenftigerweise vorhersehbare Manipulationsversuche Dritter zu keiner "
|
||||
"gefaehrlichen Situation fuehren — die Sicherheit der Sicherheitsfunktionen muss erhalten bleiben."
|
||||
),
|
||||
"severity": "HIGH",
|
||||
"evidence_type": "code",
|
||||
"norm_references": ["Maschinenverordnung (EU) 2023/1230, Anhang III, 1.2.1", "EN ISO 13849", "prEN 50742 (Entwurf)"],
|
||||
"source_regulation": SOURCE_REGULATION,
|
||||
},
|
||||
]
|
||||
@@ -29,3 +29,13 @@ def test_no_digital_element_not_in_scope():
|
||||
assert d["in_scope"] is False
|
||||
assert d["classification"] == "NOT_IN_SCOPE"
|
||||
assert d["counts"]["code"] == 0
|
||||
|
||||
|
||||
def test_machinery_adds_tagged_machinery_reg_obligations():
|
||||
d = client.post("/api/v1/cra/readiness", json={
|
||||
"intended_use": "App fuer Industrieanlagen", "connected_to_internet": True, "is_machinery": True}).json()
|
||||
assert "Maschinen-VO 2023/1230" in d["regulations"]
|
||||
items = d["guideline"]["code"] + d["guideline"]["process"] + d["guideline"]["document"]
|
||||
assert any(it["source"] == "Maschinen-VO" for it in items)
|
||||
assert any(it["req_id"] == "MR-1.1.9" for it in items)
|
||||
assert any(it["source"] == "CRA" for it in items)
|
||||
|
||||
Reference in New Issue
Block a user