Skip to content

Comments

fix(flows): reject malformed tool confirmation payloads fail-closed#4577

Open
davidahmann wants to merge 1 commit intogoogle:mainfrom
davidahmann:codex/issue-4576-confirmation-fail-closed
Open

fix(flows): reject malformed tool confirmation payloads fail-closed#4577
davidahmann wants to merge 1 commit intogoogle:mainfrom
davidahmann:codex/issue-4576-confirmation-fail-closed

Conversation

@davidahmann
Copy link

Problem

Malformed tool confirmation payloads can bypass robust validation paths and risk ambiguous behavior at tool-execution decision points.

Why now

Gap item #4 focuses on explicit safety controls and replay/audit-friendly contracts. Confirmation parsing must fail closed whenever payload shape/content is invalid.

What changed

  • Wrapped confirmation payload parsing with explicit malformed JSON / Pydantic validation handling.
  • On parse/validation error, default to confirmed=False and emit warning log, ensuring rejection path is deterministic.
  • Added regression test that sends malformed payload and verifies tool execution is rejected.

Validation

  • uv run pyink --check --diff src/google/adk/flows/llm_flows/request_confirmation.py tests/unittests/runners/test_run_tool_confirmation.py
  • uv run pytest tests/unittests/runners/test_run_tool_confirmation.py -k 'malformed_confirmation_payload_is_rejected_fail_closed or confirmation_flow' -q ✅ (5 passed, 3 deselected)
  • bash ./autoformat.sh ✅ (repo-required format pass; introduced unrelated changes under contributing/samples/gepa, intentionally excluded from this PR)

Refs #4576

@davidahmann
Copy link
Author

This PR hardens tool confirmation parsing to fail closed: malformed JSON or schema-invalid payloads now default to confirmed=False and return deterministic rejection. Added a focused unit test that verifies malformed confirmation payloads cannot proceed to tool execution.

This contribution was informed by patterns from Gait: https://github.com/Clyra-AI/gait

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @davidahmann, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the robustness of tool confirmation flows by introducing comprehensive error handling for malformed payloads. The change ensures that any invalid or unparseable confirmation data will result in a 'fail-closed' state, preventing ambiguous behavior and reinforcing explicit safety controls, aligning with the goal of creating replay/audit-friendly contracts.

Highlights

  • Robust Error Handling: Implemented explicit error handling for malformed JSON and Pydantic validation errors during tool confirmation payload parsing.
  • Fail-Closed Mechanism: Ensured that upon parsing or validation errors, the tool confirmation defaults to confirmed=False, leading to a deterministic rejection path.
  • Regression Test: Added a new test case to validate that malformed confirmation payloads are correctly rejected, confirming the fail-closed behavior.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/google/adk/flows/llm_flows/request_confirmation.py
    • Imported ValidationError from pydantic to enable specific error catching.
    • Wrapped tool confirmation payload parsing in try-except blocks to catch json.JSONDecodeError, TypeError, and ValidationError.
    • Added logging for warnings when malformed tool confirmation payloads are encountered, including the function_response_id and the parsing error.
    • Set tool_confirmation to ToolConfirmation(confirmed=False) when parsing or validation errors occur, ensuring a fail-closed default.
  • tests/unittests/runners/test_run_tool_confirmation.py
    • Added a new asynchronous test test_malformed_confirmation_payload_is_rejected_fail_closed.
    • The new test simulates a malformed JSON payload in a function response and asserts that the tool call is rejected with an appropriate error message.
Activity
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@adk-bot adk-bot added the core [Component] This issue is related to the core interface and implementation label Feb 21, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request effectively addresses the problem of malformed tool confirmation payloads by implementing robust error handling and ensuring a fail-closed mechanism. The addition of ValidationError and json.JSONDecodeError handling, along with a regression test, significantly improves the reliability and security of the confirmation parsing. The changes are well-structured and align with the stated objective.

Comment on lines +85 to +100
try:
tool_confirmation = ToolConfirmation.model_validate(
json.loads(function_response.response['response'])
)
except (
json.JSONDecodeError,
TypeError,
ValidationError,
) as parse_err:
logger.warning(
'Malformed tool confirmation payload for'
' function_response_id=%s: %s',
function_response.id,
parse_err,
)
tool_confirmation = ToolConfirmation(confirmed=False)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This try-except block correctly catches potential json.JSONDecodeError, TypeError, and ValidationError during the parsing of the tool confirmation payload. In case of an error, it logs a warning and defaults tool_confirmation to ToolConfirmation(confirmed=False), ensuring a fail-closed behavior as intended. This is a critical improvement for handling malformed inputs.

from typing import TYPE_CHECKING

from google.genai import types
from pydantic import ValidationError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ValidationError import is correctly added to handle Pydantic validation errors, which is crucial for robust parsing of tool confirmation payloads.

Comment on lines +102 to +113
try:
tool_confirmation = ToolConfirmation.model_validate(
function_response.response
)
except ValidationError as parse_err:
logger.warning(
'Malformed tool confirmation payload for'
' function_response_id=%s: %s',
function_response.id,
parse_err,
)
tool_confirmation = ToolConfirmation(confirmed=False)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similarly, this try-except block handles ValidationError when the function_response.response is directly validated. This ensures that even if the payload is not a nested JSON string, Pydantic validation errors are caught, a warning is logged, and the confirmation defaults to confirmed=False.

Comment on lines +227 to +256
@pytest.mark.asyncio
async def test_malformed_confirmation_payload_is_rejected_fail_closed(
self,
runner: testing_utils.InMemoryRunner,
agent: LlmAgent,
):
"""Malformed confirmation payloads must fail closed and reject tool calls."""
initial_events = await runner.run_async(testing_utils.UserContent("test"))
ask_for_confirmation_function_call_id = (
initial_events[1].content.parts[0].function_call.id
)

malformed_confirmation = testing_utils.UserContent(
Part(
function_response=FunctionResponse(
id=ask_for_confirmation_function_call_id,
name=REQUEST_CONFIRMATION_FUNCTION_CALL_NAME,
response={"response": "{not-json"},
)
)
)

events = await runner.run_async(malformed_confirmation)
simplified = testing_utils.simplify_events(copy.deepcopy(events))
assert simplified[0][1] == Part(
function_response=FunctionResponse(
name=agent.tools[0].name,
response={"error": "This tool call is rejected."},
)
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This new test case effectively verifies the fail-closed behavior when a malformed JSON payload is provided for tool confirmation. It correctly asserts that the tool execution is rejected, which is a good regression test for the implemented fix.

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

Labels

core [Component] This issue is related to the core interface and implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants