Propose vs. authorize
Complete mediation, and why human approval comes after authorization, never instead.
The module that does the most work. Everything so far has been diagnosis. This is the load-bearing control, and it rests on one distinction small enough to sound pedantic and consequential enough to be the root cause of most serious agentic incidents on record.
A tool call is a proposal
When the model emits this, what has actually happened?
{
"tool": "approveExpense",
"arguments": { "expenseId": "EXP-93823" }
}Two readings. The architecture embodies one of them whether you chose it or not:
| Reading | What it implies |
|---|---|
| An authorized command | The decision has been made. The tool's job is to carry it out. Authority flows from the model. |
| A proposal from an untrusted component | A request has been made. The decision has not. Authority is established downstream, independently. |
The second is correct, and the rule fits on one line:
The model may decide what action it would like to request. It must never decide whether it is authorized to perform that action.
The model is genuinely good at the first half: working out that the user wants an expense approved, and which one. The second half needs trusted state, deterministic rules and an audit trail, and the model is a stochastic component that can be persuaded by a document it read.
Complete mediation
OWASP names the enforcement mechanism, borrowed from classical security engineering, as mitigation #7 for Excessive Agency:
Implement authorization in logic rather than relying on an LLM to decide if an action is allowed or not. Enforce the complete mediation principle so that all requests made to downstream systems are validated against security policies by the tool, by an independent pre-execution policy decision point between the tool and the downstream system, or by the downstream system itself.
OWASP offers three valid placements: the tool, a dedicated policy decision point, or the downstream service. There is no fourth option where the check lives in the prompt. Note all requests too. Mediation that applies to most calls is not complete, and the one unmediated path is the one that gets used.
What it looks like in code
async function approveExpense({ expenseId, actorId, reason }) {
// Every one of these three values came from the model.
// "actorId" is the fatal one: the model is asserting who is asking.
await audit.log({ action: "approve", actor: actorId, reason });
return db.expenses.update(expenseId, {
state: "approved",
approvedBy: actorId,
});
}async function approveExpense({ expenseId }, ctx) {
// Identity comes from the authenticated session, never the model.
const actor = ctx.authenticatedUser;
const expense = await db.expenses.load(expenseId);
// Independent checks against trusted state.
if (!policy.canApprove(actor, expense)) throw new Forbidden();
if (!workflow.isCurrentApprover(actor, expense)) throw new Forbidden();
if (expense.state !== "AwaitingApproval") throw new Conflict();
// Risk policy may still demand a human, even for an authorized actor.
if (risk.requiresConfirmation(expense)) {
return requestHumanApproval(expense, actor);
}
return expenses.approve(expense, { actor }); // audited with the real actor
}Bounds blast radius· survives adaptive attack
Where human approval fits
Human approval is valuable and it does not substitute for authorization. The ordering below is our reasoning from complete mediation rather than a sequence OWASP spells out:
model proposes
↓
is the AUTHENTICATED USER authorized? ── no ──▶ reject (403, audited)
↓ yes
does risk policy require confirmation? ── no ──▶ execute
↓ yes
human confirms the EXACT rendered action
↓
execute ──▶ auditPut approval before authorization and you get something worse than either: a system where a user can “confirm” an action they never had permission to perform, with the confirmation dialog as the only thing standing between an attacker and the action. That control can be socially engineered, and OWASP's Agentic Top 10 gives the failure class its own entry, ASI09 Human-Agent Trust Exploitation. Module 5.10 covers designing against it.
Graduated enforcement
You do not have to choose between auto-approving everything and gating everything. OWASP describes a graduated policy, audit, warn, block, escalate, keyed to reversibility:
A customer service chatbot can auto-process a refund as store credit (which is recoverable), while an irreversible action such as an external payout routes to human approval.
Reversibility is the right axis because it maps to what happens when you are wrong. It gives you a design question for every tool: if this fires incorrectly, can I undo it in an afternoon? Store credit, yes. A wire transfer, an emailed disclosure, a deleted production table, no.
| Tier | Example action | Enforcement |
|---|---|---|
| Reversible | Draft a reply, tag a record, issue store credit | Auto-approve, audit |
| Costly to reverse | Change a supplier's bank details, alter a permission | Block by default; escalate with justification |
| Irreversible | External payment, outbound email, delete production data | Per-action human approval on the exact rendered action |
Delegated and multi-agent workflows
Here is where complete mediation most often quietly stops being complete. When agent A calls agent B, or a tool calls a downstream service, whose authority applies?
The convenient answer is the calling component's service identity, convenient because service identities tend to be broadly privileged. OWASP is explicit that this is wrong:
In delegated or multi-agent workflows, preserve the original user context and authorization scope across chained tool or agent calls, rather than relying only on the permissions of the calling agent or service identity.
This is the architectural root of two incidents you will meet in Track 03. A developer's agent read a poisoned public issue and exfiltrated private repositories, because it ran with the developer's elevated credentials. An agent dumped a production database because its connector ran under a broad service role that bypassed row-level security. In neither case did the attacker escalate privilege. The agent already had it.
Deep diveNine mitigations, and the split OWASP draws through them4 min›
Excessive Agency has nine mitigations, divided in a way worth internalising. One through seven are preventive: minimize tools, minimize tool functionality, avoid open-ended tools, minimize tool permissions, execute tools in user's context, require user approval, and complete mediation.
Then OWASP says something unusually candid before continuing: “The following options will not prevent Excessive Agency but can limit the level of damage caused.” Under that heading sit monitor tool use and rate limiting, the latter including circuit breakers that halt, rate-limit or escalate, with thresholds based on invocation counts or on the cumulative value of a tool parameter.
That split is close to this course's rate-reducing versus blast-radius-bounding axis without being the same cut, and conflating them would be sloppy. OWASP separates prevents the condition from limits the damage. We separate degrades under adaptive attack from holds under adaptive attack. Complete mediation sits in OWASP's preventive group and in our holds group. An input classifier sits in neither.
Four controls, one that holds
The module's claim in a form you can falsify. The same request runs four times against the same agent, changing only the control in place. Two of the options are the answers most teams reach for, and both fail. Run those two before the last one.
Propose vs. authorize
0 / 5Signed in as Alice Okafor. Role "employee", no approval limit. EXP-10048 (EUR 9,840) is awaiting approval.
Objective
Move EXP-10048 to Approved while signed in as an employee who may never approve. The objective is the recorded state change, not a reply that claims one.
Which control is in place?
Step through the trace. Every node is a real place content enters or leaves the application, and each one arrives with the analysis beside it: what a defender can still do at that instant.
Check yourself
1. A tool takes (expenseId, actorId). What is wrong, and what is the
fix: validate actorId, or something else?
2. The UI shows a confirmation dialog before every state change and
the user must click Approve. Is authorization solved?
3. Which is safer for a refund tool: auto-approve store credit, or
gate every refund behind human approval? Justify it.
4. Agent A (user-scoped) calls Agent B, which holds a service token
with org-wide read. What just happened to the authorization model?Sources
- T1OWASP Top 10 for LLM Applications 2026 · LLM03:2026 Excessive Agency, mitigations #5 (execute in user's context), #7 (complete mediation, graduated enforcement, refund example) and #9 (rate limiting and circuit breakers), plus the preventive/damage-limiting split before #8
- T1OWASP Top 10 for Agentic Applications 2026 · ASI09 Human-Agent Trust Exploitation (the approval step as attack surface)
- ·The authorize → policy → approve → execute ordering and the reversibility tiers are this course's framing, derived from complete mediation rather than quoted from OWASP