Skip to content

Delegation chains & deny-capable tool authorization (confused-deputy defense)#5

Merged
ZubeidHendricks merged 1 commit into
mainfrom
delegation-authz
Jun 17, 2026
Merged

Delegation chains & deny-capable tool authorization (confused-deputy defense)#5
ZubeidHendricks merged 1 commit into
mainfrom
delegation-authz

Conversation

@ZubeidHendricks

Copy link
Copy Markdown
Owner

What

A framework-level defense against the multi-agent confused deputy — an agent with legitimate authority tricked into using it for a request it shouldn't have (the failure mode in the Kagenti talk: a sub-agent reading patient records because a token rode along in the context).

Implements stages 1–2 of the design doc added here (docs/proposals/delegation-and-tool-authz.md). It's the framework half of the story: TOAD produces and propagates the delegation chain and offers a deny point at the tool boundary. Enforcement/signing/mTLS at the infra tier stays a platform's job (Kagenti/Istio) — complementary, not competing.

How — "secure the identity, not the path"

  • DelegationContext / Principal — the originating user (subject) plus the ordered chain of acting agents. Seeded once via RunOptions.delegation.
  • Propagation — flows into every tool via ToolRunContext.delegation, and extends by one hop through each asTool sub-agent (extendChain), so a sub-agent's own tool calls authorize against the full chain.
  • authorizeToolCall hook — deny-capable, sees { tool, input, delegation, agent }, runs at the tool-call seam before the tool executes. Return false to deny just that call (the model gets an error result and continues); throw AuthorizationError to abort the run. Config + per-call hooks AND-merge (both must allow).

The point: authorization is decided against the identity chain, which travels with the request — not a call path the agent loop picks at runtime and which RBAC-on-topology can't express for agents.

Why it matters (the headline test)

delegation.test.ts includes the confused-deputy scenario end-to-end: billing may read patient_records; it delegates to a verifier sub-agent that tries the same tool. Because the chain at the sub-agent is [billing, verifier] and the policy requires every actor to be permitted, the read is denied — even though verifier was reached through a legitimate caller. The orchestrator calling the tool directly ([billing]) is still allowed.

Surface (all additive, all opt-in)

New exports: DelegationContext, Principal, ToolCallRequest, extendChain, AuthorizationError. An agent that sets neither delegation nor authorizeToolCall behaves exactly as before.

Verified

  • 74 runtime tests pass (+12): propagation into leaf tools, asTool chain extension, allow/deny/throw, chain-wide policy across composition, AND-merge, and backward-compat (no chain ⇒ ctx.delegation undefined, tool runs).
  • typecheck + prettier clean; runtime builds. Compiler unaffected (177 pass).

Not in this PR (stages 3–4, proposed in the doc)

serveMcp boundary header (Toad-Delegation) for gateway enforcement; optional JWS signing; a declarative .agent allow: block compiled by toac.

🤖 Generated with Claude Code

A framework-level defense against the multi-agent confused deputy: an agent
tricked into using legitimate authority for a request it shouldn't have.
Implements stages 1–2 of docs/proposals/delegation-and-tool-authz.md.

- DelegationContext / Principal: the originating user + the chain of acting
  agents, seeded via RunOptions.delegation.
- Propagation: the chain flows into every tool via ToolRunContext.delegation
  and extends by one hop through each asTool sub-agent (extendChain).
- authorizeToolCall hook: deny-capable, sees the full chain, runs at the
  tool-call seam before the tool executes. `false` denies one call (the model
  gets an error result and continues); throwing AuthorizationError aborts the
  run. Config + per-call hooks AND-merge.

So a sub-agent can be barred from a resource regardless of a token it inherited
through the context — authorization is against the identity chain, not a call
path the agent loop picks at runtime. Fully opt-in, backward compatible.

New exports: DelegationContext, Principal, ToolCallRequest, extendChain,
AuthorizationError. 12 new tests (74 runtime total), incl. the confused-deputy
composition scenario.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ZubeidHendricks ZubeidHendricks merged commit eb97880 into main Jun 17, 2026
1 check passed
@ZubeidHendricks ZubeidHendricks deleted the delegation-authz branch June 17, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant