---
title: Source Authorization in LLM Agents
url: https://www.emergentmind.com/topics/source-authorization
type: topic
---

# Source Authorization in LLM Agents

Capability-gated tool access in LLM agents is an authorization problem, not merely a schema-validation problem. In tool-using systems that read untrusted content while holding side-effecting tools such as payments, email, CRM, and infrastructure APIs, the central claim of "Capability Gates Are Not Authorization: Confused-Deputy Failures in LLM Agent Frameworks" is that exposing a tool to the model does not answer whether a specific model-emitted invocation with concrete argument values is permitted. The paper argues that if a runtime accepts parsed, well-typed tool arguments as sufficient authority, then the model becomes the de facto authorization oracle, producing a classic confused-deputy failure mode; its audit of LangChain/LangGraph, LlamaIndex, and the Stripe Agent Toolkit finds capability gating by default but no deterministic fail-closed per-call value authorization gate by default [2606.28679].

## 1. Capability exposure and authorization are distinct controls

The paper distinguishes sharply between **capability gating** and **true source/value authorization**. Capability gating is a menu-level control: it decides whether the model can call a tool at all, whether a tool exists in the registry, and whether the argument schema type-checks. True authorization asks a different question: whether the exact call is allowed, whether the concrete argument values are permitted for the current principal or session, and whether policy allows the transaction amount, destination, account, URL, or intent [2606.28679].

This distinction is central because a schema can validate shape but not authority. A well-typed argument such as `account=acct_ATTACKER_999` remains unauthorized if the verified set permits only `acct_MERCHANT_001`. The paper therefore treats type correctness as orthogonal to authorization correctness. In this model, tool existence, name resolution, and argument parsing are necessary operational steps, but they are not authorization decisions.

A broader implication is that any runtime that treats model-emitted structured arguments as self-authenticating collapses the boundary between planning and permission. The paper’s terminology is explicit: **tool exposure is not authorization**. That formulation rejects the common assumption that a registered tool plus a valid JSON shape is enough to justify side effects.

## 2. Confused-deputy failures in tool-using agents

The paper frames the vulnerability as a classic **confused deputy**. The runtime or tooling is privileged; the model is an untrusted parser/planner over text; attacker-controlled content influences the model’s proposed tool call; and the runtime then executes that call using its own authority [2606.28679]. The deputy is therefore “confused” into exercising authority on behalf of attacker-controlled input rather than on behalf of a verified principal intent.

The failure surface is broad because the triggering condition is not malformed output but syntactically valid output with unauthorized semantics. The paper lists harmful but valid-looking actions such as payout to an attacker account, refund to an attacker destination, secret exfiltration, and SSRF or infrastructure calls. The danger arises specifically when untrusted content and side-effecting tools coexist in the same execution path.

This mechanism aligns with the broader open-world-agent literature on the **Authorization-Execution Gap (AEG)**, defined as the divergence between what a principal intends to authorize and what an open-world agent ultimately executes [2605.11003]. In that broader framing, unauthorized action execution, unauthorized disclosure, and persistent corruption are downstream manifestations of the same mismatch. The confused-deputy analysis in [2606.28679] can be read as a concrete runtime instance of channel-level corruption during execution, where environmental content becomes actionable without being treated as untrusted.

## 3. Cross-framework audit of default dispatch behavior

The audit asks a narrow, operational question: after the model emits a tool call, is there a deterministic fail-closed authorization check over the concrete arguments before side effect? Across pinned public-source commits and versions, the paper’s answer is negative for all three audited stacks by default [2606.28679].

| Stack | Default provided | Not provided by default |
|---|---|---|
| LangChain / LangGraph | Capability gating; schema validation | Deterministic fail-closed per-call authorization over concrete values |
| LlamaIndex | Tool-name existence; schema-shape checks | Deterministic per-call value authorization |
| Stripe Agent Toolkit | Static action allowlists; Stripe restricted API key scope | Per-transaction re-authorization of model-supplied values |

