STRIDE Threat Model Template
STRIDE per element fixes how many questions your design makes applicable before anybody asks one. This pack counts them, then reports coverage against that total.
Free download · No account needed
Every STRIDE template hands you the same thing: six rows, one per letter, fill each one in. The template ranking first for this search says it outright, that there should be at least one issue identified for each of S, T, R, I, D and E. So a system's coverage target is six. That is a floor being read as a finish line, and it is why threat models get signed off with every arrow on the architecture diagram untouched.
STRIDE per element is not six categories per box, and the matrix is not folklore. OWASP Threat Dragon encodes it in one file: an external entity carries two of the six, a data flow three, a data store four, a process all six. Count your design and the arithmetic falls out. Two external entities, five processes, three stores and ten flows is 76 applicable cells, and that number exists before anybody has a security thought.
Seventy-six is the denominator. This pack computes it from the census, opens every cell as a row whether or not anyone has answered it, and reports coverage as a fraction. Every empty cell says which kind of empty it is: ruled out with the reason recorded, never opened, or waiting on a property of the system nobody wrote down. Those three read identically in a normal threat table. The one that ships is never opened, and no threat modelling tool has a word for it.
What comes in the pack
Element Inventory, with the type as drawn beside the type that counts
One row per element and per data flow. The verbatim column holds what your source material shows: a circle, two parallel lines, a vendor stencil, an unlabelled arrow, or nothing at all when the element was named only in prose. The normalised column holds the type the matrix runs on, and the cell count follows it. When the two disagree that is usually the most useful finding on the row, and it is always the deflating direction that hurts.
Threat Register keyed on the cell, not on the threat
Every applicable cell gets a row whether or not anybody has answered it, so an unasked question is something you can read rather than an absence you cannot. Five states: answered, ruled out with the reason, ruled out with no reason recorded, never opened, and blocked naming the property nobody supplied. A fifth row type records the cells the matrix does not make applicable, so they stop reading as gaps.
Trust Boundary Register that attributes every crossing
One row per boundary, with who controls the far side and every flow that crosses it, including the flows that cross two boundaries in a single hop. It surfaces the two boundaries a diagram cannot: the one that exists in a routing table and was never drawn, and the one drawn with nothing crossing it, which contributes no cells and therefore cannot be wrong.
Three documents a security reviewer actually reads
A narrative built on the four questions, with coverage stated in the opening block. A mitigation plan sequenced by what blocks the release rather than by threat. A residual risk note where every accept carries a named person and a review date. All three ship filled with a worked model rather than a skeleton, including the reasoning behind choosing eliminate over mitigate.
Six prompts that work the boundary, not the backlog
Take the census, mark the trust boundaries, work every cell on one boundary's crossings in a single pass, answer one cell properly, write the narrative, close out for sign-off. Working a boundary is the unit that pays: its crossings usually share both their exposure and their fix, so you get one design decision instead of eight findings that turn out to be the same finding.
A standard your team fills in once
The four rulings that move the denominator. Whether a service another team owns is a process or an external entity. Whether a stream with a retention window is a store. Where the model's edge sits, and what has to be true to ship. Every prompt reads it, so the registers inherit one vocabulary instead of drifting between reviewers.
How it works
- 1
Send the design
An architecture document, diagrams in any form, the Terraform config, an API spec, or design review notes. River reads the prose as carefully as the pictures, because the elements nobody models are drawn nowhere.
- 2
The census computes the denominator
Every element and flow lands in the inventory, its type transcribed as drawn then normalised. The cell count follows the normalised type, and the column totals. That total is what a review wants.
- 3
Boundaries get marked and every cell opens
River finds the boundaries from the design rather than the drawing, then opens every applicable cell as a row. A first pass full of unasked questions is the correct output, not an incomplete one.
- 4
Work a boundary, then close out
Answer a boundary's crossings in one pass and name the design change that closes the most cells. Close-out writes the coverage total on the day of signing, so a later reader knows what shipped.
Frequently asked questions
Why 76 cells and not six?
Because the six categories do not all apply to every element. Microsoft's STRIDE description names six threats; the applicability matrix says which apply where, and Threat Dragon's source encodes it as two for an external entity, three for a flow, four for a store, six for a process. Count your design and the total falls out.
Is the per-element matrix a River invention?
No. It is written down in the source of the reference tools. Threat Dragon's stride.json lists the applicable categories per element type, and the same file's neighbouring module repeats the matrix as a comment table. This pack reads the matrix straight off it rather than paraphrasing a blog post about it.
Do I still need a data flow diagram?
You need the census, and the diagram is how most teams get to it. River takes the census from whatever you have, including prose, and ships the flow and its boundaries as readable text beside the sheets. If the design was never written down, reconstruct the architecture first. A picture nobody can count is how a model ends up with six rows.
Will automated threat generation do this for me?
It will fill in the parts it covers, which are lopsided. OWASP pytm's rule library holds 114 threats whose target classes run server 54, process 44, data flow 16, data store 5, external entity 2. Your stores come back nearly clean because of the library rather than your design.
Why does an empty cell need a reason?
Because four different things look identical without one. Ruled out on purpose, ruled out and never explained, never asked, and blocked on a fact nobody recorded all render as silence. The reference tool's status list offers not started, needs investigation, not applicable and mitigated, and none of them means nobody looked.
Can I ship below full coverage?
Yes, and most teams do. Coverage below the total is a normal release state when every gap is enumerated, owned and dated. What does not ship is a gap nobody knows the size of, which is the whole reason the pack reports a fraction rather than a list.
How does this fit with the rest of our documents?
It sits upstream of findings. Use it on a design before it ships; use vulnerability triage for scanner output on a system that already exists, and keep the resulting design decisions in the decision record pack so a later reader knows what was accepted.
Find out what your threat model never asked
Send the design. River takes the census, computes the applicable total, and opens every cell you have not answered as a row you can read.
Install the threat model pack