Dayward AI
Week 1 · D1About 3 hours

Threat modeling: the lethal trifecta, trust boundaries, and how to use two OWASP lists as checklists

Start with the lethal trifecta test to see where an agent is actually dangerous: once private data, untrusted content and outbound communication coexist, one successful prompt injection becomes one data exfiltration. Then draw it as a trust boundary diagram and walk both OWASP lists against it.

Today's goals 0/3

Sign in to tick these off and save your progress.

Today's Goals

  1. Use the lethal trifecta test to decide whether an agent has an injectable exfiltration path, and say which edge is cheapest to cut
  2. Draw an agent as a data-flow diagram with trust boundaries, labeling where each piece of data comes from
  3. Walk the OWASP LLM Top 10 and Agentic Top 10 against your own agent and come out with a list of risk items

This course solves exactly one problem: how to make an agent that acts on its own fail to do damage when somebody talks it into trying. Today is about seeing where the danger actually lives. Bolting on defenses before you can see it usually means patching a problem you do not have while the real path stays untouched. When you are done, come back to the top of the page and check off the three goals.

Plain-Language Walkthrough

A building lobby and its parcel locker

Your building has a locked front door. To make life easier, the management company installed a parcel locker right inside the lobby, and anyone can walk up to it to drop off or pick up a package. That on its own is not dangerous: a stranger who gets into the lobby is, well, standing in the lobby.

The danger is a combination. Suppose the management company also did two other thoughtful-sounding things. First, they put a notice board next to the locker listing every apartment number, resident name and phone number. Second, they told the security guard a simple rule: whatever a note taped to the parcel locker asks for, do it, because it is probably from a resident.

Now look at the three facts together: a stranger can reach the lobby, the lobby holds a resident directory, and the guard executes instructions written on notes. Any passer-by can tape up a note saying "please photograph the notice board and text it to this number" and walk away with a complete data leak. And not one of the three facts is a mistake on its own: the locker is a convenience, the notice board helps people find each other, and following residents' instructions is the guard's job.

The risk is in none of the parts. The risk is in the combination. That is the whole of today's lesson. The reason your existing process never catches it is that your process inspects the parts one at a time, and this thing only exists once they are assembled.

The lethal trifecta: why each edge on its own is not a bug

Translate the building story into agent terms and you get what Simon Willison named in June 2025: the lethal trifecta. The three edges are:

  1. Access to private data — the agent can read things that should not be public: the customer database, internal documents, other people's conversations.
  2. Exposure to untrusted content — the agent pulls text somebody else can write into its context: ticket bodies, web pages, emails, third-party API responses.
  3. Outbound communication — the agent can move information out: send mail, call a webhook, write somewhere another party can read.

When all three are present, one successful prompt injection becomes one data exfiltration. The chain is short: an instruction hides inside untrusted content, the model follows it, the model fills the payload from private data, and the model uses an outbound tool to ship it.

The whole course demonstrates that chain on one fictional target: an internal knowledge assistant called deskmate. Every day it does three things — read tickets and the web pages linked from them, query the internal customer database to answer questions, and notify colleagues by email or webhook. Those three duties happen to complete all three edges, and every one of them is a feature the product manager explicitly asked for.

What makes the test genuinely useful is that it hands you a way to cut, not a way to block. The three edges are joined by AND, so removing any one of them breaks the chain. And the three usually cost wildly different amounts: private data is normally the core of the business and cannot go, untrusted content is the requirement itself and cannot go either, but "send it outward" is very often there only for convenience — plenty of agents could downgrade sending to "write the draft back onto the ticket and let a human press send." The mechanical way to pick an edge is to count the tools involved; the smallest one is the cheapest cut:

