pytm – A Pythonic Framework for Threat Modeling

Define your system in Python using the elements and properties described in the pytm framework. Based on your definition, pytm can generate, a Data Flow Diagram (DFD), a Sequence Diagram and most important of all, threats to your system.


  • Linux/MacOS
  • Python 3.x
  • Graphviz package
  • Java (OpenJDK 10 or 11)
  • plantuml.jar

Usage [-h] [--debug] [--dfd] [--report REPORT] [--exclude EXCLUDE] [--seq] [--list] [--describe DESCRIBE]

optional arguments:
  -h, --help           show this help message and exit
  --debug              print debug messages
  --dfd                output DFD (default)
  --report REPORT      output report using the named template file (sample template file is under docs/
  --exclude EXCLUDE    specify threat IDs to be ignored
  --seq                output sequence diagram
  --list               list known threats
  --describe DESCRIBE  describe the contents of a given class

Currently available elements are: TM, Element, Server, ExternalEntity, Datastore, Actor, Process, SetOfProcesses, Dataflow, Boundary and Lambda.

Also read: Guardicore – Cyber Threat Intelligence Tool to Discover Malicious IPs and Domains

The available properties of an element can be listed by using --describe followed by the name of an element:

(pytm) ➜  pytm git:(master) ✗ ./ --describe Element

For the security practitioner, you may add new threats to the file:

Threats = {
    "DF1": {
        "description": "Dataflow not authenticated",
        "target": Dataflow,
        "condition": "target.authenticatedWith is False"
    "SR1": {
        "description": "Server not hardened",
        "target": Server,
        "condition": "target.isHardened is False"


The file contains strings that run through eval() -> make sure the file has correct permissions or risk having an attacker change the strings and cause you to run code on their behalf. The logic lives in the “condition”, where members of “target” can be logically evaluated. Returning a true means the rule generates a finding, otherwise, it is not a finding.**

The following is a sample file that describes a simple application where a User logs into the application and posts comments on the app. The app server stores those comments into the database. There is an AWS Lambda that periodically cleans the Database.

# !/usr/bin/env python3

from pytm.pytm import TM, Server, Datastore, Dataflow, Boundary, Actor

tm = TM("my test tm")
tm.description = "another test tm"

User_Web = Boundary("User/Web")
Web_DB = Boundary("Web/DB")

user = Actor("User")
user.inBoundary = User_Web

web = Server("Web Server")
web.OS = "CloudOS"
web.isHardened = True

db = Datastore("SQL Database (*)")
db.OS = "CentOS"
db.isHardened = False
db.inBoundary = Web_DB
db.isSql = True
db.inScope = False

my_lambda = Lambda("cleanDBevery6hours")
my_lambda.hasAccessControl = True
my_lambda.inBoundary = Web_DB

my_lambda_to_db = Dataflow(my_lambda, db, "(λ)Periodically cleans DB")
my_lambda_to_db.protocol = "SQL"
my_lambda_to_db.dstPort = 3306

user_to_web = Dataflow(user, web, "User enters comments (*)")
user_to_web.protocol = "HTTP"
user_to_web.dstPort = 80 = 'Comments in HTML or Markdown'
user_to_web.order = 1

web_to_user = Dataflow(web, user, "Comments saved (*)")
web_to_user.protocol = "HTTP" = 'Ack of saving or error message, in JSON'
web_to_user.order = 2

web_to_db = Dataflow(web, db, "Insert query with comments")
web_to_db.protocol = "MySQL"
web_to_db.dstPort = 3306 = 'MySQL insert statement, all literals'
web_to_db.order = 3

db_to_web = Dataflow(db, web, "Comments contents")
db_to_web.protocol = "MySQL" = 'Results of insert op'
db_to_web.order = 4


Diagrams are output as Dot and PlantUML.

When --dfd argument is passed to the above file it generates output to stdout, which is fed to Graphviz’s dot to generate the Data Flow Diagram: --dfd | dot -Tpng -o sample.png

Generates this diagram:


The following command generates a Sequence diagram. --seq | java -Djava.awt.headless=true -jar plantuml.jar -tpng -pipe > seq.png

Generates this diagram:


The diagrams and findings can be included in the template to create a final report: --report docs/ | pandoc -f markdown -t html > report.html

The templating format used in the report template is very simple:

# Threat Model Sample

## System Description


## Dataflow Diagram

![Level 0 DFD](dfd.png)

## Dataflows

Name|From|To |Data|Protocol|Port

## Findings

{findings:repeat:* {{item.description}} on element "{{}}"
Also read: CyBot – Open Source Threat Intelligence Chat Bot

Currently supported threats

AA01 - Dataflow not authenticated
HA01 - Server not hardened
AU01 - Logs created: verify if sensitive data is stored
AU02 - Potential weak protections for audit data
AC01 - Process Memory Tampered
AC02 - Replay Attacks
CR01 - Collision Attacks
AU03 - Risks from logging
AA02 - Authenticated Data Flow Compromised
IN01 - Potential SQL Injection Vulnerability
IN02 - XML DTD and XSLT Processing
IN03 - JavaScript Object Notation Processing/XSS
IN04 - Cross Site Scripting
AC03 - The Data Store Could Be Corrupted
AA03 - Weakness in SSO Authorization
AC04 - Elevation Using Impersonation
AC05 - Elevation by Changing the Execution Flow in a process
OT01 - Cross Site Request Forgery
DO01 - Potential Excessive Resource Consumption
DO02 - Potential Process Crash or Stop
DO03 - Data Flow Is Potentially Interrupted
DO04 - Data Store Inaccessible
AA04 - Authorization Bypass
DE01 - Data Flow Sniffing
AC06 - Weak Access Control for a Resource
DS01 - Weak Credential Storage
DE02 - Weak Credential Transit
AA05 - Weak Authentication Scheme
LB01 - Lambda does not authenticate source of request
LB02 - Lambda has no access control
LB03 - Lambda does not handle resource consumption


Leave a Reply

Your email address will not be published. Required fields are marked *