For **LangChain**, the audited path is a model output parser producing something like `AgentAction(tool, tool_input, ...)`, followed by a dispatcher resolving `name_to_tool_map[agent_action.tool]` and calling `tool.run(tool_input, ...)`. For **LangGraph**, `ToolNode._run_one` resolves `tools_by_name.get(call["name"])` and invokes the tool. The finding is that these paths provide capability gating and schema validation, but no default fail-closed per-call authorization over concrete values before execution. Callback hooks are characterized as observability rather than a mandatory veto, and human-in-the-loop middleware can be added but is not the default complete-mediation boundary [2606.28679].

For **LlamaIndex**, the central dispatch checks that the tool name exists in `tools_by_name` and then calls the selected tool with model-supplied keyword arguments. The paper again finds tool existence and schema-shape checks, but not deterministic per-call value authorization by default. Human-in-the-loop behavior occurs only if tool authors explicitly build it into workflow code [2606.28679].

For the **Stripe Agent Toolkit**, the case is treated as especially sensitive because it sits near money-moving operations. The toolkit exposes coarse controls such as static action allowlists and Stripe restricted API key scope, but the paper classifies these as capability-level controls rather than per-transaction authorization. The public toolkit forwards model-supplied values such as `amount`, `currency`, `customer`, `payment_intent`, `price`, `quantity`, and `redirect_url`, and therefore does not re-authorize each model-emitted money-related call with concrete arguments before execution [2606.28679].

## 4. ScopeGate and deterministic fail-closed mediation

As a reference control, the paper introduces **ScopeGate**, a deterministic **Policy Decision Point / Policy Enforcement Point** placed downstream of the model and upstream of side-effecting tools. It receives a proposed tool call `c=(name,args)`, trusted context $\Gamma$, and operator-authored policy $P$, and returns either `Allow` or `Deny`; no LLM participates in the decision [2606.28679].

The decision rule is written as:

$$
\text{Decide}(c,\Gamma,P)\in\{\text{Allow},\text{Deny}\}.
$$

Its five stages are:

1. **Scope**: if the tool name is not in policy tools, return `Deny(scope)`.
2. **Authorization**: for each allowed-argument constraint, if the value is not in the allowlist, return `Deny(authz)`.
3. **Money ceiling**: for money tools, let $a = args[r.amountField]$ and allow only if
   $$
   \text{finite}(a)\land 0 \le a \le \text{ceiling},
   $$
   otherwise return `Deny(money)`.
4. **Idempotency**: if a side-effecting tool needs idempotency and no trusted idempotency key is present, return `Deny(idempotency)`.
5. **Default deny**: only calls passing all checks are allowed; runtime errors return `Deny(error)`.

The paper emphasizes that ScopeGate **fails closed** on policy miss or runtime error. It also stresses that the money-ceiling check is an affirmative validity test, not merely a comparison: `NaN`, infinities, booleans-as-integers, and nonnumeric containers are denied [2606.28679].

The policy model is operator-authored and out-of-band. It includes tool scopes, allowlists, money ceilings, idempotency requirements, and verified sets such as $\{\texttt{acct\_MERCHANT\_001}\}$. The paper states that these policy elements must not be sourced from model output, directly or indirectly; policy data must come from a trusted root outside the model’s context window and write path. Its normative rule is correspondingly strict: no policy element may originate from any LLM output, policy must remain immutable during the conversation, and every access to authority must be mediated. This is the complete-mediation principle applied to tool calls [2606.28679].

## 5. Evaluation, attack suites, and measured outcomes

The evaluation has two aims: to show that the default dispatch gap is real and to test whether ScopeGate blocks unauthorized calls without over-blocking benign ones. The concrete LangChain proof of concept uses `update_payout_account` and `issue_refund` against a mocked merchant backend. A compromised model output proposes
$$
update\_payout\_account(account=\texttt{acct\_ATTACKER\_999}),
$$
and the result differs completely by enforcement path: unguarded framework dispatch executes the payout and records a side effect, whereas the ScopeGate-guarded path denies at `authz` because the attacker account is not in the verified set, with zero side effects recorded. The paper also includes a companion LlamaIndex proof of concept showing the same default dispatch gap [2606.28679].

