Threat model: support agent (P5)¶
Method: the four questions from threat modeling, with STRIDE plus the OWASP LLM Top 10 (2026) and Agentic Top 10. Last reviewed 2026-11-14.
1. What are we building?¶
flowchart LR
subgraph Internet[Untrusted]
C[Customer browser or app]
end
subgraph App[Our service · trust boundary]
API[FastAPI /chat<br/>token -> user_id] --> GW[Gateway<br/>rate limit, budget]
GW --> IG[Input guards]
IG --> AG[Agent loop]
AG --> TL[Tools: order status,<br/>refund request]
AG --> OG[Output guards]
TL --> DB[(Orders DB)]
TL --> Q[(Refund queue)]
end
subgraph Ext[Third parties]
LLM[Model provider]
OBS[Trace backend]
end
C -->|1 untrusted text| API
AG -->|2 prompts| LLM
LLM -->|3 untrusted output| AG
App -->|4 spans| OBS
Q -->|5 human review| H[Support staff]
Assets: customers' order and payment data, the shop's money, the model budget, the brand, and service availability.
2. What can go wrong? 3. What are we doing about it?¶
| # | Threat | Where | Risk IDs | L / I | Decision and control | Verified by |
|---|---|---|---|---|---|---|
| T1 | Prompt injection or jailbreak makes the agent misbehave | Boundary 1 | LLM01, ASI01 | High / Med | Mitigate: input injection checks; and design so a fooled model can't do harm (T2, T4) | test_blocks_jailbreak; evals jailbreak-dan, prompt-extraction |
| T2 | Agent issues a refund or makes a commitment | Tools, output | LLM03, LLM07, ASI09 | Med / High | Eliminate: no money-moving tool; refunds queue for humans. Output check blocks promises | test_refund_is_a_request_for_a_human...; test_output_guard_catches_jailbreak... |
| T3 | Customer reads another customer's order | get_order_status |
LLM02, ASI03 | High / High | Mitigate: ownership check with session user ID; same "not found" reply for missing and foreign orders | test_cannot_see_another_customers_order; eval other-customers-order |
| T4 | Model chooses or spoofs the user identity | Tool arguments | ASI03 | Med / High | Eliminate: user_id never comes from the model; set from the auth token |
Code review of tools.py, api.py |
| T5 | Card or Aadhaar numbers reach the model, logs or replies | Boundaries 1, 2, 4 | LLM02 | High / High | Mitigate: input masking with checksums; output PII check; spans store no message text | test_card_number_is_never_echoed; test_spans_never_contain_message_text_or_pii |
| T6 | Flood of requests runs up cost or takes the service down | Gateway | LLM06 | High / Med | Mitigate: per-user rate limit, daily token budget, input size limit, step limit, provider spend cap | test_rate_limit_*, test_daily_token_budget, load test |
| T7 | System prompt or tool schemas extracted | Boundary 3 | LLM08 | High / Low | Accept: they contain no secrets or authorization logic. Extraction attempts are blocked as a courtesy | Review of SYSTEM_PROMPT |
| T8 | Unsafe links or HTML in replies | Boundary 3 | LLM10 | Med / Med | Mitigate: link allowlist on output; the client must render replies as text | test_output_guard_blocks_risky_replies |
| T9 | Stolen API token or provider key | API, config | STRIDE: spoofing | Med / High | Mitigate: keys only in env or secret store; per-user tokens revocable; rotate on exposure | Secret scan in CI (to add) |
| T10 | Compromised dependency or base image | Build | LLM04 | Low / High | Mitigate: pinned lockfiles; slim base image; non-root container; dependency scanning | uv sync --locked; Dockerfile review |
| T11 | No record of who did what during an incident | All | STRIDE: repudiation | Med / Med | Mitigate: one trace per request with user ID, decisions and tool calls | Tracing tests |
| T12 | Refund queue reviewers approve fraudulent requests | Human review | ASI09 | Med / Med | Mitigate (future): show order history and risk signals in the review screen | Not yet implemented |
L / I = likelihood / impact.
4. Did we do a good enough job?¶
- Every mitigated threat except T9, T10 and T12 has an automated test, and CI blocks a release if a security eval case fails.
- Open items: add secret scanning and dependency scanning to CI (T9, T10); design the reviewer screen (T12); move rate-limit counters to Redis before running more than one process per instance; run an automated red-team scan against staging each release.
- Revisit this model when adding a tool, a data source, memory, or a new channel such as WhatsApp.