trifecta.js
// This tests capability, not behavior: if it is on the tool list, the agent has it.
// Whether an attacker can reach it does not depend on how you normally use it.
export function assessTrifecta(config) {
  const privateData = config.tools.filter((t) => t.sensitivity === 'private').map((t) => t.name)
  const untrusted = config.tools.filter((t) => t.returnsUntrusted).map((t) => t.name)
  // Untrusted content has two sources: external content a tool returns, and the user input itself
  if (config.userInputTrust === 'untrusted') untrusted.push('<user input>')
  const externalComms = config.tools.filter((t) => t.effect === 'send').map((t) => t.name)
 
  const edges = [
    ['private data access', privateData],
    ['untrusted content', untrusted],
    ['outbound communication', externalComms],
  ]
  const complete = edges.every(([, tools]) => tools.length > 0)
  // The edge backed by the fewest tools is the one that is cheapest to cut
  const cheapest = [...edges].sort((a, b) => a[1].length - b[1].length)[0][0]
  return { privateData, untrusted, externalComms, complete, cheapest }
}

Note that the test is about capability, not behavior. "Our agent never sends customer records out" is not an argument — what an attacker gets to use is the tool list, not your habits.

Direct and indirect injection: the attacker never has to talk to your agent

The mechanics of prompt injection were covered once already, on day 22 of the 30-day course: everything the model receives is flattened into one stretch of text, and your system prompt and somebody else's content are just two spans of characters that arrived in some order — no span carries a tamper-proof seal. So it is not a bug that can be fully fixed, and the industry consensus is to assume it will succeed and make success worthless.

For threat modeling you only need to push one step further. Direct injection is an attacker talking to your agent. Indirect injection is an attacker writing into something your agent is going to read sooner or later. The second is the hard one: they need no account on your system and never show up in your logs; it is enough to leave a paragraph in a ticket's notes field, or in a corner of a page that fetch_page will one day retrieve. Anywhere the agent reads content into context is attack surface, and that single sentence decides how you draw the next section's trust boundaries.

Drawing trust boundaries: label every piece of data with where it came from

The first diagram in threat modeling is not an architecture diagram. It is a data-flow diagram with one dashed line across it. The only difference between the two sides of that line: inside is what you can vouch for, outside is what somebody else can write.

Three steps. List every source that pours anything into the context. Mark each source trusted or untrusted. List every exit that can produce an external side effect. Then look for a path that runs from an untrusted entry all the way to some exit. For deskmate it looks like this:

user question untrusted deskmate context read_ticket ticket body untrusted fetch_page page body untrusted search_customers customer database private send_email exit post_webhook exit update_ticket exit
Mermaid source
mermaidmermaid
flowchart LR
  U[user question untrusted] --> A[deskmate context]
  T[read_ticket ticket body untrusted] --> A
  W[fetch_page page body untrusted] --> A
  C[search_customers customer database private] --> A
  A --> E[send_email exit]
  A --> H[post_webhook exit]
  A --> K[update_ticket exit]

The part worth staring at is not the boxes, it is the direction of the arrows. Three untrusted arrows and one private arrow converge into one context, and three arrows leave that context for the outside world. That converge-then-diverge shape is itself the alarm. Once untrusted content and private data meet inside one context, and that context is wired to an exit, the model has become the guard following the note.

Two mistakes beginners make here. First, tool return values are entries too. Plenty of people mark only user input as untrusted and forget that the page body fetch_page brings back was written, in full, by a stranger. Second, exits are more than "send a message." Any write an outsider can observe is an exit: content written back onto a ticket is visible to the submitter, and content spliced into a URL has already landed in somebody's access log by the time the request fails. The list of exfiltration exits is usually longer than the one you wrote down.

What the two OWASP lists are each for

With the diagram drawn, the next question is "besides this one big path, what did I fail to think of?" That calls for a checklist, and OWASP maintains two of them.

The OWASP Top 10 for LLM Applications covers the model application: what goes wrong between input going in and output coming back. Three entries matter today — LLM01 prompt injection, LLM02 sensitive information disclosure, LLM06 excessive agency.

The OWASP Top 10 for Agentic Applications (published 9 December 2025, numbered ASI01 through ASI10) covers agents that act: they have loops, tools, memory and possibly other agents as colleagues, none of which the LLM list reaches. Four entries matter today — ASI01 agent goal hijacking, ASI02 tool misuse, ASI03 identity and privilege abuse, ASI06 memory and context poisoning.

