STRIDE threat analysis for Choreo.ThreatModel.
Automatically generates threats based on element types, data-flow topology, and trust-boundary crossings.
STRIDE categories
| Category | Targets | Question |
|---|---|---|
| Spoofing | External entities, processes | Can someone impersonate this? |
| Tampering | Processes, data stores, flows | Can data be modified? |
| Repudiation | External entities, processes | Can actions be denied? |
| Information Disclosure | Processes, data stores, flows | Can data leak? |
| Denial of Service | Processes, data stores, flows | Can this be overwhelmed? |
| Elevation of Privilege | Processes, data stores | Can an attacker gain access? |
Further reading
Summary
Functions
Returns all paths from external entities to data stores.
Returns all data flows that cross a trust boundary.
Returns data stores that are reachable from an external entity (directly or indirectly).
Returns processes that sit in low-trust boundaries but access high-sensitivity data stores.
Generates STRIDE threats for every element and data flow in the model.
Summarises threats by STRIDE category and severity.
Returns unencrypted data flows that cross a trust boundary.
Validates a threat model and returns a list of issues.
Functions
@spec attack_paths(Choreo.ThreatModel.t()) :: [[Yog.node_id()]]
Returns all paths from external entities to data stores.
Each result is a list of node IDs representing a path from the internet to data at rest. These are the attack vectors that an adversary would follow.
Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_external_entity(:user)
...> |> Choreo.ThreatModel.add_process(:api)
...> |> Choreo.ThreatModel.add_data_store(:db)
...> |> Choreo.ThreatModel.data_flow(:user, :api)
...> |> Choreo.ThreatModel.data_flow(:api, :db)
iex> paths = Choreo.ThreatModel.Analysis.attack_paths(model)
iex> [:user, :api, :db] in paths
trueThis analysis answers the question: "What are the attack vectors from outside to data at rest?"
@spec cross_boundary_flows(Choreo.ThreatModel.t()) :: [ {Yog.node_id(), Yog.node_id(), String.t() | nil, String.t() | nil} ]
Returns all data flows that cross a trust boundary.
Each result is {from, to, from_boundary, to_boundary}.
Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_trust_boundary("internet", level: 0)
...> |> Choreo.ThreatModel.add_trust_boundary("app", level: 2)
...> |> Choreo.ThreatModel.add_external_entity(:user, boundary: "internet")
...> |> Choreo.ThreatModel.add_process(:api, boundary: "app")
...> |> Choreo.ThreatModel.data_flow(:user, :api)
iex> flows = Choreo.ThreatModel.Analysis.cross_boundary_flows(model)
iex> length(flows)
1
iex> Enum.any?(flows, fn {from, to, _, _} -> from == :user and to == :api end)
trueThis analysis answers the question: "Which data flows cross a trust boundary?"
@spec exposed_data_stores(Choreo.ThreatModel.t()) :: [Yog.node_id()]
Returns data stores that are reachable from an external entity (directly or indirectly).
These are high-value targets because they contain data at rest and are exposed to untrusted input.
Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_external_entity(:user)
...> |> Choreo.ThreatModel.add_process(:api)
...> |> Choreo.ThreatModel.add_data_store(:db)
...> |> Choreo.ThreatModel.data_flow(:user, :api)
...> |> Choreo.ThreatModel.data_flow(:api, :db)
iex> Choreo.ThreatModel.Analysis.exposed_data_stores(model)
[:db]This analysis answers the question: "Which data stores are reachable from external entities?"
@spec high_risk_processes(Choreo.ThreatModel.t()) :: [Yog.node_id()]
Returns processes that sit in low-trust boundaries but access high-sensitivity data stores.
These are risky because compromised process code can leak or tamper with sensitive data.
Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_process(:api)
...> |> Choreo.ThreatModel.add_data_store(:db, sensitivity: :confidential)
...> |> Choreo.ThreatModel.data_flow(:api, :db)
iex> Choreo.ThreatModel.Analysis.high_risk_processes(model)
[:api]This analysis answers the question: "Which processes access sensitive data from low-trust zones?"
@spec stride_threats( Choreo.ThreatModel.t(), keyword() ) :: [ %{ id: String.t(), category: atom(), target: Yog.node_id() | {Yog.node_id(), Yog.node_id()}, description: String.t(), severity: :low | :medium | :high | :critical, mitigation: String.t() } ]
Generates STRIDE threats for every element and data flow in the model.
Returns a list of threat structs:
%{
id: String.t(),
category: :spoofing | :tampering | :repudiation | :information_disclosure | :denial_of_service | :elevation_of_privilege,
target: Yog.node_id(),
description: String.t(),
severity: :low | :medium | :high | :critical,
mitigation: String.t()
}Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_trust_boundary("internet", level: 0)
...> |> Choreo.ThreatModel.add_trust_boundary("app", level: 2)
...> |> Choreo.ThreatModel.add_external_entity(:user, boundary: "internet")
...> |> Choreo.ThreatModel.add_process(:api, boundary: "app")
...> |> Choreo.ThreatModel.data_flow(:user, :api)
iex> threats = Choreo.ThreatModel.Analysis.stride_threats(model)
iex> Enum.any?(threats, & &1.category == :spoofing)
true
iex> Enum.any?(threats, & &1.target == :user)
true
iex> Enum.any?(threats, & match?({:user, :api}, &1.target))
trueThis analysis answers the question: "What threats exist in my architecture?"
@spec threat_summary(Choreo.ThreatModel.t()) :: %{ by_category: %{required(atom()) => %{required(atom()) => non_neg_integer()}}, by_severity: %{required(atom()) => non_neg_integer()}, total: non_neg_integer() }
Summarises threats by STRIDE category and severity.
Returns a map of %{category => %{severity => count}} plus totals.
Useful for dashboards and executive reporting.
Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_trust_boundary("app")
...> |> Choreo.ThreatModel.add_process(:api, boundary: "app")
iex> summary = Choreo.ThreatModel.Analysis.threat_summary(model)
iex> summary.total > 0
true
iex> is_map(summary.by_category)
true
iex> is_map(summary.by_severity)
trueThis analysis answers the question: "How are threats distributed by category and severity?"
@spec unencrypted_boundary_flows(Choreo.ThreatModel.t()) :: [ {Yog.node_id(), Yog.node_id()} ]
Returns unencrypted data flows that cross a trust boundary.
These are prime targets for interception and tampering.
Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_trust_boundary("internet", level: 0)
...> |> Choreo.ThreatModel.add_trust_boundary("app", level: 2)
...> |> Choreo.ThreatModel.add_external_entity(:user, boundary: "internet")
...> |> Choreo.ThreatModel.add_process(:api, boundary: "app")
...> |> Choreo.ThreatModel.data_flow(:user, :api)
iex> Choreo.ThreatModel.Analysis.unencrypted_boundary_flows(model)
[{:user, :api}]This analysis answers the question: "Which cross-boundary flows are unencrypted?"
@spec validate(Choreo.ThreatModel.t()) :: [{:error | :warning, String.t()}]
Validates a threat model and returns a list of issues.
Checks for:
- elements not assigned to a trust boundary
- unencrypted cross-boundary flows
- processes without privilege level
- data stores without sensitivity classification
Examples
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_trust_boundary("app")
...> |> Choreo.ThreatModel.add_process(:api, boundary: "app", privilege: :user)
...> |> Choreo.ThreatModel.add_data_store(:db, boundary: "app", sensitivity: :internal)
...> |> Choreo.ThreatModel.data_flow(:api, :db, encrypted: true)
iex> Choreo.ThreatModel.Analysis.validate(model)
[]
iex> model = Choreo.ThreatModel.new()
iex> model = model
...> |> Choreo.ThreatModel.add_process(:api)
iex> issues = Choreo.ThreatModel.Analysis.validate(model)
iex> Enum.any?(issues, fn {_sev, msg} -> String.contains?(msg, "trust boundary") end)
trueThis analysis answers the question: "Is the threat model structurally sound?"