agentleFS
aliefauzanaliefauzan/Orthogram/skills/drawing-threat-models/SKILL.md

drawing-threat-models

Skill2 starsChanged 2 months agoMITView on GitHub

Use when drawing a threat model or data flow diagram — external entities, processes, data stores, data flows, trust boundaries, and STRIDE threats on the flows that cross them — as a .drawio / draw.io file. Also called a DFD, security architecture review or STRIDE diagram, and for fixing one that has no boundaries or whose flows are unlabelled

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use the folder the user names.

    line 27
  2. Very specific:

    Never leave a build script, a .drawio or a preview in the project root, and never write outside the project you were pointed at.

    line 34
  3. Very specific:

    Every element takes an optional icon_name= — the same diagrams library the architecture skill uses.

    line 49

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Drawing threat models

A .drawio data flow diagram for threat modelling, in the same visual language as the other drawing-* skills. Four element kinds, trust boundaries, and a numbered threat table under the picture.

Build it with dfd_drawio.py (this directory). Check it with check_routing.py. Look at it with preview.py.

Where the files go

Everything this skill produces lives in one folder, never loose in the project root:

docs/diagrams/
  api-threat-model.py             # the script that builds it
  api-threat-model.drawio         # the diagram
  api-threat-model.preview.svg    # what you looked at

Use the folder the user names. Otherwise put it under docs/diagrams/ when the project has a docs/ directory, and diagrams/ at the project root when it does not — the build script, the .drawio, and any preview or PNG together. write() creates the folder if it is missing, so f.write("docs/diagrams/api-threat-model.drawio") is all it takes, and preview.py writes the SVG beside its input.

Never leave a build script, a .drawio or a preview in the project root, and never write outside the project you were pointed at. One folder, named files — a diagram whose script sits next to it can be regenerated; one scattered across a repository root cannot be found again.

The elements

entitywhite box — something outside the system: a person, a partner, another company. You cannot change it and cannot trust it.
processellipse — something the system does. Code you own.
storetwo rules — something at rest: a database, a bucket, a queue, a log.
flowan arrow — data moving, labelled with what moves
boundarya dashed red frame — where trust changes

Every element takes an optional icon_name= — the same diagrams library the architecture skill uses. Find one with:

python3 ~/.claude/skills/drawing-threat-models/dfd_drawio.py bigquery

A process is an ellipse, which is narrowest exactly where a label is widest, so it keeps 30px at the sides against a box's 12. With an icon the label reads from the left of what is left; without one it is centred.

The output contract

  1. Every flow says what data moves. flow() requires a label. "session cookie" and "card number" crossing the same boundary are not the same risk, and an unlabelled arrow cannot be threat-modelled at all.
  2. At least one trust boundary. A DFD with no boundary is a data flow diagram, not a threat model — the boundaries are what turn it into one.
  3. Every flow that crosses a boundary is examined. Mark it crosses=; it turns red, which is a prompt, not a verdict. STRIDE is applied per element, and a crossing is where the interesting ones live.
  4. check_routing.py reporting 0 FAIL.

The pieces

from dfd_drawio import Model, L, R, T, B

m = Model("Checkout", "STRIDE · v2")
user = m.entity("Shopper", "browser", col=0, row=0)
api  = m.process("Orders API", "Cloud Run", col=1, row=0)
db   = m.store("Cloud SQL", "orders, PII", col=1, row=1)

net = m.boundary("Internet | DMZ", cols=[0, 0], rows=[0, 1])
gcp = m.boundary("Google Cloud project", cols=[1, 2], rows=[0, 1])

f1 = m.flow(user, api, "credentials + cart", exit=R(), entry=L(),
            direct=True, crosses=net)
m.threat(f1, "S", "Credential stuffing against the login endpoint",
         mitigation="Device check + per-identity rate limit")

threat() takes a STRIDE letter, numbers itself, puts a red disc on the flow and writes the row in the table underneath. The letter alone is a quiz, so the table spells it out: Spoofing · credentials + cart — …, with the mitigation in green where there is one. A threat with no mitigation stays black, which is the point of writing it down.

letterprotects
SSpoofingauthentication
TTamperingintegrity
RRepudiationnon-repudiation
IInformation disclosureconfidentiality
DDenial of serviceavailability
EElevation of privilegeauthorisation

Geometry

element200 × 64
column pitch360, row pitch 210
boundary34 outside the elements it holds

The page measures itself, and leaves room under the picture for the table.

Before handing it over

S=~/.claude/skills/drawing-threat-models
python3 "$S/check_routing.py" docs/diagrams/model.drawio        # must report 0 FAIL
python3 "$S/preview.py" docs/diagrams/model.drawio              # writes docs/diagrams/model.preview.svg
rsvg-convert -w 1400 -o docs/diagrams/model.png docs/diagrams/model.preview.svg

Then read it for what a checker cannot see: a store nothing writes to, a process with no inputs, a boundary drawn where no trust actually changes, a flow crossing a boundary with no threat listed against it.

Common mistakes

No trust boundariesThen it is a data flow diagram. The boundaries are the model.
A boundary around everythingIf nothing crosses it, it says nothing. Draw it where trust actually changes.
Flows labelled with a verb"sends" is not data. Say what moves: "card token, amount".
Threats with no mitigation and no ownerFine as a first pass, and the table shows them in black so they stand out. Do not ship the model that way.
STRIDE applied to the diagramIt is applied per element and per flow. Six letters against each crossing beats a list of six for the whole picture.
A process you do not own drawn as a processIf you cannot change it, it is an external entity, and that changes every threat about it.

preview.py writes a plain SVG — open it in any browser if you have no rsvg-convert. The PNG step is for pasting into a document, not for looking at it.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.