The static bypass suite contains **48 adversarial vectors**, including case, whitespace, zero-width, homoglyph, null-byte mutations, type confusion, unhashable arguments, tool-name mutations, money edge cases, and missing idempotency. The reported result is **0/48 bypasses**. The paper notes that an internal prototype bug initially allowed `NaN` through a naive money comparison, but the current range check denies `NaN`; this was a prototype bug, not a framework bug [2606.28679].

The adaptive attacker experiment is a **40-iteration adaptive run** in which the attacker uses deny reasons to adapt. The reported results are **0/29 unauthorized attempts allowed**, **29 denied**, **9 malformed/parse errors**, and **2 benign calls allowed**. The paper explicitly characterizes this as bounded evidence rather than a universal proof. The benign-control set contains **10 benign controls** with **0/10 false-denies**. In the **Latam-GPT payment-agent containment** experiment, **10/10 unauthorized payout attempts** are denied; the model attempted unauthorized payout actions in all trials, and ScopeGate blocked them all. A broader containment vignette reports **11/11 harm-bearing egress actions** denied in the exercised scenarios [2606.28679].

The paper also reports a separate susceptibility sweep over model classes. Deployment-tier models show **mean attempted unauthorized-call rate 0.603**, while flagship models show **mean 0.189**, so deployment-tier models attempted unauthorized calls about **3.2× more often** in that sweep. The paper is careful to say that this is attempt rate, not breach rate, and that the tier grouping is an economics/deployment inference rather than a claim about provider production operations [2606.28679].

## 6. Limits, controversies, and relation to adjacent authorization frameworks

The paper is explicit about scope. **Containment is not a cure**: ScopeGate does not prevent prompt injection, does not detect poisoned weights, and does not repair model behavior; it only bounds what a compromised model can do through governed tools. The quantitative results are bounded by the written vectors and attacker budget; there is no universal bypass-impossibility proof. No live third-party service was attacked, no real payment or egress occurred, remote Stripe MCP internals were not audited, the findings concern public-source client and framework defaults and design postures, and **no CVE is asserted** [2606.28679].

These limitations place the work inside a broader shift in agent security toward execution-time authorization integrity. The AEG position paper argues that authorization integrity cannot be guaranteed by one-shot upfront filtering or only post-hoc audit, because critical divergences arise during execution and handoff; it therefore proposes checks such as **Authority Attribution Check**, **Scope Compliance Check**, and **Recomposition Authorization Check** during execution [2605.11003]. This suggests that the per-call mediation demanded in [2606.28679] is one instance of a wider architectural principle.

Two contemporaneous lines of work extend that principle along different axes. **Intent-Governed Access Control (IGAC)** treats the user’s expressed intent as a monotone, auditable policy attribute for agent tool use and enforces the invariant that user intent may only reduce authority, never expand it [2606.22916]. **AuthGraph** introduces a dual-graph defense that compares execution provenance against an authorization graph derived in a clean, isolated context, enabling parameter-source-level checks such as whether a value used in `book_flight.flight_id` originated from the authorized tool observation rather than from an injected hotel listing [2605.26497]. Relative to those systems, the contribution of [2606.28679] is narrower but precise: a framework-level demonstration that capability gates and schema checks do not constitute authorization, together with a deterministic fail-closed per-call mediation pattern built around scope, authorization, money ceiling, idempotency, and default deny.

The resulting bottom line is doctrinal rather than framework-specific. In tool-using LLM systems, once untrusted content can influence the model, the existence of a tool and the validity of its argument shape do not justify execution. Authorization must be re-applied to each concrete call, with concrete values, before side effects occur.

Source: https://www.emergentmind.com/topics/source-authorization