Files
hermes-agent/tests/gateway/test_platform_entry_plugin_seams.py
teknium1 c19dd99597 fix: a user plugin cannot mark a core platform trusted_inbound
PluginContext.register_platform is last-writer-wins, so a user plugin could re-register 'telegram'
with trusted_inbound=True and every sender would skip the allowlist and pairing. The flag is now
refused for a platform core ships (a static Platform member or a bundled plugins/platforms/ plugin)
unless the registering plugin is bundled. Home Assistant is no longer either, so its catalog plugin
keeps it. `hermes plugins validate` prints a line for every trusted_inbound platform (and fails one
naming a core platform).

Also: the MCP collision test docstring no longer refers to the toolset it used to collide with.
2026-10-04 00:08:20 -07:00

88 lines
3.9 KiB
Python

"""PlatformEntry seams a plugin platform uses instead of a core special case.
``trusted_inbound`` (no human sender: allowlists/pairing do not apply) and ``display_tier`` (built-in
display defaults) were Home Assistant branches in core; the homeassistant catalog plugin is their
consumer. Driven through the real registry, authz and display resolver.
"""
from __future__ import annotations
import pytest
from gateway.config import Platform
from gateway.display_config import resolve_display_setting
from gateway.platform_registry import PlatformEntry, platform_registry
from gateway.session import SessionSource
@pytest.fixture
def plugin_platform():
names = []
def _register(name: str, **fields):
platform_registry.register(PlatformEntry(
name=name, label=name.title(), adapter_factory=lambda cfg: None, check_fn=lambda: True,
source="plugin", **fields))
names.append(name)
return Platform(name)
yield _register
for name in names:
platform_registry.unregister(name)
def _runner():
from gateway.run import GatewayRunner
runner = object.__new__(GatewayRunner)
runner.pairing_store = None
return runner
def _event_source(platform):
return SessionSource(platform=platform, user_id="event-bus", chat_id="events", user_name="bus", chat_type="dm")
def test_trusted_inbound_platform_bypasses_allowlists(plugin_platform, monkeypatch):
for key in ("GATEWAY_ALLOWED_USERS", "GATEWAY_ALLOW_ALL_USERS"):
monkeypatch.delenv(key, raising=False)
trusted = plugin_platform("seamtrusted", trusted_inbound=True)
plain = plugin_platform("seamplain")
runner = _runner()
assert runner._is_user_authorized(_event_source(trusted)) is True
# Control: the default stays default-deny for an unknown sender.
assert runner._is_user_authorized(_event_source(plain)) is False
def test_display_tier_supplies_builtin_defaults_below_user_overrides(plugin_platform):
plugin_platform("seamminimal", display_tier="minimal")
plugin_platform("seamnotier")
assert resolve_display_setting({}, "seamminimal", "tool_progress") == "off"
assert resolve_display_setting({}, "seamminimal", "tool_preview_length") == 0
assert resolve_display_setting({}, "seamminimal", "streaming") is False
# The user's per-platform override still wins over the registered tier.
user = {"display": {"platforms": {"seamminimal": {"tool_progress": "all"}}}}
assert resolve_display_setting(user, "seamminimal", "tool_progress") == "all"
# No tier: the global defaults, as before.
assert resolve_display_setting({}, "seamnotier", "tool_progress") == resolve_display_setting({}, "nosuchplatform", "tool_progress")
def test_platform_entry_seam_defaults_are_inert():
entry = PlatformEntry(name="x", label="X", adapter_factory=lambda cfg: None, check_fn=lambda: True)
assert (entry.trusted_inbound, entry.display_tier, entry.shared_env_prefixes) == (False, "", ())
def test_user_plugin_cannot_mark_a_core_platform_trusted_inbound():
from hermes_cli.plugins import PluginContext, PluginManager, PluginManifest
ctx = PluginContext(PluginManifest(name="seamevil", key="seamevil", source="user"),
PluginManager(scope_key=platform_registry.current_scope_key()))
with pytest.raises(ValueError, match="trusted_inbound"):
ctx.register_platform("telegram", "Telegram", adapter_factory=lambda cfg: None, check_fn=lambda: True,
trusted_inbound=True)
# Control: a platform core does not ship (Home Assistant's case) may declare it.
try:
ctx.register_platform("seameventbus", "Bus", adapter_factory=lambda cfg: None, check_fn=lambda: True,
trusted_inbound=True)
assert platform_registry.get("seameventbus").trusted_inbound is True
finally:
platform_registry.unregister("seameventbus", scope=platform_registry.current_scope_key())