mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(security): cover remaining catalog credential paths
This commit is contained in:
parent
27a1042b13
commit
cf34a1e8c7
3 changed files with 48 additions and 6 deletions
|
|
@ -18,14 +18,13 @@ def url_origin(url: str) -> tuple[str, str, int | None]:
|
|||
"""Return a normalized (scheme, hostname, effective port) origin."""
|
||||
parsed = urllib.parse.urlparse(url)
|
||||
scheme = (parsed.scheme or "").lower()
|
||||
try:
|
||||
port = parsed.port
|
||||
except ValueError:
|
||||
port = None
|
||||
# Accessing ``parsed.port`` validates malformed/non-numeric ports. Let the
|
||||
# ValueError fail the request closed instead of collapsing it to a default.
|
||||
port = parsed.port
|
||||
return (
|
||||
scheme,
|
||||
(parsed.hostname or "").lower().rstrip("."),
|
||||
port or _DEFAULT_PORTS.get(scheme),
|
||||
port if port is not None else _DEFAULT_PORTS.get(scheme),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import json
|
|||
import logging
|
||||
import urllib.request
|
||||
|
||||
from hermes_cli.urllib_security import open_credentialed_url
|
||||
from providers import register_provider
|
||||
from providers.base import ProviderProfile
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ class AnthropicProfile(ProviderProfile):
|
|||
req.add_header("x-api-key", api_key)
|
||||
req.add_header("anthropic-version", "2023-06-01")
|
||||
req.add_header("Accept", "application/json")
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
with open_credentialed_url(req, timeout=timeout) as resp:
|
||||
data = json.loads(resp.read().decode())
|
||||
return [
|
||||
m["id"]
|
||||
|
|
|
|||
|
|
@ -98,6 +98,13 @@ def test_url_origin_normalizes_default_ports_and_trailing_dot():
|
|||
443,
|
||||
)
|
||||
assert url_origin("http://example.test") != url_origin("https://example.test")
|
||||
assert url_origin("https://example.test:0") == (
|
||||
"https",
|
||||
"example.test",
|
||||
0,
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
url_origin("https://example.test:not-a-port")
|
||||
|
||||
|
||||
def test_cross_host_redirect_drops_arbitrary_credentials_on_wire():
|
||||
|
|
@ -337,6 +344,41 @@ class _LmStudioSourceHandler(BaseHTTPRequestHandler):
|
|||
pass
|
||||
|
||||
|
||||
def test_anthropic_profile_drops_x_api_key_on_redirect(monkeypatch):
|
||||
import importlib
|
||||
|
||||
AnthropicProfile = importlib.import_module(
|
||||
"plugins.model-providers.anthropic"
|
||||
).AnthropicProfile
|
||||
|
||||
source = _server()
|
||||
sink = _server()
|
||||
_RecordingHandler.requests = []
|
||||
_RecordingHandler.redirect_status = 302
|
||||
_RecordingHandler.redirect_to = f"http://localhost:{sink.server_port}/sink"
|
||||
|
||||
original_request = urllib.request.Request
|
||||
|
||||
def local_anthropic_request(url, *args, **kwargs):
|
||||
if url == "https://api.anthropic.com/v1/models":
|
||||
url = f"http://127.0.0.1:{source.server_port}/redirect"
|
||||
return original_request(url, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(urllib.request, "Request", local_anthropic_request)
|
||||
try:
|
||||
result = AnthropicProfile(name="anthropic").fetch_models(
|
||||
api_key="anthropic-secret", timeout=3
|
||||
)
|
||||
finally:
|
||||
source.shutdown()
|
||||
sink.shutdown()
|
||||
|
||||
assert result == []
|
||||
_, headers = _RecordingHandler.requests[-1]
|
||||
assert "x-api-key" not in headers
|
||||
assert headers["accept"] == "application/json"
|
||||
|
||||
|
||||
def test_lmstudio_load_post_drops_bearer_on_redirect(monkeypatch):
|
||||
from hermes_cli import models
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue