---
name: threat-model-walkthrough
description: STRIDE-based threat model for a public-facing surface (write endpoint, form, Worker). Produces a one-page THREAT-MODEL.md with trust boundaries, data flows, and per-category threat analysis. Invoke when the surface is first designed and on material change.
---

# Threat Model Walkthrough

Produces a living threat-model document for a public-facing surface. The document is the per-site artifact that grounds the `public-endpoint-review` checklist - severity, priority, and scope all trace back to what the threat model identifies.

## When to Use

- When a new public-facing surface is being designed, before code is written
- When an existing surface gains new flows, fields, or side effects (material change)
- When a security incident prompts re-evaluation
- As a prerequisite to `public-endpoint-review` (that skill fails without a threat model present)

## Scope note

This skill targets public-facing surfaces with write endpoints. Authenticated user-session surfaces require a more involved threat model that this skill does not provide (see `A - Accounts/` for that pattern).

## Process

### Step 1: Describe the surface

Write, in plain language, what the surface does. Five to ten sentences. Include:

- What the user-facing flow is (what a visitor does to interact)
- What data is collected
- What backing store the data lands in
- Who reads it downstream (operator, integration, nobody)
- What value it delivers to the business

Resist the urge to jump to threats. The description is the artifact the rest of the walkthrough builds on.

### Step 2: Identify trust boundaries

Draw a mental (or real) diagram. For each flow segment, mark:

- Which zone it's in (untrusted, DMZ, trusted)
- What authenticates across the boundary (if anything)
- What controls the data crossing the boundary (TLS, Worker, ACL)

For a typical Cloudflare-hosted form, the zones are:

```
[Browser: UNTRUSTED]
    |   HTTPS (TLS terminates at CF edge)
[Cloudflare edge: TRUSTED SINK]
    |   Worker binding
[Cloudflare Worker: TRUSTED COMPUTE]
    |   Outbound HTTPS + bearer token
[Backing store: TRUSTED SINK]
```

Each arrow is a trust boundary. Controls on each arrow are the candidates for hardening.

### Step 3: Inventory data flows

For each user-facing flow, enumerate:

- Input fields (what the user provides)
- Transformations (what the Worker / handler does)
- Output (what lands where)
- Auth / authz at each step

Keep it terse. One flow = 3-6 bullet points.

### Step 4: STRIDE analysis

For each trust boundary or flow, run the six STRIDE categories. Not every category applies to every flow; note which apply and which don't.

#### S - Spoofing identity

- Who could impersonate a legitimate user / operator / system?
- What prevents it today?
- What residual risk remains?

Typical for public forms: anyone can POST without auth; mitigations are honeypot + Turnstile + origin check (not identity, but bot vs human).

#### T - Tampering with data

- Who could modify data in flight or at rest?
- TLS covers in-flight. What covers at-rest?
- If a bad actor got PAT access, what could they modify?

Typical: TLS covers edge→CF→Worker. PAT-scoped writes to Airtable limit blast radius. Airtable's record history covers operator-side tampering.

#### R - Repudiation

- If someone did something malicious (or legitimate but contested), can we prove it?
- Is there an audit trail? Can it be tampered with?

Typical: Cloudflare access log + Airtable record history. Both are external to our control; both are reasonable evidence.

#### I - Information disclosure

- What data, if leaked, would matter?
- Where could it leak (logs, errors, headers, timing)?
- What about the data at rest: who can read it?

Typical: PAT leak is the critical one. Error messages leaking schema (second-order). Log files accidentally exposing PII.

#### D - Denial of service

- How could an attacker make the endpoint unavailable or expensive?
- Is the endpoint on a paid tier where abuse = cost?
- What's the rate limit posture?

Typical: unauthenticated public endpoints are DoS targets; Cloudflare edge absorbs volumetric; app-level rate limit covers slow abuse.

#### E - Elevation of privilege

- If an attacker got one level of access (form submitter), how far could they pivot?
- If the Worker itself was compromised, what could it reach?

Typical: form submitter is un-privileged; Worker has PAT scoped to one base. Pivot from form to Worker requires a Worker-code vulnerability (input validation escape).

### Step 5: Identify high-value targets

Of the surfaces / flows in the scope, which are the most attractive to an attacker and why? Rank them. High-value = anything where abuse has selection leverage (e.g., a charity application that gets something free) or where data is disproportionately sensitive.

### Step 6: Map threats to controls

Build a table: per threat, what's the current control, what's the residual risk, what's the proposed additional control.

### Step 7: Name the pen-test trigger

The threat model MUST include an explicit "when do we commission a third-party pen test?" statement. This is the escalation threshold that keeps baseline-hardening honest. Criteria examples:

- After N months of real traffic at production scale
- When PII scope expands beyond current limits
- When an incident reveals the baseline was insufficient
- When a sibling surface adopts the same pattern (economies of scale)

## Output format

File: `<surface-root>/THREAT-MODEL.md`

```markdown
# Threat Model: <Surface Name>

- **Last reviewed:** YYYY-MM-DD
- **Review cadence:** on-event (updated when surface changes materially)
- **Status:** active

## Surface description

{Step 1 output}

## Trust boundaries

{Step 2 output - diagram + zone description}

## Data flows

### Flow 1: {name}

- Input: ...
- Transform: ...
- Output: ...
- Auth: ...

### Flow 2: {name}

...

## STRIDE analysis

### Spoofing
{Step 4 S}

### Tampering
{Step 4 T}

### Repudiation
{Step 4 R}

### Information disclosure
{Step 4 I}

### Denial of service
{Step 4 D}

### Elevation of privilege
{Step 4 E}

## High-value targets

{Step 5 ranked list}

## Threat / control matrix

| Threat | Current control | Residual risk | Proposed additional control |
|---|---|---|---|
| ... | ... | ... | ... |

## Pen-test escalation trigger

{Step 7 - the explicit criteria}

## Change history

- **YYYY-MM-DD v0.1** - Initial walkthrough during {context}.
```

## Anti-patterns

Watch for these in threat-model drafts:

- **Generic threats without evidence** - "SQL injection is possible" without pointing to where. Every threat must reference the flow and the input.
- **Mitigations that don't mitigate** - "We use HTTPS" as a mitigation for tampering-at-rest. HTTPS is in-flight only.
- **Over-reliance on Cloudflare** - "Cloudflare handles DDoS" is true for volumetric; slow-and-low abuse still needs application-layer protection.
- **No pen-test trigger** - the trigger is not optional. If you can't name a threshold, the threshold is "we'll never commission one," which is wrong.
- **Threat model written AFTER hardening** - the threat model drives what to harden. If code is shipped and the threat model is retrofit, the code is cited as justification for whatever it already does, which misses the point.

## Reference

- Standard: `Operations/.ops/standards/application-security.md`
- Companion skills: `public-endpoint-review`, `security-hardening-deploy`
- STRIDE reference: Microsoft Threat Modeling (Howard, Swiderski et al.) - classic STRIDE decomposition
- MITRE ATT&CK: https://attack.mitre.org/ (pattern library for what real attackers do)
