Chapter 30 — Verification laboratory I
Lesson map
Lesson 30 published. Map, example, EX088.
flowchart TD
N0["Learning outcomes"]
N1["30.1 Plan"]
N2["30.2 Execution"]
N3["30.3 Report"]
N4["Practice note"]
N5["30.1b Golden fixtures and inventory"]
N6["Field note — independent green"]
N7["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX088"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["30.1 Plan"]
N2["30.2 Execution"]
N3["30.3 Report"]
N4["Practice note"]
N5["30.1b Golden fixtures and inventory"]
N6["Field note — independent green"]
N7["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX088"]
[Learning outcomes]
|
v
[30.1 Plan]
|
v
[30.2 Execution]
|
v
[30.3 Report]
|
v
[Practice note]
|
v
[30.1b Golden fixtures and inventory]
|
v
[Field note — independent green]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX088]
Learning outcomes
You will (1) run a verification plan against ABP's monitor and zoning; (2) record pass/fail with evidence hashes; (3) stop the lab if a safety test fails.
30.1 Plan
Tests: allow in-scope reads; deny bad function codes; deny out-of-range writes; fail-closed missing policy; reachability SIS from L4 is false; cycle time under budget with tap. Each row: id, setup, action, expected, evidence path.
At ABP, each row of the plan names the requirement it checks, such as S-1 for the missing-policy row and R-12 for the reachability row. The cycle-time row uses the bench copy of the trunk to PLC-A with the tap installed, checking the time stays well under the 50 ms stop. Rows are written before the lab runs.
30.2 Execution
Use synthetic hosts and benches only. Capture stdout and config hashes. Any safety failure (fail-open or SIS reachable) stops the lab and opens a blocker — no continue to gather data.
# Lab 23 - reachability count on a synthetic Purdue-layered OT network
# Nodes: L5-ERP, L4-Office, L3.5-DMZ, L3-Hist, L2-SCADA, L1-PLC, L1-SIS, L0-IO
edges_flat=[("L5","L4"),("L4","L3"),("L3","L2"),("L2","L1-PLC"),("L2","L1-SIS"),("L1-PLC","L0"),("L1-SIS","L0"),("L4","L2")]
edges_zoned=[("L5","L4"),("L4","L3.5"),("L3.5","L3"),("L3","L2"),("L2","L1-PLC"),("L1-SIS","L0"),("L1-PLC","L0")] # SIS not reachable from SCADA
def reach(edges, start):
g={};
for a,b in edges: g.setdefault(a,[]).append(b)
seen=set(); stack=[start]
while stack:
n=stack.pop()
if n in seen: continue
seen.add(n); stack.extend(g.get(n,[]))
return seen
for name,e in (("flat",edges_flat),("zoned",edges_zoned)):
r=reach(e,"L4")
print(f"{name}: L4 reaches {sorted(r)} count={len(r)} writes_to_SIS={'L1-SIS' in r}")
Expected output:
flat: L4 reaches ['L0', 'L1-PLC', 'L1-SIS', 'L2', 'L3', 'L4'] count=6 writes_to_SIS=True
zoned: L4 reaches ['L0', 'L1-PLC', 'L2', 'L3', 'L3.5', 'L4'] count=6 writes_to_SIS=False
Lena's lab at ABP runs on the bench range with synthetic hosts and fixtures. When the missing-policy row failed once, the lab stopped and a blocker was opened against S-1; no further rows were run until it was fixed, because continuing would have produced results about a monitor already known to be unsafe.
30.3 Report
One-page lab report: environment versions, results table, open blockers, sign-off. Attach hashes.
Synthetic case — Ashford Batch Plant. Lab I failed the missing-policy row once; release halted until S-1 was fixed.
Lena's report for each Lab I run lists the tool versions, the fixture-set hash, each row's result and any open blockers, and it is signed by the independent runner. Hashes of logs and exports are attached, so a reviewer can confirm the evidence matches what was run. A report without hashes is not evidence.
30.4 Errors and limits
Labs on shared benches collide; book the synthetic range.
Lena books ABP's bench range for each lab run on the shared calendar, with the release under test in the booking. Two teams once ran different builds on the same bench at overlapping times, and their logs became impossible to separate. Since then, a lab run without a booking does not count as release evidence, and the bench is reset to a known state before each booked run. Booking takes minutes.
Practice note
Lab I for a monitor or allow-list change is run by someone who did not implement it. The recorded pass needs an independent green checklist.
30.1b Golden fixtures and inventory
Keep a sealed directory of synthetic fixtures with known expected decisions. A bench run counts as release evidence for the monitor only when its fixtures hash-match the sealed set. Photograph or export the lab environment inventory into the lab report. It passed on my machine without inventory is not transferable evidence.
Fixture changes are reviewed like code: a new expected decision must name the requirement it comes from. Lab result logs are evidence; retain them with the release tag.
Lena's fixture set is sealed and hashed.
Field note — independent green
The implementer of a hotfix offered to run Lab I to save time. The reviewer refused, ran the safety rows personally, and found a path that still ALLOWed when the policy file was a directory instead of a file. Independent runners catch assumptions authors stop seeing.
30.5 Worked example — reading a laboratory result honestly (synthetic ABP)
Laboratory I replays a recorded day of ABP traffic plus 40 injected write attempts through the permit function and the alarm logic. Results: 40 of 40 injected writes blocked; 2 legitimate engineering writes blocked because the engineering workstation was missing from the allow-list during commissioning; 0 alarms suppressed. Lena reports the two false blocks as a design defect, not noise — in a live plant they would have stopped a scheduled recipe change — and adds a commissioning-mode vector set. The lab report states what was not tested: performance under load, serial links and SIS-B, which is out of scope by design.
Exercises
- EX088 — Row. Write the fail-closed lab row. Rubric: 3 = complete row; 2 = partial; 1 = vague; 0 = none.
- EX089 — Stop. What happens on SIS reachable from L4? Rubric: 3 = stop, blocker, no plant use; 2 = note only; 1 = continue; 0 = none.
- EX090 — Evidence. Three hashes to attach. Rubric: 3 = policy, binary/version, result log; 2 = two; 1 = one; 0 = none.
Synthesis
Verify safety first; stop on fail-open or SIS exposure; hash everything.
Continue studying — chapter 30 of 50
← Chapter 29 (draft) · Course chapter index · Chapter 31 (draft) →