Skip to content

fix(tools): check_require_confirmation fails closed on non-bool callable return - #7012

Open
boopathi-376 wants to merge 1 commit into
google:mainfrom
boopathi-376:fix/7010-confirmation-bool
Open

fix(tools): check_require_confirmation fails closed on non-bool callable return#7012
boopathi-376 wants to merge 1 commit into
google:mainfrom
boopathi-376:fix/7010-confirmation-bool

Conversation

@boopathi-376

Copy link
Copy Markdown

Link to Issue or Description of Change

Closes: #7010

Problem

FunctionTool.check_require_confirmation and McpTool.check_require_confirmation used cast(bool, ...) on the return value of a user-supplied require_confirmation callable. typing.cast is a static type-checker hint only — it performs no runtime coercion or validation.

As a result, a predicate that fell off the end of a branch without an explicit return (implicitly returning None) was passed straight through to if require_confirmation:, which evaluates None as falsy — silently letting the tool run without requesting confirmation, even though the caller had explicitly opted into the confirmation gate.

Solution

Replaced cast(bool, ...) with a real isinstance(result, bool) check in both FunctionTool.check_require_confirmation and McpTool.check_require_confirmation:

  • If the callable returns an actual bool, behavior is unchanged.
  • If it returns anything else (including None), the gate now fails closed and requires confirmation, rather than silently skipping it.

Behavior change (intentional): callers whose require_confirmation callable relies on a falsy non-bool return (e.g. None, 0, "") to mean "skip confirmation" will now get confirmation required instead. This is the security-relevant fix — a confirmation gate should fail closed on an ambiguous/unanswered predicate rather than open. Truthy non-bool returns (e.g. a string reason like "amount over limit") are unaffected — they already meant "confirm" and continue to.

Testing Plan

Unit Tests

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Added regression tests in both test_function_tool.py and test_mcp_tool.py covering:

  • callable returns None (implicit fallthrough) → now requires confirmation
  • callable returns a truthy non-bool string → still requires confirmation (no regression)
  • callable returns explicit bool False → still runs without confirmation (no regression)

pytest results:

tests/unittests/tools/test_function_tool.py: 41 passed, 3 warnings in 2.06s
tests/unittests/tools/mcp_tool/test_mcp_tool.py: 94 passed, 102 warnings in 9.24s

Manual E2E Tests

Not applicable — change is isolated to a runtime type-check in two check_require_confirmation methods, fully covered by the unit tests above.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

Credit to @mahirhir for the detailed repro and root-cause analysis in #7010.

…ble return

cast(bool, ...) is a static-only type hint and has no effect at runtime, so a require_confirmation callable that returns None was silently treated as falsy, letting the tool run without confirmation. Replace the cast with an isinstance(result, bool) runtime check that fails closed on any non-bool return. Fixes google#7010
@google-cla

google-cla Bot commented Sep 4, 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.

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.

require_confirmation callable returning a non-bool skips the confirmation gate: cast(bool, ...) is not a runtime check

2 participants