fix: redact credentials from generate_content_config.http_options in debug logs

Merge https://github.com/google/adk-python/pull/6546

PiperOrigin-RevId: 967026896
This commit is contained in:
prasanna8585
2026-08-19 00:15:28 -07:00
committed by Copybara-Service
parent ff4567df38
commit 1cd6f464e5
2 changed files with 57 additions and 7 deletions
+18 -4
View File
@@ -717,14 +717,28 @@ def _build_request_log(req: LlmRequest) -> str:
exclude={
'system_instruction': True,
'tools': tools_exclusion if req.config.tools else True,
# Callers may put credentials in per-request headers, so the
# transport options never go to the log.
'http_options': True,
# `http_options` carries caller-supplied credentials:
# `headers` commonly holds an Authorization bearer token,
# and `extra_body` / `*client_args` are free-form
# passthroughs that can hold auth material too. None of
# it may reach a debug log. Mirrors the same exclusion
# applied to trace spans in telemetry/tracing.py.
'http_options': {
'httpx_client': True,
'httpx_async_client': True,
'aiohttp_client': True,
'headers': True,
'extra_body': True,
'client_args': True,
'async_client_args': True,
},
},
)
)
except Exception:
config_log = repr(req.config.model_copy(update={'http_options': None}))
# Do not fall back to repr(req.config) here: an unredacted repr would
# reintroduce the same credential leak this function exists to avoid.
config_log = '<error building config log>'
return f"""
LLM Request:
+39 -3
View File
@@ -2563,7 +2563,7 @@ def test_build_request_log_function_declarations_in_second_tool():
def test_build_request_log_fallback_to_repr_on_all_failures(monkeypatch):
"""Test that _build_request_log falls back to repr() if model_dump fails."""
"""Test that _build_request_log falls back to placeholder if model_dump fails."""
llm_request = LlmRequest(
model="gemini-2.5-flash",
@@ -2584,9 +2584,45 @@ def test_build_request_log_fallback_to_repr_on_all_failures(monkeypatch):
log_output = _build_request_log(llm_request)
# Should still succeed using repr()
# Should still succeed using the placeholder
assert "Config:" in log_output
assert "GenerateContentConfig" in log_output
assert "<error building config log>" in log_output
def test_build_request_log_redacts_http_options_credentials():
"""Test that _build_request_log redacts sensitive fields in http_options."""
llm_request = LlmRequest(
model="gemini-2.5-flash",
contents=[Content(role="user", parts=[Part.from_text(text="Hello")])],
config=types.GenerateContentConfig(
temperature=0.7,
http_options=types.HttpOptions(
headers={"Authorization": "Bearer secret_token"},
extra_body={"secret_key": "some_secret"},
client_args={"token": "arg_secret"},
async_client_args={"token": "async_secret"},
base_url="https://example.com/api",
),
),
)
log_output = _build_request_log(llm_request)
assert "Config:" in log_output
# base_url should be present
assert "https://example.com/api" in log_output
# sensitive http_options fields should NOT be present in log_output
assert "secret_token" not in log_output
assert "secret_key" not in log_output
assert "arg_secret" not in log_output
assert "async_secret" not in log_output
assert "'headers'" not in log_output
assert "'extra_body'" not in log_output
assert "'client_args'" not in log_output
assert "'async_client_args'" not in log_output
assert "'httpx_client'" not in log_output
assert "'httpx_async_client'" not in log_output
assert "'aiohttp_client'" not in log_output
@pytest.mark.asyncio