◇ OpenCyber Academy
Bachelor's · Chapter 3 · English

Chapter 3 — Programming I

Open, free learning for anyone who wants to protect people and technology. No account is required to read this text or its worked answers.

Chapter 3 — Programming I

Lesson map

Lesson 03 published. Map, example, EX005.

flowchart TD
  N0["Learning outcomes and connection"]
  N1["3.1 Why a security student programs"]
  N2["3.2 The core constructs"]
  N3["3.4 Practical activity"]
  N4["Chapter synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4
  N4 --> EX["Worked example"]
  EX --> Q["Exercise EX005"]

Logical drawing

flowchart LR
  N0["Learning outcomes and connection"]
  N1["3.1 Why a security student programs"]
  N2["3.2 The core constructs"]
  N3["3.4 Practical activity"]
  N4["Chapter synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4 --> Q["EX005"]
[Learning outcomes and connection]
    |
    v
[3.1 Why a security student programs]
    |
    v
[3.2 The core constructs]
    |
    v
[3.4 Practical activity]
    |
    v
[Chapter synthesis]
    |
    v
[Worked example] --> [Exercise EX005]

Learning outcomes and connection

After this chapter you can (1) write short Python programs using variables, conditions, loops, functions, lists and dictionaries; (2) read structured text (CSV) with the standard library; (3) turn a question about a system ("which devices are past end of support?") into a filter, a count and a report; and (4) test a program against a small case whose answer you already know. It builds on Chapter 2's device life cycle and prepares Chapter 8, where programs become robust against bad input.

3.1 Why a security student programs

Security work in critical services is mostly about knowing what you have and noticing what changed. Both need data processing: inventories with thousands of rows, logs with millions of lines, advisories published daily. A spreadsheet works for fifty rows; a short program works for fifty thousand and can be rerun tomorrow. Python is used here because its standard library reads CSV, JSON, dates and databases without installing anything, and because readable code is easier to review — a property that matters when your output informs a decision about patient safety.

3.2 The core constructs

A variable names a value (year = 2026). A condition chooses between paths (if eos < year:). A loop repeats work for each item (for row in rows:). A function packages a step with a name and inputs so it can be tested alone. A list holds items in order; a dictionary maps keys to values (row["os"]). The csv module turns each line of a CSV file into a dictionary keyed by the header. The collections.Counter class counts how often each value appears. With these you can answer most inventory questions.

Two disciplines matter from the start. First, test against a known answer: before running on the real file, run on five rows you checked by hand. Second, print what you excluded, not only what you found: a filter that silently drops rows with a blank date will report "no unsupported devices" for exactly the devices nobody documented.

3.3 Worked example and Lab 2: CVH inventory triage (synthetic)

This case is synthetic. CVH's clinical engineering team exports an eight-row sample of its device inventory. The question: which devices run software past its end of support, where are they, and are any non-clinical devices sitting on the clinical network?

# Lab 2 - triage a synthetic hospital device inventory with the standard library (offline)
import csv, io
from collections import Counter
DATA = """asset,type,os,os_end_of_support,network,clinical
INF-01,infusion pump,RTOS-x 2.1,2030,clinical-vlan,yes
INF-02,infusion pump,RTOS-x 1.4,2021,clinical-vlan,yes
IMG-01,imaging workstation,Windows 7,2020,clinical-vlan,yes
IMG-02,imaging workstation,Windows 10,2025,clinical-vlan,yes
LAB-01,lab analyser,Linux 3.10,2019,lab-vlan,yes
ADM-01,office pc,Windows 11,2031,corporate,no
ADM-02,office pc,Windows 10,2025,clinical-vlan,no
BMS-01,building controller,vendor fw 4,2027,bms-vlan,no
"""
YEAR = 2026
rows = list(csv.DictReader(io.StringIO(DATA)))
unsupported = [r for r in rows if int(r["os_end_of_support"]) < YEAR]
print("assets:", len(rows), "| unsupported:", len(unsupported))
for r in unsupported:
    print(f"  {r['asset']:7} {r['type']:20} {r['os']:12} EoS {r['os_end_of_support']} clinical={r['clinical']}")
print("by network:", dict(Counter(r["network"] for r in unsupported)))
misplaced = [r["asset"] for r in rows if r["clinical"] == "no" and r["network"] == "clinical-vlan"]
print("non-clinical devices on clinical VLAN:", misplaced)

Expected output:

assets: 8 | unsupported: 5
  INF-02  infusion pump        RTOS-x 1.4   EoS 2021 clinical=yes
  IMG-01  imaging workstation  Windows 7    EoS 2020 clinical=yes
  IMG-02  imaging workstation  Windows 10   EoS 2025 clinical=yes
  LAB-01  lab analyser         Linux 3.10   EoS 2019 clinical=yes
  ADM-02  office pc            Windows 10   EoS 2025 clinical=no
by network: {'clinical-vlan': 4, 'lab-vlan': 1}
non-clinical devices on clinical VLAN: ['ADM-02']

Interpretation: four clinical devices are unsupported, which is a replacement-planning problem with compensating controls (Chapter 2). ADM-02 is different: an unsupported office PC on the clinical network is the cheapest finding to fix — upgrade it and move it. The rows are teaching data: the device labels are invented, and although some operating-system names are familiar, treat every end-of-support year here as sample input and check any real product against its vendor's published lifecycle page.

3.4 Practical activity

Extend Lab 2: (1) add a row with an empty os_end_of_support and observe the error; (2) change the program so such rows are reported as "unknown — needs owner" rather than crashing or being skipped; (3) write one assert that checks the count of unsupported devices for the original eight rows is 5.

Step (2) is the important one. A row reported as "unknown — needs owner" puts a name on a gap; a skipped row hides it, and a crash stops the report for everyone. The assert in step (3) is a test with a known answer: if a later change to the filter makes it print 4 or 6, the test fails before anyone reads a wrong report.

3.5 Common errors and limits

In Lab 2 the second error would have hidden VEN-01 in EX005, the third would have made the 2025 devices look supported again if the year were left at 2025, and the fourth applies to the whole export: ADM-02 appears on the clinical VLAN only because someone recorded its network correctly.

Exercises — chapter 3

EX005 — Unknowns are findings. Objective: make missing data visible. Inputs: Lab 2 plus a ninth row VEN-01,ventilator,,,clinical-vlan,yes. Steps: modify the program to print an "unknown" section; explain why an unknown support date on a ventilator should be escalated. Expected evidence: code change, output and two sentences. Rubric: 3 = no crash, VEN-01 reported as unknown, escalation reason tied to clinical consequence; 2 = works without reasoning; 1 = crash avoided by skipping; 0 = none.

EX006 — A function you can test. Objective: separate logic from data. Inputs: Lab 2. Steps: write def is_unsupported(row, year) -> bool, test it with three hand-made rows (supported, unsupported, boundary year equal to year), and state your rule for the boundary. Expected evidence: function, tests and the rule. Rubric: 3 = correct function, all three tests, boundary rule stated and justified; 2 = boundary not discussed; 1 = function only; 0 = none.

Chapter synthesis

Short, readable programs turn inventories into decisions. Filter, count and report — and always report what you could not classify. Test against cases you know, keep logic in functions, and remember that the program is only as true as its data.

Continue studying — chapter 03 of 40

← Chapter 02 · Course chapter index · Chapter 04 →

Continue to the next authored lesson.