Implements ScanType::PlcControlLogic — the missing scanner for PlcSps targets (they classified but ran nothing). This is the core of the SPS onboarding demo.
New pipeline::plc
Real IEC 61131-3 Structured Text front end — lexer + recursive-descent parser → AST (POUs, typed VAR sections, statements, expressions), tolerant recovery.
PLCopen XML extractor — pulls each ST POU's interface vars + <ST> body and reconstructs ST, so .st files and PLCopen projects share one path.
Implements **`ScanType::PlcControlLogic`** — the missing scanner for `PlcSps` targets (they classified but ran nothing). This is the core of the SPS onboarding demo.
## New `pipeline::plc`
- **Real IEC 61131-3 Structured Text front end** — lexer + recursive-descent parser → AST (POUs, typed VAR sections, statements, expressions), tolerant recovery.
- **PLCopen XML extractor** — pulls each ST POU's interface vars + `<ST>` body and reconstructs ST, so `.st` files and PLCopen projects share one path.
- **8 semantic, guard-aware rules** (CWE + remediation each):
- hardcoded credential (CWE-798), default/weak password (CWE-1393)
- safety-interlock / watchdog bypass (CWE-1384)
- array indexed by unvalidated input (CWE-129)
- **division without a zero-guard** (CWE-369) — *suppressed* when an enclosing `IF d <> 0` proves non-zero (the payoff of a real parser vs regex)
- insecure comm: auth/encryption disabled + cleartext OT ports (CWE-319)
- unstructured JMP (CWE-691)
- `PlcControlLogicScanner` (Scanner impl) → `Finding`s (fingerprint, file, line, severity).
## Wiring
`run_target_pipeline`: when the plan includes `PlcControlLogic`, `run_plc_scan` ingests the `PlcProject` artifact, analyzes, and persists findings.
## Fixtures + tests
`examples/plc-demo/` — a vulnerable pump-station `.st` + a PLCopen `conveyor.xml`. **5 tests**: parser, all-rules-fire, guarded-clean-is-quiet, end-to-end tree scan.
Adds `roxmltree`.
## Follow-ups
- A public OpenPLC/CODESYS sample as a 2nd fixture (in progress).
- Demo on the deployment: onboard a PlcSps target with a PLC project → scan → findings.
Local: fmt + agent/mcp clippy clean; PLC tests 5/5.
Implements ScanType::PlcControlLogic — the missing piece for PlcSps targets,
which previously classified but ran no scan.
New `pipeline::plc`:
- A real IEC 61131-3 Structured Text front end: lexer + recursive-descent parser
→ AST (POUs, typed VAR sections, statements, expressions). Tolerant recovery so
odd constructs never sink a file.
- PLCopen XML extractor: pulls each ST POU's interface vars + `<ST>` body and
reconstructs equivalent ST, so raw `.st` files and PLCopen projects share one
analysis path.
- Eight semantic, guard-aware rules over the AST → findings: hardcoded
credentials, default/weak passwords, safety-interlock/watchdog bypass, array
indexed by unvalidated input, division without a zero-guard (suppressed when an
enclosing `IF <d> <> 0` proves it), insecure comm (auth/encryption disabled),
and cleartext OT protocol ports, plus unstructured JMP. Each carries CWE +
remediation.
- `PlcControlLogicScanner` (Scanner impl) walks the project tree and emits
`Finding`s (dedup fingerprint, file, line, severity).
Wired into `run_target_pipeline`: when the scan plan includes PlcControlLogic,
`run_plc_scan` ingests the PlcProject artifact, analyzes it, and persists the
findings (findings_count handled by run_target).
Demo fixtures under examples/plc-demo/ (a vulnerable pump-station `.st` + a
PLCopen `conveyor.xml`). Tests: parser, all-rules-fire, guarded-clean-is-quiet,
and an end-to-end tree scan — 5 passing.
Adds `roxmltree` (read-only XML) for PLCopen parsing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI clippy (rust 1.94.0, overly_complex_bool_expr) flagged the disabling
check as a logic bug: the `watchdog && matches!(value, Int(0))` term is
fully subsumed by the preceding `matches!(value, Int(0))`. Simplify to
`Bool(false) || Int(0)` — behavior is unchanged (a safety/watchdog signal
driven to FALSE or 0 is still a bypass), and `watchdog` stays used in the
outer guard. All 5 PLC tests still pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second demo fixture (public-sample shape) to complement the all-rules
pump_station.st: a timed pedestrian-crossing state machine adapted from
the OpenPLC traffic-light example, extended with a SCADA/Modbus uplink and
a maintenance override. Mostly sound control logic with three planted,
field-realistic defects (hardcoded SCADA password, cleartext Modbus master,
maintenance mode that drops the pedestrian safety permit).
The regression test asserts the scanner surfaces those defects while staying
quiet on the guarded duty-cycle division and the JMP-free CASE machine —
demonstrating low false positives on real-world-shaped code.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sharang
merged commit 6d02b138c6 into main2026-07-16 08:31:44 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements
ScanType::PlcControlLogic— the missing scanner forPlcSpstargets (they classified but ran nothing). This is the core of the SPS onboarding demo.New
pipeline::plc<ST>body and reconstructs ST, so.stfiles and PLCopen projects share one path.IF d <> 0proves non-zero (the payoff of a real parser vs regex)PlcControlLogicScanner(Scanner impl) →Findings (fingerprint, file, line, severity).Wiring
run_target_pipeline: when the plan includesPlcControlLogic,run_plc_scaningests thePlcProjectartifact, analyzes, and persists findings.Fixtures + tests
examples/plc-demo/— a vulnerable pump-station.st+ a PLCopenconveyor.xml. 5 tests: parser, all-rules-fire, guarded-clean-is-quiet, end-to-end tree scan.Adds
roxmltree.Follow-ups
Local: fmt + agent/mcp clippy clean; PLC tests 5/5.