Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,14 @@ devbox.lock

# zed
.zed

# Downloaded CycloneDX schema (tests/fetch_cyclonedx_schema.sh)
tests/schemas/

# Local tooling and working files
.codex/
.mcp.json
.vscode/
AGENTS.md
tasks/
uv.lock
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ below).

## [Unreleased]

### Added

- `--tmbom` CLI flag: prints a CycloneDX 2.0 TM-BOM (blueprint, threats and
risks) as JSON on stdout.
- `Control` element (name, category, status, `mitigates`, `appliesTo`,
`effectiveness`), also in `--json` output and input.
- `Threat`/`Finding` attributes `stride` (default `unassigned`), `impact`,
`capec`, `cwe`.
- `Actor.kind` and `Dataflow.isEncrypted`.
- `controls` is available to report templates; `docs/basic_template.md`,
`advanced_template.md` and `reveal.md` now list `Control` objects (the section
is omitted when there are none).

## [1.4.0] - 2026-05-21

### BREAKING
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ options:
--describe DESCRIBE describe the properties available for a given element
--list-elements list all elements which can be part of a threat model
--json JSON output a JSON file
--tmbom output a CycloneDX 2.0 TM-BOM (threat model BOM) as
JSON
--levels LEVELS [LEVELS ...]
Select levels to be drawn in the threat model (int
separated by comma).
Expand Down Expand Up @@ -482,6 +484,41 @@ python tm2.py --dfd | dot -Tpng -o sample_json.png

```

## Exporting a CycloneDX TM-BOM

`--tmbom` prints the model as a [CycloneDX](https://cyclonedx.org/) 2.0
threat model BOM (TM-BOM) JSON document on stdout:

```
./tm.py --tmbom > tm.cdx.json
```

| pytm | TM-BOM |
|------|--------|
| `Boundary` | `blueprints[].zones` (trust zones, nesting via `parent`) |
| flows crossing zones | `blueprints[].boundaries` |
| `Server`, `Process`, `Lambda`, `ExternalEntity`, `Agent`, `LLM`, `Actor` (`kind` unset) | `blueprints[].assets` |
| `Actor` with `kind` set | `blueprints[].actors` (person / organization / system / persona) |
| `Control` | `controls`, linked from threats and risk responses |
| `Datastore`, `Data`, `Dataflow`, `Assumption` | `dataStores`, `dataSets`, `flows`, `assumptions` |
| findings, one per threat library SID | `threats.threats` |
| every finding (including ones excluded by an assumption) | `risks.risks`, with `Finding.response` as the risk response |

Notes:

* The output follows the CycloneDX 2.0 draft schema, pinned to
[`CycloneDX/specification@906c579`](https://github.com/CycloneDX/specification/tree/906c57979d786c935787213ae1693a98097858ce/schema/2.0).
The schema is unpublished and may change. The tests validate against it when
it is present; fetch it with `tests/fetch_cyclonedx_schema.sh` (otherwise
those tests are skipped).
* pytm-specific details with no schema field (controls, ports, mitigation
text...) are kept as `pytm:`-prefixed `properties`.
* Threats carry CAPEC attack patterns and CWE weaknesses. STRIDE category,
impact, actor kind, control status and `Dataflow.isEncrypted` are exported
only when you set them. See `docs/tmbom.md` for the full mapping.
* `bom-ref`s are derived from element names, so they are stable across runs.
`serialNumber` and `metadata.timestamp` change on every run.

## Making slides!

Once a threat model is done and ready, the dreaded presentation stage comes in - and now pytm can help you there as well, with a template that expresses your threat model in slides, using the power of [RevealMD](https://github.com/webpro/reveal-md)! Just use the template docs/reveal.md and you will get some pretty slides, fully configurable, that you can present and share from your browser.
Expand Down
16 changes: 16 additions & 0 deletions docs/advanced_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,22 @@ Finding Count|{{item:call:getFindingCount}}|
}


{controls:if:
## Controls
}

{controls:repeat:
Name|{{item.name}}
|:----|:----|
Description|{{item.description}}|
Category|{{item.category.name}}|
Status|{{item.status.name}}|
Effectiveness|{{item.effectiveness}}|
Mitigates|{{item.mitigates:repeat:{{{{item}}}}<br>}}|
Applies To|{{item.appliesTo:repeat:{{{{item.name}}}}<br>}}{{item.appliesTo:not:whole model}}|

}

{tm.excluded_findings:if:
# Excluded Threats
}
Expand Down
12 changes: 11 additions & 1 deletion docs/basic_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,17 @@ Name|Description|Classification

&nbsp;

## Potential Threats
{controls:if:## Controls
&nbsp;

Name|Category|Status|Mitigates|Applies To
|:----:|:----:|:----:|:----:|:----:|
{controls:repeat:|{{item.name}}|{{item.category.name}}|{{item.status.name}}|{{item.mitigates:repeat:{{{{item}}}}<br>}}|{{item.appliesTo:repeat:{{{{item.name}}}}<br>}}{{item.appliesTo:not:whole model}}|
}

&nbsp;

}## Potential Threats

&nbsp;
&nbsp;
Expand Down
19 changes: 19 additions & 0 deletions docs/reveal.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,22 @@
----
}}
}

{controls:if:---

## Controls

----

{controls:repeat:
- **name** : {{item.name}}
- **description** : {{item.description}}
- **category** : {{item.category.name}}
- **status** : {{item.status.name}}
- **effectiveness** : {{item.effectiveness}}
- **mitigates** : {{item.mitigates:repeat:{{{{item}}}}<br>}}
- **applies to** : {{item.appliesTo:repeat:{{{{item.name}}}}<br>}}{{item.appliesTo:not:whole model}}

----
}
}
Loading
Loading