One sentence holds the split: the LLM list governs what the model said; the ASI list governs what the agent did. A support bot that only answers questions is mostly covered by the LLM list. An agent that calls tools, mutates state and remembers things across turns will lose a whole class of risk without the ASI list — the same injection that merely makes a model talk nonsense in a single exchange gets written into long-term memory on an agent that remembers, and then fires on every later turn. That is ASI06.

The right way to use a checklist is to ask, entry by entry, "what does this look like in my system" — not to tick boxes saying "we do not have that problem." For deskmate, LLM01 looks like the ticket notes field, and ASI02 looks like send_email letting the model choose the recipient freely. If you cannot write down the concrete shape, you have not thought it through yet.

The output is not a document, it is a risk table you can tick through

The most common failure mode of threat modeling is producing prose: even-handed, impossible to fault, useless to act on, and never opened again three months later.

The way out is to give every risk item mandatory fields, and the critical one is evidence: which tools in the config make this risk true. A risk item with evidence can be challenged to its face ("that tool was removed last month"). One without evidence can only be nodded past.

risks.js
// Every risk must answer three things: severity, the matching OWASP entry, and what makes it true.
export function buildRiskItems(config, verdict) {
  const items = []
  if (verdict.complete) {
    items.push({
      id: 'R-TRIFECTA',
      title: 'lethal trifecta complete: one indirect injection is enough to exfiltrate data',
      severity: 'critical',
      owasp: ['LLM01 prompt injection', 'LLM02 sensitive information disclosure', 'ASI01 agent goal hijacking'],
      evidence: [
        `private data: ${verdict.privateData.join(', ')}`,
        `untrusted content: ${verdict.untrusted.join(', ')}`,
        `outbound communication: ${verdict.externalComms.join(', ')}`,
      ],
      mitigation: 'cut any one edge; if none can be cut, put outbound communication behind an approval gate and an egress allowlist',
    })
  }
  for (const tool of config.tools) {
    if (tool.effect === 'send' && !tool.reversible) {
      items.push({
        id: `R-IRREVERSIBLE-${tool.name}`,
        title: `${tool.name} is an irreversible outbound action, so a mistake gets no second chance`,
        severity: 'high',
        owasp: ['LLM06 excessive agency', 'ASI02 tool misuse'],
        evidence: [tool.description],
        mitigation: 'add an approval gate and allowlist the recipients and destination addresses',
      })
    }
  }
  return items
}

That code is the core of today's lab. It is deliberately dumb — nothing but if statements, no model involved. Threat modeling is work you finish before writing code; it needs no running agent, and it should never depend on a model to tell you what is wrong with your own system.

One more field deserves its own paragraph. Severity is not a gut call. The very same "this tool returns untrusted content" is high on an agent with the full trifecta and drops to medium on an agent with no outbound communication, because the second one can be hijacked and still send nothing. Severity is a property of the path, not of the point — which is the same statement as "cut one edge," said the other way round.

Which layer each of the five days works on

Here is the position this course takes, and every remaining day comes back to it:

A detector is not a boundary. The architecture is.

Anything an adaptive attack can walk through is not a defense line, it is a filter. A real boundary means that once untrusted input arrives, this agent is structurally incapable of doing the bad thing. The five days are ordered by how close each layer gets to that bar:

DayLayerOne line
D1judgmentsee where the three edges are, and work out which is cheapest to cut
D2inputhow much each of four input-side tiers stops, and where the ceiling is
D3architecturesix design patterns that drive attack success to zero while keeping the agent useful
D4runtimepolicy engine, egress allowlist, per-user credentials, audit log
D5processattack suite, threshold gates, kill switch and incident response

From tomorrow on, every day reports two numbers at once: ASR (attack success rate) and utility (task completion under attack). Any proposal that reports only the first is untrustworthy — change the agent to do nothing at all and ASR drops to zero instantly, taking utility with it.

Source Reading

Hands-On Lab

🧪 D1 lab: a threat modeller

