CI / Check (push) Has been skipped
CI / Detect Changes (push) Successful in 3s
CI / Deploy Agent (push) Successful in 3m48s
CI / Deploy Dashboard (push) Successful in 2m51s
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Successful in 2m2s
58 lines
1.7 KiB
Smalltalk
58 lines
1.7 KiB
Smalltalk
(*
|
|
* Demo PLC program — pump-station control (IEC 61131-3 Structured Text).
|
|
*
|
|
* Deliberately vulnerable, for the compliance-scanner PLC control-logic demo.
|
|
* Each issue below is flagged by pipeline::plc::rules.
|
|
*)
|
|
|
|
FUNCTION_BLOCK PumpStationCtrl
|
|
VAR_INPUT
|
|
OperatorCmd : INT; (* HMI command index — untrusted *)
|
|
FlowSetpoint : REAL;
|
|
END_VAR
|
|
VAR_OUTPUT
|
|
PumpSpeed : REAL;
|
|
Fault : BOOL;
|
|
END_VAR
|
|
VAR
|
|
HmiPassword : STRING := 'admin123'; (* hardcoded + default credential *)
|
|
ApiKey : STRING := 'sk_live_9c1f2a'; (* hardcoded secret *)
|
|
PumpProfiles : ARRAY[0..7] OF REAL;
|
|
Safety_Enable : BOOL := TRUE;
|
|
Watchdog_Kick : INT := 1;
|
|
MeasuredFlow : REAL;
|
|
ScaleFactor : REAL;
|
|
i : INT;
|
|
END_VAR
|
|
|
|
(* Operator can index the profile table with an unvalidated command. *)
|
|
PumpSpeed := PumpProfiles[OperatorCmd];
|
|
|
|
(* Divisor is a live process value that can read zero on a stopped line. *)
|
|
ScaleFactor := FlowSetpoint / MeasuredFlow;
|
|
|
|
(* Safety interlock disabled straight from application logic. *)
|
|
IF OperatorCmd = 99 THEN
|
|
Safety_Enable := FALSE;
|
|
Watchdog_Kick := 0;
|
|
END_IF;
|
|
|
|
(* Unauthenticated Modbus/TCP link on the cleartext OT port. *)
|
|
Modbus_TCP_Connect(IP := '10.10.5.20', PORT := 502, AUTH := FALSE, PASSWORD := 'plc');
|
|
|
|
(* Unstructured jump around the fault handler. *)
|
|
IF MeasuredFlow > 1000.0 THEN
|
|
JMP trip;
|
|
END_IF;
|
|
|
|
(* A correctly guarded division — must NOT be flagged. *)
|
|
IF ScaleFactor <> 0.0 THEN
|
|
PumpSpeed := PumpSpeed / ScaleFactor;
|
|
END_IF;
|
|
|
|
RETURN;
|
|
trip:
|
|
Fault := TRUE;
|
|
PumpSpeed := 0.0;
|
|
END_FUNCTION_BLOCK
|