The OWASP Top 10 for LLM Applications describes what goes wrong when a model reads and writes text. Agents add something more dangerous: they act. They call APIs, run code, remember things across sessions and hand work to other agents. On 9 December 2025 the OWASP GenAI Security Project published a separate list for that setting, the OWASP Top 10 for Agentic Applications for 2026, with entries numbered ASI01 to ASI10.
A list of risks does not secure anything. This article turns it into an assessment you can run against a real agent: a threat model that places each entry on the architecture, a control, a test and an evidence artifact for every entry, a gateway that enforces several controls in one place, and a worked assessment of a customer-support agent. Three entries already have their own deep dives on this site, so for those the article links out rather than repeating them.
The ten entries at a glance
Here are the ten entries, with titles lightly shortened (ampersands written as and) and a one-line reading of each. The control column names the control that does most of the work; real coverage needs more than one.
| ID | Risk | What goes wrong | Primary control |
|---|---|---|---|
| ASI01 | Agent Goal Hijack | Text the agent reads redirects its objective | Treat ingested content as data; gate actions |
| ASI02 | Tool Misuse and Exploitation | Permitted tools used harmfully, chained or in bulk | Per-call policy, rate and spend limits |
| ASI03 | Identity and Privilege Abuse | Agent acts with more authority than the user has | Delegated, scoped, short-lived credentials |
| ASI04 | Agentic Supply Chain Vulnerabilities | A trusted tool, model, prompt or server is malicious | Pin, verify and allowlist components |
| ASI05 | Unexpected Code Execution | Generated or triggered code runs unsandboxed | Isolated sandbox, no ambient credentials |
| ASI06 | Memory and Context Poisoning | Injected memory steers later sessions | Provenance on writes, scoped stores |
| ASI07 | Insecure Inter-Agent Communication | Agent messages forged, replayed or altered | Authenticated, signed messages |
| ASI08 | Cascading Failures | One fault spreads through tools and agents | Budgets, timeouts, circuit breakers |
| ASI09 | Human-Agent Trust Exploitation | People approve confident output unchecked | Approvals that show evidence and diffs |
| ASI10 | Rogue Agents | Agent drifts or colludes outside its mandate | Behavioural monitoring and a kill switch |
The deep dives cover the first-ranked entries: agent hijacking for ASI01, tool abuse for ASI02 and memory poisoning for ASI06. For how the agentic list relates to the LLM list, see the OWASP LLM Top 10 assessment guide.
Threat-model the agent first
Start the assessment with a data-flow diagram of the agent, not with the list. Draw every input the model can read, every place it can write, every tool and every other agent it talks to, and mark which inputs an outsider can influence. Then place the entries on the diagram. Most land on edges, not boxes: goal hijack enters on the untrusted-content edge, tool misuse and privilege abuse leave on the tool edge, poisoning goes in and out of the memory edge, and cascading failure is a property of the whole path.
Two questions decide most of the risk. First, can untrusted content reach the model in the same context where it can call a high-impact tool? If yes, assume ASI01 will succeed eventually, because no prompt-level defence is reliable, and design so a hijacked agent still cannot do serious harm. Second, whose authority does each tool call carry? An agent that holds one broad service credential turns every successful hijack into a privilege escalation, the confused-deputy problem described in agent authorization.
Controls that cover several entries
Ten risks do not need ten products. A small number of architectural controls cover most entries, and placing them on the action path means they hold even when the model is fooled.
- A tool-call gateway. Every call passes a policy check: is this agent allowed this tool, with these arguments, for this user, at this volume? It covers ASI02 and ASI03, contains ASI01 and ASI10, and is the natural place for ASI08 budgets.
- Delegated identity. The agent calls tools with a token scoped to the user it serves and the task, not with a service account. This is the main ASI03 control.
- Taint tracking. Mark a task as tainted once untrusted content enters its context. Tainted tasks lose high-impact tools or need human approval.
- Isolation. Run generated code in a sandbox with no network by default and no credentials, as in sandboxing agents with Docker. This is ASI05.
- Provenance and signing. Record the source of every memory write, and authenticate agent-to-agent messages with per-agent keys. This covers ASI06 and ASI07.
- Supply-chain hygiene. Pin versions and hashes of MCP servers, plugins and prompt templates, and review tool descriptions as code, since they are instructions the model follows. This is ASI04.
A tool-call gateway
The sketch below shows the shape of such a gateway. It is deliberately small: scope check, call and spend budgets, a taint-aware approval step for high-impact tools, an append-only audit record of every decision, and execution with the user's token rather than the agent's.
import json, time
from dataclasses import dataclass
@dataclass
class Call:
task_id: str
agent_id: str
user_id: str # the human the agent acts for
tool: str
args: dict
tainted: bool # untrusted content entered this task's context
class ToolGateway:
def __init__(self, scopes, high_impact, tools, approver, audit,
max_calls=30, max_spend=50.0):
self.scopes, self.high_impact = scopes, high_impact
self.tools, self.approver, self.audit = tools, approver, audit
self.max_calls, self.max_spend = max_calls, max_spend
self.calls, self.spend = {}, {}
def decide(self, call):
if call.tool not in self.scopes.get(call.agent_id, set()):
return "deny: tool not in agent scope" # ASI02/ASI03
if self.calls.get(call.task_id, 0) >= self.max_calls:
return "deny: call budget exhausted" # ASI08
cost = float(call.args.get("amount", 0))
if self.spend.get(call.task_id, 0.0) + cost > self.max_spend:
return "deny: spend budget exhausted" # ASI02/ASI08
if call.tool in self.high_impact and call.tainted:
return "approve" if self.approver(call) else "deny: approval refused" # ASI01/ASI09
return "allow"
def invoke(self, call, user_token):
verdict = self.decide(call)
self.audit.write(json.dumps({"ts": time.time(), "task": call.task_id,
"agent": call.agent_id, "user": call.user_id, "tool": call.tool,
"args": call.args, "tainted": call.tainted, "verdict": verdict}) + "\n")
if verdict not in ("allow", "approve"):
raise PermissionError(verdict)
self.calls[call.task_id] = self.calls.get(call.task_id, 0) + 1
self.spend[call.task_id] = self.spend.get(call.task_id, 0.0) + float(call.args.get("amount", 0))
return self.tools[call.tool](user_token, **call.args) # user-scoped tokenTwo details matter. The approver must show the human what the call will do and why it is being asked, including the untrusted source that tainted the task; a bare yes or no prompt trains people to click yes, which is ASI09 in action. And the audit log is evidence for the assessment: you can count denials, approvals and budget hits per agent, and the same records drive ASI10 monitoring.
Turning the list into tests
Each entry becomes at least one test with a pass condition and an artifact you can show an auditor. Run the tests against the deployed system, with its real tools pointed at staging, not against the model in a notebook.
| ID | Test | Pass evidence |
|---|---|---|
| ASI01 | Plant instructions in a document the agent will read | No unapproved action; gateway log shows taint |
| ASI02 | Ask for 500 refunds of 1 dollar | Budget denial in the log |
| ASI03 | User A asks about user B's account | Downstream API returns 403 under A's token |
| ASI04 | Swap an MCP server binary for an unsigned one | Agent refuses to load it |
| ASI05 | Get generated code to read environment variables or call out | Sandbox has none and no egress |
| ASI06 | Write a fake policy into memory, start a new session | Write tagged untrusted, not retrieved as policy |
| ASI07 | Replay or alter a message between agents | Signature or nonce check rejects it |
| ASI08 | Make one tool time out or return errors | Breaker opens, task fails cleanly |
| ASI09 | Review the approval screen for a risky call | Shows source, arguments and impact |
| ASI10 | Kill an agent mid-task | Stops within the target time, no orphaned work |
Worked assessment: a support agent
Consider a support agent that reads inbound tickets, searches a knowledge base, issues refunds up to 200 dollars, emails customers and asks a billing sub-agent for invoice details. Its memory stores summaries of past conversations per customer. Here is how an assessment of this illustrative system would run.
The threat model shows the ticket body is attacker-controlled and reaches the same context as the refund and email tools. That is the central finding: an ASI01 payload in a ticket can try to issue a refund to a different customer or email internal data out. The test confirms that before remediation the agent attempts both. The fix is not a better system prompt. Tickets mark the task tainted, refunds on tainted tasks need approval, the email tool only sends to the address on the ticket's own account, and refunds run under a token scoped to that customer, which also closes the cross-account ASI03 test.
The billing sub-agent accepts any JSON message on an internal queue, so a compromised component could ask it for any invoice: an ASI07 finding, fixed with per-agent signing keys and an allowlist of message types. Memory summaries are written from ticket text, so a ticket saying the customer is pre-approved for unlimited refunds could persist; the fix is tagging summaries as untrusted and never retrieving them as policy. The code sandbox and supply chain entries are judged not applicable because the agent runs no code and loads no external tools; the assessment records that decision and its reason, so the next review revisits it if the design changes.
The output is a table of ten rows, each with a status, the test that produced it, the evidence and an owner. Here four rows were findings, now fixed and retested (ASI01, ASI03, ASI06, ASI07), two were not applicable with reasons (ASI04, ASI05), and the remaining four (ASI02, ASI08, ASI09, ASI10) passed their tests. Re-running the same tests after each release is what makes it an assessment programme instead of a one-off review.
How assessments go wrong
- Testing the model, not the system. A model that refuses a jailbreak in a playground proves nothing about the deployed agent with tools.
- Checkbox coverage. Marking an entry covered because a guardrail product exists, without a test that exercises it.
- Prompt-only fixes. Instructions such as ignore commands in documents reduce attempts but do not stop a determined payload; controls must sit outside the model.
- Over-broad approvals. Asking a human about every call produces fatigue and blind approval, which converts ASI01 into ASI09.
- Ignoring the not-applicable rows. They become applicable silently when someone adds a code tool or a new MCP server.
- Tests that never fail. If a planted injection payload has never once produced an attempted action in your test logs, the payload is too weak or the test is not reaching the model. Keep one known-bad configuration that must fail, as a control for the test itself.
- No owner. Findings that sit with the security team while the agent team ships weekly go stale within one release. Every row needs a named engineering owner.
Once the assessment exists, run it continuously. The gateway's audit log gives you the operational signals: denials per agent per day, approval rate and median time to approve, budget exhaustion events, and calls made by tainted tasks. A sudden rise in denials after a release usually means a prompt or tool description changed; a sudden fall in approval time usually means people have stopped reading. Alert on both, and review a sample of approved high-impact calls every week.
Trade-offs
Strong controls cost autonomy. Taint-based approvals slow the agent on exactly the inputs that make it useful, such as customer email. Tight budgets cause false denials on large legitimate tasks. Per-user tokens require identity plumbing many tool APIs do not support, and signing between agents adds key management. The practical balance is to rank tools by impact and reversibility, apply the strictest controls to the irreversible ones, such as payments, deletes and external messages, and let read-only tools run freely. The list itself is young; expect wording and ordering to change in later editions, so map your tests to the risks rather than hard-coding the numbers.
What to do next
- Draw the data-flow diagram of your agent and mark every attacker-influenced input.
- Place ASI01 to ASI10 on the diagram and note which are not applicable, with a reason.
- Put a tool-call gateway in front of every tool and log every decision.
- Replace service credentials with user-scoped, task-scoped tokens.
- Add taint tracking and require evidence-rich approval for high-impact calls on tainted tasks.
- Write the ten tests above against staging and run them on every release.
- Sign agent-to-agent messages and pin every external tool and server.
- Set call, spend and time budgets, and test the kill switch.