// Craig's Threat Modeller
┌───────────────────────────────────────┐
│  CRAIG'S THREAT MODELLER              │
│  .arch + .controls → .md              │
└───────────────────────────────────────┘

A threat model your team writes as text files in git.

download docs source

The project window on the public-web system. The palette lists the technologies on the left. The canvas draws an end user, an edge zone holding CloudFront and a load balancer, and an application VPC holding an application server, a customer database and a document store. The sidebar on the right counts the threats by severity, says 5 of 165 controls are in place, and shows the controls of the Credential Theft threat, three of them implemented.
// the application draws the same two files, and the sidebar answers each threat
sources=2 outputs=1 catalogue=v1.0.1 platforms=macos+linux net=0
// download

version: latest · source: github.com/craigjbass/threat-modeller · license: MIT

threatmodeller.dmg
The macOS application, with the command line executable inside it.
threatmodeller-cli-macos.tar.gz
The command line executable for macOS, with the catalogue beside it.
threatmodeller-cli-linux-x86_64.tar.gz
The command line executable for Linux on x86_64. Static, so it runs in any container.
threatmodeller-cli-linux-aarch64.tar.gz
The command line executable for Linux on aarch64.
// how it works
┌──────────┐   compile   ┌──────────────┐   check   ┌──────────┐
│  .arch   │ ──────────▶ │  .controls   │ ────────▶ │ exit 0/1 │
└──────────┘             └──────┬───────┘           └──────────┘
                                │ report
                                ▼
                          ┌───────────┐
                          │    .md    │
                          └───────────┘
  1. A person writes payments.arch: the technologies, the zones, the components and the flows.
  2. threatmodeller compile reads the architecture, raises every threat the catalogue holds for it, and writes a stanza into payments.controls.
  3. A person fills in each stanza: the status of every control, a note, and a compensating control where one applies.
  4. 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
// 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

The command line executable:

$ tar xzf threatmodeller-cli-macos.tar.gz
$ ln -sf "$PWD/threatmodeller" ~/.local/bin/threatmodeller
$ export PATH="$HOME/.local/bin:$PATH"

In continuous integration:

$ threatmodeller library verify && threatmodeller check

The application installs the same executable: threatmodeller ▸ Install Command Line Tool… writes a link in ~/.local/bin, and asks for no password.

// license: MIT · catalogue: Threat Model Library v1.0.1 (CC BY 4.0) · source: github.com/craigjbass/threat-modeller · version: latest