CI / Check (pull_request) Successful in 5m20s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
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>
93 lines
3.2 KiB
Smalltalk
93 lines
3.2 KiB
Smalltalk
(*
|
|
* Pedestrian-crossing traffic-light controller.
|
|
*
|
|
* Structure adapted from the classic OpenPLC "traffic light" example
|
|
* (github.com/thiagoralves/OpenPLC_v3 examples) — a timed state machine
|
|
* driving vehicle + pedestrian lamps, extended with a SCADA/Modbus link
|
|
* and a maintenance override so it reads like a real deployed program.
|
|
*
|
|
* The control logic itself is sound; the security-relevant defects are the
|
|
* kind that slip into field code under deadline: a hardcoded SCADA password,
|
|
* a cleartext Modbus/TCP master, and a maintenance mode that drops the
|
|
* pedestrian safety permit. Everything else should stay quiet.
|
|
*)
|
|
PROGRAM TrafficLight
|
|
VAR
|
|
State : INT := 0; (* 0 GreenVeh, 1 Amber, 2 RedVeh/WalkPed, 3 FlashPed *)
|
|
Tmr : TON;
|
|
StateElapsed : TIME;
|
|
CycleMs : DINT := 0;
|
|
|
|
(* Lamp outputs *)
|
|
VehGreen : BOOL := FALSE;
|
|
VehAmber : BOOL := FALSE;
|
|
VehRed : BOOL := FALSE;
|
|
PedWalk : BOOL := FALSE;
|
|
PedStop : BOOL := TRUE;
|
|
|
|
(* Pedestrian safety permit — must be TRUE before the WALK phase asserts *)
|
|
PedPermit : BOOL := TRUE;
|
|
PedButton : BOOL := FALSE;
|
|
|
|
(* SCADA / remote monitoring *)
|
|
ScadaUser : STRING := 'operator';
|
|
ScadaPassword : STRING := 'Tr@ffic2019'; (* hardcoded SCADA credential *)
|
|
ModbusReady : BOOL := FALSE;
|
|
|
|
(* Maintenance override *)
|
|
MaintMode : BOOL := FALSE;
|
|
LampCount : INT := 5;
|
|
DutyPct : INT;
|
|
END_VAR
|
|
|
|
(* ---- SCADA uplink: publish state to the control room over Modbus/TCP ---- *)
|
|
IF NOT ModbusReady THEN
|
|
Modbus_TCP_Master(IP := '10.20.0.5', PORT := 502, AUTH := FALSE, USER := ScadaUser, PASS := ScadaPassword);
|
|
ModbusReady := TRUE;
|
|
END_IF;
|
|
|
|
(* ---- Duty-cycle for the flashing pedestrian lamp (guarded division) ---- *)
|
|
IF LampCount <> 0 THEN
|
|
DutyPct := (CycleMs * 100) / LampCount;
|
|
END_IF;
|
|
|
|
(* ---- Maintenance override: flash amber, hand control to the technician ---- *)
|
|
IF MaintMode THEN
|
|
VehGreen := FALSE;
|
|
VehRed := FALSE;
|
|
VehAmber := NOT VehAmber;
|
|
PedPermit := FALSE; (* drops the pedestrian safety permit in code *)
|
|
PedWalk := FALSE;
|
|
PedStop := TRUE;
|
|
ELSE
|
|
(* ---- Normal timed state machine ---- *)
|
|
Tmr(IN := TRUE, PT := T#5s);
|
|
StateElapsed := Tmr.ET;
|
|
|
|
CASE State OF
|
|
0: (* vehicles go, pedestrians stop *)
|
|
VehGreen := TRUE; VehAmber := FALSE; VehRed := FALSE;
|
|
PedWalk := FALSE; PedStop := TRUE;
|
|
IF PedButton AND Tmr.Q THEN
|
|
State := 1; Tmr(IN := FALSE);
|
|
END_IF;
|
|
1: (* amber transition *)
|
|
VehGreen := FALSE; VehAmber := TRUE;
|
|
IF Tmr.Q THEN State := 2; Tmr(IN := FALSE); END_IF;
|
|
2: (* vehicles stop, pedestrians walk — only if permitted *)
|
|
VehAmber := FALSE; VehRed := TRUE;
|
|
IF PedPermit THEN
|
|
PedWalk := TRUE; PedStop := FALSE;
|
|
END_IF;
|
|
IF Tmr.Q THEN State := 3; Tmr(IN := FALSE); END_IF;
|
|
3: (* flashing don't-walk before returning to green *)
|
|
PedWalk := NOT PedWalk;
|
|
IF Tmr.Q THEN
|
|
State := 0; PedButton := FALSE; Tmr(IN := FALSE);
|
|
END_IF;
|
|
ELSE
|
|
State := 0;
|
|
END_CASE;
|
|
END_IF;
|
|
END_PROGRAM
|