feat(plc): analyse graphical logic (FBD/LD) from PLCopen XML (#169)
CI / Check (push) Has been skipped
CI / Detect Changes (push) Successful in 3s
CI / Deploy Agent (push) Successful in 5m23s
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped

This commit was merged in pull request #169.
This commit is contained in:
2026-07-16 11:43:49 +00:00
parent 3a53a1d7f2
commit 386d8457d6
3 changed files with 376 additions and 18 deletions
+33
View File
@@ -197,4 +197,37 @@ mod tests {
"the CASE state machine uses no JMP"
);
}
/// Graphical logic must be analysed too: an FBD POU (blocks + in/out
/// variables) is translated to synthetic ST, so the same rules fire on the
/// cleartext Modbus block, the hardcoded HMI password and the safety write.
#[test]
fn fbd_graphical_body_is_analysed() {
let all = analyze_tree(&demo_dir(), "demo-target");
let fbd: Vec<_> = all
.iter()
.filter(|f| {
f.file_path
.as_deref()
.is_some_and(|p| p.ends_with("pump_fbd.xml"))
})
.collect();
assert!(
!fbd.is_empty(),
"pump_fbd.xml (FBD) should produce findings"
);
let rules: HashSet<&str> = fbd.iter().filter_map(|f| f.rule_id.as_deref()).collect();
for r in [
"plc-insecure-comm", // Modbus_TCP_Master(AUTH := FALSE)
"plc-insecure-protocol-port", // PORT := 502
"plc-hardcoded-credential", // HmiPassword := 'admin123'
"plc-safety-bypass", // Safety_Enable := FALSE
] {
assert!(
rules.contains(r),
"expected rule {r} from FBD; got {rules:?}"
);
}
}
}