feat(cra): hardware path — derive cyber findings from networked components
For hardware CE projects (no repo) each networked component (controller/hmi/ gateway/drive/remote_access/sensor) yields typical ICS vulnerability CLASSES (real CWE + "CISA-ICS — product-specific check" framing, NO fabricated CVEs); they flow through the same CRA engine. /assess accepts components[]. MappedFinding now echoes title/location/cwe so the response is self-contained for any finding source. Live CISA-ICS/NVD per-product CVE lookup is the later enrichment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ from pydantic import BaseModel
|
||||
from compliance.services.cra_finding_mapper import assess_findings_payload
|
||||
from compliance.services.cra_snapshot_store import save_snapshot, list_snapshots, get_snapshot
|
||||
from compliance.services.cra_use_case_controls import enrich_findings_with_breadth
|
||||
from compliance.services.cra_component_findings import findings_from_components
|
||||
from database import SessionLocal
|
||||
from .tenant_utils import get_tenant_id
|
||||
|
||||
@@ -44,18 +45,31 @@ class SafetyFunctionIn(BaseModel):
|
||||
vulnerable_to: Optional[List[str]] = None
|
||||
|
||||
|
||||
class ComponentIn(BaseModel):
|
||||
name: str
|
||||
component_class: Optional[str] = "" # controller | hmi | gateway | drive | remote_access | sensor
|
||||
networked: Optional[bool] = False
|
||||
vendor: Optional[str] = ""
|
||||
product: Optional[str] = ""
|
||||
|
||||
|
||||
class AssessRequest(BaseModel):
|
||||
findings: List[FindingIn]
|
||||
findings: List[FindingIn] = []
|
||||
# customer priorities for the discretionary tier: {objective: high|medium|low}.
|
||||
# objectives: access | data | network_api | supply_updates | monitoring.
|
||||
weights: Optional[Dict[str, str]] = None
|
||||
# CE-risk-assessment safety functions for the cyber-meets-safety bridge.
|
||||
safety_functions: Optional[List[SafetyFunctionIn]] = None
|
||||
# hardware path: networked components -> derived cyber findings (no repo).
|
||||
components: Optional[List[ComponentIn]] = None
|
||||
|
||||
|
||||
def _payload(body: AssessRequest) -> dict:
|
||||
findings = [f.model_dump() for f in body.findings]
|
||||
if body.components:
|
||||
findings = findings + findings_from_components([c.model_dump() for c in body.components])
|
||||
return {
|
||||
"findings": [f.model_dump() for f in body.findings],
|
||||
"findings": findings,
|
||||
"weights": body.weights,
|
||||
"safety_functions": [s.model_dump() for s in body.safety_functions] if body.safety_functions else None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user