◇ OpenCyber Academy
Engineering · Chapter 9 · English

Chapter 9 — Programming II

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 9 — Programming II

Lesson map

Lesson 09 published. Map, example, EX025.

flowchart TD
  N0["Learning outcomes"]
  N1["9.1 From scripts to programs"]
  N2["9.2 Data structures"]
  N3["9.3 Errors"]
  N4["9.4 Unit tests"]
  N5["9.5 Edge cases"]
  N6["9.6 Performance"]
  N7["9.7 Reviews"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
  N7 --> EX["Worked example"]
  EX --> Q["Exercise EX025"]

Logical drawing

flowchart LR
  N0["Learning outcomes"]
  N1["9.1 From scripts to programs"]
  N2["9.2 Data structures"]
  N3["9.3 Errors"]
  N4["9.4 Unit tests"]
  N5["9.5 Edge cases"]
  N6["9.6 Performance"]
  N7["9.7 Reviews"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX025"]
[Learning outcomes]
    |
    v
[9.1 From scripts to programs]
    |
    v
[9.2 Data structures]
    |
    v
[9.3 Errors]
    |
    v
[9.4 Unit tests]
    |
    v
[9.5 Edge cases]
    |
    v
[9.6 Performance]
    |
    v
[9.7 Reviews]
    |
    v
[Worked example] --> [Exercise EX025]

Learning outcomes

You will (1) structure larger programs into modules; (2) use data structures for efficient lookups; (3) handle errors and edge cases deliberately; (4) write unit tests for detection logic.

9.1 From scripts to programs

As Amara's tools grow, she separates parsing, logic and output into modules. Each can be tested and replaced without touching the others.

A single long script mixes concerns: changing the output format risks breaking the parser. Modules with clear boundaries let Amara swap the parser for a new log format while the counting logic stays untouched and tested. The structure also lets Paolo review one module at a time, which keeps reviews short and focused. Each module has its own tests, and a small main program ties them together.

9.2 Data structures

Dictionaries give fast lookups — for example, mapping a username to its failure count. Sets answer membership questions quickly. Choosing the right structure turns minutes into seconds on large logs.

Looking up a username in a list means scanning every entry until a match appears; with a million entries, that is slow when repeated for every log line. A dictionary jumps to the entry almost directly. Sets do the same for 'have I seen this before?' questions, which detection logic asks constantly, such as whether an address is already on a watch list.

9.3 Errors

Programs meet missing fields, odd encodings and huge lines. Deliberate handling — catching specific errors, counting them and continuing — keeps analyses complete and honest.

Catching every error with one broad handler hides real bugs, such as a mistyped variable name. Catching specific errors, such as a value that cannot be parsed as a time, handles expected problems while letting unexpected ones surface. The count of handled errors goes into the output so readers know how many records were set aside. Silence is worse.

9.4 Unit tests

Python's unittest module lets Amara write tests such as "five failures for distinct users in one network should trigger; four should not". Tests run automatically before each change is merged.

A pair of tests on either side of the threshold pins down exactly where the rule's behaviour changes. Running tests automatically before merging means a later change that shifts the threshold by accident fails immediately, rather than being discovered when a real attack slips through. Tests also serve as documentation of what the rule is meant to do.

9.5 Edge cases

Boundary values (exactly the threshold), empty input and duplicate events are where detection code usually breaks. Tests target them first.

A rule that triggers on 'more than five' and one that triggers on 'five or more' differ only at exactly five, and that is where bugs hide. Empty input tests that the code reports nothing rather than crashing. Duplicate events, common when a log forwarder retries, test that the same failure is not counted twice. Amara lists these cases before writing the rule, not after a bug appears.

9.6 Performance

For a day of portal logs (2 million lines), reading line by line uses little memory; loading all into a list may exhaust it.

Two million lines of a few hundred bytes each can occupy far more memory once each line becomes a separate Python object. Reading one line at a time and updating counters keeps memory use flat. Amara measures both approaches on a sample day and records the peak memory of each, which justifies the streaming design in the review.

9.7 Reviews

Paolo reviews code for correctness and clarity; a second pair of eyes catches logic slips that tests missed.

Tests check what the author thought to test; a reviewer asks about what was not considered. Paolo spots that Amara's code treats usernames as case-sensitive while the portal does not, so 'U5' and 'u5' were counted separately. A test is added for that case, and the review notes become a checklist for future detection code. Reviews are logged with the reviewer's name, so later readers know someone checked the code before it went live.

Logging inside tools

Amara's tools log their own actions: input file, its hash, records read, records skipped and output written. When a result is questioned weeks later, the tool's log shows exactly what it processed. This self-logging costs a few lines of code and saves hours of doubt.

Worked example

Amara's test for "exactly 5 failures" fails: her code uses "> 5" while the design says "at least 5". She asks Paolo which is intended; they choose "≥ 5", update the design document and the code together, and keep the test.

Synthetic case. Tests and data are synthetic.

Errors and limits

Tests encode understanding at the time written; if the threat changes, tests may enforce outdated logic.

Exercises

Synthesis

Structured code, suitable data structures and tests on edge cases make detection logic reliable.

Continue studying — chapter 09 of 50

← Chapter 08 (draft) · Course chapter index · Chapter 10 (draft) →