PromptFu
01 Foundations1.5Foundation14 min

Propose vs. authorize

Complete mediation, and why human approval comes after authorization, never instead.

LLM03:2026Propose-vs-authorize simulation

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?

Model output
{
  "tool": "approveExpense",
  "arguments": { "expenseId": "EXP-93823" }
}

Two readings. The architecture embodies one of them whether you chose it or not:

ReadingWhat it implies
An authorized commandThe decision has been made. The tool's job is to carry it out. Authority flows from the model.
A proposal from an untrusted componentA 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 LLM03:2026 · mitigation #7, Complete mediation

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

Trusts the model's arguments
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,
  });
}
Re-derives authority from trusted state
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
}
The model supplies one thing: which expense. Identity, permission, workflow position and state all come from sources the model cannot influence.

Bounds blast radius· survives adaptive attack

Complete mediation is the strongest control in this course. It works when the model is fully compromised, because it never asks the model anything. It is deterministic, so you can test it without a model in the loop, and it is auditable, which no prompt-based control can be.

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:

Course framing · derived from complete mediation
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  ──▶  audit

Put 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.
OWASP LLM03:2026 · mitigation #7

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.

TierExample actionEnforcement
ReversibleDraft a reply, tag a record, issue store creditAuto-approve, audit
Costly to reverseChange a supplier's bank details, alter a permissionBlock by default; escalate with justification
IrreversibleExternal payment, outbound email, delete production dataPer-action human approval on the exact rendered action
Tiers are ours; the audit/warn/block/escalate vocabulary and the refund example are OWASP's.

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.
OWASP LLM03:2026 · mitigation #5, Execute tools in user's context

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 / 5

Signed 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.

Synthetic world. The confirmation-dialog steps appear only under the human-approval option, because that control adds steps rather than removing them.

Check yourself

Self-check
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