test(auth): update nous jwt-only expectations
This commit is contained in:
@@ -155,17 +155,17 @@ def test_auth_add_nous_oauth_persists_pool_entry(tmp_path, monkeypatch):
|
||||
assert not any(item["source"] == "manual:device_code" for item in entries)
|
||||
entry = device_code_entries[0]
|
||||
assert entry["source"] == "device_code"
|
||||
assert entry["agent_key"] == "ak-test"
|
||||
assert entry["agent_key"] == token
|
||||
assert entry["portal_base_url"] == "https://portal.example.com"
|
||||
|
||||
# `hermes auth add nous` must also populate providers.nous so the
|
||||
# 401-recovery path (resolve_nous_runtime_credentials) can mint a fresh
|
||||
# agent_key when the 24h TTL expires. If this mirror is missing, recovery
|
||||
# 401-recovery path (resolve_nous_runtime_credentials) can refresh an
|
||||
# invoke JWT when the token expires. If this mirror is missing, recovery
|
||||
# raises "Hermes is not logged into Nous Portal" and the agent dies.
|
||||
singleton = payload["providers"]["nous"]
|
||||
assert singleton["access_token"] == token
|
||||
assert singleton["refresh_token"] == "refresh-token"
|
||||
assert singleton["agent_key"] == "ak-test"
|
||||
assert singleton["agent_key"] == token
|
||||
assert singleton["portal_base_url"] == "https://portal.example.com"
|
||||
assert singleton["inference_base_url"] == "https://inference.example.com/v1"
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Regression tests for Nous Portal inference_base_url host-allowlist validation.
|
||||
|
||||
A poisoned ``inference_base_url`` from the Portal refresh / agent-key-mint
|
||||
response (network MITM, malicious response injection) would otherwise be
|
||||
persisted to auth.json and forwarded the user's legitimate agent_key
|
||||
A poisoned ``inference_base_url`` from a Portal refresh response (network
|
||||
MITM, malicious response injection) would otherwise be persisted to
|
||||
auth.json and forwarded with the user's legitimate invoke JWT
|
||||
bearer on every subsequent proxy request, exfiltrating their inference
|
||||
budget and opening a response-injection channel into the IDE / chat
|
||||
client. ``_validate_nous_inference_url_from_network()`` blocks any URL
|
||||
@@ -11,7 +11,7 @@ outside the allowlist at the source.
|
||||
These tests verify:
|
||||
|
||||
1. The validator's host + scheme rules.
|
||||
2. Each of the five NETWORK call sites in ``auth.py`` calls the validator
|
||||
2. Each of the two NETWORK call sites in ``auth.py`` calls the validator
|
||||
rather than the unrestricted ``_optional_base_url`` helper.
|
||||
3. The proxy adapter applies the validator as belt-and-suspenders.
|
||||
4. The env-var override path (``NOUS_INFERENCE_BASE_URL``) is NOT
|
||||
@@ -124,7 +124,7 @@ class TestValidatorRules:
|
||||
|
||||
|
||||
class TestCallSiteWiring:
|
||||
"""Verify the validator is actually wired into all 5 NETWORK call sites.
|
||||
"""Verify the validator is actually wired into all auth.py NETWORK call sites.
|
||||
|
||||
These are not behaviour-end-to-end tests (the surrounding code is
|
||||
several hundred lines per site with extensive HTTP mocking
|
||||
@@ -161,7 +161,7 @@ class TestCallSiteWiring:
|
||||
)
|
||||
|
||||
def test_validator_wired_at_all_known_call_sites(self):
|
||||
"""All 5 known NETWORK sites use the validator. If this count
|
||||
"""All 2 known auth.py NETWORK sites use the validator. If this count
|
||||
drops, someone removed protection; if it grows, audit the new
|
||||
site to be sure validation is appropriate."""
|
||||
source = self._read_auth_source()
|
||||
@@ -171,8 +171,8 @@ class TestCallSiteWiring:
|
||||
mint_count = source.count(
|
||||
'_validate_nous_inference_url_from_network(mint_payload.get("inference_base_url"))'
|
||||
)
|
||||
assert refresh_count == 3, f"expected 3 refresh sites, found {refresh_count}"
|
||||
assert mint_count == 2, f"expected 2 mint sites, found {mint_count}"
|
||||
assert refresh_count == 2, f"expected 2 refresh sites, found {refresh_count}"
|
||||
assert mint_count == 0, f"expected 0 mint sites, found {mint_count}"
|
||||
|
||||
def test_proxy_adapter_also_validates(self):
|
||||
"""The Nous proxy adapter applies the validator as defense-in-depth
|
||||
|
||||
@@ -260,8 +260,8 @@ def test_nous_adapter_quarantines_terminal_refresh_failure(tmp_path, monkeypatch
|
||||
assert stored.get("credential_pool", {}).get("nous") == []
|
||||
|
||||
|
||||
def test_nous_adapter_get_credential_raises_when_no_agent_key_returned(tmp_path, monkeypatch):
|
||||
"""If the refresh helper succeeds but produces no agent_key, we surface a clear error."""
|
||||
def test_nous_adapter_get_credential_raises_when_no_jwt_returned(tmp_path, monkeypatch):
|
||||
"""If the refresh helper succeeds but produces no JWT, we surface a clear error."""
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
_write_auth_store(tmp_path, {
|
||||
"access_token": "access-tok",
|
||||
@@ -273,7 +273,7 @@ def test_nous_adapter_get_credential_raises_when_no_agent_key_returned(tmp_path,
|
||||
return_value={"access_token": "a", "refresh_token": "r"},
|
||||
):
|
||||
adapter = NousPortalAdapter()
|
||||
with pytest.raises(RuntimeError, match="did not return a usable agent_key"):
|
||||
with pytest.raises(RuntimeError, match="did not return a usable inference JWT"):
|
||||
adapter.get_credential()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user