Code location: labs/agent-security-5days/day-01-threat-model

Acceptance criteria:

  1. The verdict for deskmate is that the trifecta is complete, with the matching tool names listed under each of the three edges
  2. The first risk item is [CRITICAL] R-TRIFECTA, and its evidence lists the tools behind each of the three edges
  3. There are 6 items in total, sorted from highest severity to lowest
  4. The control config deskmate-no-send flips the verdict to trifecta incomplete, drops to 3 items, and has no critical
  5. pnpm typecheck exits 0

The lab is pure local computation — no model, no network. Today's skill is judging whether an agent is dangerous before you run it. Before you start, guess for yourself: which of deskmate's six tools carry each of the three edges? Run it afterwards, and the edge you got wrong is what you actually learned today. If you get stuck, read the comments in solution/src/model/; every function explains why it decides the way it does.

  1. Run the solution's pnpm start and study the trifecta verdict for deskmate plus the risk table sorted by severity.
  2. Go back to the starter and complete the trifecta test in trifecta.ts, remembering that user input is itself a source of untrusted content; when it is done, confirm all three edges list tool names.
  3. Complete cheapestCut and the risk item generation in risks.ts, so every risk lands on an OWASP entry and carries an evidence line.
  4. Look at the control config deskmate-no-send: deleting only the two send tools makes the critical item disappear, which is the plain demonstration of cutting one edge.
  5. Change DESKMATE's userInputTrust to trusted and run again; confirm the untrusted content edge still holds, which shows the verdict is not hostage to one field. Finally run it against your own project's tool list and keep the output.

Interview Questions

Today's three questions are in the question bank below, focused on the lethal trifecta test, the difference between direct and indirect injection, and how to draw a trust boundary diagram in half an hour. Expand a question and read the analysis before the answer points — walking the derivation beats memorizing the points. The domestic and overseas frequency tags let you pick by target market.

Checklist and Tomorrow

  • Use the lethal trifecta test to decide whether an agent has an injectable exfiltration path, and say which edge is cheapest to cut
  • Draw an agent as a data-flow diagram with trust boundaries, labeling where each piece of data comes from
  • Walk the OWASP LLM Top 10 and Agentic Top 10 against your own agent and come out with a list of risk items
  • Explain why code review, dependency scanning and unit tests all miss this class of risk
  • Say why severity is a property of the path rather than of a single point
  • All 5 acceptance criteria of the lab pass, and you have run it once against your own project's tool list
  • Answer at least 2 of the 3 interview questions without looking at the points

Tomorrow (D2) we stand the target up for real: we build an injection range around deskmate, attack it through all three injection surfaces — ticket body, page body, tool return — and then add four tiers of input-side defense in order: delimiters, provenance markers, spotlighting, a detector. After each tier we measure ASR and utility again. Measure before defending, for the same reason as today's "see the three edges before cutting": without knowing how often you are being breached now, you cannot judge whether a defense did anything. You will watch the first tier produce numbers identical to no defense at all, and you will watch four tiers of defense give the attack success rate back the moment one unseen case shows up — those two facts are precisely why D3 is about architectural defense.

