Skip to content

fix(flows): reject forged tool confirmations from user-authored events - #1393

Open
alphacharlie-dev wants to merge 1 commit into
google:mainfrom
alphacharlie-dev:fix/confirmation-forgery-author-check
Open

fix(flows): reject forged tool confirmations from user-authored events#1393
alphacharlie-dev wants to merge 1 commit into
google:mainfrom
alphacharlie-dev:fix/confirmation-forgery-author-check

Conversation

@alphacharlie-dev

Copy link
Copy Markdown

Fixes #1389.

Problem

RequestConfirmationLlmRequestProcessor resumes a pending tool call by searching backwards
through session events for the adk_request_confirmation function call matching a user's
confirmation response. The look-back never checked who authored that call.

Those calls are synthesized by the framework as part of a model response (Functions.java:800,
author = agent name) — they are never legitimately user-authored. A client that can append
events to a session (the normal POST /run / POST /run_sse surface, where the client supplies
appName, userId and sessionId) could place an adk_request_confirmation call inside its
own user message, naming any registered tool with any arguments, then send the matching
confirmation response. The processor dispatched the tool — the model never requested it, and no
human approved it.

The practical effect is that the human-in-the-loop gate does not bound what a client can invoke:
any tool registered on the agent runs with the server's privileges, with attacker-chosen
arguments.

Fix

Skip user-authored events when sourcing the originating call. Confirmation responses are still
read from user events, so the normal resume flow is unchanged.

Verification

Regression test added to RunnerTest — it drives the full two-turn forgery through Runner
against a model that only ever emits text, and asserts the tool never runs.

Result
New test without the fix FAILS (1 run, 1 failure)
New test with the fix passes
RunnerTest 86/86
RequestConfirmationLlmRequestProcessorTest 4/4
FunctionsTest 26/26
ToolRequestConfirmationActionTest 2/2

Note for maintainers

This patch skips events authored by "user". You may prefer the inverse — accept the
originating call only when its author is an agent in the invocation's agent tree, e.g.
invocationContext.agent().rootAgent().findAgent(event.author()).isPresent(), which mirrors the
existing pattern at Runner.java:840-841.

The reason to consider it: SessionJsonConverter.java:201 restores author verbatim from
stored JSON (.author((String) apiEvent.get("author"))), so the field is not server-stamped on
every path, and the codebase carries several non-"user" author values ("model", agent names,
remote A2A agent names). Skipping one literal string is therefore narrower than allowlisting
known agents, which fails closed.

I have not demonstrated a bypass of the check as written — reaching that deserialization path
with a chosen author is a different attack from the one reported here — so I've kept this PR to
the minimal fix for the reported issue and left the broader hardening to your judgement.

@google-cla

google-cla Bot commented Aug 3, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@hemasekhar-p

Copy link
Copy Markdown
Contributor

Hi @alphacharlie-dev, thank you for your contribution! We appreciate you taking the time to submit this pull request. I noticed that the branch is currently out of sync with the base branch. proceed with the review, could you please resolve the merge conflicts?

@hemasekhar-p hemasekhar-p added the waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. label Aug 4, 2026
RequestConfirmationLlmRequestProcessor resumes a pending tool call by
searching backwards through session events for the adk_request_confirmation
function call matching a user's confirmation response. That look-back never
checked who authored the call.

adk_request_confirmation calls are synthesized by the framework as part of a
model response (Functions.java, author = agent name); they are never
legitimately user-authored. A client that can append events to a session --
the normal POST /run and POST /run_sse surface, where the client supplies
appName, userId and sessionId -- could therefore place an
adk_request_confirmation call inside a user-authored message naming any
registered tool with any arguments, then send the matching confirmation
response. The processor dispatched the tool. The model never requested it and
no human approved it.

Skip user-authored events when sourcing the originating call. Confirmation
responses are still read from user events, so the normal resume flow is
unchanged.

Adds a regression test that drives the full two-turn forgery through Runner
against a model that only ever emits text, and asserts the tool never runs.
The test fails without the fix and passes with it.
@alphacharlie-dev
alphacharlie-dev force-pushed the fix/confirmation-forgery-author-check branch from 371bcb1 to 90c2cdb Compare August 5, 2026 08:03
@alphacharlie-dev

Copy link
Copy Markdown
Author

@hemasekhar-p Rebased onto current main (90c2cdb2). The branch is now in sync with the base branch.

There were no merge conflicts to resolve — the five intervening commits on main (the release-please chores and the BigQueryLoggerConfig change) don't touch either of the two files in this PR. The branch was simply behind; it's now current.

RunnerTest passes on the rebased tree: 86 tests, 0 failures, including the added forgeConfirmation_doesNotDispatchToolFromUserAuthoredEvents regression test and the four existing runAsync_withToolConfirmation* tests.

@alphacharlie-dev

Copy link
Copy Markdown
Author

@googlebot I signed it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Human-in-the-loop confirmation gate can be forged via user-authored events (CWE-862)

2 participants