Skip to content
Merged
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ If you find STRIDE GPT useful, please consider supporting the project:
- **MITRE ATT&CK & ATLAS mapping**: Threats are annotated with standardized adversary technique IDs (MITRE ATT&CK Enterprise for traditional infrastructure attacks, ATLAS for ML/LLM-specific attacks) — surfaced as columns in markdown, linked pills in HTML, and `mitre_attack` properties in SARIF
- **Architectural pattern detection**: Automatically detects RAG pipelines, multi-agent systems, code execution environments, tool ecosystems, and more from application descriptions (inspired by [CSA MAESTRO](https://cloudsecurityalliance.org/blog/2025/02/06/agentic-ai-threat-modeling-framework-maestro))
- Multi-modal: Use architecture diagrams, flowcharts, etc. as inputs for threat modelling across all supported vision-capable models
- **Data Flow Diagrams**: Generate DFDs from your application description (or parse an uploaded DFD image), edit the Mermaid source live, and feed the confirmed diagram back into the Threat Model and Attack Tree prompts as the authoritative system model. CLI `/analyze` also emits a system-level DFD alongside its findings
- Generates attack trees to enumerate possible attack paths
- Suggests possible mitigations for identified threats
- Supports DREAD risk scoring for identified threats
Expand Down Expand Up @@ -70,6 +71,10 @@ This video is an excellent resource for anyone interested in understanding how S

## Changelog

### Unreleased

- **Data Flow Diagram support** (closes #56): New "Data Flow Diagram" tab in the Streamlit UI generates DFDs from the application description, parses uploaded DFD images via vision-capable models, and renders an editable Mermaid source pane with a live preview. Ticking *"Use this DFD for the threat model"* stores the confirmed diagram in session state and splices it into subsequent Threat Model and Attack Tree prompts as the authoritative system model — closing the description → DFD → review → refined threat model loop the issue asks for. The CLI's `/analyze` agent also produces a system-level DFD after synthesis, rendered as a Mermaid block in the markdown report, carried in the JSON `data_flow_diagram` field, and shown in the HTML view via a CDN-loaded Mermaid runtime. DFD generation is wrapped so a bad diagram never fails a good report.

### Version 0.17 (latest)

- **MITRE ATT&CK and ATLAS integration** (closes #36): Threats emitted by `/analyze` and `/quick` can now carry standardized adversary technique IDs from MITRE ATT&CK Enterprise (v17.1) and MITRE ATLAS (2026.05). Two new progressive-disclosure reference cards (`mitre_enterprise`, `mitre_atlas`) instruct the agent to use only the catalogued IDs and names, eliminating ID hallucination. Cards are regenerated from upstream STIX/YAML via `scripts/refresh_mitre_cards.py` rather than written from model recall. Techniques render as a `MITRE ATT&CK` column in markdown tables, clickable sky-tinted pills in HTML, and `properties.mitre_attack` arrays in SARIF.
Expand Down
11 changes: 10 additions & 1 deletion apps/web/attack_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@

# Function to create a prompt to generate an attack tree
def create_attack_tree_prompt(
app_type, authentication, internet_facing, sensitive_data, app_input
app_type,
authentication,
internet_facing,
sensitive_data,
app_input,
confirmed_dfd: str | None = None,
):
prompt = f"""APPLICATION TYPE: {app_type}
AUTHENTICATION METHODS: {authentication}
Expand All @@ -17,6 +22,10 @@ def create_attack_tree_prompt(
APPLICATION DESCRIPTION: {app_input}
"""

if confirmed_dfd:
from stride_gpt.core.prompts import dfd_to_prompt_section
prompt += dfd_to_prompt_section(confirmed_dfd)

# Add GenAI attack vectors for both Generative AI and Agentic AI applications
if app_type in ["Generative AI application", "Agentic AI application"]:
prompt += """
Expand Down
111 changes: 111 additions & 0 deletions apps/web/dfd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""Streamlit-facing provider wrappers for Data Flow Diagram generation.

Mirrors `apps/web/attack_tree.py`: thin per-provider wrappers around the
core `generate_dfd` / `parse_dfd_from_image` functions, with session-state
plumbing for thinking/reasoning displays.
"""

from __future__ import annotations

import streamlit as st

from stride_gpt.core.dfd import generate_dfd, parse_dfd_from_image
from stride_gpt.core.schemas import LLMConfig


__all__ = [
"get_dfd_anthropic",
"get_dfd_from_image_anthropic",
"get_dfd_from_image_google",
"get_dfd_from_image_openai",
"get_dfd_google",
"get_dfd_groq",
"get_dfd_lm_studio",
"get_dfd_mistral",
"get_dfd_openai",
]


# ---------------------------------------------------------------------------
# Generation from a textual description
# ---------------------------------------------------------------------------


def get_dfd_openai(api_key: str, model_name: str, prompt: str) -> str:
config = LLMConfig(provider="OpenAI API", model_name=model_name, api_key=api_key)
mermaid, _ = generate_dfd(config, prompt)
return mermaid


def get_dfd_anthropic(api_key: str, model_name: str, prompt: str) -> str:
config = LLMConfig(
provider="Anthropic API",
model_name=model_name,
api_key=api_key,
use_thinking=st.session_state.get("use_thinking", False),
)
mermaid, response = generate_dfd(config, prompt)
if response.thinking:
st.session_state["last_thinking_content"] = response.thinking
return mermaid


def get_dfd_google(api_key: str, model_name: str, prompt: str) -> str:
config = LLMConfig(provider="Google AI API", model_name=model_name, api_key=api_key)
mermaid, response = generate_dfd(config, prompt)
if response.thinking:
st.session_state["last_thinking_content"] = response.thinking
return mermaid


def get_dfd_groq(api_key: str, model_name: str, prompt: str) -> str:
config = LLMConfig(provider="Groq API", model_name=model_name, api_key=api_key)
mermaid, response = generate_dfd(config, prompt)
if response.reasoning:
with st.expander("View model's reasoning process", expanded=False):
st.write(response.reasoning)
return mermaid


def get_dfd_lm_studio(
lm_studio_endpoint: str, model_name: str, prompt: str, api_key: str = "not-needed"
) -> str:
config = LLMConfig(
provider="LM Studio Server",
model_name=model_name,
api_key=api_key,
api_base=lm_studio_endpoint,
)
mermaid, _ = generate_dfd(config, prompt)
return mermaid


def get_dfd_mistral(api_key: str, model_name: str, prompt: str) -> str:
config = LLMConfig(provider="Mistral API", model_name=model_name, api_key=api_key)
mermaid, _ = generate_dfd(config, prompt)
return mermaid


# ---------------------------------------------------------------------------
# Parsing a user-uploaded DFD image
# ---------------------------------------------------------------------------


def get_dfd_from_image_openai(api_key: str, model_name: str, base64_image: str) -> str:
config = LLMConfig(provider="OpenAI API", model_name=model_name, api_key=api_key)
mermaid, _ = parse_dfd_from_image(config, base64_image)
return mermaid


def get_dfd_from_image_anthropic(
api_key: str, model_name: str, base64_image: str, media_type: str = "image/png"
) -> str:
config = LLMConfig(provider="Anthropic API", model_name=model_name, api_key=api_key)
mermaid, _ = parse_dfd_from_image(config, base64_image, media_type=media_type)
return mermaid


def get_dfd_from_image_google(api_key: str, model_name: str, base64_image: str) -> str:
config = LLMConfig(provider="Google AI API", model_name=model_name, api_key=api_key)
mermaid, _ = parse_dfd_from_image(config, base64_image)
return mermaid
Loading
Loading