"""Derive cyber findings from a machine's networked components (hardware path). For a hardware CE project there is no source repo to scan. Instead, each *networked* component (PLC, HMI, gateway, drive, remote access) carries typical ICS vulnerability CLASSES — well-established categories repeatedly flagged in CISA ICS advisories. We emit one finding per class so they flow through the same CRA engine. Honesty: these are vulnerability CLASSES (with a real CWE), framed as "product-specific check against CISA ICS advisories" — NOT fabricated CVE numbers. The concrete CVEs per vendor/product are a later live CISA-ICS / NVD enrichment. """ # component_class -> typical ICS vulnerability classes (suffix, title, cwe, severity, category) _CLASS_VULN_TEMPLATES = { "controller": [ # PLC / safety controller / motion controller ("ctrl-noauth", "Steuerungsprotokoll ohne Authentifizierung erreichbar", "CWE-306", "high", "auth"), ("ctrl-fw", "Firmware-/Programm-Update ohne Signaturpruefung", "CWE-494", "high", "updates"), ("ctrl-debug", "Offener Programmier-/Debug-Port", "CWE-1188", "medium", "config"), ], "hmi": [ # operator panel / SCADA HMI ("hmi-default", "Default-/Werks-Zugangsdaten", "CWE-1392", "critical", "auth"), ("hmi-cleartext", "Unverschluesselte Web-/Bedienoberflaeche", "CWE-319", "high", "crypto"), ], "gateway": [ # IoT / fieldbus / telemetry gateway ("gw-telemetry", "Unverschluesselte Telemetrie / Feldbus", "CWE-319", "high", "crypto"), ("gw-bruteforce", "Kein Brute-Force-/Rate-Limit-Schutz am Zugang", "CWE-307", "medium", "auth"), ], "drive": [ # VFD / servo drive ("drv-param", "Antriebsparameter ueber unauthentifizierten Kanal aenderbar", "CWE-306", "high", "auth"), ], "remote_access": [ # remote maintenance / VPN box ("ra-default", "Fernzugang mit Default-Passwort", "CWE-259", "critical", "auth"), ("ra-nomfa", "Keine starke Authentifizierung (MFA) fuer Fernzugang", "CWE-287", "high", "auth"), ], "sensor": [ # networked sensor ("sns-integrity", "Messwerte ohne Integritaetsschutz uebertragen", "CWE-345", "medium", "integrity"), ], } _SOURCE = "Komponenten-Klasse (CISA-ICS-typisch — produktspezifisch pruefen)" def findings_from_components(components: list) -> list: """components: [{name, component_class, networked, vendor?, product?}]. Returns finding dicts (ScannerFinding-shaped) for the NETWORKED components only.""" out = [] for comp in components or []: if not comp.get("networked"): continue klass = (comp.get("component_class") or "").lower() name = comp.get("name") or klass or "Komponente" for suffix, title, cwe, severity, category in _CLASS_VULN_TEMPLATES.get(klass, []): out.append({ "id": "{}-{}".format(name, suffix).replace(" ", "_"), "title": "{} ({})".format(title, name), "cwe": cwe, "severity": severity, "category": category, "location": name, "source": _SOURCE, }) return out