Lab 02 — Threat Model Validator

Difficulty: 3/5 | Runs locally: yes

Pairs with the Phase 03 WARMUP Chapters 2–4 (threat modeling, STRIDE/DFDs, attack trees & abuse cases).

Why This Lab Exists (Purpose & Goal)

Most "threat models" in industry are theater: a filled-in STRIDE checklist that changed no decision, added no mitigation, and was never tested. The goal of this lab is to encode the difference between a real threat model and a performative one as a machine-checkable standard — a validator that rejects vague, untestable entries and demands that every threat connect to a concrete, verifiable control.

The Concept, In the Weeds

Threat modeling answers four questions (Shostack): What are we building? What can go wrong? What are we going to do about it? Did we do a good job? The output that matters is decisions — mitigations, accepted risks, design changes — not a document. The validator enforces that each threat names:

asset · actor · trust boundary · precondition · impact · owner · mitigation · verification method

The crucial rejection: an entry like "use encryption" is not a mitigation — it has no asset, no actor, no trust boundary, and above all no testable control. A real entry reads: "an attacker on the public network (actor) reading customer PII (asset) crossing the internet→edge boundary (precondition) is mitigated by enforced TLS 1.2+, owned by the platform team, verified by an automated test that rejects any cleartext endpoint." The presence of a verification method is what turns a wish into a control — and what lets the threat model connect, later, to the release gate (Lab 01) and the compliance evidence chain (Phase 07).

The validator also catches dangling threats (no mitigation) and duplicate threats, the two ways a model silently develops blind spots.

Why This Matters for Protecting the Company

Threat modeling is the cheapest place to fix security — a bad trust-boundary decision caught in a design review costs a conversation; the same flaw caught after launch costs a redesign, a migration, and possibly a breach. But threat modeling only pays off if it is rigorous; a checklist-only model gives false assurance, which is worse than none. A validator that enforces testable mitigations turns threat modeling from a ritual into an engineering control that demonstrably reduces risk and feeds the rest of the secure-SDLC machinery.

Build It

Implement the validator: every threat must name an asset, actor, trust boundary, precondition, impact, owner, mitigation, and verification method. Reject checklist entries (like "use encryption") that lack a testable control; flag dangling and duplicate threats.

LAB_MODULE=solution pytest -q

Secure Implementation Patterns

The anti-pattern (checklist theater). A "threat" with no asset, no boundary, and no testable control:

- threat: "SQL injection"
  mitigation: "use encryption"        # unrelated, untestable; no asset/actor/boundary/verification

The secure pattern — make the model machine-checkable (mirrors solution.py):

REQUIRED = ("id", "asset", "actor", "boundary", "precondition", "impact",
            "owner", "mitigation", "verification")

def validate(threats: list[dict]) -> tuple[str, ...]:
    issues, seen = [], set()
    for i, threat in enumerate(threats):
        for field in REQUIRED:
            if not str(threat.get(field, "")).strip():
                issues.append(f"{i}.{field}:missing")              # every threat names the full chain
        if threat.get("id") in seen:
            issues.append(f"{i}.id:duplicate")                     # no duplicate threats
        seen.add(threat.get("id"))
        if str(threat.get("verification", "")).lower() in {"review", "manual", "tbd"}:
            issues.append(f"{i}.verification:not-specific")        # a TESTABLE control, not "we'll review"
    return tuple(sorted(issues))

The decisive rule is the last one: a verification of review/manual/tbd is rejected. A mitigation is only real if it has an automatable verification — which is exactly what lets the threat connect to the release gate (Lab 01) and the compliance evidence chain (Phase 07).

A complete threat entry looks like:

- id: TM-014
  asset: customer PII
  actor: unauthenticated internet user
  boundary: internet -> edge
  precondition: export endpoint reachable pre-auth
  impact: cross-tenant data disclosure
  owner: platform-team
  mitigation: object-level authZ enforced per row in the export job
  verification: test_export_rejects_cross_tenant_rows  # an actual CI test name

Production practices to carry forward:

  • STRIDE per element/flow is a prompt to reason, not a form — walk each trust-boundary crossing.
  • Wire validation into CI so a PR that adds a feature without a corresponding tested mitigation fails — the model stays alive with the system.
  • Every verification is an automatable check (a test id, a policy query), so "did we do a good job?" is answerable, not asserted.
  • Track residual risk explicitly for threats consciously not fully mitigated (owner + acceptance).

Validation — What You Should Be Able to Do Now

  • Tell a real threat from checklist theater, and explain why a verification method is the line between them.
  • Run STRIDE as a prompt to reason about your data flows, not as a form to fill — and connect each threat to asset/actor/boundary/mitigation/test.
  • Explain why a threat model's deliverable is decisions and tested mitigations, not a document.

The Broader Perspective

This lab installs the habit that distinguishes engineers who reduce risk from those who document it: every claimed control must be tied to a test that proves it operates. That single discipline — requirement → control → test → evidence — is the connective tissue of the entire secure SDLC and of compliance done as engineering (Phase 07). It is also a mindset for life as a security engineer: be suspicious of any "we handle that" that cannot point to a passing test. A threat without a verification method is a hope; security is not built on hope.

Interview Angle

  • "Threat-model a multi-tenant export feature." — Draw the DFD with trust boundaries (internet↔edge, tenant↔tenant, app↔storage); STRIDE the crossings; top threats are cross-tenant data in a shared export job and a long-lived public download link; mitigate with per-row authZ, short-lived signed URLs, isolation, quotas — each with a verification method. End in tested mitigations, not a checklist.

Extension (Stretch)

Generate a data-flow diagram and 20 abuse cases for a fictional product, and feed the validated model into the Lab 01 release gate.

References

  • Phase 03 WARMUP Chapters 2–4; Adam Shostack, Threat Modeling: Designing for Security; OWASP Threat Modeling Cheat Sheet.