OpenCyber Academy
Bachelor's · Chapter 38 · English
Course code: OCA-108Chapter 38: OCA-108-C038

Chapter 38 — Capstone implementation of a response workflow

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

Lesson map

Lesson 38 published. Map, example, EX112.

flowchart TD
  N0["Learning outcomes and connection"]
  N1["38.1 Workflows as state machines"]
  N2["38.2 Access gates"]
  N3["38.3 Evidence store"]
  N4["38.4 Tests and rollback"]
  N5["Chapter synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5
  N5 --> EX["Worked example"]
  EX --> Q["Exercise EX112"]

Logical drawing

flowchart LR
  N0["Learning outcomes and connection"]
  N1["38.1 Workflows as state machines"]
  N2["38.2 Access gates"]
  N3["38.3 Evidence store"]
  N4["38.4 Tests and rollback"]
  N5["Chapter synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX112"]
[Learning outcomes and connection]
    |
    v
[38.1 Workflows as state machines]
    |
    v
[38.2 Access gates]
    |
    v
[38.3 Evidence store]
    |
    v
[38.4 Tests and rollback]
    |
    v
[Chapter synthesis]
    |
    v
[Worked example] --> [Exercise EX112]

Learning outcomes and connection

You will be able to (1) implement a small case workflow as explicit states and transitions; (2) enforce an access gate on sensitive transitions; (3) store evidence with integrity records; (4) run normal and failure test cases; and (5) roll back safely. This chapter implements the capstone proposed in Chapter 35 in a local, simulated environment; Chapter 39 evaluates it.

38.1 Workflows as state machines

A case moves through states — new, triaged, contained, recovered, closed — by transitions with rules: who may perform them and what must exist first. Modelling the workflow as a state machine (Chapter 6) makes rules testable and violations visible.

In Lab 38 the states and allowed transitions are written as a dictionary, so the rules are data that a reviewer can read in one place. A transition not in that table, such as moving straight from new to closed, is denied with an explicit reason rather than silently accepted.

38.2 Access gates

Some transitions need specific authority: containment of production systems requires the incident lead; closure requires the service owner's acceptance. A gate checks the actor's role and records the decision either way.

In Lab 38 the gate denies an analyst's attempt to contain HC-0301 and an engineer's attempt to close it, and both denials appear in the log. Recording refusals matters as much as recording approvals: they show the gates were tested in use, and a pattern of denials may reveal a role definition that does not match how people actually work. Note the lab's limit: the caller supplies its own role, so a real system must look up the role from an authenticated identity.

38.3 Evidence store

Attach evidence by reference with a hash (Chapter 17) and record who attached it and when. The store is append-only: corrections add new entries rather than editing old ones.

In Lab 38 the sign-in export is stored with the first twelve characters of its SHA-256 digest and the name of the analyst who attached it. Containment was denied until that item existed. A full system would keep the whole digest, record the time and store the log somewhere the workflow's own users cannot edit, so that the evidence record survives a dispute.

38.4 Tests and rollback

Test normal paths, forbidden transitions, unauthorized actors and missing evidence. Rollback means reverting a state change by a new, recorded transition (for example contained → triaged when containment is lifted), never by deleting history.

Lab 38 exercises a rollback from contained to triaged and then contains again; all three steps stay in the log. Its tests include a wrong role, missing evidence and a forbidden closure, and the final line, ten log entries with three denials, gives a quick check that every attempt was recorded. Run the tests after each change to the transition table, because a rule added for one case can silently block another.

38.5 Worked example and lab (synthetic): minimal workflow

Save as lab38.py.

# Lab 38 - a minimal case workflow with role gates, evidence hashes and an append-only log (stdlib only)
import hashlib

TRANSITIONS = {  # (from, to): roles allowed
    ("new", "triaged"): {"analyst", "lead"},
    ("triaged", "contained"): {"lead"},
    ("contained", "triaged"): {"lead"},          # recorded rollback
    ("contained", "recovered"): {"lead", "engineer"},
    ("recovered", "closed"): {"owner"},
}
REQUIRES_EVIDENCE = {"contained", "closed"}

class Case:
    def __init__(self, cid):
        self.cid, self.state, self.log, self.evidence = cid, "new", [], []
    def attach(self, actor, name, data):
        h = hashlib.sha256(data).hexdigest()[:12]
        self.evidence.append((name, h, actor))
        self.log.append(("attach", actor, name, h))
    def move(self, actor, role, to):
        allowed = TRANSITIONS.get((self.state, to))
        if allowed is None:
            result = "DENIED: no such transition"
        elif role not in allowed:
            result = f"DENIED: role {role} not allowed"
        elif to in REQUIRES_EVIDENCE and not self.evidence:
            result = "DENIED: evidence required"
        else:
            result = "OK"
            self.state = to
        self.log.append(("move", actor, f"{to}", result))
        return result

c = Case("HC-0301")
tests = [
    ("ana", "analyst", "triaged"),
    ("ana", "analyst", "contained"),      # wrong role
    ("lee", "lead", "contained"),         # no evidence yet
]
for t in tests:
    print(t, "->", c.move(*t))
c.attach("ana", "idp-signins.json", b"synthetic sign-in export")
for t in [("lee", "lead", "contained"), ("lee", "lead", "triaged"),
          ("lee", "lead", "contained"), ("eng", "engineer", "recovered"),
          ("eng", "engineer", "closed"), ("own", "owner", "closed")]:
    print(t, "->", c.move(*t))
print("final state:", c.state, "| log entries:", len(c.log),
      "| denied:", sum(1 for e in c.log if e[3].startswith("DENIED")))

Expected output:

('ana', 'analyst', 'triaged') -> OK
('ana', 'analyst', 'contained') -> DENIED: role analyst not allowed
('lee', 'lead', 'contained') -> DENIED: evidence required
('lee', 'lead', 'contained') -> OK
('lee', 'lead', 'triaged') -> OK
('lee', 'lead', 'contained') -> OK
('eng', 'engineer', 'recovered') -> OK
('eng', 'engineer', 'closed') -> DENIED: role engineer not allowed
('own', 'owner', 'closed') -> OK
final state: closed | log entries: 10 | denied: 3

Interpretation: the gates work for the tested paths: an analyst cannot contain, containment needs evidence, an engineer cannot close. The rollback (contained → triaged) is a recorded transition. The log keeps denied attempts — they are evidence about the process. Limits: roles are passed in by the caller and not authenticated (a real system must verify identity); "any evidence" satisfies the evidence rule (a stricter rule would require specific evidence types); and the log is in memory, not tamper-evident storage.

38.6 Common errors and limits

Exercises — chapter 38

Attempt each exercise before opening the worked results. Every case, organization, person, address and number in an exercise is synthetic unless a cited public source is named.

EX112 — Add a gate. Steps: require that closure is denied unless an evidence item named starting with acceptance exists; add a test showing denial and success. Rubric: 3 = rule implemented, both tests shown with output; 2 = rule without tests; 1 = not run; 0 = none.

EX113 — Failure cases. Steps: list five additional failure tests the capstone should include. Rubric: 3 = unknown state, repeated transition, closed case reopened without rule, evidence attached after closure, unauthenticated actor; 2 = three; 1 = one; 0 = none.

EX114 — Hardening plan. Steps: describe three changes needed to move from the lab to a pilot with real users. Rubric: 3 = authentication and role lookup from directory, tamper-evident persistent log, specific evidence requirements and retention, plus access controls on evidence; 2 = two; 1 = one; 0 = none.

Chapter synthesis

Implement workflows as explicit states and gated transitions, keep evidence by hash in an append-only record, test failures as carefully as successes, and roll back by recorded transitions — while stating the prototype's limits.

Worked answers

EX112. In move, before setting state: if to == "closed" and not any(n.startswith("acceptance") for n, _, _ in self.evidence): result = "DENIED: acceptance evidence required". Test: after recovery, owner closing → denied; attach acceptance-owner.txt → owner closing → OK. Report the printed lines.

EX113. (1) Transition from an unknown or misspelled state. (2) Repeating a transition (e.g., triaged → triaged), which should be denied or explicitly allowed. (3) Reopening a closed case — define a closed → triaged rule with role and reason, or deny. (4) Attaching evidence after closure — allowed only as an addendum with a reason. (5) An actor with no role or an unknown role.

EX114. (1) Authenticate users and look up roles from the directory rather than trusting parameters. (2) Persist the log and evidence index in tamper-evident storage (append-only, hashed chain or write-once store) with backups. (3) Define evidence requirements per transition and retention rules, and restrict evidence access by role, with access logging.