fix: stop auth header prefix accumulating on repeated Custom Tool calls

When a Custom Tool uses API Key authentication with a Bearer or Basic
prefix, assembling_request mutated runtime.credentials in-place,
prepending the prefix to api_key_value on every call. Subsequent
invocations would read the already-prefixed value and prefix it again,
producing headers like "Bearer Bearer Bearer token".

Use a local variable instead of writing back to the shared credentials
dict, so the stored value is never modified.

Fixes #35974
This commit is contained in:
EvanYao826 2026-05-10 21:33:57 +08:00
parent b95e6f6a7a
commit d03d958d49
2 changed files with 33 additions and 5 deletions

View File

@ -100,16 +100,17 @@ class ApiTool(Tool):
elif not isinstance(credentials["api_key_value"], str):
raise ToolProviderCredentialValidationError("api_key_value must be a string")
api_key_value = credentials["api_key_value"]
if "api_key_header_prefix" in credentials:
api_key_header_prefix = credentials["api_key_header_prefix"]
if api_key_header_prefix == "basic" and credentials["api_key_value"]:
credentials["api_key_value"] = f"Basic {credentials['api_key_value']}"
elif api_key_header_prefix == "bearer" and credentials["api_key_value"]:
credentials["api_key_value"] = f"Bearer {credentials['api_key_value']}"
if api_key_header_prefix == "basic" and api_key_value:
api_key_value = f"Basic {api_key_value}"
elif api_key_header_prefix == "bearer" and api_key_value:
api_key_value = f"Bearer {api_key_value}"
elif api_key_header_prefix == "custom":
pass
headers[api_key_header] = credentials["api_key_value"]
headers[api_key_header] = api_key_value
elif credentials["auth_type"] == "api_key_query":
# For query parameter authentication, we don't add anything to headers

View File

@ -91,6 +91,33 @@ def test_assembling_request_auth_header_assembly():
assert tool.assembling_request(parameters={}) == {}
def test_assembling_request_auth_header_prefix_no_accumulation():
"""Regression test for #35974: calling assembling_request multiple times
must not accumulate Bearer/Basic prefixes on the stored credential."""
tool = _build_tool()
tool.runtime.credentials = {
"auth_type": "api_key_header",
"api_key_header_prefix": "bearer",
"api_key_value": "my-token",
}
# Call 3 times — header must always be "Bearer my-token", never accumulate
for _ in range(3):
headers = tool.assembling_request(parameters={})
assert headers["Authorization"] == "Bearer my-token"
# Verify the stored credential was NOT mutated
assert tool.runtime.credentials["api_key_value"] == "my-token"
# Same for "basic" prefix
tool.runtime.credentials["api_key_header_prefix"] = "basic"
tool.runtime.credentials["api_key_value"] = "abc"
for _ in range(3):
headers = tool.assembling_request(parameters={})
assert headers["Authorization"] == "Basic abc"
assert tool.runtime.credentials["api_key_value"] == "abc"
def test_assembling_request_runtime_auth_errors():
tool = _build_tool()