Chapter 4 — Data models
Lesson map
Lesson 04 published. Map, example, EX010.
flowchart TD
N0["Learning outcomes"]
N1["4.1 What a finding record needs"]
N2["4.2 Controlled vocabularies"]
N3["4.3 Identifiers over prose"]
N4["4.4 Validation as a gate"]
N5["Lab B — record validator"]
N6["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6
N6 --> EX["Worked example"]
EX --> Q["Exercise EX010"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["4.1 What a finding record needs"]
N2["4.2 Controlled vocabularies"]
N3["4.3 Identifiers over prose"]
N4["4.4 Validation as a gate"]
N5["Lab B — record validator"]
N6["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> Q["EX010"]
[Learning outcomes]
|
v
[4.1 What a finding record needs]
|
v
[4.2 Controlled vocabularies]
|
v
[4.3 Identifiers over prose]
|
v
[4.4 Validation as a gate]
|
v
[Lab B — record validator]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX010]
Learning outcomes
You will (1) design a minimal record for a finding; (2) validate records for missing fields, wrong types and illegal values; (3) explain why controlled vocabularies make reports comparable; (4) link findings to evidence through identifiers rather than prose.
4.1 What a finding record needs
A finding is a claim: on this asset, this weakness exists, shown by this evidence, with this severity. The minimal fields are therefore an identifier, the asset, an ATT&CK technique ID or other weakness reference, a severity from an agreed list, and a pointer to evidence. Remediation owner, due date and status come later in the life cycle.
Finding F-01 needs id, asset, evidence id, severity and scenario before it may enter the report builder. lab_records rejects incomplete rows. Priya will not argue severity on a record that still prints missing evidence.
4.2 Controlled vocabularies
If one tester writes "urgent", another "P1" and a third "very bad", nobody can count criticals. A fixed set — low, medium, high, critical — lets dashboards add and compare. ATT&CK IDs such as T1078 do the same job for adversary behaviour: "valid accounts" phrased five ways is still one technique.
TRT's vocabulary is low, medium, high, critical — never urgent. lab_records flags F-02 for severity urgent so dashboards stay countable. Northpoint's writers get the same four words in the ROE appendix, so TRT dashboards remain comparable across providers and across engagements.
4.3 Identifiers over prose
Evidence is linked by an ID such as E-001, not by "see the screenshot from Tuesday". IDs survive renaming, translation and copying into a ticket system. Each ID must be unique within the engagement.
In ENG-712, F-09 points at E-002 by identifier, so renaming the PNG or exporting the wiki to another format does not break the link. Ticket systems receive the identifiers unchanged when findings are copied across. Before the report builder runs, chat references such as the wiki screenshot are rewritten as evidence identifiers, so every claim in the draft points to one specific file.
4.4 Validation as a gate
Records are validated before they enter the report. A validator that lists every problem, rather than stopping at the first, saves round trips with the tester.
The validator lists every problem on a record in one pass. F-03 missing evidence and a bad severity on another row both appear before the author rewrites. Round trips drop when the gate is complete: on ENG-712's first dry run a validator that stopped at the first problem cost a day of back-and-forth with Northpoint's authors.
Lab B — record validator
"""Lab B (ch 04): validate synthetic finding records against a minimal schema."""
REQUIRED = {"id": str, "asset": str, "technique": str, "severity": str, "evidence": str}
SEVERITIES = {"low", "medium", "high", "critical"}
records = [
{"id": "F-01", "asset": "trt-portal", "technique": "T1078", "severity": "high", "evidence": "E-001"},
{"id": "F-02", "asset": "trt-wiki", "technique": "T1213", "severity": "urgent", "evidence": "E-002"},
{"id": "F-03", "asset": "trt-vpn", "technique": "T1133", "severity": "medium"},
]
for r in records:
problems = [f"missing {k}" for k in REQUIRED if k not in r]
problems += [f"{k} not {t.__name__}" for k, t in REQUIRED.items() if k in r and not isinstance(r[k], t)]
if r.get("severity") and r["severity"] not in SEVERITIES:
problems.append(f"bad severity '{r['severity']}'")
print(r["id"], "OK" if not problems else "; ".join(problems))
Run with python3 lab_records.py. Verified output:
F-01 OK
F-02 bad severity 'urgent'
F-03 missing evidence
Worked example
Northpoint delivers three draft findings. F-01 passes. F-02 uses "urgent", which is not in the agreed severity list. F-03 has no evidence pointer, so nothing supports the claim. Priya returns F-02 and F-03 with the validator's messages and accepts F-01 into the tracker.
Synthetic case. These three findings and their assets are fictional TRT examples.
Errors and limits
A schema guarantees shape, not truth: a well-formed record can still describe something that never happened. Keep the vocabulary short; too many severity grades produce argument instead of action.
Exercises
- EX010 — Unsupported claim. According to the lab, what is wrong with F-03, and why does that block it from the report? Rubric: 3 = missing evidence; a finding without evidence cannot be verified; 2 = names problem only; 1 = says OK; 0 = none.
- EX011 — Vocabulary repair. How should F-02's severity be fixed, and who decides the value? Rubric: 3 = map to an agreed level (e.g. high/critical) by the tester with reviewer agreement, using the rating method; 2 = picks a value only; 1 = add "urgent" to list; 0 = none.
- EX012 — Extra field. Propose one additional field for the remediation phase and justify it in a sentence. Rubric: 3 = sensible field (owner, due date, status, retest date) with reason; 2 = field only; 1 = irrelevant field; 0 = none.
Synthesis
A finding is a structured claim with a fixed vocabulary and an evidence pointer; validate before you report.
Continue studying — chapter 04 of 30
← Chapter 03 (draft) · Course chapter index · Chapter 05 (draft) →