Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Python-based threat modeling using pytm library for programmatic STRIDE analysis, data flow diagram generation, and automated security threat identification. Use when: (1) Creating threat models programmatically using Python code, (2) Generating data flow diagrams (DFDs) with automatic STRIDE threat identification, (3) Integrating threat modeling into CI/CD pipelines and shift-left security practices, (4) Analyzing system architecture for security threats across trust boundaries, (5) Producing t
.claude/skills/aiskillstore-pytm/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 177% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 132% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 355% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 266% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 189% | 0% |
pytm is a Python library for programmatic threat modeling based on the STRIDE methodology. It enables security engineers to define system architecture as code, automatically generate data flow diagrams (DFDs), identify security threats across trust boundaries, and produce comprehensive threat reports. This approach integrates threat modeling into CI/CD pipelines, enabling shift-left security and continuous threat analysis.
Create a basic threat model:
python#!/usr/bin/env python3 from pytm import TM, Server, Dataflow, Boundary, Actor # Initialize threat model tm = TM("Web Application Threat Model") tm.description = "E-commerce web application" # Define trust boundaries internet = Boundary("Internet") dmz = Boundary("DMZ") internal = Boundary("Internal Network") # Define actors and components user = Actor("Customer") user.inBoundary = internet web = Server("Web Server") web.inBoundary = dmz db = Server("Database") db.inBoundary = internal # Define data flows user_to_web = Dataflow(user, web, "HTTPS Request") user_to_web.protocol = "HTTPS" user_to_web.data = "credentials, payment info" user_to_web.isEncrypted = True web_to_db = Dataflow(web, db, "Database Query") web_to_db.protocol = "SQL/TLS" web_to_db.data = "user data, transactions" # Generate threat report and diagram tm.process()
Install pytm:
bashpip install pytm # Also requires graphviz for diagram generation brew install graphviz # macOS # or: apt-get install graphviz # Linux
Progress: ] 1. Define system scope and trust boundaries ] 2. Identify all actors (users, administrators, external systems) ] 3. Map system components (servers, databases, APIs, services) ] 4. Define data flows between components with security attributes ] 5. Run tm.process() to generate threats and DFD ] 6. Review STRIDE threats and add mitigations ] 7. Generate threat report with scripts/generate_report.py
Work through each step systematically. Check off completed items.
pytm automatically identifies threats based on STRIDE categories:
For each identified threat:
references/risk_matrix.md)threat.mitigation = "description"Define system architecture programmatically:
pythonfrom pytm import TM, Server, Datastore, Dataflow, Boundary, Actor, Lambda tm = TM("Microservices Architecture") # Cloud boundaries internet = Boundary("Internet") cloud_vpc = Boundary("Cloud VPC") # API Gateway api_gateway = Server("API Gateway") api_gateway.inBoundary = cloud_vpc api_gateway.implementsAuthentication = True api_gateway.implementsAuthorization = True # Microservices auth_service = Lambda("Auth Service") auth_service.inBoundary = cloud_vpc order_service = Lambda("Order Service") order_service.inBoundary = cloud_vpc # Data stores user_db = Datastore("User Database") user_db.inBoundary = cloud_vpc user_db.isEncryptedAtRest = True # Data flows with security properties client_to_api = Dataflow(Actor("Client"), api_gateway, "API Request") client_to_api.protocol = "HTTPS" client_to_api.isEncrypted = True client_to_api.data = "user credentials, orders" api_to_auth = Dataflow(api_gateway, auth_service, "Auth Check") api_to_auth.protocol = "gRPC/TLS" auth_to_db = Dataflow(auth_service, user_db, "User Lookup") auth_to_db.protocol = "TLS" tm.process()
Automate threat modeling in continuous integration:
yaml# .github/workflows/threat-model.yml name: Threat Model Analysis on: [push, pull_request] jobs: threat-model: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: Install dependencies run: | pip install pytm sudo apt-get install -y graphviz - name: Generate threat model run: python threat_model.py - name: Upload DFD diagram uses: actions/upload-artifact@v3 with: name: threat-model-dfd path: '*.png' - name: Check for unmitigated threats run: python scripts/check_mitigations.py threat_model.py
Generate comprehensive threat documentation:
bash# Run threat model with report generation python threat_model.py # Generate markdown report ./scripts/generate_report.py --model threat_model.py --output threat_report.md # Generate JSON for tool integration ./scripts/generate_report.py --model threat_model.py --format json --output threats.json
Report includes:
Log the following for security governance:
scripts/)generate_report.py - Generate markdown/JSON threat reports with STRIDE categorizationcheck_mitigations.py - Validate all identified threats have documented mitigationsthreat_classifier.py - Classify threats by severity using DREAD or custom risk matrixtemplate_generator.py - Generate threat model templates for common architecturesreferences/)stride_methodology.md - Complete STRIDE methodology guide with threat examplesrisk_matrix.md - Risk assessment framework with likelihood and impact scoringcomponent_library.md - Reusable pytm components for common patterns (APIs, databases, cloud services)mitigation_strategies.md - Common mitigation patterns mapped to STRIDE categories and OWASP controlsassets/)templates/web_application.py - Web application threat model templatetemplates/microservices.py - Microservices architecture templatetemplates/mobile_app.py - Mobile application threat model templatetemplates/iot_system.py - IoT system threat model templatedfd_styles.json - Custom graphviz styling for professional diagramspythonfrom pytm import TM, Server, Datastore, Dataflow, Boundary, Actor tm = TM("Three-Tier Web Application") # Boundaries internet = Boundary("Internet") dmz = Boundary("DMZ") internal = Boundary("Internal Network") # Components user = Actor("End User") user.inBoundary = internet lb = Server("Load Balancer") lb.inBoundary = dmz lb.implementsNonce = True web = Server("Web Server") web.inBoundary = dmz web.implementsAuthentication = True web.implementsAuthenticationOut = False app = Server("Application Server") app.inBoundary = internal app.implementsAuthorization = True db = Datastore("Database") db.inBoundary = internal db.isSQL = True db.isEncryptedAtRest = True # Data flows Dataflow(user, lb, "HTTPS").isEncrypted = True Dataflow(lb, web, "HTTPS").isEncrypted = True Dataflow(web, app, "HTTP").data = "session token, requests" Dataflow(app, db, "SQL/TLS").data = "user data, transactions" tm.process()
pythonfrom pytm import TM, Lambda, Datastore, Dataflow, Boundary, Actor tm = TM("Cloud Microservices") cloud = Boundary("Cloud Provider VPC") user = Actor("Mobile App") # Serverless functions api_gateway = Lambda("API Gateway") api_gateway.inBoundary = cloud api_gateway.implementsAPI = True auth_fn = Lambda("Auth Function") auth_fn.inBoundary = cloud # Managed services cache = Datastore("Redis Cache") cache.inBoundary = cloud cache.isEncrypted = True db = Datastore("DynamoDB") db.inBoundary = cloud db.isEncryptedAtRest = True # Data flows Dataflow(user, api_gateway, "API Call").protocol = "HTTPS" Dataflow(api_gateway, auth_fn, "Auth").protocol = "internal" Dataflow(auth_fn, cache, "Session").isEncrypted = True Dataflow(api_gateway, db, "Query").isEncrypted = True tm.process()
Define organization-specific threats:
pythonfrom pytm import TM, Threat tm = TM("Custom Threat Model") # Add custom threat to component web_server = Server("Web Server") custom_threat = Threat( target=web_server, id="CUSTOM-001", description="API rate limiting bypass using distributed requests", condition="web_server.implementsRateLimiting is False", mitigation="Implement distributed rate limiting with Redis", references="OWASP API Security Top 10 - API4 Unrestricted Resource Consumption" ) web_server.threats.append(custom_threat)
Focus on cross-boundary threats:
python# Identify all trust boundary crossings for flow in tm.dataflows: if flow.source.inBoundary != flow.sink.inBoundary: print(f"Cross-boundary flow: {flow.name}") print(f" From: {flow.source.inBoundary.name}") print(f" To: {flow.sink.inBoundary.name}") print(f" Encrypted: {flow.isEncrypted}") print(f" Authentication: {flow.implementsAuthentication}")
Trust boundary crossings require extra scrutiny:
Symptoms: Expected STRIDE threats not appearing in generated report
Solution:
isSQL=True for databases)isEncrypted, protocol)references/stride_methodology.md for threat conditionsSymptoms: DFD not generated or graphviz errors
Solution:
bash# Verify graphviz installation dot -V # Install graphviz if missing brew install graphviz # macOS sudo apt-get install graphviz # Linux # Test pytm with simple model python -c "from pytm import TM; tm = TM('test'); tm.process()"
Symptoms: Identified threats don't apply to your architecture
Solution:
threat.condition = "..."threat.mitigation = "N/A - using managed service"references/component_library.mdSymptoms: Threat model doesn't reflect current architecture
Solution:
scripts/check_mitigations.py to validate completenessCreate organization-specific threat library:
python# custom_threats.py from pytm import Threat def add_custom_threats(tm): """Add organization-specific threats to threat model""" # Cloud-specific threats cloud_misconfiguration = Threat( id="CLOUD-001", description="Misconfigured cloud storage bucket exposes sensitive data", condition="datastore.inBoundary.name == 'Cloud' and not datastore.isEncrypted", mitigation="Enable encryption at rest and bucket policies" ) # API-specific threats api_abuse = Threat( id="API-001", description="API endpoint abuse through lack of rate limiting", condition="server.implementsAPI and not server.implementsRateLimiting", mitigation="Implement rate limiting and API key rotation" ) return [cloud_misconfiguration, api_abuse]
Add DREAD scoring to threats:
pythonclass ScoredThreat(Threat): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.damage = 0 # 0-10 self.reproducibility = 0 self.exploitability = 0 self.affected_users = 0 self.discoverability = 0 def dread_score(self): return (self.damage + self.reproducibility + self.exploitability + self.affected_users + self.discoverability) / 5 # Usage threat = ScoredThreat( target=component, description="SQL Injection", damage=9, reproducibility=8, exploitability=7, affected_users=10, discoverability=6 ) print(f"DREAD Score: {threat.dread_score()}/10")
Customize DFD output with graphviz attributes:
python# Set custom colors for trust boundaries internet.color = "red" dmz.color = "orange" internal.color = "green" # Customize diagram output tm.graph_options = { "rankdir": "LR", # Left to right layout "bgcolor": "white", "fontname": "Arial", "fontsize": "12" }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-20 | pass→fail | 12,467 | 12,860 | +3% | 1 | 1 | 0% | 2,201 | 6,362 | +189% | 0 | 0 | — |
case-01 | fail→pass | 10,162 | 8,899 | -12% | 1 | 1 | 0% | 2,185 | 6,048 | +177% | 0 | 0 | — |
case-02 | fail→pass | 12,035 | 7,874 | -35% | 1 | 1 | 0% | 2,522 | 5,839 | +132% | 0 | 0 | — |
case-03 | pass→pass | 10,652 | 5,527 | -48% | 1 | 1 | 0% | 2,001 | 5,095 | +155% | 0 | 0 | — |
case-04 | pass→pass | 3,007 | 2,747 | -9% | 1 | 1 | 0% | 529 | 4,611 | +772% | 0 | 0 | — |
case-05 | pass→pass | 8,932 | 4,181 | -53% | 1 | 1 | 0% | 1,657 | 4,905 | +196% | 0 | 0 | — |
case-06 | fail→pass | 6,710 | 4,368 | -35% | 1 | 1 | 0% | 1,072 | 4,877 | +355% | 0 | 0 | — |
case-21 | pass→pass | 13,323 | 10,498 | -21% | 1 | 1 | 0% | 2,490 | 5,930 | +138% | 0 | 0 | — |
case-07 | fail→pass | 8,025 | 5,647 | -30% | 1 | 1 | 0% | 1,384 | 5,072 | +266% | 0 | 0 | — |
case-08 | fail→pass | 9,363 | 5,592 | -40% | 1 | 1 | 0% | 1,777 | 5,140 | +189% | 0 | 0 | — |
case-09 | fail→pass | 11,916 | 7,885 | -34% | 1 | 1 | 0% | 2,168 | 5,661 | +161% | 0 | 0 | — |
case-10 | pass→pass | 9,652 | 4,117 | -57% | 1 | 1 | 0% | 1,762 | 4,963 | +182% | 0 | 0 | — |
case-22 | pass→pass | 14,148 | 13,412 | -5% | 1 | 1 | 0% | 2,613 | 6,730 | +158% | 0 | 0 | — |
case-11 | fail→pass | 13,349 | 5,467 | -59% | 1 | 1 | 0% | 2,389 | 5,043 | +111% | 0 | 0 | — |
case-12 | pass→pass | 6,771 | 4,355 | -36% | 1 | 1 | 0% | 1,109 | 4,744 | +328% | 0 | 0 | — |
case-13 | fail→fail | 14,676 | 6,866 | -53% | 1 | 1 | 0% | 2,704 | 5,354 | +98% | 0 | 0 | — |
case-14 | fail→pass | 5,524 | 2,497 | -55% | 1 | 1 | 0% | 908 | 4,496 | +395% | 0 | 0 | — |
case-15 | fail→pass | 9,897 | 3,313 | -67% | 1 | 1 | 0% | 1,566 | 4,748 | +203% | 0 | 0 | — |
case-16 | pass→pass | 11,562 | 3,708 | -68% | 1 | 1 | 0% | 2,121 | 4,428 | +109% | 0 | 0 | — |
case-17 | fail→pass | 8,888 | 1,739 | -80% | 1 | 1 | 0% | 1,669 | 4,365 | +162% | 0 | 0 | — |
case-18 | fail→pass | 12,340 | 1,888 | -85% | 1 | 1 | 0% | 2,360 | 4,383 | +86% | 0 | 0 | — |
case-19 | fail→pass | 11,427 | 1,701 | -85% | 1 | 1 | 0% | 1,963 | 4,366 | +122% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.