A person writes payments.arch: the technologies, the zones, the components and the flows.
threatmodeller compile reads the architecture, raises every threat the catalogue holds for it, and writes a stanza into payments.controls.
A person fills in each stanza: the status of every control, a note, and a compensating control where one applies.
threatmodeller check exits 1 while a threat has no answer, so a pull request that adds a database and answers nothing fails the build.
// the two files
threatmodel/payments.arch — a person writes it.
system "Payments" {
catalogue = "v1.0.1"
zone "app" {
kind = "private"
network = "vpc"
reduces_risk_by = 30
component "api" {
technology = "aws-ec2"
name = "Application Server"
data = "confidential"
}
}
component "attacker" {
technology = "actor-attacker"
data = "public"
}
flow attacker -> api
}
threatmodel/payments.controls — the compiler writes it, then a person fills it in.
controls for "Payments" {
catalogue = "v1.0.1"
threat "t-credential-theft" on component "api" {
severity = "critical"
score = 90
control "Enforce MFA on all administrative access" {
status = "implemented"
note = "Okta, enforced group-wide"
}
compensating "Break-glass account watched by the SIEM" {
reduces_risk_by = 40
rationale = "Standing keys are gone; the one account left alerts on use."
}
}
threat "t-mitm" on flow "attacker->api" { }
}
A threat with an empty block has no answer, and check exits 1 until somebody writes one.
// features
the architecture language
One file states the technologies, the zones, the components and the flows. The language guide states the grammar of every block and attribute.
the generated controls file
The compiler writes a stanza for every threat the architecture raises, and merges new threats into the file a person has already answered. An answer a threat no longer needs stays in the file, marked stale.
the build gate
The executable exits 1 for an unanswered threat, a stale answer, or a library file that does not match the lock file. A diagnostic prints as threatmodel/payments.arch:12:5: error: no technology "aws-ec3" in catalogue v1.0.1, which an editor and a build log both read.
the macOS canvas
The application draws the same files, and Auto Sync writes the files when the model changes and redraws the diagram when a file changes on disk.
shared element libraries
A team writes technologies, threats and controls into a .lib file and shares it. The label prefixes every id it declares, so two teams cannot clash. library.lock.json records the repository, the tag and the sha256 of each file.
a fixed scoring order
Base severity, then a severity override, then the zone reduction, then the pathway mitigation, then one compensating control. Two compensating controls give the stronger of the two, not the sum.
markdown reports
threatmodeller report writes one .md file for each system. A report is an artefact, so a synchronise does not write it.
your own git, no credential
The application runs git as a child process, so a private repository works through the ssh-agent key and the credential helper you already have. This application holds no credential, reads none and prompts for none.
// why text files, not a diagram tool
a diagram tool
The model lives in a file only that tool opens.
A reviewer reads an image, and cannot see what changed.
The diagram and the code drift apart, and nothing measures the distance.
The answer to a threat lives in a spreadsheet beside it, or in nobody's head.
The build knows nothing about the model.
Craig's Threat Modeller
The model is two text files beside the code.
A reviewer reads the diff, line by line, in the pull request.
A new component raises its threats the moment the file changes.
The answer to a threat sits in the .controls file, with a note and a status.
The build fails while a threat has no answer.
// install
The application:
$ open threatmodeller.dmg
# drag threatmodeller to Applications