A threat model is one of the most expensive documents an engineering team produces, and one of the most consistently wasted. A team spends two to four hours walking trust boundaries, scoring likelihood and consequence, writing mitigations. The output lands in a wiki page. Six weeks later the compliance team starts a NIS2 gap analysis from a blank template and re-documents the same system — the same data flows, the same controls, the same risks — because nobody told them the threat model already contained 80% of the answer.
The two documents describe the same system. The engineer calls the entry "Tampering at the partner-agent boundary, mitigated by structural input isolation." The auditor calls it "a cybersecurity risk-management measure under NIS2." It is the same control. The only thing missing is the wiring that lets one artifact be read by both desks.
We built that wiring into the threat-modeling workflow. This post is how it works and why the citation layer is the part that makes it hold up.
The two documents are the same facts in different clothes#
Start with what a STRIDE walk actually produces. For each trust boundary in your data-flow diagram, you record a threat, its likelihood and consequence, the controls already in place that reduce it, and the residual risk after those controls. That is the structure of our STRIDE workflow, described in detail in how we threat-model AI systems.
Now look at what a risk-based security regime asks for. NIS2 requires essential and important entities to take appropriate and proportionate technical, operational and organisational measures to manage the risks to the security of their network and information systems — risk identification, then proportionate controls, then evidence that you did both. DORA frames the same expectation as an ICT risk-management framework: identify ICT risks, apply controls, test, report. ISO/IEC 27001 frames it as Annex A controls selected against a documented risk assessment.
Every one of those regimes is asking the question a threat model already answers: what can go wrong with this system, and what have you done about it? The threat model is the risk assessment. The mitigations column is the control set. The residual-risk column is the proportionality argument. The regimes differ in vocabulary and in which obligations they enumerate, but the underlying artifact is one risk-and-control register, not three.
The waste comes from treating them as three.
What the mapping looks like in practice#
Here is a single STRIDE row, carried all the way through to the regulatory measure it satisfies. The system is a payments API exposing a delegated-payment endpoint to partner agents — the worked example from our STRIDE post.
| Field | Value |
|---|---|
| Boundary | Free-text input from partner agent → payment-orchestration LLM |
| STRIDE category | Tampering (prompt injection) |
| Threat | Untrusted partner content hijacks the orchestrating model and rewrites a payment instruction |
| Mitigation | Structural isolation: tool results sandboxed as data; instructions never sourced from upstream content |
| Residual risk | Medium |
| NIS2 measure | Cybersecurity risk-management measures — security of operations and handling of untrusted input |
| DORA measure | ICT risk-management framework — protection and prevention controls |
| ISO 27001 | Annex A control family covering secure development and input handling |
The right-hand three rows are the difference between a wiki page and an audit artifact. They are not annotations a human types after the fact. They are produced by the same agent that ran the walk, by asking the gateway which regulatory measures does this control answer to and grounding the answer against the live legal corpus rather than the model's memory.
The mapping is many-to-many on purpose. The prompt-injection mitigation above also helps satisfy GDPR's security-of-processing duty when the payment data is personal data. And the NIS2 risk-management obligation is satisfied not by this one threat but by the full set the walk produces. We record both directions: threat → measures, and measure → threats. The second direction is what an auditor reads — show me every control you have against this obligation — and it falls out of the first for free.
Why the citation has to be grounded, not generated#
This is the part that separates a defensible artifact from a liability.
A model that maps threats to regulations by recall will produce something that looks perfect and is occasionally wrong in ways you cannot see. We have watched a retrieval pipeline state, with full formatting confidence, that "Article 47 of NIS2 requires a 48-hour breach notification." The article number was wrong, the obligation lived elsewhere, and the figure was recalled rather than retrieved — three independent errors in one clean-looking citation, none of which left a trace in the output. We covered that failure class in cited answers vs. RAG for regulated work.
For a wiki page, a wrong article number is an annoyance. For an audit pack you hand to a regulator or an insurer, a fabricated citation destroys the credibility of the whole document. The breach-notification duty under GDPR Article 33 runs on a 72-hour clock — a report that mis-cites its own legal basis is not a defensible filing, and the same logic applies to every control-to-obligation claim in a threat model offered as evidence.
So we ground every regulatory claim. When the workflow attaches a NIS2 or DORA or ISO citation to a mitigation, it comes from a gateway tool result that retrieved and validated the provision, not from the model's training data. The mechanism is the same one behind every cited answer the gateway produces:
search(query="cybersecurity risk-management measures", frameworks=["NIS2"])
search(query="ICT risk-management framework", frameworks=["DORA"])
get_provision(jurisdiction="EU", law="GDPR", article="33")
validate_citation(jurisdiction="EU", law="GDPR", article="33")
If the gateway cannot confirm an article exists and says what the mapping claims, the citation does not ship. A threat with no defensible regulatory basis after that search is marked as exactly that — an honest gap — rather than dressed in a plausible-looking article number. The grounding ratio is a hard refusal, not a soft degradation: below the configured threshold the gateway refuses to synthesize rather than fill in from memory. A mis-cited control is worse than a declared unknown when the reader is an auditor.
Running it: threat model and gap analysis as one walk#
The two workflows already share a spine. STRIDE, LINDDUN, and the gap-analysis workflows in the gateway all follow the same shape — scope the system, generate a data-flow diagram, walk the elements, score, report — and they differ only in the question set and the control catalog plugged in. That shared structure is what makes the dual-output trick cheap.
A team that wants both an engineering threat model and a regulatory gap analysis from one effort does it like this. Connect any MCP client — Claude, Copilot in VS Code or Studio, Cursor, anything that speaks OAuth 2.1 MCP — to the gateway, then run the STRIDE walk first:
start_workflow(
workflow_type="threat_model",
entity_description="Payments API: delegated-payment endpoint exposed to
partner agents; processes personal + financial data;
EU-deployed; in scope for NIS2 and DORA."
)
Walk scoping → DFD → per-boundary STRIDE → scoring, the loop described in the STRIDE post. Then point the same system scope at the regulatory catalog. The gap-analysis workflow loads a control catalog by framework — nis2 pulls the NIS2 risk-management measures, dora pulls the DORA ICT-risk controls, cra pulls the EU Cyber Resilience Act essential requirements — and walks the controls instead of the six STRIDE categories. Because both ran against the same data-flow diagram and the same asset list, the agent can join them: each gap-analysis control is matched to the threats whose mitigations already address it, and each unmatched control is a genuine open item rather than a paperwork miss.
The report stage is where the dual output materializes. The same generate_report call that re-resolves citations for the threat model produces a control register keyed to article-level obligations — one export the engineers read as a threat table, the same export the auditor reads as a measure-by-measure coverage matrix. If you want the gap analysis as its own deliverable, the gap-analysis workflow produces it standalone; if you are working toward an EU AI Act conformity file, the AI Act readiness path overlays the same machinery on the Act's requirements.
The mapping table is the load-bearing part#
A threat-to-regulation map is only as good as the catalog behind it, and this is where most home-grown attempts quietly fall apart. Hand-maintaining a spreadsheet that says "control X satisfies NIS2 measure Y and ISO control Z" works until the regulation is amended, the control library is reorganized, or someone copies a stale row into next year's audit pack. The mapping rots and nobody notices until an auditor checks one cell.
We keep the mapping live by deriving it from the corpus rather than storing it as prose. The control catalogs are versioned YAML in the workflow definitions, the regulatory text comes from the law and EU-regulation corpora behind the gateway — currently 27 audited law jurisdictions live, out of 119 law corpora built, plus an EU regulations corpus of 98 instruments and 5,008 addressable provisions and 262 security frameworks — and every citation in a generated report is re-resolved at report time with a paragraph-hash drift check. If the underlying provision moved or changed, the report says so instead of carrying a citation that was true last quarter. You can see the breadth of what is mappable on the coverage page, and the citation-validation mechanics on how it works.
This is the difference between a mapping you maintain and a mapping that maintains itself. The threat model does not get stale relative to the regulation, because both are read from the same versioned source at the moment the report is generated.
What this changes for the two desks#
For the engineering team, nothing about the threat-modeling discipline changes. You still scope the system, draw the data-flow diagram, walk the boundaries, and argue residual risk. The only addition is that each mitigation now carries the regulatory measure it answers to — surfaced by the agent, not typed by you.
For the compliance team, the change is larger. The NIS2 or DORA gap analysis no longer starts from a blank template and a series of interview requests. It starts from the threat model the engineers already produced, with the controls already identified and the obligations already cited. The compliance officer's job shifts from re-documenting the system to reviewing coverage: which obligations are fully met by existing mitigations, which are partially met, which are open. That review is the work that actually requires their judgment. The transcription that used to consume it does not.
And when the auditor asks the question every audit comes down to — show me the control behind this obligation, and show me it is real — the answer is one artifact away, with a citation the regulator already accepts.
Try it#
Tier requirement. The
start_workflow/submit_response/generate_reporttools that drive STRIDE, LINDDUN, and the gap-analysis workflows are gated to Team and Company subscriptions. Free and Premium tiers can read the law and list workflows but cannot run a workflow end-to-end. See /pricing for the tier matrix.
If your AI client speaks MCP and you are on Team or Company:
- Connect it to the gateway —
https://gateway.ansvar.eu/mcp, OAuth 2.1, two-minute setup per the quickstart. - Run a STRIDE walk on a system in scope for NIS2 or DORA via the threat-modeling workflow, then run the matching
gap_analysisframework against the same scope. - Ask your agent: "For each threat we identified, map the mitigation to the NIS2, DORA, and ISO 27001 measures it satisfies, ground every citation through the Ansvar gateway, and flag any control with no defensible regulatory basis as an open gap."
The agent runs both walks, joins them on the shared data-flow diagram, and returns one report the engineers read as a threat table and the auditor reads as a cited coverage matrix.
A threat model that dies in a wiki is a sunk cost. A threat model that doubles as cited compliance evidence is the cheapest audit preparation you will ever do — because you did the work the moment you decided to ship the system safely, and the citations were waiting in the corpus the whole time.