From 525caadd8c042e2f429d2ce0cad1babdc909c95f Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 21 Mar 2026 16:42:46 -0700 Subject: [PATCH] fix: prevent Anthropic token leaking to third-party anthropic_messages providers (salvage #2383) (#2389) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: prevent Anthropic token fallback leaking to third-party anthropic_messages providers When provider is minimax/alibaba/etc and MINIMAX_API_KEY is not set, the code fell back to resolve_anthropic_token() sending Anthropic OAuth credentials to third-party endpoints, causing 401 errors. Now only provider=="anthropic" triggers the fallback. Generalizes the Alibaba-specific guard from #1739 to all non-Anthropic providers. * fix: set provider='anthropic' in credential refresh tests Follow-up for cherry-picked PR #2383 — existing tests didn't set agent.provider, which the new guard requires to allow Anthropic token refresh. --------- Co-authored-by: 0xbyt4 <35742124+0xbyt4@users.noreply.github.com> --- run_agent.py | 17 +++++++++-------- tests/test_run_agent.py | 3 +++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/run_agent.py b/run_agent.py index 3f19def0a..ef5a92b83 100644 --- a/run_agent.py +++ b/run_agent.py @@ -681,10 +681,11 @@ class AIAgent: if self.api_mode == "anthropic_messages": from agent.anthropic_adapter import build_anthropic_client, resolve_anthropic_token - # Alibaba/DashScope use their own API key; do not fall back to ANTHROPIC_TOKEN (Fixes #1739 401). - _base = (base_url or "").lower() - _is_alibaba_dashscope = (self.provider == "alibaba") or ("dashscope" in _base) or ("aliyuncs" in _base) - effective_key = (api_key or "") if _is_alibaba_dashscope else (api_key or resolve_anthropic_token() or "") + # Only fall back to ANTHROPIC_TOKEN when the provider is actually Anthropic. + # Other anthropic_messages providers (MiniMax, Alibaba, etc.) must use their own API key. + # Falling back would send Anthropic credentials to third-party endpoints (Fixes #1739, #minimax-401). + _is_native_anthropic = self.provider == "anthropic" + effective_key = (api_key or resolve_anthropic_token() or "") if _is_native_anthropic else (api_key or "") self.api_key = effective_key self._anthropic_api_key = effective_key self._anthropic_base_url = base_url @@ -3340,9 +3341,9 @@ class AIAgent: def _try_refresh_anthropic_client_credentials(self) -> bool: if self.api_mode != "anthropic_messages" or not hasattr(self, "_anthropic_api_key"): return False - # Alibaba/DashScope use their own API key; do not refresh from ANTHROPIC_TOKEN (Fixes #1739 401). - _base = (getattr(self, "_anthropic_base_url", None) or "").lower() - if (self.provider == "alibaba") or ("dashscope" in _base) or ("aliyuncs" in _base): + # Only refresh credentials for the native Anthropic provider. + # Other anthropic_messages providers (MiniMax, Alibaba, etc.) use their own keys. + if self.provider != "anthropic": return False try: @@ -3768,7 +3769,7 @@ class AIAgent: if fb_api_mode == "anthropic_messages": # Build native Anthropic client instead of using OpenAI client from agent.anthropic_adapter import build_anthropic_client, resolve_anthropic_token, _is_oauth_token - effective_key = fb_client.api_key or resolve_anthropic_token() or "" + effective_key = (fb_client.api_key or resolve_anthropic_token() or "") if fb_provider == "anthropic" else (fb_client.api_key or "") self._anthropic_api_key = effective_key self._anthropic_base_url = getattr(fb_client, "base_url", None) self._anthropic_client = build_anthropic_client(effective_key, self._anthropic_base_url) diff --git a/tests/test_run_agent.py b/tests/test_run_agent.py index d5de62aa4..81e16b702 100644 --- a/tests/test_run_agent.py +++ b/tests/test_run_agent.py @@ -2413,6 +2413,7 @@ class TestAnthropicCredentialRefresh: agent._anthropic_client = old_client agent._anthropic_api_key = "sk-ant-oat01-stale-token" agent._anthropic_base_url = "https://api.anthropic.com" + agent.provider = "anthropic" with ( patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="sk-ant-oat01-fresh-token"), @@ -2908,6 +2909,7 @@ class TestOAuthFlagAfterCredentialRefresh: def test_oauth_flag_updates_api_key_to_oauth(self, agent): """Refreshing from API key to OAuth token must set flag to True.""" agent.api_mode = "anthropic_messages" + agent.provider = "anthropic" agent._anthropic_api_key = "sk-ant-api-old" agent._anthropic_client = MagicMock() agent._is_anthropic_oauth = False @@ -2926,6 +2928,7 @@ class TestOAuthFlagAfterCredentialRefresh: def test_oauth_flag_updates_oauth_to_api_key(self, agent): """Refreshing from OAuth to API key must set flag to False.""" agent.api_mode = "anthropic_messages" + agent.provider = "anthropic" agent._anthropic_api_key = "sk-ant-setup-old" agent._anthropic_client = MagicMock() agent._is_anthropic_oauth = True