The recipe at a glance
The ten steps of the training on one page. Each lesson teaches one or two of them.
Rules that hold throughout
- An unanswered fact stays unknown. It is never read as "no".
- A guess can remove a finding for good: one absent required leg means no finding.
- An element with no zone on any boundary around it reads as Internal. It is the one fact with a default, so give every outside party a zone.
- A drawn flow beats an egress attribute: answering "egress restricted" while a drawn flow still reaches an outsider keeps the way out, with a conflict note.
- A vendor destination is a question, not an answer: can an untrusted party read what reaches it?
- Origin "Not assessed" is an answer and counts as untrusted; an empty origin is not an answer.
- A Context Ingress whose source is in an Untrusted zone, or is an Untrusted Content Source or a Public AI Artifact Hub, makes its content untrusted even with no item drawn on it.
- A control never removes a finding. It credits a route: "control modelled", or "detected only" for detection.
- Assert outbound flows complete is an answer, not a shortcut: it settles an element's ways out as absent. Use it only when someone who knows confirms every outbound flow is drawn.
- A new tool, channel or hand-off reopens the answers it touches: ask again before you keep an approval or a control answer given before it existed.
- Assert not a hand-off is an answer too: use it only when someone who knows confirms that the flow hands no work on and the receiving agent's model never reads what it carries.
0. Draw the agent
One Agent Service per agent, or per group of agents with the same tools, model and context sources. A runtime product (a harness, an agent server or a platform) is its own element. Draw an Agent Runtime boundary only around two or more agents when no container holds exactly their process space. Never draw below the agent: a tool call is a flow, and what it calls (an MCP server, an API, a shell, a harness) is an element. A hand-off from one agent to another is an Agent Tool Call, and its result a Context Ingress back, drawn directly between the two agents.
Why: Every agent pattern is about one agent. A tool drawn as its own agent splits one agent's facts across elements that cannot add up.
Ask:
- Which agents does the system run, and which of them share the same tools, model and context sources?
- What hosts each agent: a harness, an agent server, a platform, or nothing (a standalone service)?
- Which agents hand work to another agent, and which agent holds the tools that act?
Classes: Agent Service, Agent Harness, Agent Server, Agent Runtime, Agent Code Execution Sandbox.
What the analysis reads here:
- An agent: Agent Service.
- A remote agent you reach over A2A: Remote A2A Agent.
- A harness that runs an agent and approves its actions: Agent Harness.
- A boundary around agents that share one process space: Agent Runtime.
- A sandbox the agent runs code in: Agent Code Execution Sandbox.
- An agent platform that hosts agents: AnythingLLM, Dify, Flowise, Langflow, LibreChat, LobeHub, MaxKB, n8n, Open WebUI, OpenClaw, RAGFlow.
- Something that runs code or tools for an agent: Agent Harness, Agent Server, AnythingLLM, Dify, Flowise, Langflow, LibreChat, LobeHub, MaxKB, Open WebUI, OpenClaw, RAGFlow, n8n.
- An executor that can read files in the agent's space: Agent Harness.
- An MCP server: Local MCP Server, Remote MCP Server.
1. Say what data moves
Draw each data item once and classify it: its sensitivity, whether it is a credential, and its origin (who can write it).
Why: The facts about content live on the data items: private data, untrusted content and credentials are read from them.
Ask:
- Which content does each agent read: tickets, documents, web pages, tool results, memory?
- For each item: how sensitive is it, is it a credential, and who can write it?
- Which secrets does each agent, harness, MCP server or sandbox hold, and what does each one unlock?
Classes: User Input, Customer & Personal Data, User Credentials, Agent Credential, System Prompt & Agent Instructions.
Data item fields: Sensitivity, Credential, Origin.
What the analysis reads here:
- A credential: Agent Credential, STS Temporary Credentials, Authentication Tokens, User Credentials, and 9 more in modules you may have installed.
- Agent memory: Conversation State / Checkpoint, Knowledge Item.
- A knowledge item: Knowledge Item.
2. Attach the data
Attach each item to every flow that carries it, above all every Context Ingress into an agent, and to every element that holds it: credential holders, stores and MCP servers. Attach a credential to its holder (the container, when it sits in an environment variable or a file) and to the tool call that presents it.
Why: A classified item that rides on no flow settles nothing at the agent.
Ask:
- Which flows carry each item into the agent?
- Which elements hold each item at rest?
Classes: Context Ingress, Agent Tool Call, Knowledge Write.
What the analysis reads here:
- Content flowing into an agent: Context Ingress.
- An agent calling a tool, an MCP server or another agent: Agent Tool Call.
- A source whose normal traffic is the product's own: Public AI Artifact Hub.
3. Mark who is outside
Put the open internet, and anyone who can write content the agent reads without being one of your own vetted staff or systems, in a boundary with the Untrusted zone (Open internet); an account does not make content trusted. Put vendor-operated elements in a boundary with the Vendor zone (Trusted external). The zone is what counts: a zone-only boundary needs no class. Never leave an outside party outside every boundary: it reads as internal.
Why: Zones decide who can read what the agent sends, and who can write what it reads.
Ask:
- Who can write content that reaches the agent? Anyone who can sign up counts as outside.
- Which elements does a vendor operate?
Classes: Network Segment, Container.
Zones: Untrusted (Open internet), Vendor (Trusted external), Internal.
What the analysis reads here:
- A model endpoint: never counted as a way out: Hosted LLM API, Self-Hosted LLM Inference.
4. Draw tools that reach any host
Draw every flow an agent's tools can open, outbound ones included. A tool that can reach any host (a web fetch, a shell, an image fetched while rendering) gets one flow to an "Arbitrary internet hosts" element in the Untrusted boundary, and the content it brings back as a Context Ingress.
Why: A tool that can reach any host is both a way out and a way in. Nothing infers it from an attribute: it counts only when it is drawn.
Ask:
- Which of the agent's tools can reach a host the attacker chooses?
- Can it reach internal addresses or the cloud metadata endpoint? If so, draw those flows too.
Classes: Untrusted Content Source, Agent Tool Call, HTTP/S Request, Context Ingress.
What the analysis reads here:
- A source whose content is untrusted by its class: Untrusted Content Source, Public AI Artifact Hub.
- A frontend that renders model output: Agent Chat Frontend.
5. State what the agent can do
Answer the element facts the drawing cannot show: what the agent's tools can do and whether they need approval, the harness approval mode, egress restrictions, and who can write to each store. A yes-or-no field offers Yes, No and Not answered; it starts at Not answered, and choosing Not answered takes an answer back.
Why: Whether an agent can act on what it reads, and whether anything stops it, is not visible in a drawing.
Ask:
- Can the agent's tools change anything outside the conversation: send, post, update, delete, pay?
- Does every such action need a person's approval?
- Can the agent run code?
- Is the agent's network egress limited to an allowlist?
- Are the agent's tool credentials limited to the user it acts for?
- Does the chat frontend render images or links in model output?
Looks safe is the answer a guess would pick. Write it only when someone who knows has said so.
| On | Field | Looks safe | What it decides |
|---|---|---|---|
| Agent Service | Consequential Tools Present (consequential_tools_present, under Human Oversight) | false | Whether the agent can act at all; whether it holds tools that act |
| Agent Service | All Consequential Action Paths Require Approval (consequential_tools_all_require_approval, under Human Oversight) | true | Whether its actions wait for a person |
| Agent Service | Approval Gate Present (approval_gate_present, under Human Oversight) | — | Whether its actions wait for a person |
| Agent Chat Frontend | All Consequential Actions Require Blocking In-Chat Approval (consequential_tools_all_require_approval, under Authorization & RBAC) | true | Whether its actions wait for a person |
| Untrusted Content Source | Human Approval for High-Risk Actions (human_approval_for_high_risk_actions, under Authorization & RBAC) | required | Whether its actions wait for a person |
| Agent Code Execution Sandbox | Consequential Computer-Use Actions Confirmed (computer_use_consequential_actions_confirmed, under Computer Use) | true | Whether its actions wait for a person |
| Agent Service | Code Execution Tools Present (code_execution_tools_present, under Application Security) | false | Whether it can run code; whether it holds tools that act |
| Local MCP Server, Remote MCP Server | Destructive Tools Exposed (destructive_tools_exposed, under Authorization & RBAC) | false | Whether it holds tools that act |
| Agent Service | Network Egress Default-Deny Allowlist (network_egress_default_deny_allowlist, under Network & Connectivity) | true | Whether a way out is closed |
| Agent Tool Call | Agent Egress Restricted (agent_egress_restricted, under Network & Connectivity) | true | Whether a way out is closed |
| Agent Harness | Network Egress Default-Deny Allowlist (network_egress_default_deny_allowlist, under Network & Connectivity) | true | Whether a way out is closed |
| Local MCP Server | Server Network Egress Restricted (server_network_egress_restricted, under Data Handling & Residency) | true | Whether a way out is closed |
| Agent Chat Frontend | Markdown Image Rendering Policy (markdown_image_rendering_policy, under Output Rendering Exfiltration) | disabled, allowlisted_trusted_domains_only | Whether a way out is closed |
| Agent Harness | Harness Approval Mode (harness_approval_mode, under Authorization & RBAC) | default, plan (on-request, granular: the agents' own approval answers decide) | How the harness approves actions |
| Agent Harness | All-Approvals-Off Launch Flags in Use (dangerously_skip_permissions_in_use, under Authorization & RBAC) | false | A switch that skips approval whatever the mode |
| Context Ingress | Human Approval for Actions After Untrusted Ingress (human_approval_for_actions_after_untrusted_ingress, under Authorization & RBAC) | true | Whether a path into an action waits for a person |
| Context Ingress | Untrusted Ingress Can Trigger Consequential Actions (untrusted_ingress_can_trigger_consequential_actions, under Authorization & RBAC) | false | Whether a path into an action waits for a person |
| Agent Tool Call | Human Approval for Sensitive Tool Calls (human_approval_for_sensitive_tool_calls, under Authorization & RBAC) | true | Whether a path into an action waits for a person |
| Agent Service | Tool Credentials Least Privilege in User Context (tool_credentials_least_privilege_user_context, under Authorization & RBAC) | true | Whether the agent's tool credentials are limited to the user it acts for |
| Vector Database | Authentication Required for Every Request (authentication_required, under Authentication & Access) | true | Who can write to a store the agent reads |
| Vector Database | Writes Restricted to Ingestion Pipeline (index_write_restricted_to_pipeline, under Data Integrity) | true | Who can write to a store the agent reads |
| Vector Database | API Not Publicly Reachable (not_publicly_reachable, under Network & Connectivity) | true | Who can write to a store the agent reads |
| Agent Memory Service | Memory Writes Gated From Untrusted Content (memory_write_gated_from_untrusted_content, under Memory Integrity) | true | Who can write to a store the agent reads |
| Agent Memory Service | Memory Endpoints Require Authentication (memory_endpoints_require_authentication, under Authentication & Access) | true | Who can write to a store the agent reads |
| Agent Credential | Credential Expiry Enforced (credential_expiry_enforced, under Secrets & Credentials) | true | How strong a credential is |
| Agent Credential | Key Rotation Enabled (key_rotation_enabled, under Secrets & Credentials) | true | How strong a credential is |
| Agent Credential | Resource Scope Restricted (resource_scope_restricted, under Authorization & RBAC) | true | How strong a credential is |
| Agent Credential | Permission Scope Minimized (permission_scope_minimized, under Authorization & RBAC) | true | How strong a credential is |
| Agent Tool Call | Handoff Permission Mode Not Widened (handoff_permission_mode_not_widened, under Authorization & RBAC) | true | Whether a hand-off widens the permissions |
| Agent Server | Server Secrets Readable by Tool Code (server_secrets_readable_by_tool_code, under Secrets & Credentials) | false | Whether tool code can read the server's secrets |
| Agent Server | Runtime API Authentication Enforced (api_authentication_enforced, under Authentication & Access) | true | How an outsider gets a first foothold |
| Langflow | Custom Components Allowed (allow_custom_components, under Code Execution) | false | How an outsider gets a first foothold |
| Langflow | Auto-Login Enabled (langflow_auto_login, under Authentication & Access) | false | How an outsider gets a first foothold |
| Local MCP Server | Launcher Package Version Pinned (launcher_package_version_pinned, under Supply Chain Integrity) | true | How an outsider gets a first foothold |
| Local MCP Server | Server Package Provenance Vetted (server_package_provenance_vetted, under Supply Chain Integrity) | true | How an outsider gets a first foothold |
| Local MCP Server | mcp-remote Bridge Used (mcp_remote_bridge_used, under Application Security) | false | How an outsider gets a first foothold |
| Local MCP Server | mcp-remote Version Patched (mcp_remote_version_patched, under Application Security) | true | How an outsider gets a first foothold |
| Harness approval mode | The agent it runs |
|---|---|
default | waits for approval |
plan | waits for approval |
acceptEdits | acts without approval |
auto | acts without approval |
dontAsk | acts without approval |
bypassPermissions | acts without approval |
never | acts without approval |
on-request | does not decide: also answer the agent's own approval facts |
granular | does not decide: also answer the agent's own approval facts |
6. Leave what you do not know open
Write a value only when someone who knows has stated it, or you found it in evidence you can point to. Leave every other fact unanswered, on purpose. This holds for both answers: guessing that a tool exists is as much a guess as guessing that it does not.
Why: An unanswered fact comes back as a question that names what would settle it. A guess makes the question go away without settling anything.
What a guess does:
| If you guess | What happens |
|---|---|
| Answers on components | |
consequential_tools_all_require_approval = true (Agent Service) | The agent's actions read as approved through its own answer, so injection to action is no longer raised through them, unless another answer says actions run unapproved: approval_gate_present = false on the agent, a chat frontend or a source that approves automatically, or a drawn path answered unapproved. With consequential tools answered false, the answer covers nothing the agent runs and does not count. |
consequential_tools_present = false (Agent Service) | With code execution also answered false, and no MCP server it calls answering destructive tools true, the agent reads as holding no tools that act: the question about them closes, and injection to action cannot be raised through its own tools. With code execution open, the question stays; with code execution true, or such a server, it is raised as before. The agent's own approval answer then counts only toward actions running unapproved. |
code_execution_tools_present = false (Agent Service) | The agent reads as running no code: with consequential tools also false, injection to action is no longer raised through its own tools. Whether it reads files stays open: the question moves to the harness that might read them for it. |
network_egress_default_deny_allowlist = true (Agent Service) | The agent's own undrawn ways out read as closed. A drawn flow from it that reaches an outsider still counts. |
harness_approval_mode = default or plan (Agent Harness) | Every agent the harness runs reads as waiting for approval, so injection to action is no longer raised for them through the harness, unless the agent's own approval answer, or dangerously_skip_permissions_in_use on the harness, says otherwise. |
markdown_image_rendering_policy = disabled or allowlisted_trusted_domains_only (Agent Chat Frontend) | Every undrawn way out through images rendered in the chat frontend reads as closed. A drawn flow from it still counts. |
| Answers on data items | |
| Sensitivity = Public (data item) | With Credential = No, the item no longer counts as private, and an element that handles only it no longer counts as a sensitive target or as something a credential unlocks. |
| Credential = No (data item) | The item is no longer treated as a credential: no credential chain starts from it. An item whose class is a credential class counts as one while the answer is open; another item counts only once it is answered Yes. |
| Origin = Authenticated first party (data item) | The item no longer counts as untrusted, unless the flow carrying it comes from an Untrusted zone or an untrusted source class. |
| Origin = Curated internal (data item) | The item counts as trusted, as for first party. For a knowledge base fed from outside, the knowledge route loses it, unless the ingestion is drawn: a pipeline's Knowledge Write carries the least trusted origin it ingests, whatever origin is stated. |
| Origin = Agent relayed (data item) | The item counts as trusted on the agent's own routes, so injection to action loses it as untrusted input. If the relaying agent and its hand-off are drawn, the relayed route still carries the relayer's untrusted input to the trifecta. |
| Origin = Model generated (data item) | Content the model wrote counts as trusted on the agent's own routes, even when it wrote it from untrusted content: the taint is laundered there. Read back from the agent's own memory it reads undetermined instead, and Facts to establish says second-order poisoning is not analysed. Through a store another agent reads, the writer's untrusted input still reaches the reader when the same item is drawn on the write and on the read. |
| Answers on paths | |
human_approval_for_sensitive_tool_calls = true (Agent Tool Call) | That path reads as approved. Injection to action is no longer raised only when every path into and out of the agent is approved, untrusted content reaches it no other way (a tool server, a knowledge item), and it runs no code off its drawn calls. |
human_approval_for_actions_after_untrusted_ingress = true, or untrusted_ingress_can_trigger_consequential_actions = false (Context Ingress) | As for an approved tool call, for the path the untrusted content comes in on. |
| Zones and assertions | |
| Assert outbound flows complete (on an element) | Every undrawn way out of that element reads as closed; its drawn flows are still followed. It is the user's answer to give, never a shortcut. |
| Zone = Internal for a boundary drawn Untrusted (Open internet) or Public (Internet-facing) | Content from it no longer counts as untrusted by zone, and what reaches it no longer counts as a way out: untrusted input and the trifecta's way out can both fall, and a public-facing foothold goes. |
| Zone = Internal for a boundary drawn Vendor (Trusted external) | What reaches the vendor reads as staying inside: the analysis stops asking whether an untrusted party can read it. |
7. Draw where each credential sits and what it opens
Draw each credential where it really sits: on the container or host that holds it, on every flow that presents it, and the store or service it opens at the end of that flow. A cloud role that a host's metadata endpoint serves sits on the host: draw the agent's container inside the host and the role on the host, with no arrow to the endpoint. Answer a credential's own facts (expiry, rotation, scope) and the host's metadata settings only as the team states them.
Why: A credential is a chain: an injected agent runs where a credential can be reached, takes it, and opens something with it. The analysis follows the chain through what you draw, and finds a metadata endpoint's role with no arrow drawn. A credential drawn in the wrong place, or not at all, hides the chain.
Ask:
- Which credentials sit where the agent runs: in its container, or on its host, served by the host's metadata endpoint?
- What does each credential open, and over which flow?
- Can the agent run code, or fetch an address it is given, where a credential can be reached?
- Is the host's metadata endpoint limited (IMDSv2, a hop limit), and does the agent's container share the host's PID, IPC or network namespace?
- Does the agent's approval answer still cover the tool it just gained?
Classes: Agent Credential, EC2 Instance, STS Temporary Credentials, Container.
What the analysis reads here:
- A cloud credential a host's metadata endpoint serves: STS Temporary Credentials, and 1 more in modules you may have installed.
Looks safe is the answer a guess would pick. Write it only when someone who knows has said so.
| On | Field | Looks safe | What it decides |
|---|---|---|---|
| STS Temporary Credentials | IMDS HTTP Endpoint Enabled (imds_http_endpoint_enabled, under Network & Connectivity) | false | Whether the host's metadata endpoint is cut off from the agent, or the credential is kept where it can read it |
| STS Temporary Credentials | IMDSv2 Required (HttpTokens=required) (imdsv2_required, under Authentication & Access) | true, and only together with imds_hop_limit = 1 | Whether the host's metadata endpoint is cut off from the agent, or the credential is kept where it can read it |
| STS Temporary Credentials | IMDS PUT Response Hop Limit (imds_hop_limit, under Network & Connectivity) | 1, and only together with imdsv2_required = true | Whether the host's metadata endpoint is cut off from the agent, or the credential is kept where it can read it |
| EC2 Instance, EKS Node Group | IMDSv2 Required (imdsv2_required, under Authentication & Access) | true, and only together with imds_hop_limit = 1 | Whether the host's metadata endpoint is cut off from the agent, or the credential is kept where it can read it |
| EC2 Instance, EKS Node Group | IMDS PUT Response Hop Limit (imds_hop_limit, under Authentication & Access) | 1, and only together with imdsv2_required = true | Whether the host's metadata endpoint is cut off from the agent, or the credential is kept where it can read it |
| STS Temporary Credentials | Credentials Materialized in Logs / Env (credentials_materialized_in_logs_or_env, under Secrets & Credentials) | false | Whether the host's metadata endpoint is cut off from the agent, or the credential is kept where it can read it |
| Container | Host PID/IPC/network namespaces not shared (container_host_namespaces_not_shared, under Host Isolation) | true | A container whose isolation from its host decides whether a hop limit cuts the metadata reach |
8. Draw how knowledge and memory get in
Draw an ingestion pipeline with what it reads, and its write into the knowledge base as a Knowledge Write carrying the items it writes. Draw what the agent remembers as a memory service it writes to and reads from, and set the origin of what it keeps to the least trusted origin of what it read when it wrote it, never Model generated. Answer who can write to each store only as the team states it.
Why: What the agent reads tomorrow can come from what an attacker wrote today. A Knowledge Write carries the least trusted origin its pipeline ingested, whatever the stored item is stated as; a generic flow carries nothing, and the stored item's stated origin decides alone.
Ask:
- What feeds the knowledge base, and through which pipeline?
- What does the agent keep in memory, and what did it write it from?
- Who can write to each store the agent reads?
Classes: RAG Ingestion Pipeline, Knowledge Write, Vector Database, Agent Memory Service, Knowledge Item.
What the analysis reads here:
- A write that carries what an ingestion pipeline took in: Knowledge Write.
9. Draw tools from others and agents handing work to agents
Draw a tool server another party runs (an MCP server) as its own element, in that party's boundary, with the facts its tools carry, and the agent's call to it as an Agent Tool Call. Draw a hand-off to another agent as an Agent Tool Call and what comes back as a Context Ingress. Draw every way the receiving agent takes content in, and state each item's origin from where it really comes from.
Why: A tool server acts for the agent and sends content back to it; a hand-off carries the first agent's untrusted input to the second. Neither shows unless it is drawn as what it is.
Ask:
- Which tool servers does the agent call, who runs them, and can their tools change anything?
- Does the agent hand work to another agent, and over which flow?
- What does the receiving agent read, and who writes it?
Classes: Remote MCP Server, Agent Tool Call, Context Ingress, Agent Service.
