mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-01 01:51:44 +00:00
security(runtime_provider): close OLLAMA_API_KEY substring-leak sweep miss (#13522)
Two call sites still used a raw substring check to identify ollama.com:
hermes_cli/runtime_provider.py:496:
_is_ollama_url = "ollama.com" in base_url.lower()
run_agent.py:6127:
if fb_base_url_hint and "ollama.com" in fb_base_url_hint.lower() ...
Same bug class as GHSA-xf8p-v2cg-h7h5 (OpenRouter substring leak), which
was fixed in commit dbb7e00e via base_url_host_matches() across the
codebase. The earlier sweep missed these two Ollama sites. Self-discovered
during April 2026 security-advisory triage; filed as GHSA-76xc-57q6-vm5m.
Impact is narrow — requires a user with OLLAMA_API_KEY configured AND a
custom base_url whose path or look-alike host contains 'ollama.com'.
Users on default provider flows are unaffected. Filed as a draft advisory
to use the private-fork flow; not CVE-worthy on its own.
Fix is mechanical: replace substring check with base_url_host_matches
at both sites. Same helper the rest of the codebase uses.
Tests: 67 -> 71 passing. 7 new host-matcher cases in
tests/test_base_url_hostname.py (path injection, lookalike host,
localtest.me subdomain, ollama.ai TLD confusion, localhost, genuine
ollama.com, api.ollama.com subdomain) + 4 call-site tests in
tests/hermes_cli/test_runtime_provider_resolution.py verifying
OLLAMA_API_KEY is selected only when base_url actually targets
ollama.com.
Fixes GHSA-76xc-57q6-vm5m
This commit is contained in:
parent
4cc5065f63
commit
7fc1e91811
4 changed files with 148 additions and 4 deletions
|
|
@ -106,3 +106,55 @@ class TestBaseUrlHostMatchesEdgeCases:
|
|||
|
||||
def test_trailing_dot_on_domain_stripped(self):
|
||||
assert base_url_host_matches("https://openrouter.ai/v1", "openrouter.ai.") is True
|
||||
|
||||
|
||||
class TestOllamaUrlHostCheck:
|
||||
"""GHSA-76xc-57q6-vm5m — ollama.com was using a raw substring match for
|
||||
credential selection (same bug class as GHSA-xf8p-v2cg-h7h5 for OpenRouter).
|
||||
These tests lock in that the base_url_host_matches fix correctly rejects
|
||||
the same attack vectors for Ollama.
|
||||
"""
|
||||
|
||||
def test_ollama_com_path_injection_rejected(self):
|
||||
"""http://evil.test/ollama.com/v1 — ollama.com appears in the path,
|
||||
not the host. Must not be treated as Ollama Cloud."""
|
||||
assert base_url_host_matches(
|
||||
"http://127.0.0.1:9000/ollama.com/v1", "ollama.com"
|
||||
) is False
|
||||
|
||||
def test_ollama_com_subdomain_lookalike_rejected(self):
|
||||
"""ollama.com.attacker.test is a separate host, not ollama.com."""
|
||||
assert base_url_host_matches(
|
||||
"http://ollama.com.attacker.test:9000/v1", "ollama.com"
|
||||
) is False
|
||||
|
||||
def test_ollama_com_localtest_me_rejected(self):
|
||||
"""ollama.com.localtest.me resolves to 127.0.0.1 via localtest.me
|
||||
but its true hostname is localtest.me, not ollama.com."""
|
||||
assert base_url_host_matches(
|
||||
"http://ollama.com.localtest.me:9000/v1", "ollama.com"
|
||||
) is False
|
||||
|
||||
def test_ollama_ai_is_not_ollama_com(self):
|
||||
"""Different TLD. ollama.ai is not ollama.com."""
|
||||
assert base_url_host_matches(
|
||||
"https://ollama.ai/v1", "ollama.com"
|
||||
) is False
|
||||
|
||||
def test_localhost_ollama_port_is_not_ollama_com(self):
|
||||
"""http://localhost:11434/v1 is a local Ollama install, but its
|
||||
hostname is localhost, so OLLAMA_API_KEY (an ollama.com-only secret)
|
||||
must not be sent."""
|
||||
assert base_url_host_matches(
|
||||
"http://localhost:11434/v1", "ollama.com"
|
||||
) is False
|
||||
|
||||
def test_genuine_ollama_com_matches(self):
|
||||
assert base_url_host_matches(
|
||||
"https://ollama.com/api/generate", "ollama.com"
|
||||
) is True
|
||||
|
||||
def test_ollama_com_subdomain_matches(self):
|
||||
assert base_url_host_matches(
|
||||
"https://api.ollama.com/v1", "ollama.com"
|
||||
) is True
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue