mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
fix(api-server): reject a non-ASCII bearer token with 401 instead of crashing
_check_auth gates every OpenAI-compatible API server endpoint. It compared the client's raw bearer token against the configured key with hmac.compare_digest on two str values. compare_digest raises TypeError on a str containing non-ASCII characters, and the token comes straight from the Authorization header — so a request with a single non-ASCII byte in the key (a stray unicode char, a smart quote, a pasted BOM) crashed the handler with an unhandled TypeError. Every endpoint calls _check_auth without a try/except, so the framework turned that into a 500 Internal Server Error instead of the intended 401 Invalid API key. Compare as bytes, matching web_server.py's dashboard-token check (hmac.compare_digest(auth.encode(), expected.encode())). Encoding both sides keeps the timing-safe comparison and its semantics identical for valid keys while making a non-ASCII token fail closed with a clean 401. Adds regression tests: a non-ASCII bearer token returns 401 (no raise), and a non-ASCII configured key still authenticates against its exact value.
This commit is contained in:
parent
27b31bb7ff
commit
efb6c21498
2 changed files with 28 additions and 1 deletions
|
|
@ -1236,7 +1236,13 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||
auth_header = request.headers.get("Authorization", "")
|
||||
if auth_header.startswith("Bearer "):
|
||||
token = auth_header[7:].strip()
|
||||
if hmac.compare_digest(token, self._api_key):
|
||||
# Compare as bytes: ``hmac.compare_digest`` raises TypeError on a
|
||||
# str containing non-ASCII characters, and ``token`` is the raw
|
||||
# client-supplied header. A stray non-ASCII byte in the key would
|
||||
# otherwise crash this handler (500) instead of returning a clean
|
||||
# 401. Encoding both sides keeps the timing-safe comparison and
|
||||
# matches web_server.py's dashboard-token check.
|
||||
if hmac.compare_digest(token.encode(), self._api_key.encode()):
|
||||
return None # Auth OK
|
||||
|
||||
logger.warning(
|
||||
|
|
|
|||
|
|
@ -526,6 +526,27 @@ class TestAuth:
|
|||
assert result is not None
|
||||
assert result.status == 401
|
||||
|
||||
def test_non_ascii_bearer_token_returns_401_not_500(self):
|
||||
"""A non-ASCII byte in the bearer token must be rejected with 401, not
|
||||
crash the handler: hmac.compare_digest raises TypeError on a str with
|
||||
non-ASCII characters, and the token is raw client input."""
|
||||
config = PlatformConfig(enabled=True, extra={"key": "sk-test123"})
|
||||
adapter = APIServerAdapter(config)
|
||||
mock_request = MagicMock()
|
||||
mock_request.headers = {"Authorization": "Bearer ské-not-the-key"}
|
||||
result = adapter._check_auth(mock_request) # must not raise
|
||||
assert result is not None
|
||||
assert result.status == 401
|
||||
|
||||
def test_non_ascii_key_config_still_authenticates(self):
|
||||
"""A non-ASCII configured key must still match its exact value byte for
|
||||
byte (bytes comparison keeps this working)."""
|
||||
config = PlatformConfig(enabled=True, extra={"key": "sk-tést-kéy"})
|
||||
adapter = APIServerAdapter(config)
|
||||
mock_request = MagicMock()
|
||||
mock_request.headers = {"Authorization": "Bearer sk-tést-kéy"}
|
||||
assert adapter._check_auth(mock_request) is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Concurrency cap (gateway.api_server.max_concurrent_runs) — #7483
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue