Threat Modeling · Aug 1, 2026 · 7 min read

Threat model drift: when the diagram stops matching the deployment

A threat model drawn at architecture review drifts the moment the first commit lands - detecting and closing that gap requires continuous IaC parsing, not a quarterly workshop.

A threat model drawn at architecture review is a snapshot - accurate the day it was made, and wrong by the next sprint. Services appear, network paths shift, IAM roles accumulate, and bucket policies change without ceremony. The diagram stays on the wiki. The attacker works from the real deployment. That gap is threat model drift, and a quarterly workshop does not close it - a continuous IaC parse does.

01Why diagrams rot faster than code

Code is the system; the diagram describes an intent. A Terraform pull request that opens a new security group rule changes the actual attack surface in the time it takes to merge. A data flow diagram in a wiki takes a human to discover the change, redraw the affected trust boundaries, re-evaluate the new flows under STRIDE, and reach team consensus - by which point several more changes have landed. Even teams that run threat modeling seriously cannot keep a manual diagram current at modern deploy cadence. The result is not a diagram that is slightly stale - it is a diagram that has never seen the last six weeks of commits.

Architecture review (day 0) - threat model current Internet ──► ALB ──► api-service ──► RDS (private subnet) Six weeks later - diagram unchanged, deployment changed Internet ──► ALB ──► api-service ──► RDS (private subnet) └──► admin-service (SG: 0.0.0.0/0:443, no auth gate) └──► debug endpoint :8080 reachable from public subnet Internet ──► s3://audit-logs-prod ACL: public-read (hotfix commit) Threat model still shows day-0 surface. Three new trust-boundary crossings, zero new threat entries, zero mitigations assigned.
Six weeks of normal development produces an architecture the threat model has never evaluated. Each undocumented crossing carries no mitigations and no owner. The attacker does not read the diagram.

02IaC as the live source of threat-model truth

The alternative to trusting a human-maintained diagram is parsing the infrastructure definitions that actually describe the deployment. Terraform, CloudFormation, Kubernetes manifests, Helm charts and docker-compose files are typed, diffable and version-controlled. Every field is a fact with a file and line number - not an intent expressed in a diagramming tool that nobody is required to update before merging. Parsing them on each commit turns the threat model into a continuous projection of the real deployment rather than a snapshot of an earlier intent.

main.tf - parsed to trust-boundary edgeshcl
resource "aws_security_group_rule" "admin_public_ingress" {
  type        = "ingress"
  from_port   = 443
  to_port     = 443
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]  # internet-zone actor reaches admin component
}

resource "aws_s3_bucket_acl" "audit_logs" {
  bucket = "audit-logs-prod"
  acl    = "public-read"  # internet actor reads data asset without auth
}

# Parser output (new commit vs previous state):
+ trust_edge: internet --> admin-service  port=443  zone_crossing=yes
+ trust_edge: internet --> s3/audit-logs-prod  auth=none  sensitivity=high

Each resource maps to a typed component in the threat model: a security group rule becomes a trust-boundary edge with source zone, destination zone and port set; a public-read S3 ACL becomes an internet-accessible data store with a sensitivity label derived from the resource name and applied tags. When that edge was absent in the previous commit, it is a new trust-boundary crossing - and new crossings produce new threat entries automatically, anchored to the IaC line that created them. The diagram is a rendered output of that parse, not its input.

03What undetected drift enables

The impact of a drifted threat model is not just a stale picture. It is a real attack surface carrying no threat entries, no mitigations assigned and no owner who knows it exists. An admin service opened to the public internet to speed up a hotfix that was never reverted is a target with no defense-in-depth review behind it. The threat model did not fail to mitigate the threat - it never looked at that component. DAST finds the open port; the IaC parse gives it the context: this crossing was not present before this commit, and nothing in the model accounts for it.

Confirmed - HighInternet-reachable admin endpoint absent from threat model - /admin/users
IaC delta (commit a3f9c2): aws_security_group_rule.admin_public_ingress cidr_blocks=["0.0.0.0/0"] Runtime probe ──► GET /admin/users HTTP 200 (no authentication challenge observed) Threat model coverage ──► none - endpoint absent from all DFD nodes and trust-boundary entries
Oracle: IaC structural parse (file:line fact) confirmed by DAST runtime probe. Severity high - public admin surface, no mitigation on record, no assigned owner, no prior threat entry for this component.
PropertyManual DFD reviewIaC-grounded continuous model
Source of truthHuman-drawn diagram in a wiki or Confluence pageParsed Terraform, K8s, Helm or CloudFormation at commit time
Update cadenceQuarterly review cycle or ad hoc after major changesEvery commit that touches infrastructure definitions
Trust boundariesEstimated from diagram intent, subject to interpretationParsed from security group rules, network policies, IAM bindings
New-surface detectionRequires a reviewer to notice and flag the infrastructure changeCommit diff surfaces new trust-boundary crossings automatically
Evidence per threatMeeting notes, STRIDE worksheet, narrative in a ticketfile:line in IaC plus DAST runtime confirmation where reachable

04Closing drift continuously

The cadence question has a direct answer: the threat model must update at least as fast as the infrastructure it describes. That is achievable only if the update is automatic. Parsing IaC on each commit, computing the delta against the previous model state, and projecting new trust-boundary crossings into threat entries removes the human bottleneck. Security engineers review the new entries - a bounded set scoped to what changed in this commit - rather than trying to reverse-engineer what drifted across a month of merges. A threat model that is always current is also one that can be gated: a commit that introduces a critical-rated, unmitigated trust-boundary crossing fails the build in the same way that a DAST critical finding does.

DAST provides the runtime layer that confirms structural findings. A new internet-reachable endpoint identified by IaC parsing is immediately probed: authentication challenge check, parameter discovery, injection surface scan against a benign marker. The threat entry is grounded in both the structural fact (IaC says this port is open to the internet) and the runtime confirmation (DAST reached the service and received a substantive response). Neither source alone is sufficient: IaC can describe a port that nothing listens on; DAST alone finds the live endpoint but cannot tell you whether the architect intended it to be public or whether any mitigations exist. The combination is the proof.

0
manual diagram updates required per deploy
100%
of IaC trust-boundary changes produce threat entries
7d
SLA for confirmed-high threat findings from drift

Threat model drift is not a process failure - it is a structural one. A process that requires humans to keep a diagram synchronized with a system that changes on every merge will always fall behind. The fix is treating IaC as the threat model: every resource a typed component, every security group rule a trust-boundary edge, every commit a re-evaluation of the surface. The diagram becomes an output of that parse, not the input to a quarterly meeting - and an attacker who knows what is actually deployed never had the advantage of working from a drawing that is six weeks old.

Evidence over heuristicsReachable beats foundMonitor → Block: rolling out a gate without slowing teamsIaC-grounded threat modelingAir-gapped AppSec with no phone-homeOne platform vs three tools
See it on your own app →