feat(cra): NIST/OWASP security golden-set crosswalk + full measure texts in CRA tab

Crosswalk (cra_security_crosswalk.py): deterministic, hand-curated CRA Annex I ->
NIST 800-53 Rev5 + OWASP Top 10:2021 mapping, the authoritative Security Golden
Set (no RAG; semantic breadth comes later via the shared Controls-API). Mapper
attaches NIST/OWASP refs per finding; golden-set completeness pinned by test
(every requirement has >=1 NIST ref). CRA tab now shows the NIST/OWASP best-
practice refs per finding and the full curated measure texts + norm references
(from measures_library_cra.go).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-13 21:24:53 +02:00
parent cadc34dad4
commit 43ae33975d
5 changed files with 225 additions and 45 deletions
@@ -14,6 +14,7 @@ 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.services.cra_security_crosswalk import security_refs_for
_REQ_INDEX = {r["req_id"]: r for r in ANNEX_I_REQUIREMENTS}
_SEV_ORDER = {"LOW": 1, "MEDIUM": 2, "HIGH": 3, "CRITICAL": 4}
@@ -89,6 +90,8 @@ class MappedFinding:
iso27001_ref: list = field(default_factory=list)
risk_level: str = "LOW"
measures: list = field(default_factory=list)
nist_refs: list = field(default_factory=list) # NIST 800-53 control IDs (golden-set crosswalk)
owasp_refs: list = field(default_factory=list) # [{code, label}] OWASP Top 10:2021
rationale: str = ""
unmapped: bool = False
@@ -162,6 +165,7 @@ def map_finding(f: ScannerFinding) -> MappedFinding:
for m in _REQ_INDEX[rid].get("mapped_measures", []):
if m not in measures:
measures.append(m)
refs = security_refs_for(reqs)
return MappedFinding(
finding_id=f.id,
requirement_ids=reqs,
@@ -170,6 +174,8 @@ def map_finding(f: ScannerFinding) -> MappedFinding:
iso27001_ref=list(primary.get("iso27001_ref", [])),
risk_level=_SEV_BY_RANK.get(risk_rank, "LOW"),
measures=measures,
nist_refs=refs["nist"],
owasp_refs=refs["owasp"],
rationale="{}: {}".format(primary["req_id"], primary.get("title", "")),
)