PageSourceSearch

https://ansvar.eu/assets/SampleThreatModel-DTE1ISyi.js

js ansvar.eu collected 2026-10-03 20:20:21 UTC 17,869 bytes, 11 lines download raw bytes

1import{u as l,j as e,S as d,L as r,b as c}from"./index-BcYtsrGw.js";const n=[{id:"TM-01",stride:"Spoofing",threat:"Stolen or replayed OAuth access token impersonates a customer at the API edge",boundary:"B1",severity:"High",likelihood:"Possible",mitigation:"Short-lived, sender-bound access tokens; encrypted token storage; monitor for compromised service accounts",citations:[{label:"STRIDE-API-OAUTH-001"}],status:"cited"},{id:"TM-02",stride:"Spoofing",threat:"Session credential forged or predicted via a weak random number generator; account takeover",boundary:"B1",severity:"High",likelihood:"Possible",mitigation:"CSPRNG-backed session identifiers; new session token on authentication (ASVS V3.2.1); randomness verified per WSTG",citations:[{label:"STRIDE-CRYPTO-RANDOM-001"},{label:"CAPEC-196",url:"https://capec.mitre.org/data/definitions/196.html"},{label:"OWASP ASVS V3.2.1",url:"https://github.com/OWASP/ASVS/blob/v4.0.3_release/4.0/docs_en/OWASP%20Application%20Security%20Verification%20Standard%204.0.3-en.flat.json"},{label:"OWASP WSTG 4.2 — session mgmt schema",url:"https://github.com/OWASP/wstg/blob/v4.2/document/4-Web_Application_Security_Testing/06-Session_Management_Testing/01-Testing_for_Session_Management_Schema.md"}],status:"cited"},{id:"TM-03",stride:"Tampering",threat:"Prompt injection via customer message overrides system instructions",boundary:"B2",severity:"High",likelihood:"Likely",mitigation:"Input/output guardrail + privileged-instruction isolation",citations:[{label:"OWASP LLM01"},{label:"CWE-77"}],regulation:"EU AI Act Art. 15",status:"cited"},{id:"TM-04",stride:"Tampering",threat:"Returns-flow step skipping: the final refund-confirmation request is submitted directly, bypassing the eligibility checks of earlier steps",boundary:"B1 → B3",severity:"Medium",likelihood:"Possible",mitigation:"Server-side state machine over the returns flow; every stage transition validated server-side",citations:[{label:"CAPEC-140",url:"https://capec.mitre.org/data/definitions/140.html"}],status:"cited"},{id:"TM-05",stride:"Repudiation",threat:"A refund issued through the agent cannot later be attributed to a specific customer instruction and agent decision",boundary:"B3",severity:"Medium",likelihood:"Possible",mitigation:"Tamper-evident audit log of prompt, tool call, parameters, and approval — retained per policy",citations:[],status:"analyst_draft"},{id:"TM-06",stride:"Information disclosure",threat:"Context-window leakage exposes another tenant's PII",boundary:"B2 / B4",severity:"High",likelihood:"Possible",mitigation:"Per-request context isolation + retrieval scoping",citations:[{label:"CWE-200"}],regulation:"NIS2 Art. 21 / GDPR Art. 32 (candidates)",status:"regulatory_basis_unresolved"},{id:"TM-07",stride:"Denial of service",threat:"Unbounded chat traffic drives model-call spend and saturates the returns queue — no per-client budget",boundary:"B1 / B2",severity:"Medium",likelihood:"Likely",mitigation:"Per-client rate limits; token budgets; queue backpressure",citations:[],status:"analyst_draft"},{id:"TM-08",stride:"Elevation of privilege",threat:"Agent calls the refund tool beyond intended scope (tool-permission escalation)",boundary:"B3",severity:"Critical",likelihood:"Possible",mitigation:"Per-tool RBAC, deny-by-default + human-in-loop on financial actions",citations:[{label:"ATT&CK T1548"},{label:"CWE-269"}],regulation:"DORA Art. 6",status:"cited"},{id:"TM-09",stride:"Elevation of privilege",threat:"Authentication bypass on the internal tool API reaches the refund endpoint without passing the agent's controls",boundary:"B3",severity:"High",likelihood:"Possible",mitigation:"Authenticate every internal hop; step-up authentication before privilege-changing actions (see §4, IAC-16.3)",citations:[{label:"CAPEC-115",url:"https://capec.mitre.org/data/definitions/115.html"}],status:"cited"}],h=[{control:"AAT-29.2 — Agent least-privilege scoping",maps:"TM-08",fragment:"Confines each agent to the minimum permissions, assets, services, and network reach…"},{control:"IAC-16.3 — Step-up authentication for privilege changes",maps:"TM-08, TM-09",fragment:"Asking to alter a privilege level triggers an extra authentication check before…"},{control:"IAC-20 — Least-privilege logical access",maps:"TM-08, TM-09",fragment:"…each task needs, keeping every grant aligned with the least-privilege principle."},{control:"IAC-21 — Least privilege for processes",maps:"TM-01",fragment:"Acce
1ss is held to the minimum required, permitting only the processes needed…"}],m=[{id:"B1",name:"Internet → API edge",detail:"Anonymous internet traffic meets the authenticated API surface (OAuth 2.0 tokens, sessions)."},{id:"B2",name:"Untrusted input → model context",detail:"Customer text is assembled into the same context window as privileged system instructions."},{id:"B3",name:"Model output → privileged action",detail:"Agent output selects and parameterises tool calls — including the refund tool, a financial action."},{id:"B4",name:"Tenant isolation",detail:"Retrieval crosses into a multi-tenant store holding PII and order history."}],u=`[Customer / attacker]
2      |  chat + REST over HTTPS                 <- B1: internet -> edge
3[Returns API edge]   (OAuth 2.0 access token, session)
4      |  prompt assembly                        <- B2: untrusted input -> model context
5[LLM support agent]  (system instructions + customer message + retrieved context)
6      |  tool calls                             <- B3: model output -> privileged action
7[Order lookup] ---- [Refund tool]  (financial action)
8      |  queries                                <- B4: tenant isolation
9[(Customer & order store -- multi-tenant, PII)]`,p=({status:i})=>i==="regulatory_basis_unresolved"?e.jsxs("span",{className:"sdl-flag",children:["flagged — ",e.jsx("code",{children:"regulatory_basis_unresolved"})]}):i==="analyst_draft"?e.jsx("span",{className:"sdl-flag",children:"analyst draft — no fetched source; held for senior review"}):e.jsx("span",{className:"sdl-status-cited",children:"cited"}),t=({num:i,title:s})=>e.jsxs("div",{className:"sdl-section-head",children:[e.jsxs("div",{className:"kicker",children:["Section ",i]}),e.jsx("h2",{className:"h2-section",children:s})]}),f=()=>{l({title:"Sample threat model — STRIDE example, full deliverable · Ansvar AI",description:"A complete sample threat model for a fictional LLM-backed returns API — STRIDE register, fetched citations, control mapping, CRA duties, honest gaps.",path:"/services/sample-threat-model"});const i=n.filter(s=>s.status==="cited").length;return e.jsxs("div",{children:[e.jsx(d,{}),e.jsxs("main",{children:[e.jsx("section",{className:"ms-page-hero",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx("span",{className:"eyebrow",children:e.jsxs(r,{to:"/services",className:"eyebrow-link",children:[e.jsx("span",{className:"dot"}),"Services"]})}),e.jsx("h1",{className:"h1-page mt-5 mb-4",children:"Sample threat model — STRIDE, LLM-backed returns API"}),e.jsx("p",{className:"sdl-meta-line",children:"Acme Returns AB · sample deliverable · fictional system · produced with the same engine and format as a paid engagement"}),e.jsx("p",{className:"sdl-meta-line",children:"Document TM-ACME-2026-001 · Scope: single application · Method: STRIDE over DFD · Status: sample — senior review pending"}),e.jsxs("div",{className:"callout-warn sdl-banner",role:"note",children:[e.jsx("strong",{children:"This is a fictional sample."})," Acme and its returns API don't exist — the system is invented so we can publish a complete deliverable without exposing a client. It was produced with the same gateway engine, the same sources, and the same format as a paid engagement. A sample has no client and nothing to certify, so the sign-off block in §7 shows the format and stays blank by design — on a paid engagement the reviewing practitioner signs there. Every linked citation is a real row fetched from the gateway's corpora; the risk scores and the system itself illustrate the format."]})]})}),e.jsx("section",{className:"ms-section sdl-section",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx(t,{num:"1",title:"Executive summary"}),e.jsxs("p",{className:"sdl-prose",children:["The Acme Returns API fronts an LLM-backed support agent that answers return questions, looks up orders, and — within limits — issues refunds. A STRIDE walk over its data-flow diagram produced ",n.length," threats across four trust boundaries. Two stand out: prompt injection through the customer channel (TM-03) and the agent calling the refund tool beyond its intended scope (TM-08) — together they turn a support conversation into an unaudited financial action. First moves: deny-by-default per-tool RBAC with human approval on refunds, guardrail isolation of privileged instructions, and per-request context isolation so one tenant's PII never reaches another's session."]}),e.jsxs("p",{className:"sdl-prose",children:[i," of ",n.length," register rows carry citations; the linked ones were fetched live from the gateway's corpora during the run. Two rows (TM-05, TM-07) matched no source row and ship as marked analyst drafts; one row (TM-06) lists candidate provisions but its regulatory basis is unresolved. None of the three is papered over."]})]})}),e.jsx("section",{className:"ms-section sdl-section",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx(t,{num:"2",title:"System & trust boundaries"}),e.jsx("p",{className:"sdl-prose",children:"Acme Returns AB (fictional) runs a customer-support returns flow: a public chat channel where an LLM agent handles return requests end-to-e
9nd. Components: the returns API edge (authentication, session handling), the LLM support agent (system instructions plus per-request retrieved context), two tools the agent can call (order lookup and refund), and a multi-tenant customer & order store holding PII."}),e.jsxs("figure",{className:"sdl-dfd-fig",children:[e.jsx("img",{src:"/services/acme-dfd.svg",alt:"Data-flow diagram: a customer sends a return question through the returns API edge (trust boundary B1) into the LLM support agent (B2); the agent calls the order-lookup and refund tools (B3), which query the multi-tenant customer and order store (B4); retrieved context flows back into the agent, and a cited answer returns to the customer.",width:669,height:1006,loading:"lazy",decoding:"async"}),e.jsx("figcaption",{className:"sdl-dfd-caption",children:"Data-flow diagram with the four trust boundaries. Text version:"}),e.jsxs("details",{className:"sdl-dfd-details",children:[e.jsx("summary",{children:"Show the text form"}),e.jsx("pre",{className:"mono-block sdl-dfd",children:u})]})]}),e.jsx("ul",{className:"sdl-boundaries",children:m.map(s=>e.jsxs("li",{children:[e.jsx("span",{className:"sdl-id",children:s.id}),e.jsxs("span",{children:[e.jsxs("strong",{children:[s.name,"."]})," ",s.detail]})]},s.id))})]})}),e.jsx("section",{className:"ms-section sdl-section",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx(t,{num:"3",title:"Threat register"}),e.jsx("p",{className:"sdl-prose",children:"One row per threat, walked per element and boundary. Severity and likelihood are analyst-assigned and would be confirmed or corrected in senior review."}),e.jsx("div",{className:"sdl-table-wrap",children:e.jsxs("table",{className:"sdl-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"ID"}),e.jsx("th",{children:"STRIDE"}),e.jsx("th",{children:"Threat & boundary"}),e.jsx("th",{children:"Severity"}),e.jsx("th",{children:"Mitigation"}),e.jsx("th",{children:"Citations"}),e.jsx("th",{children:"Regulation"}),e.jsx("th",{children:"Status"})]})}),e.jsx("tbody",{children:n.map(s=>e.jsxs("tr",{className:s.status!=="cited"?"sdl-row-flagged":void 0,children:[e.jsx("td",{children:e.jsx("span",{className:"sdl-id",children:s.id})}),e.jsx("td",{children:s.stride}),e.jsxs("td",{children:[s.threat," ",e.jsx("span",{className:"sdl-id sdl-boundary-tag",children:s.boundary})]}),e.jsxs("td",{children:[e.jsx("span",{className:`sdl-sev sdl-sev-${s.severity.toLowerCase()}`,children:s.severity})," ",e.jsxs("span",{className:"sdl-likelihood",children:["(",s.likelihood,")"]})]}),e.jsx("td",{children:s.mitigation}),e.jsx("td",{className:"sdl-cite",children:s.citations.length===0?e.jsx("span",{className:"sdl-cite-none",children:"—"}):s.citations.map((a,o)=>e.jsxs("span",{children:[o>0?" · ":null,a.url?e.jsx("a",{href:a.url,target:"_blank",rel:"noopener noreferrer",children:a.label}):a.label]},a.label))}),e.jsx("td",{children:s.regulation??"—"}),e.jsx("td",{children:e.jsx(p,{status:s.status})})]},s.id))})]})}),e.jsx("p",{className:"sdl-legend",children:"Linked citations are rows the gateway returned during this run, with source URL, publisher, and license preserved. Unlinked identifiers (OWASP LLM01, CWE-77, ATT&CK T1548, CWE-269, CWE-200 and the regulation references) carry over from the previously published sample register for this system. Rows with no matching source say so instead of carrying an invented reference."})]})}),e.jsx("section",{className:"ms-section sdl-section",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx(t,{num:"4",title:"Control mapping"}),e.jsx("p",{className:"sdl-prose",children:"Register rows mapped to controls from the gateway's security-controls catalog. Fragments are quoted as fetched."}),e.jsx("div",{className:"sdl-table-wrap",children:e.jsxs("table",{className:"sdl-table sdl-table-controls",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Control"}),e.jsx("th",{children:"Maps to"}),e.jsx("th",{children:"Fetched description"})]})}),e.jsx("tbody",{children:h.map(s=>e.jsxs("tr",{children:[e.jsx("td",{className:"sdl-cite",children:s.url?e.jsx("a",{href:s.url,target:"_blank",rel:"noopener noreferrer",children:s.control}):s.control}),e.jsx("td",{children:e.jsx("span",{className:"sdl-id",children:s.maps})}),e.jsxs("td",{children:["“",s.fragment,"”"]})]},s.control))})]})})]})}),e.jsx("section",{className:"ms-section sdl-section",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx(t,{num:"5",title:"Product-security duties (CRA)"}),e.jsx("p",{className:"sdl-prose",children:"If the returns agent ships to customers as a product with digital elements — for example as an embeddable support widget — the Cyber Resilience Act's essential cybersecurity requirements attach to it. The gateway returned the operative annex:"}),e.jsxs("p",{className:"sdl-cite sdl-prose",children:[e.jsx("a",{href:"https://eur-lex.europa.eu/eli/reg/2024/2847/oj#anx_I",target:"_blank",rel:"noopener noreferrer",children:"Cyber Resilience Act, Annex I — Reg (EU) 2024/2847"})," ","— “ESSENTIAL CYBERSECURITY REQUIREMENTS — Part I Cybersecurity requirements relating to the properties of products with digital elements” (Publications Office of the European Union)."]}),e.jsx("p",{className:"sdl-prose",children:"In a paid engagement each register row is mapped to the specific Annex I point it engages. This sample stops at the annex level: the fetched excerpt covers the Part I heading, not the itemised list, and we don't cite deeper than what was fetched. Applicability is a scoping input, not a conclusion — whether Acme's deployment is a product placed on the market, rather than a pure service, decides if the CRA attaches at all. A real engagement records that determination; here it is marked open."})]})}),e.jsx("section",{className:"ms-section sdl-section",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx(t,{num:"6",title:"Method note"}),e.jsxs("ul",{className:"sdl-method",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"STRIDE over the DFD."})," All six categories walked per element and boundary of the §2 diagram; classic categories extended for the LLM surface — Tampering to prompt injection, Elevation of privilege to tool-permission escalation, Information disclosure to context-window leakage."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Sources fetched through the Ansvar gateway."})," The STRIDE pattern library, CAPEC, OWASP (ASVS 4.0.3, WSTG 4.2), the security-controls catalog, and the Cyber Resilience Act text — every linked citation is a row the gateway returned during the run, with source URL, publisher, and license preserved. Nothing is cited from model recall."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Refusal discipline."})," No fetched source → the row says so (TM-05, TM-07). Regulatory basis 
9not confirmable at article level → the row is flagged"," ",e.jsx("code",{children:"regulatory_basis_unresolved"})," (TM-06). Gaps are marked, never filled with plausible text."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"What a paid engagement adds."})," Your real architecture and a scoping call; enrichment across ATT&CK, CWE, and your sector's regulations; and a named senior reviewer who confirms or corrects every row before it ships."]})]})]})}),e.jsx("section",{className:"ms-section sdl-section",children:e.jsxs("div",{className:"ms sdl-doc",children:[e.jsx(t,{num:"7",title:"Sign-off"}),e.jsx("pre",{className:"mono-block sdl-signoff",children:`Senior review:   [shown for format — fictional sample, nothing to certify]
10Reviewer:        [named reviewer — OSCP / CISSP / AI red team]
11Date:            [—]`}),e.jsx("p",{className:"sdl-prose",children:"A paid engagement ships only after the named reviewer has validated every row and signed here. This sample is published unsigned, on purpose, so you can read the document exactly as it leaves the engine."}),e.jsxs("p",{className:"sdl-prose",children:["Want this for a real system?"," ",e.jsx(r,{className:"sec-run-link",to:"/services#threat-model",children:"Scope a threat model →"})]})]})})]}),e.jsx(c,{})]})};export{f as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.