From d03d958d4995b59ef96ed176547ddafad569ec9f Mon Sep 17 00:00:00 2001 From: EvanYao826 <2869018789@qq.com> Date: Sun, 10 May 2026 21:33:57 +0800 Subject: [PATCH 1/2] 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 --- api/core/tools/custom_tool/tool.py | 11 ++++---- .../unit_tests/core/tools/test_custom_tool.py | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/api/core/tools/custom_tool/tool.py b/api/core/tools/custom_tool/tool.py index 168e5f4493a..1ad40ca3835 100644 --- a/api/core/tools/custom_tool/tool.py +++ b/api/core/tools/custom_tool/tool.py @@ -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 diff --git a/api/tests/unit_tests/core/tools/test_custom_tool.py b/api/tests/unit_tests/core/tools/test_custom_tool.py index f525baeaf23..c6df7ab34eb 100644 --- a/api/tests/unit_tests/core/tools/test_custom_tool.py +++ b/api/tests/unit_tests/core/tools/test_custom_tool.py @@ -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() From a574c22ccdd0734fdc2d4e6fe9d518ec241a24cb Mon Sep 17 00:00:00 2001 From: EvanYao826 <2869018789@qq.com> Date: Fri, 15 May 2026 14:21:33 +0800 Subject: [PATCH 2/2] ci: retrigger E2E tests