mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
test(provider): pin api.anthropic.com → anthropic_messages URL detection
Add a dedicated `TestDirectAnthropicHost` class to `test_detect_api_mode_for_url.py` covering the native Anthropic host shape (bare, trailing slash, /v1 suffix, uppercase host) plus the two negative-space regressions that matter for security: lookalike subdomains (`api.anthropic.com.attacker.test`) and path-segment spoofing (`https://proxy.example.test/api.anthropic.com/v1`) must NOT be classified as native — leaking an Anthropic OAuth token to either would be the worst case. Refs #32243.
This commit is contained in:
parent
a344c92050
commit
a2251b40eb
1 changed files with 53 additions and 5 deletions
|
|
@ -1,10 +1,15 @@
|
|||
"""Tests for hermes_cli.runtime_provider._detect_api_mode_for_url.
|
||||
|
||||
The helper maps base URLs to api_modes for three cases:
|
||||
* api.openai.com → codex_responses
|
||||
* api.x.ai → codex_responses
|
||||
* */anthropic → anthropic_messages (third-party gateways like MiniMax,
|
||||
Zhipu GLM, LiteLLM proxies)
|
||||
The helper maps base URLs to api_modes for four cases:
|
||||
* api.openai.com → codex_responses
|
||||
* api.x.ai → codex_responses
|
||||
* api.anthropic.com → anthropic_messages (Pro/Max OAuth is only billed
|
||||
against /v1/messages; the
|
||||
chat_completions shim counts
|
||||
against a separate empty
|
||||
"extra usage" pool, see #32243)
|
||||
* */anthropic → anthropic_messages (third-party gateways like MiniMax,
|
||||
Zhipu GLM, LiteLLM proxies)
|
||||
|
||||
Consolidating the /anthropic detection in this helper (instead of three
|
||||
inline ``endswith`` checks spread across _resolve_runtime_from_pool_entry,
|
||||
|
|
@ -38,6 +43,49 @@ class TestCodexResponsesDetection:
|
|||
assert _detect_api_mode_for_url("https://api.x.ai.example/v1") is None
|
||||
|
||||
|
||||
class TestDirectAnthropicHost:
|
||||
"""Native api.anthropic.com → /v1/messages. Pinned for issue #32243.
|
||||
|
||||
The Anthropic OpenAI-compat ``/chat/completions`` shim on the same
|
||||
host bills against a separate "extra usage" pool that Pro/Max OAuth
|
||||
subscriptions don't fund, so a fresh OAuth credential 400s with
|
||||
"out of extra usage" the moment a request lands there. The detector
|
||||
must keep ``api.anthropic.com`` on the native Messages API.
|
||||
"""
|
||||
|
||||
def test_bare_host(self):
|
||||
assert _detect_api_mode_for_url("https://api.anthropic.com") == "anthropic_messages"
|
||||
|
||||
def test_with_trailing_slash(self):
|
||||
assert _detect_api_mode_for_url("https://api.anthropic.com/") == "anthropic_messages"
|
||||
|
||||
def test_with_v1_suffix(self):
|
||||
# The Anthropic SDK appends /v1/messages itself but the user's
|
||||
# config may persist the /v1 form — must still resolve.
|
||||
assert _detect_api_mode_for_url("https://api.anthropic.com/v1") == "anthropic_messages"
|
||||
|
||||
def test_uppercase_host_tolerated(self):
|
||||
assert _detect_api_mode_for_url("https://API.ANTHROPIC.COM/v1") == "anthropic_messages"
|
||||
|
||||
def test_lookalike_subdomain_does_not_match(self):
|
||||
# ``api.anthropic.com.attacker.test`` is an attacker-controlled
|
||||
# host; the registrable label is ``attacker``, not Anthropic.
|
||||
# Must NOT be routed to anthropic_messages — leaking an
|
||||
# Anthropic OAuth token there is the worst case.
|
||||
assert (
|
||||
_detect_api_mode_for_url("https://api.anthropic.com.attacker.test/v1")
|
||||
is None
|
||||
)
|
||||
|
||||
def test_anthropic_path_segment_does_not_match(self):
|
||||
# A reverse proxy under an unrelated host whose path *contains*
|
||||
# ``api.anthropic.com`` should not be classified as native.
|
||||
assert (
|
||||
_detect_api_mode_for_url("https://proxy.example.test/api.anthropic.com/v1")
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
class TestAnthropicMessagesDetection:
|
||||
"""Third-party gateways that speak the Anthropic protocol under /anthropic."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue