Skip to content

Threat Modeling

Structured security analysis of the platform, split by surface. Each category page is a self-contained STRIDE pass over one attack surface: its assets, trust boundaries, threats, controls, and residual risk.

Use this section to answer "what could go wrong here, and what stops it?" for a given part of the system. For how a surface works, follow the cross-links to the architecture and operations pages.

Method

Each category page follows the same shape:

  1. Metadata block — created / last-reviewed / next-review-due dates, reviewers, version, surface owner. Every page carries one; see the template below.
  2. Data flow diagram (DFD) — the entry points, processes, data stores, and the trust boundaries crossed. Elements are numbered (External entity, Process, Data store, Flow) so threats reference them.
  3. DFD elements — a table naming each numbered element, so the STRIDE rows are traceable.
  4. Assets and trust boundaries — what is worth protecting and where control changes hands.
  5. STRIDE — Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege. One table per element-class, each row tagged with the DFD ref it applies to: threat → control → residual.
  6. Findings and recommendations — prioritized, actionable. Rolled up into the register below.

Residual-risk levels: Low (control holds, accepted), Medium (control depends on a convention or has a gap worth closing), High (active exposure — none open at time of writing).

Metadata template

Copy this block to the top of every category page and keep the dates current:

| | |
|---|---|
| **Created** | YYYY-MM-DD |
| **Last reviewed** | YYYY-MM-DD |
| **Next review due** | YYYY-MM-DD (6-month cadence, or on any change to the surface) |
| **Reviewers** | name@tomoda.life |
| **Version** | 1.0 |
| **Surface owner** | team / area |

Review cadence. Each surface is re-reviewed every 6 months, or immediately when the surface changes (a new control, a new component, a new trust boundary). Bump Version and the Last reviewed date on any substantive edit; move Next review due forward. A page past its review date is a finding in its own right.

DFD conventions

Threat-model pages are the one place in this repo that use Mermaid instead of the dg-* HTML primitives — DFDs are graphs with crossing edges and boundary subgraphs that the box primitives render poorly. Material renders Mermaid natively. Draw each DFD as a ```mermaid flowchart LR:

DFD element Mermaid node
External entity (E) stadium E1([E1 · App user])
Process (P) rectangle P1[P1 · verify token]
Data store (D) cylinder D1[(D1 · user store)]
Trust boundary subgraph name[trust boundary: …]
Data flow (F) edge label -->|F2 ID token JWT|

Prefer one diagram per path — a single combined graph gets dense fast. Label every edge with its flow id and payload. A flow whose edge crosses into a subgraph is a trust-boundary crossing; those are where most threats live.

Categories

Category Surface Last reviewed Next due
Authentication Google/Apple sign-in, app vs admin auth paths, oauth2-proxy, Dex, token verification 2026-08-09 2027-02-09

More categories are added as surfaces are modeled (secrets & supply chain, network & ingress, data plane). Add a page under docs/security/threat-model/, follow the shape above, and register its findings below.

Findings register

Open items across all categories, highest priority first. Close a row when the fix ships; keep the history in git.

Priority Category Finding Logged Status
Medium Authentication Shared OAuth client couples app and admin login; app-side incident can disrupt operator access. Decouple by giving oauth2-proxy/Dex their own client. 2026-08-09 Open
Medium Authentication No CI guard fails a PR that exposes an admin Ingress without the oauth2-proxy middleware. Resolved by policy/admin_ingress_auth.rego (conftest, fail-closed). 2026-08-09 Resolved
Low Authentication pgAdmin/redis-insight record the operator only in the proxy access log (no per-operator attribution). 2026-08-09 Open
Low Authentication Nonce is optional server-side; a client omitting it loses replay protection. Accepted (ID tokens are short-lived). 2026-08-09 Accepted