Skip to content

fix(bigtable): stop resolving view parameters from caller-writable state - #7132

Open
prasanna8585 wants to merge 1 commit into
google:mainfrom
prasanna8585:fix/bigtable-view-parameter-state-fallback
Open

prasanna8585 wants to merge 1 commit into
google:mainfrom
prasanna8585:fix/bigtable-view-parameter-state-fallback

Conversation

@prasanna8585

@prasanna8585 prasanna8585 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

BigtableParameterizedViewTool wraps Bigtable's execute_sql to automatically inject values (like user_id) into a parameterized view's VIEW_PARAMETERS(), so the view itself can restrict which rows a query is allowed to see. The class's own docstring states this "securely restricts query execution to the logged-in user's data" -- but the resolution logic had an undocumented fallback: when a configured view_parameter_name did not match a strongly-typed attribute of tool_context (which user_id always does, via ReadonlyContext), it fell back to tool_context.state[param_name].

tool_context.state is writable by the caller -- the same fact this same PR's sibling fix (BigQuery/AgentEngineSandboxComputer, in query_tool.py and sandbox_computer.py) already established for a different pair of tools in this same file tree. A view parameter taken from state lets the caller choose which user's (or tenant's) rows a "securely restricted" parameterized view returns, defeating the row- level restriction the feature exists to provide. This is realistic for any application-defined scoping parameter beyond the documented user_id example -- tenant_id, org_id, customer_id, or any other custom name a multi-tenant deployment configures via view_parameter_names, none of which are built-in ToolContext/ReadonlyContext attributes.

Verified this was never previously addressed: the feature was introduced in 14a24f2 (PR #6128, "Support parameterized views with secure parameter injection") and has had no subsequent fix; the only later commit touching this file is an unrelated mypy-typing refactor.

Fix: remove the tool_context.state fallback entirely. Only names that resolve to a real tool_context attribute are honored now; anything else is silently omitted from view_parameters, exactly as an already- missing attribute was previously handled.

Verified: existing test suite (14 tests) run and pass, including the two pre-existing tests that only exercise the safe, strongly-typed path (user_id), which are unaffected. The test that asserted the vulnerable fallback behavior (test_bigtable_parameterized_view_tool_execution_session_state_fallback) is replaced with test_bigtable_parameterized_view_tool_ignores_state_for_custom_parameters, asserting the opposite: a caller-controlled tenant_id sitting in session state is not used. Confirmed this new test actually catches the bug by temporarily reverting the fix locally and re-running it -- it fails against the vulnerable code and passes against the fix. test_bigtable_parameterized_view_tool_execution_multiple_parameters is updated to match: tenant_id/agent_id (state-only) are now correctly omitted from the result, while user_id (a real attribute) still resolves.

BigtableParameterizedViewTool wraps Bigtable's execute_sql to
automatically inject values (like user_id) into a parameterized view's
VIEW_PARAMETERS(), so the view itself can restrict which rows a query
is allowed to see. The class's own docstring states this "securely
restricts query execution to the logged-in user's data" -- but the
resolution logic had an undocumented fallback: when a configured
view_parameter_name did not match a strongly-typed attribute of
tool_context (which user_id always does, via ReadonlyContext), it fell
back to tool_context.state[param_name].

tool_context.state is writable by the caller -- the same fact this
same PR's sibling fix (BigQuery/AgentEngineSandboxComputer, in
query_tool.py and sandbox_computer.py) already established for a
different pair of tools in this same file tree. A view parameter taken
from state lets the caller choose which user's (or tenant's) rows a
"securely restricted" parameterized view returns, defeating the row-
level restriction the feature exists to provide. This is realistic for
any application-defined scoping parameter beyond the documented
user_id example -- tenant_id, org_id, customer_id, or any other custom
name a multi-tenant deployment configures via view_parameter_names,
none of which are built-in ToolContext/ReadonlyContext attributes.

Verified this was never previously addressed: the feature was
introduced in 14a24f2 (PR google#6128, "Support parameterized views with
secure parameter injection") and has had no subsequent fix; the only
later commit touching this file is an unrelated mypy-typing refactor.

Fix: remove the tool_context.state fallback entirely. Only names that
resolve to a real tool_context attribute are honored now; anything
else is silently omitted from view_parameters, exactly as an already-
missing attribute was previously handled.

Verified: existing test suite (14 tests) run and pass, including the
two pre-existing tests that only exercise the safe, strongly-typed
path (user_id), which are unaffected. The test that asserted the
vulnerable fallback behavior
(test_bigtable_parameterized_view_tool_execution_session_state_fallback)
is replaced with
test_bigtable_parameterized_view_tool_ignores_state_for_custom_parameters,
asserting the opposite: a caller-controlled tenant_id sitting in
session state is not used. Confirmed this new test actually catches
the bug by temporarily reverting the fix locally and re-running it --
it fails against the vulnerable code and passes against the fix.
test_bigtable_parameterized_view_tool_execution_multiple_parameters is
updated to match: tenant_id/agent_id (state-only) are now correctly
omitted from the result, while user_id (a real attribute) still
resolves.
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.

2 participants