Files
aiturk-hermes-ide/tests/gateway/test_goal_gate_access.py
T

132 lines
3.9 KiB
Python

"""Gateway authorization tests for shell-backed goal quality gates."""
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock
import pytest
from gateway.config import GatewayConfig, Platform, PlatformConfig
from gateway.session import SessionSource
def _source(user_id: str = "user-1", *, chat_type: str = "dm") -> SessionSource:
return SessionSource(
platform=Platform.DISCORD,
user_id=user_id,
chat_id=f"{chat_type}-1",
chat_type=chat_type,
)
def _runner(*, admins=(), group_admins=()):
from gateway.run import GatewayRunner
runner = object.__new__(GatewayRunner)
runner.config = GatewayConfig(
platforms={
Platform.DISCORD: PlatformConfig(
enabled=True,
extra={
"allow_admin_from": list(admins),
"group_allow_admin_from": list(group_admins),
},
)
}
)
manager = MagicMock()
manager.add_gate.return_value = SimpleNamespace(
command="touch /tmp/host-in-the-shell",
max_retries=3,
timeout_seconds=300,
)
runner._get_goal_manager_for_event = AsyncMock(
return_value=(manager, SimpleNamespace(session_id="session-1"))
)
return runner, manager
def _event(user_id: str = "user-1", *, chat_type: str = "dm"):
event = MagicMock()
event.source = _source(user_id, chat_type=chat_type)
event.get_command_args.return_value = (
"gate add touch /tmp/host-in-the-shell"
)
return event
@pytest.mark.asyncio
@pytest.mark.parametrize(
("admins", "user_id"),
[
((), "user-1"),
(("admin-1",), "user-1"),
],
)
async def test_gateway_gate_add_requires_explicit_admin(admins, user_id):
"""Allowed chat users must not turn /goal into an unrestricted host shell."""
from gateway.run import GatewayRunner
runner, manager = _runner(admins=admins)
result = await GatewayRunner._handle_goal_command(runner, _event(user_id))
assert "explicitly configured gateway admin" in result
manager.add_gate.assert_not_called()
@pytest.mark.asyncio
async def test_gateway_explicit_admin_can_add_goal_gate():
"""The fix preserves the documented quality-gate capability for operators."""
from gateway.run import GatewayRunner
runner, manager = _runner(admins=("admin-1",))
result = await GatewayRunner._handle_goal_command(runner, _event("admin-1"))
assert "Gate added" in result
manager.add_gate.assert_called_once_with(
"touch /tmp/host-in-the-shell"
)
@pytest.mark.asyncio
async def test_gateway_group_gate_add_uses_group_admin_scope():
"""DM admin status must not silently grant host-shell access in groups."""
from gateway.run import GatewayRunner
runner, manager = _runner(
admins=("dm-admin",),
group_admins=("group-admin",),
)
denied = await GatewayRunner._handle_goal_command(
runner,
_event("dm-admin", chat_type="group"),
)
allowed = await GatewayRunner._handle_goal_command(
runner,
_event("group-admin", chat_type="group"),
)
assert "explicitly configured gateway admin" in denied
assert "Gate added" in allowed
manager.add_gate.assert_called_once_with(
"touch /tmp/host-in-the-shell"
)
@pytest.mark.asyncio
async def test_gateway_non_admin_can_still_list_goal_gates():
"""Non-admins retain read-only visibility into the active goal's gates."""
from gateway.run import GatewayRunner
runner, manager = _runner(admins=("admin-1",))
manager.render_gates.return_value = "- 1. $ scripts/run_tests.sh"
event = _event("user-1")
event.get_command_args.return_value = "gate list"
result = await GatewayRunner._handle_goal_command(runner, event)
assert result == "- 1. $ scripts/run_tests.sh"
manager.render_gates.assert_called_once_with()