flowchart TB
subgraph INPUTS["Three Input Modalities"]
IAC[Terraform / IaC<br/>resource graph · trust boundaries · exposure]
OAS[OpenAPI / spec<br/>endpoints · schemas · auth schemes]
DIO[draw.io / Mermaid<br/>manual annotation · implicit flows]
end
INPUTS --> MERGE
subgraph MERGE["Normalized Model Merge"]
N[resources + endpoints + trust boundaries + manual flows<br/>→ resolve into DFD element types<br/>external entity · process · data store · data flow]
end
MERGE --> RENDER
subgraph RENDER["DFD Renderer (Mermaid)"]
D[text-based · diff-able · reviewable in PRs<br/>the architecture-as-ground-truth]
end
RENDER --> STRIDE
subgraph STRIDE["STRIDE Engine"]
R[per DFD element:<br/>rule lookup → baseline threats]
L[LLM enrichment → system-specific context]
R --> L
L --> TL[structured threat list<br/>per element · STRIDE-categorized]
end
TL --> TRACK[(threat tracker<br/>findings, like bugs)]
style MERGE fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style RENDER fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style STRIDE fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style INPUTS fill:#1a1015,stroke:#5eead4,color:#e4e4e8
Reading: The stack ingests three modalities — declarative IaC (the resource topology), OpenAPI (the API surface), and manual annotation (implicit flows) — merges them into a normalized DFD model, renders it as diff-able Mermaid, and runs STRIDE with an LLM-enrichment layer that turns rule-lookup boilerplate into system-specific threats. The DFD is derived from ground truth (the artifacts that define the system), so it cannot drift from the implementation. The threat list is tracked like bugs.
flowchart LR
subgraph ELEMENTS["DFD Element Types"]
EE[External Entity]
P[Process]
DS[Data Store]
DF[Data Flow]
end
EE --> EE_S["Spoofing<br/>Repudiation"]
P --> P_S["Spoofing<br/>Tampering<br/>Repudiation<br/>DoS<br/>EoP"]
DS --> DS_S["Tampering<br/>Repudiation<br/>Info Disclosure<br/>DoS"]
DF --> DF_S["Tampering<br/>Repudiation<br/>Info Disclosure<br/>DoS"]
EE_S --> RULE[rule lookup<br/>baseline threats]
P_S --> RULE
DS_S --> RULE
DF_S --> RULE
RULE --> ENRICH[LLM enrichment<br/>+ system-specific context<br/>from merged model]
ENRICH --> OUT[actionable, specific threats]
style ELEMENTS fill:#1a1015,stroke:#5eead4,color:#e4e4e8
style RULE fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style ENRICH fill:#0d1b2a,stroke:#5eead4,color:#5eead4
Reading: STRIDE maps cleanly to DFD element types — external entities are spoofing/repudiation candidates, processes span the full set, data stores and flows carry tampering/disclosure/DoS. A rule lookup generates the baseline threats per element. The LLM enrichment is where the value compounds: it takes the generic baseline and attaches the system-specific context (resource names, data classifications, trust boundaries) that turns "consider Spoofing" into "the payment webhook has no mTLS — spoofing enables fraudulent refunds." The rule lookup provides the coverage; the LLM provides the relevance.
flowchart TB
subgraph MANUAL["Manual Threat Modeling (quarterly ritual)"]
M1[whiteboard session<br/>DFD drawn from memory]
M2[STRIDE pass<br/>same recurring threats]
M3[→ document<br/>decays immediately]
M1 --> M2 --> M3
end
subgraph HARNESS["Harness Stack (continuous, design-time)"]
H1[ingest Terraform + OpenAPI<br/>on every change]
H2[DFD regenerated from ground truth<br/>cannot drift]
H3[STRIDE re-run<br/>new threats as findings]
H4[→ threat tracker<br/>stays current]
H1 --> H2 --> H3 --> H4
end
MANUAL -.the inversion.-> HARNESS
style MANUAL fill:#1a1015,stroke:#9494a0,color:#9494a0
style HARNESS fill:#0d1b2a,stroke:#5eead4,color:#5eead4
Reading: The manual ritual produces a DFD from memory that decays the day after the session and a STRIDE pass that surfaces the same boilerplate every time. The harness stack derives the DFD from the IaC and OpenAPI — ground truth that regenerates on every change — and re-runs STRIDE so new threats appear as findings the moment the architecture shifts. The human is not eliminated (they review the DFD and triage the threats); the decay and the boilerplate are. This is Course 2A's central inversion applied to threat modeling.
flowchart LR
PR[IaC / OpenAPI PR<br/>opened by engineer] --> CI[CI runs threat-model pipeline]
CI --> DIFF[DFD regenerated<br/>diff vs main branch]
DIFF --> REVIEW[PR review surface:<br/>+ DFD diff<br/>+ new threats introduced by this change]
REVIEW --> GATE{engineer + reviewer<br/>accept threats?}
GATE -->|yes, address or accept| MERGE[merge — architecture record updated]
GATE -->|no, redesign| REVISE[revise IaC / spec]
style PR fill:#1a1015,stroke:#5eead4,color:#e4e4e8
style CI fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style DIFF fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style REVIEW fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style GATE fill:#1a1015,stroke:#5eead4,color:#e4e4e8
Reading: Mermaid is the canonical DFD format because it is text — it diffs in git and reviews in PRs. When the threat-model pipeline runs in CI on every IaC/OpenAPI change, the PR review surface shows the DFD diff and the new threats the change introduces. Threat modeling becomes a creation-time guardrail (Course 2A S08): the engineer sees the security consequence of their architecture change before it merges, not in a quarterly meeting months later. The architecture record (the versioned Mermaid DFD + threat history) stays current because it is regenerated from ground truth on every change.
# Diagrams — SDD-08: Threat Modeling Harness Stack
---
## Diagram 1 — The Ingestion-to-STRIDE Pipeline (Architecture)
```mermaid
flowchart TB
subgraph INPUTS["Three Input Modalities"]
IAC[Terraform / IaC<br/>resource graph · trust boundaries · exposure]
OAS[OpenAPI / spec<br/>endpoints · schemas · auth schemes]
DIO[draw.io / Mermaid<br/>manual annotation · implicit flows]
end
INPUTS --> MERGE
subgraph MERGE["Normalized Model Merge"]
N[resources + endpoints + trust boundaries + manual flows<br/>→ resolve into DFD element types<br/>external entity · process · data store · data flow]
end
MERGE --> RENDER
subgraph RENDER["DFD Renderer (Mermaid)"]
D[text-based · diff-able · reviewable in PRs<br/>the architecture-as-ground-truth]
end
RENDER --> STRIDE
subgraph STRIDE["STRIDE Engine"]
R[per DFD element:<br/>rule lookup → baseline threats]
L[LLM enrichment → system-specific context]
R --> L
L --> TL[structured threat list<br/>per element · STRIDE-categorized]
end
TL --> TRACK[(threat tracker<br/>findings, like bugs)]
style MERGE fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style RENDER fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style STRIDE fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style INPUTS fill:#1a1015,stroke:#5eead4,color:#e4e4e8
```
**Reading**: The stack ingests three modalities — declarative IaC (the resource topology), OpenAPI (the API surface), and manual annotation (implicit flows) — merges them into a normalized DFD model, renders it as diff-able Mermaid, and runs STRIDE with an LLM-enrichment layer that turns rule-lookup boilerplate into system-specific threats. The DFD is derived from ground truth (the artifacts that define the system), so it cannot drift from the implementation. The threat list is tracked like bugs.
---
## Diagram 2 — STRIDE Mapped to DFD Element Types
```mermaid
flowchart LR
subgraph ELEMENTS["DFD Element Types"]
EE[External Entity]
P[Process]
DS[Data Store]
DF[Data Flow]
end
EE --> EE_S["Spoofing<br/>Repudiation"]
P --> P_S["Spoofing<br/>Tampering<br/>Repudiation<br/>DoS<br/>EoP"]
DS --> DS_S["Tampering<br/>Repudiation<br/>Info Disclosure<br/>DoS"]
DF --> DF_S["Tampering<br/>Repudiation<br/>Info Disclosure<br/>DoS"]
EE_S --> RULE[rule lookup<br/>baseline threats]
P_S --> RULE
DS_S --> RULE
DF_S --> RULE
RULE --> ENRICH[LLM enrichment<br/>+ system-specific context<br/>from merged model]
ENRICH --> OUT[actionable, specific threats]
style ELEMENTS fill:#1a1015,stroke:#5eead4,color:#e4e4e8
style RULE fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style ENRICH fill:#0d1b2a,stroke:#5eead4,color:#5eead4
```
**Reading**: STRIDE maps cleanly to DFD element types — external entities are spoofing/repudiation candidates, processes span the full set, data stores and flows carry tampering/disclosure/DoS. A rule lookup generates the baseline threats per element. The LLM enrichment is where the value compounds: it takes the generic baseline and attaches the system-specific context (resource names, data classifications, trust boundaries) that turns "consider Spoofing" into "the payment webhook has no mTLS — spoofing enables fraudulent refunds." The rule lookup provides the coverage; the LLM provides the relevance.
---
## Diagram 3 — Continuous Threat Modeling vs the Quarterly Ritual (Comparison)
```mermaid
flowchart TB
subgraph MANUAL["Manual Threat Modeling (quarterly ritual)"]
M1[whiteboard session<br/>DFD drawn from memory]
M2[STRIDE pass<br/>same recurring threats]
M3[→ document<br/>decays immediately]
M1 --> M2 --> M3
end
subgraph HARNESS["Harness Stack (continuous, design-time)"]
H1[ingest Terraform + OpenAPI<br/>on every change]
H2[DFD regenerated from ground truth<br/>cannot drift]
H3[STRIDE re-run<br/>new threats as findings]
H4[→ threat tracker<br/>stays current]
H1 --> H2 --> H3 --> H4
end
MANUAL -.the inversion.-> HARNESS
style MANUAL fill:#1a1015,stroke:#9494a0,color:#9494a0
style HARNESS fill:#0d1b2a,stroke:#5eead4,color:#5eead4
```
**Reading**: The manual ritual produces a DFD from memory that decays the day after the session and a STRIDE pass that surfaces the same boilerplate every time. The harness stack derives the DFD from the IaC and OpenAPI — ground truth that regenerates on every change — and re-runs STRIDE so new threats appear as findings the moment the architecture shifts. The human is not eliminated (they review the DFD and triage the threats); the decay and the boilerplate are. This is Course 2A's central inversion applied to threat modeling.
---
## Diagram 4 — Mermaid DFD as a Source-Controlled Artifact (Workflow)
```mermaid
flowchart LR
PR[IaC / OpenAPI PR<br/>opened by engineer] --> CI[CI runs threat-model pipeline]
CI --> DIFF[DFD regenerated<br/>diff vs main branch]
DIFF --> REVIEW[PR review surface:<br/>+ DFD diff<br/>+ new threats introduced by this change]
REVIEW --> GATE{engineer + reviewer<br/>accept threats?}
GATE -->|yes, address or accept| MERGE[merge — architecture record updated]
GATE -->|no, redesign| REVISE[revise IaC / spec]
style PR fill:#1a1015,stroke:#5eead4,color:#e4e4e8
style CI fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style DIFF fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style REVIEW fill:#0d1b2a,stroke:#5eead4,color:#5eead4
style GATE fill:#1a1015,stroke:#5eead4,color:#e4e4e8
```
**Reading**: Mermaid is the canonical DFD format because it is text — it diffs in git and reviews in PRs. When the threat-model pipeline runs in CI on every IaC/OpenAPI change, the PR review surface shows the DFD diff and the new threats the change introduces. Threat modeling becomes a creation-time guardrail (Course 2A S08): the engineer sees the security consequence of their architecture change before it merges, not in a quarterly meeting months later. The architecture record (the versioned Mermaid DFD + threat history) stays current because it is regenerated from ground truth on every change.