Interview questions

  • What is the lethal trifecta, and why is it a compositional risk rather than a single-point defect?什么是致命三件套?为什么说它描述的是组合风险而不是单点缺陷?
    Common in ChinaCommon overseasBasic#threat-modeling#lethal-trifecta

    How to reason about it · think before answering

    1. This question separates people who hunt for bugs from people who read structure. Listing the three legs is easy; explaining why none of your existing quality gates catches it is where the signal is.
    2. Name the three legs first: access to private data, exposure to untrusted content, and the ability to communicate externally. Then state immediately that they are ANDed, not ORed, and that all three must hold before a successful prompt injection becomes data exfiltration.
    3. Show why it is compositional by testing each leg alone. Querying the customer database is a product requirement, reading tickets and linked web pages is a product requirement, and sending notifications is a product requirement. None is a defect, so the risk exists only in the combination and lives in no single line of code.
    4. That leads to a strong conclusion: code review, dependency scanning and unit tests all miss it. The first two inspect one snippet or one library, the third inspects one function's inputs and outputs, while this risk is a property of the tool inventory as a whole.
    5. Land it on how the criterion is used: it prescribes removal, not defense. Breaking any one leg breaks the chain, and the three legs rarely cost the same. In most systems the cheapest cut is downgrading free-form outbound sending to drafting something a human then sends.
    6. Expect the follow-up: which leg do you cut? Give the mechanical rule, cut the leg backed by the fewest tools, and stress that the test is capability, not habit. Saying you never send customer data out is not an argument, because the attacker gets the tool inventory, not your habits.

    分析过程 · 先想清楚再作答

    1. 这题在考你把安全当成「找 bug」还是当成「看结构」。能背出三条边的人很多,能说清「为什么现有的质量闸门一条都拦不住它」的人很少,区分度全在后半句。
    2. 先给三条边:访问私有数据、接触不可信内容、能对外通信。然后立刻补一句它们是「与」的关系——三者同时具备,一次成功的提示注入才能升级成一次数据外泄。
    3. 怎么拆「为什么是组合风险」:逐条问「这一条单独存在算不算缺陷」。查客户库是产品需求,读工单和网页是产品需求,发邮件通知也是产品需求,三条都不是 bug。风险只在拼起来的时候才出现,所以它不在任何一行代码里。
    4. 由此推出一个很有说服力的结论:代码审查、依赖扫描、单元测试都发现不了它——前两个看的是单段代码和单个依赖,第三个看的是单个函数的输入输出,而这条风险是工具清单的整体属性。
    5. 结论落到用法上:判据的价值是给拆法不是给防法。三条边拆掉任意一条链就断,而三条的代价通常差很远,多数系统里最便宜的一刀是把「自由对外发送」降级成「写回草稿由人点发送」。
    6. 可预期的追问:那你怎么判断该拆哪条?答机械办法——数每条边涉及的工具数,最少的那条最省;同时强调判的是能力不是行为,「我们从来不往外发客户资料」不构成理由,攻击者用得上的是工具清单。

    Key points

    • Three legs: private data access, untrusted content, and external communication. Only all three together form a complete exfiltration path.
    • Each leg alone is a legitimate product requirement, not a defect. The risk is created by the combination.
    • That is why code review, dependency scanning and unit tests miss it: it is a property of the whole tool inventory, not of any single line of code.
    • Use it to remove, not to defend. Cutting any one leg breaks the chain, and the cheapest cut is usually turning free-form sending into a human-confirmed draft.
    • The test is capability, not behavior. If the tool is in the inventory, the capability exists regardless of how you normally use it.

    答题要点

    • 三条边:访问私有数据、接触不可信内容、能对外通信;三者同时具备才构成完整的外泄链路。
    • 每一条单独看都是正常产品需求,没有任何一条是 bug,风险是组合出来的。
    • 因此代码审查、依赖扫描、单元测试都发现不了它——它是工具清单的整体属性,不在任何一行代码里。
    • 判据的用法是拆不是防:拆掉任意一条边攻击链就断,最便宜的通常是把自由对外发送降级成人工确认。
    • 判的是能力不是行为:工具清单里有就算具备,跟你平时用不用无关。
  • What is the difference between direct and indirect prompt injection, and which is harder to defend against?直接注入和间接注入有什么区别?哪一种更难防,为什么?
    Common in ChinaCommon overseasIntermediate#prompt-injection#attack-surface

    How to reason about it · think before answering

    1. The hinge is the second half. Answering that indirect injection is harder because it is stealthier reads as marketing. The interviewer wants to know which concrete step gets harder.
    2. Start with the definitional difference. Direct injection means the attacker talks to your agent, so the payload enters through user input. Indirect injection means the attacker writes into something your agent will eventually read: a ticket comment, a web page, an email, a third-party API response.
    3. Break down the difficulty in three layers. First, entry count: user input is one channel, while tool returns give you one channel per tool and a new one with every tool you add. Second, identity: an indirect attacker needs no account and leaves no trace in your access logs, so attribution is nearly impossible. Third, timing: the payload can sit on a page for weeks until someone happens to paste that link.
    4. Add the criterion most people miss. Anything the agent reads into context is attack surface, including fields in your own database whenever those fields are filled in by external users. This is what decides which arrows get marked untrusted on the trust-boundary diagram.
    5. Conclusion: indirect is harder, and the hard part is attribution rather than detection. You cannot even say who wrote the text, so defenses have to be structural rather than attacker-identifying.
    6. Expect the follow-up: can you just filter untrusted content? Name the ceiling. Unlike SQL injection there is no syntactic boundary; in natural language instructions and data look identical, so the working assumption is that injection will succeed and the job is to make success worthless.

    分析过程 · 先想清楚再作答

    1. 这题的题眼在第二问。只答「间接注入更难防,因为更隐蔽」是营销话术,面试官想听的是「难在哪个具体环节」。
    2. 先给定义差:直接注入是攻击者自己跟你的 Agent 说话,载荷经过用户输入这个入口;间接注入是攻击者把话写进一份你的 Agent 迟早会去读的东西里——工单备注、网页正文、邮件、第三方接口返回。
    3. 怎么拆「难在哪」:分成三层说。第一层是入口数量,用户输入只有一个口子,而工具返回有多少个工具就有多少个口子,而且每加一个工具就多一个。第二层是身份,间接注入的攻击者不需要你系统的账号,也不会出现在你的访问日志里,事后溯源极难。第三层是时间差,载荷可以先躺在一个网页上,等哪天有人贴了这个链接才生效。
    4. 还要点出一个很多人漏掉的判据:凡是 Agent 会读进上下文的地方都是攻击面,包括你自己数据库里的字段,只要那个字段是外部用户填的。这一条决定了信任边界图上该把哪些箭头标成 untrusted。
    5. 结论:间接注入更难防,但难的不是检测而是归责——你连「谁写的这段话」都答不上来,所以防御必须落在结构上而不是落在识别攻击者上。
    6. 可预期的追问:那能不能把不可信内容都过滤一遍?要答出上限——注入不像 SQL 注入那样有明确的语法边界,自然语言里指令和数据长得一模一样,所以业界共识是假设它一定会成功,然后让它成功了也没用。

    Key points

    • Direct injection enters through user input; indirect injection plants the payload in content the agent will read on its own.
    • Indirect is harder: entry points scale with tool count, the attacker needs no account and leaves no log trace, and the payload can be planted long before it fires.
    • The working rule is that anything read into context is attack surface, including your own database fields when external users fill them in.
    • The genuinely hard part is attribution, not detection, so defenses must be structural rather than attacker-identifying.
    • Filtering has a ceiling: natural language has no syntactic boundary between instruction and data, so injection is not a bug that gets fixed.

    答题要点

    • 直接注入走用户输入这个口子,攻击者自己跟 Agent 说话;间接注入把载荷写进 Agent 迟早会读的内容里。
    • 间接注入更难防:入口随工具数量增长、攻击者不需要账号也不进日志、载荷可以提前埋好等待触发。
    • 判据是「凡是会被读进上下文的地方都是攻击面」,包括自己数据库里由外部用户填写的字段。
    • 真正难的是归责而不是检测,所以防御必须落在结构上而不是落在识别攻击者上。
    • 过滤有上限:自然语言里指令和数据没有语法边界,注入不是一个能被彻底修好的 bug。
  • Given an agent already in production, how would you map its trust boundary and find its highest-risk path in half an hour?给你一个已经上线的 Agent,你怎么在半小时内画出它的信任边界并找出最高危的那条链路?
    Common in ChinaCommon overseasDeep dive#threat-modeling#trust-boundary

    How to reason about it · think before answering

    1. This question tests process, not knowledge. Someone who can recite the trifecta but cannot give executable steps is exposed here; the interviewer wants evidence you have actually done it.
    2. Fix the scope first. Half an hour is not enough for a full architecture diagram, so draw only three things: every source that injects content into the context, every source that can read private data, and every exit that produces an externally observable side effect. Everything else waits.
    3. Derive entries and exits from the tool inventory rather than by asking people. For each tool ask two questions: can an outsider write what it returns, and after it runs can anyone outside see a change? Two questions classify every tool as entry, exit, both or neither.
    4. Call out two rookie traps, which immediately separates you from the pack. First, marking only user input as untrusted and forgetting tool return values. Second, reading exit as sending a message, when writing back to a ticket counts, and so does embedding data in a URL you fetch, since even a failed request leaves the domain and path in someone else's logs.
    5. Conclusion: look for a path from an untrusted entry to an exit that passes through private data. If one exists, log a critical item whose mitigation names the cheapest leg to cut, chosen by counting the tools behind each leg. Then walk both OWASP lists asking what each item looks like in this specific system.
    6. Expect the follow-up: why two lists? Explain the split. The LLM Top 10 covers the model-application layer, while the Agentic Top 10 covers what only an acting agent has, such as memory poisoning and tool misuse. A question-answering bot needs only the first; an agent that calls tools and changes state loses a whole class of risk without the second.

    分析过程 · 先想清楚再作答

    1. 这题在考流程而不是知识。会背三件套但给不出可执行步骤的人,到这一步就露馅了;面试官想确认的是你真的干过这件事。
    2. 先把范围钉死:半小时内不可能画完整架构图,所以只画三样东西——所有会往上下文里灌内容的入口、所有能读到私有数据的来源、所有能产生外部可观察副作用的出口。其余一律先不画。
    3. 怎么拆:入口和出口都从工具清单里读,不要靠问人。每个工具问两句话——它的返回值是不是外部可写的,它执行完之后外面有没有人能看见变化。两句话就能把一个工具归进入口、出口或者两者都不是。
    4. 这里要主动点出两个新手陷阱,能立刻拉开差距:一是只把用户输入标成不可信而忘了工具返回值,二是把出口理解成「发消息」——其实写回工单、拼进 URL 去请求一个外部地址,哪怕请求失败,域名和路径也已经进了对方的日志,这些都是出口。
    5. 结论:画完看有没有一条线能从不可信入口走到出口,并且中途经过私有数据。有就记一条 critical,处置写「拆哪条边最省」,判据是数每条边涉及的工具数。然后再用 OWASP 的两张清单逐条问「这一条在我这儿长什么样」,把漏网的补上。
    6. 可预期的追问:为什么是两张清单不是一张?答分工——LLM Top 10 管模型应用这一段,Agentic Top 10 管会自己动手的 Agent 才有的东西,比如记忆投毒和工具滥用。只会回答问题的机器人用前一张够了,能调工具改状态的 Agent 缺了后一张会整类漏掉。

    Key points

    • Scope it: draw only untrusted entries, private data sources and external exits, not a full architecture diagram.
    • Derive them from the tool inventory by asking, per tool, whether outsiders can write its return value and whether its effects are externally visible.
    • Two common mistakes: forgetting that tool return values are untrusted entries, and treating exits as messaging only, when ticket writebacks and data-bearing URLs also qualify.
    • Look for a path from an untrusted entry through private data to an exit. If one exists it is critical, and the mitigation names the cheapest leg to cut.
    • Close with both OWASP lists, asking what each item looks like in this system, and deliver a risk table with evidence rather than a prose document.

    答题要点

    • 限定范围:只画不可信入口、私有数据源、外部出口三样,不画完整架构图。
    • 从工具清单推导:每个工具问「返回值是不是外部可写」和「执行后外面看不看得见」两句话。
    • 两个易错点:工具返回值也是不可信入口;出口不只是发消息,写回工单和拼进 URL 的请求都算。
    • 找链路:有没有一条线从不可信入口经过私有数据走到出口,有就是 critical,处置写拆哪条边最省。
    • 最后用 LLM Top 10 与 Agentic Top 10 逐条问「这条在我这儿长什么样」补漏,产出是一张带证据的风险表而不是一份文档。

Comments