"""SBOM/DAST normalization from the scanner MCP -> CRA finding shape + mapping. Shapes pinned from the live MCP (sbom_vuln_report / list_dast_findings, 2026-06-18). """ import json from compliance.services.scanner_mcp_client import normalize_sbom_report, normalize_dast from compliance.services.cra_finding_mapper import ScannerFinding, map_finding SBOM = json.dumps({ "repo_id": "r1", "vulnerable_packages_count": 1, "total_vulnerabilities": 3, "packages": [ {"name": "dompurify", "version": "3.3.3", "package_manager": "npm", "license": "MIT", "vulnerabilities": [ {"id": "GHSA-39q2", "source": "osv", "severity": None}, {"id": "GHSA-39q2", "source": "osv", "severity": None}, # dup {"id": "GHSA-76mc", "source": "osv", "severity": "high"}]}, {"name": "clean-pkg", "version": "1.0", "package_manager": "npm", "vulnerabilities": []}, # no vulns -> skipped ], }) DAST = json.dumps([ {"_id": {"$oid": "abc123"}, "vuln_type": "security_misconfiguration", "title": "SQL backup exposure: /backup.sql", "description": "Sensitive resource accessible.", "severity": "high", "cwe": "CWE-16", "endpoint": "https://demo.x/backup.sql", "method": "GET", "exploitable": True}, ]) class TestSbom: def test_one_finding_per_vulnerable_package(self): out = normalize_sbom_report(SBOM) assert len(out) == 1 # clean-pkg skipped f = out[0] assert f["scan_type"] == "dependency" assert f["cwe"] == "CWE-1395" assert f["location"] == "npm:dompurify@3.3.3" assert f["severity"] == "high" # escalated from the one graded vuln assert "GHSA-39q2" in f["description"] and "GHSA-76mc" in f["description"] def test_maps_to_dependency_requirement_even_with_keyword_in_name(self): # CWE path dominates → CRA-AI-22, not CRA-AI-20 from a "sql"-like name out = normalize_sbom_report(json.dumps({ "repo_id": "r", "packages": [ {"name": "sqlite3", "version": "5.0", "package_manager": "npm", "vulnerabilities": [{"id": "CVE-x", "severity": "medium"}]}]})) m = map_finding(ScannerFinding.from_dict(out[0])) assert m.primary_requirement == "CRA-AI-22" def test_bad_json(self): assert normalize_sbom_report("not json") == [] assert normalize_sbom_report("{}") == [] class TestDast: def test_normalizes_dast_finding(self): out = normalize_dast(DAST) assert len(out) == 1 f = out[0] assert f["scan_type"] == "dast" assert f["cwe"] == "CWE-16" assert f["location"] == "https://demo.x/backup.sql" assert f["exploited"] is True assert "security_misconfiguration" in f["description"] def test_dast_maps_via_cwe(self): out = normalize_dast(DAST) m = map_finding(ScannerFinding.from_dict(out[0])) assert m.primary_requirement == "CRA-AI-1" # CWE-16 -> secure config assert m.finding_id == "abc123" # _id.$oid extracted def test_empty(self): assert normalize_dast("[]") == [] assert normalize_dast("not json") == []