mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-17 04:31:55 +00:00
fix(delegation): add edge-case tests and SSRF caveat docstring
Red-team QA pass found no bugs but two test gaps: - Whitespace-only api_key now tested (falls through to placeholder) - IPv6 loopback [::1] now tested (already worked, just untested) - 172.32.x boundary now tested (correctly NOT treated as private) - Docstring note about private-network SSRF caveat: placeholder key gets 401'd by real auth servers, fail-fast by design
This commit is contained in:
parent
0e4bc9474d
commit
501ac3ff0a
2 changed files with 31 additions and 0 deletions
|
|
@ -107,6 +107,18 @@ class TestLocalProviderCredentials(unittest.TestCase):
|
||||||
creds = _resolve_delegation_credentials(cfg, parent)
|
creds = _resolve_delegation_credentials(cfg, parent)
|
||||||
self.assertEqual(creds["api_key"], "my-secret-key")
|
self.assertEqual(creds["api_key"], "my-secret-key")
|
||||||
|
|
||||||
|
def test_localhost_base_url_whitespace_api_key_gets_placeholder(self):
|
||||||
|
"""Whitespace-only api_key should be treated as absent and get placeholder."""
|
||||||
|
parent = _make_mock_parent()
|
||||||
|
cfg = {
|
||||||
|
"model": "devstral-small-2:24b-cloud",
|
||||||
|
"provider": "custom",
|
||||||
|
"base_url": "http://localhost:11434/v1",
|
||||||
|
"api_key": " ",
|
||||||
|
}
|
||||||
|
creds = _resolve_delegation_credentials(cfg, parent)
|
||||||
|
self.assertEqual(creds["api_key"], "ollama")
|
||||||
|
|
||||||
# --- base_url path (remote) should still require API key ---
|
# --- base_url path (remote) should still require API key ---
|
||||||
|
|
||||||
def test_remote_base_url_still_requires_api_key(self):
|
def test_remote_base_url_still_requires_api_key(self):
|
||||||
|
|
@ -224,6 +236,16 @@ class TestIsLocalBaseUrlHelper(unittest.TestCase):
|
||||||
from tools.delegate_tool import _is_local_base_url
|
from tools.delegate_tool import _is_local_base_url
|
||||||
self.assertFalse(_is_local_base_url(None))
|
self.assertFalse(_is_local_base_url(None))
|
||||||
|
|
||||||
|
def test_ipv6_loopback(self):
|
||||||
|
"""IPv6 loopback [::1] should be recognized as local."""
|
||||||
|
from tools.delegate_tool import _is_local_base_url
|
||||||
|
self.assertTrue(_is_local_base_url("http://[::1]:11434/v1"))
|
||||||
|
|
||||||
|
def test_172_outside_private_range(self):
|
||||||
|
"""172.32.x.x is NOT in 172.16/12 and should not be treated as local."""
|
||||||
|
from tools.delegate_tool import _is_local_base_url
|
||||||
|
self.assertFalse(_is_local_base_url("http://172.32.0.1:11434/v1"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
@ -2106,6 +2106,15 @@ def _is_local_base_url(base_url: Optional[str]) -> bool:
|
||||||
- localhost / loopback (127.0.0.1, ::1)
|
- localhost / loopback (127.0.0.1, ::1)
|
||||||
- .local mDNS hostnames (e.g. studio.local)
|
- .local mDNS hostnames (e.g. studio.local)
|
||||||
- RFC 1918 private networks (10/8, 172.16/12, 192.168/16)
|
- RFC 1918 private networks (10/8, 172.16/12, 192.168/16)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Any URL that resolves as "local" by this function will receive a
|
||||||
|
placeholder ``"ollama"`` API key. If an internal service on a
|
||||||
|
private network actually requires authentication (e.g. a corporate
|
||||||
|
AI gateway at 192.168.x.x), the placeholder key will be rejected
|
||||||
|
by that server (401/403). This is intentional — local servers that
|
||||||
|
genuinely don't need auth work out-of-the-box, while misconfigured
|
||||||
|
endpoints fail loudly rather than silently.
|
||||||
"""
|
"""
|
||||||
if not base_url:
|
if not base_url:
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue