mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-18 14:12:19 +02:00
98 lines
3.0 KiB
Python
98 lines
3.0 KiB
Python
"""Callers with no resumable chat keep the original one-use approval scope.
|
|
|
|
The chat card reuses the wire value ``approve`` for chat-session scope, so any
|
|
caller that still sends ``approve`` meaning "once" has to say so explicitly or
|
|
it silently inherits a run-long gate bypass.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import src.tool_capabilities as tool_capabilities
|
|
from src.tool_approval_scopes import ToolApprovalScope
|
|
from src.tool_approvals import ToolApprovalStore
|
|
from src.tool_capabilities import ToolRunSecurityContext, capabilities_for_action
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _enable_approval_gate_for_legacy_gate_tests(monkeypatch):
|
|
monkeypatch.setattr(tool_capabilities, "TOOL_APPROVAL_GATE_ENABLED", True)
|
|
|
|
|
|
def _pending(store: ToolApprovalStore, *, session_id=""):
|
|
content = "printf exact"
|
|
return store.create(
|
|
owner="Alice",
|
|
session_id=session_id,
|
|
origin_run_id="run-1",
|
|
tool_name="bash",
|
|
content=content,
|
|
workspace=None,
|
|
external_untrusted_context_seen=True,
|
|
capabilities=capabilities_for_action("bash", content),
|
|
)
|
|
|
|
|
|
def test_single_action_grant_leaves_the_gate_armed_behind_the_sealed_action():
|
|
store = ToolApprovalStore()
|
|
pending = _pending(store)
|
|
|
|
grant = store.consume(
|
|
pending.approval_id,
|
|
decision="approve",
|
|
owner="alice",
|
|
session_id=None,
|
|
allow_continuation=False,
|
|
)
|
|
|
|
assert grant is not None
|
|
assert grant.scope is ToolApprovalScope.SINGLE_ACTION
|
|
assert grant.allow_remaining_actions is False
|
|
assert grant.grants_chat_session is False
|
|
|
|
resumed = ToolRunSecurityContext(
|
|
external_untrusted_context_seen=True,
|
|
approval_gate_bypassed=grant.allow_remaining_actions,
|
|
)
|
|
assert resumed.decision_for("bash").allowed is False
|
|
|
|
|
|
def test_chat_callers_still_get_the_continuation_scope_they_asked_for():
|
|
store = ToolApprovalStore()
|
|
pending = _pending(store, session_id="session-1")
|
|
|
|
grant = store.consume(
|
|
pending.approval_id,
|
|
decision="approve_task",
|
|
owner="alice",
|
|
session_id="session-1",
|
|
)
|
|
|
|
assert grant is not None
|
|
assert grant.scope is ToolApprovalScope.TASK
|
|
assert grant.allow_remaining_actions is True
|
|
|
|
|
|
def test_deny_is_unaffected_by_the_single_action_flag():
|
|
store = ToolApprovalStore()
|
|
pending = _pending(store)
|
|
|
|
assert store.consume(
|
|
pending.approval_id,
|
|
decision="deny",
|
|
owner="alice",
|
|
session_id=None,
|
|
allow_continuation=False,
|
|
) is None
|
|
assert store.peek(pending.approval_id) is None
|
|
|
|
|
|
def test_skill_test_approval_route_opts_out_of_continuation():
|
|
root = Path(__file__).resolve().parents[1]
|
|
skills = (root / "routes/skills_routes.py").read_text(encoding="utf-8")
|
|
|
|
approve_call = skills.index("exact_approval = tool_approval_store.consume(")
|
|
end = skills.index(")", skills.index("allow_continuation", approve_call))
|
|
assert "allow_continuation=False" in skills[approve_call:end]
|