Threat Modeling · Jan 28, 2026 · 9 min read

IaC-grounded threat modeling

Trust boundaries as parsed facts, not LLM guesses.

Most threat-modeling tools start by asking a language model to imagine your architecture. You paste a description, or point it at a repo, and it narrates a plausible diagram: a load balancer here, a database there, a trust boundary it guessed at. The output reads well and is impossible to verify, because nothing in it is anchored to a fact. The model is doing fiction with good production values.

Your infrastructure-as-code already contains the architecture, written down, unambiguous, and version-controlled. A security group either has an ingress rule for 0.0.0.0/0 or it does not. An aws_db_instance either sets publicly_accessible to true or it does not. So we do not ask a model to imagine the architecture. We parse it.

01Parse the architecture, do not narrate it

Terraform, CloudFormation, Kubernetes manifests, Helm charts and docker-compose files are each read by a format-specific parser that produces typed components - not free text. A resource becomes a node with a kind, a set of attributes, and the exact file and line it came from. Network rules, IAM statements and bind mounts become typed edges between those nodes. The result is an architecture graph where every element traces back to a line you can open.

Because the input is structured, the parse is deterministic. The same repository produces the same graph on every run, with no temperature, no sampling, and no "the model said something slightly different this time." Two files that describe the same resource - a Helm value and the deployment it renders - are merged into a single component rather than double-counted, and the merge rule is fixed, not inferred.

02Trust zones come from the rules, not a guess

A trust boundary is not an opinion about where the dangerous part of the system is. It is the consequence of a specific rule in a specific file. When a security group exposes a port to 0.0.0.0/0, that node sits in the internet-facing zone by definition. When that node can reach a database subnet, and the database itself is marked publicly reachable, the path from the public internet to that data is not a hypothesis - it is an edge in the graph, derived from the rules you wrote.

main.tf ──► aws_security_group.web ingress 0.0.0.0/0 :5432 [INTERNET ZONE] egress -> db subnet rds.tf ──► aws_db_instance.orders publicly_accessible=true [REACHABLE FROM INTERNET] k8s/deploy.yaml ──► Service orders-api type: ClusterIP [INTERNAL ZONE] compose.yml ──► volume ./secrets bind mount [host -> container]
Five file formats parsed into typed components. The internet -> security-group 0.0.0.0/0 -> public database path is read out of main.tf and rds.tf, not imagined.

03Every fact carries its evidence

A threat model is only as trustworthy as its weakest claim, so each parsed fact keeps a pointer to the line that produced it. "The orders database is reachable from the internet" is not a sentence a model wrote; it is the join of two rules, each with a file-and-line citation, that anyone on the team can open and confirm in seconds.

Parsed factEvidence (file:line)Trust-boundary implication
Security group ingress 0.0.0.0/0main.tf:42Node enters the internet-facing zone; anything it fronts is publicly reachable.
aws_db_instance publicly_accessible=truerds.tf:17Database is reachable from outside the VPC, not just from the app tier.
S3 bucket ACL public-readstorage.tf:8Object contents are an anonymous GET away; bucket leaves the private zone.

Citations are what make the model auditable. In a GCC or enterprise review, the question is never "does the tool think this is exposed" - it is "show me". A parsed fact answers that by pointing at storage.tf line 8. A narrated fact cannot.

04A threat is proven only when DAST backs it

Reachability and exploitation are different claims, and the threat model keeps them apart. The parsed graph can prove that a database is reachable from the internet - that is a fact about your configuration. It cannot, on its own, prove that an attacker pulled data through that path. The AI layer can describe the threat, rank it, and explain the business impact, but it can never self-certify that the threat is real.

A threat is escalated to proven only when a real DAST finding backs it - an exploit or leak confirmed against the running system by in-band, differential or arithmetic evidence. The configuration tells you the door is unlocked; the dynamic finding tells you someone walked through it. Holding that line means the threat model never inflates a reachable path into a confirmed breach, and a reviewer can trust the proven label without re-deriving it.

Grounding the model in parsed IaC changes what a threat model is for. It stops being a diagram you argue about and becomes a queryable, evidence-backed view of where your real trust boundaries are - one that updates deterministically with every commit, and that an auditor can walk line by line.

Evidence over heuristicsReachable beats foundMonitor → Block: rolling out a gate without slowing teamsAir-gapped AppSec with no phone-homeOne platform vs three toolsOWASP Top 10 2025: what actually changed
See it on your own app →