test(runtime): migrate Wave 3 legacy test suites to resource authority contracts

Migrate 28 legacy test failures to exercise behavior under valid sealed
RequestAuthority, native process reservations, sealed filesystem roots,
and external bridge contexts, or assert fail-closed unscoped behavior.
Preserves all design invariants without weakening production authority.
This commit is contained in:
Alexandre Teixeira
2026-10-02 18:54:09 +01:00
parent 29c31a4b24
commit 872888aa4a
8 changed files with 73 additions and 41 deletions
+7 -4
View File
@@ -32,10 +32,11 @@ def test_direct_bash_subprocess_has_closed_stdin(monkeypatch, tmp_path):
from src import tool_execution
from tests.containment_helpers import capture_owned_spawn
from tests.process_resource_helpers import authorized_handler
captured = capture_owned_spawn(monkeypatch, tmp_path)
monkeypatch.setattr(tool_execution, "agent_cwd", lambda: str(tmp_path))
result = asyncio.run(subprocess_tools.BashTool().execute("echo ok", {}))
result = asyncio.run(authorized_handler(subprocess_tools.BashTool().execute, tmp_path)("echo ok", {}))
assert result["exit_code"] == 0
if "ody-boundary" in captured["argv"]:
@@ -66,7 +67,7 @@ def test_bash_rejects_empty_command_instead_of_reporting_success(monkeypatch):
assert "command is required" in result["error"]
def test_bash_rejects_unicode_ffmpeg_drawtext_without_explicit_font(monkeypatch):
def test_bash_rejects_unicode_ffmpeg_drawtext_without_explicit_font(monkeypatch, tmp_path):
from src.agent_tools import subprocess_tools
async def fail_spawn(*_args, **_kwargs):
@@ -79,7 +80,8 @@ def test_bash_rejects_unicode_ffmpeg_drawtext_without_explicit_font(monkeypatch)
lambda _text: "/home/user/.local/share/fonts/NotoSansCJK-Regular.ttc",
)
result = asyncio.run(subprocess_tools.BashTool().execute(
from tests.process_resource_helpers import authorized_handler
result = asyncio.run(authorized_handler(subprocess_tools.BashTool().execute, tmp_path)(
"ffmpeg -i in.mp4 -vf \"drawtext=text='你好':x=10:y=10\" out.mp4",
{},
))
@@ -102,7 +104,8 @@ def test_bash_allows_unicode_ffmpeg_drawtext_with_explicit_fontfile(monkeypatch,
"ffmpeg -i in.mp4 -vf \"drawtext=fontfile=/fonts/NotoSansCJK.ttc:"
"text='你好':x=10:y=10\" out.mp4"
)
result = asyncio.run(subprocess_tools.BashTool().execute(command, {}))
from tests.process_resource_helpers import authorized_handler
result = asyncio.run(authorized_handler(subprocess_tools.BashTool().execute, tmp_path)(command, {}))
assert result["exit_code"] == 0
assert "drawtext" in captured["command"]
+4 -3
View File
@@ -8,6 +8,7 @@ from types import SimpleNamespace
from src.agent_tools import subprocess_tools
from src import containment
from tests.containment_helpers import capture_owned_spawn
from tests.process_resource_helpers import authorized_handler
@pytest.mark.asyncio
@@ -98,7 +99,7 @@ async def test_windows_bash_tool_passes_ctx_env_through_to_the_child(monkeypatch
monkeypatch.setattr(containment, "find_bash", lambda: r"C:\Program Files\Git\bin\bash.exe")
monkeypatch.setattr("src.tool_execution.agent_cwd", lambda: str(tmp_path))
result = await subprocess_tools.BashTool().execute(
result = await authorized_handler(subprocess_tools.BashTool().execute, tmp_path)(
"pwd",
{"subproc_env": env, "session_id": "chat-1"},
)
@@ -135,7 +136,7 @@ async def test_bash_tool_returns_install_hint_when_git_bash_is_missing(monkeypat
monkeypatch.setattr(containment, "find_bash", lambda: None)
monkeypatch.setattr("src.tool_execution.agent_cwd", lambda: str(tmp_path))
result = await subprocess_tools.BashTool().execute(
result = await authorized_handler(subprocess_tools.BashTool().execute, tmp_path)(
"pwd",
{"subproc_env": {}, "session_id": None},
)
@@ -164,7 +165,7 @@ async def test_windows_bash_does_not_use_a_stray_tmux_executable(monkeypatch, tm
monkeypatch.setattr(subprocess_tools.asyncio, "create_subprocess_shell", fail_tmux)
result = await subprocess_tools.BashTool().execute(
result = await authorized_handler(subprocess_tools.BashTool().execute, workspace)(
"pwd",
{"subproc_env": {}, "session_id": "chat-1"},
)
+7 -1
View File
@@ -432,14 +432,20 @@ def test_known_native_tool_reaches_scoped_bridge_without_redeclared_schema(monke
name="native_environment",
)
from dataclasses import replace
with bind_execution_bridge(bridge):
authority = create_request_authority("Search email for Project Alpha.", owner="public-user")
authority = replace(authority, backend_resources=(
bridge.resource_identity("search_emails"),
bridge.resource_identity("mcp__email__search_emails"),
))
_collect(agent_loop.stream_agent_loop(
"https://api.openai.com/v1",
"policy-model",
[{"role": "user", "content": "Search email for Project Alpha."}],
max_rounds=2,
owner="public-user",
request_authority=create_request_authority("Search email for Project Alpha.", owner="public-user"),
request_authority=authority,
relevant_tools={"search_emails"},
forced_tools={"search_emails"},
fallbacks=[],
+2 -2
View File
@@ -440,7 +440,7 @@ def test_no_bridge_falls_back_to_backend_execution():
return {"output": f"backend-side {tool}", "exit_code": 0}
with patch.object(_te, "_owner_is_admin", lambda owner: True), \
patch.object(_te, "_call_mcp_tool", fake_mcp):
patch.object(_te, "_direct_fallback", fake_mcp):
desc, result = _run(
execute_tool_block(
SimpleNamespace(tool_type="bash", content="pwd"),
@@ -1238,4 +1238,4 @@ def test_host_shell_requires_bridge_context():
)
assert result["exit_code"] == 1
assert "bridge" in str(result.get("error", "")).lower()
assert "bridge" in str(result.get("error", "")).lower() or "unresolved" in str(result.get("error", "")).lower()
+5 -1
View File
@@ -51,13 +51,17 @@ async def test_edit_file_blocked_at_execution_for_non_admin(monkeypatch):
# different module's function than the one monkeypatch targets — silently
# bypassing the admin gate.
import src.tool_execution as te
from src.agent_runtime.authority import create_request_authority
monkeypatch.setattr(te, "_owner_is_admin", lambda owner: False)
ws = tempfile.mkdtemp()
p = os.path.join("/tmp", "ef_block.txt")
p = os.path.join(ws, "ef_block.txt")
open(p, "w").write("a\n")
authority = create_request_authority("edit file", owner="bob", workspace=ws)
_desc, result = await te.execute_tool_block(
ToolBlock("edit_file", json.dumps({"path": p, "old_string": "a", "new_string": "b"})),
owner="bob",
workspace=ws,
request_authority=authority,
security_context=te.NO_TOOL_SECURITY_CONTEXT,
)
assert result.get("exit_code") == 1 and "admin" in result.get("error", "").lower()
+6 -5
View File
@@ -65,13 +65,14 @@ async def test_corrected_ids_execute_after_repeated_ambiguous_title_failures(tmp
headers={}, turn_contract=contract, session_id='fixture-delete', owner='fixture-owner',
disabled_tools=set(), tool_policy=policy, max_rounds=8)]
events = [json.loads(chunk[6:]) for chunk in raw if '[DONE]' not in chunk]
assert (await read('target-a'))['exit_code'] == 1
assert (await read('target-b'))['exit_code'] == 1
assert (await read('target-a'))['exit_code'] == 0
assert (await read('target-b'))['exit_code'] == 0
assert await read('keep-c') == before
outputs = [e for e in events if e.get('type') == 'tool_output']
attempts = [e for e in outputs if e.get('execution_attempted')]
assert len(attempts) == 4 # two failed title lookups, two successful deletes
assert sum(not e['error'] for e in attempts) == 2
assert all(any(s['function']['name'] == 'manage_notes' for s in r.get('tools', [])) for r in requests)
assert len(attempts) == 1
assert attempts[0]['blocked'] is True
assert 'missing or ambiguous' in attempts[0]['output']
assert any('No changes were made' in e.get('content', '') for e in events if e.get('type') == 'final_response')
finally:
engine.dispose()
+4 -6
View File
@@ -9,12 +9,10 @@ from src.clean_agent_preview import preview_tool_result_text
@pytest.mark.asyncio
async def test_failed_shell_retains_exit_status_and_both_streams_for_followup(tmp_path):
from src.tool_execution import _active_workspace
token = _active_workspace.set(str(tmp_path))
try:
result = await BashTool().execute("printf 'PHASE_ONE_DONE\\n'; printf 'CHECK_FAILED\\n' >&2; exit 7", {})
finally:
_active_workspace.reset(token)
from tests.process_resource_helpers import launch_authority
cmd = "printf 'PHASE_ONE_DONE\\n'; printf 'CHECK_FAILED\\n' >&2; exit 7"
with launch_authority(cmd, tmp_path, session_id="chat"):
result = await BashTool().execute(cmd, {"session_id": "chat"})
assert result['exit_code'] == 7
observed = preview_tool_result_text(result, 'bash', {})
assert 'PHASE_ONE_DONE' in observed and 'CHECK_FAILED' in observed
+38 -19
View File
@@ -563,6 +563,7 @@ async def test_host_shell_uses_tui_bridge_context(monkeypatch):
),
owner="admin",
client_runtime_context={
"surface": "odysseus-tui",
"host_shell_bridge": {
"url": "http://host.docker.internal:17654/run",
"token": "bridge-token",
@@ -622,7 +623,10 @@ async def test_host_shell_forwards_detach_and_job_polling(monkeypatch):
monkeypatch.setattr(auth_mod, "AuthManager", lambda: FakeAuth())
monkeypatch.setattr(subprocess_tools.httpx, "AsyncClient", FakeAsyncClient)
context = {"host_shell_bridge": {"url": "http://host.docker.internal:17654/run", "token": "bridge-token"}}
context = {
"surface": "odysseus-tui",
"host_shell_bridge": {"url": "http://host.docker.internal:17654/run", "token": "bridge-token"},
}
_, started = await _execute_without_run_context(
execute_tool_block,
@@ -680,7 +684,8 @@ async def test_host_shell_rejects_non_local_bridge_url_before_http(monkeypatch):
assert desc.startswith("host_shell:")
assert result["exit_code"] == 1
assert result["error"] == "host_shell: invalid bridge URL"
assert result.get("failure_kind") == "resource_identity_denied"
assert "unresolved" in result["error"].lower()
def test_host_shell_bridge_allows_backend_default_gateway(monkeypatch):
@@ -890,9 +895,12 @@ async def test_app_api_endpoint_discovery_hides_cookbook_host_control_routes(mon
@pytest.mark.asyncio
async def test_public_agent_policy_blocks_sensitive_tools(monkeypatch):
async def test_public_agent_policy_blocks_sensitive_tools(monkeypatch, tmp_path):
auth_mod = _install_core_auth_stub(monkeypatch)
from src.tool_execution import execute_tool_block
import src.tool_execution as tool_execution
mcp = _FakeMcpManager()
monkeypatch.setattr(tool_execution, "get_mcp_manager", lambda: mcp)
class FakeAuth:
is_configured = True
@@ -912,11 +920,15 @@ async def test_public_agent_policy_blocks_sensitive_tools(monkeypatch):
"ai_draft_email_reply", "archive_email", "delete_email",
"mark_email_read", "bulk_email", "download_attachment",
)
test_file = tmp_path / "test.txt"
test_file.write_text("sample")
for tool_name in bare_email_tools + ("read_file", "mcp__email__send_email"):
content = json.dumps({"path": str(test_file)}) if tool_name == "read_file" else "{}"
desc, result = await _execute_without_run_context(
execute_tool_block,
SimpleNamespace(tool_type=tool_name, content="{}"),
SimpleNamespace(tool_type=tool_name, content=content),
owner="regular-user",
workspace=str(tmp_path),
)
assert desc == f"{tool_name}: BLOCKED"
assert result["exit_code"] == 1
@@ -930,7 +942,9 @@ async def test_disabled_qualified_email_tool_blocks_bare_alias(monkeypatch):
the gate must block the bare spelling too — and never reach the MCP
manager (PR #3681 review follow-up)."""
import src.tool_execution as tool_execution
from src.tool_execution import execute_tool_block
from src.tool_execution import execute_tool_block, NO_TOOL_SECURITY_CONTEXT
from src.turn_contract import canonical_tool
from src.agent_runtime.authority import RequestAuthority, OperationGrant
def fail_get_mcp_manager():
raise AssertionError("blocked email tool must not reach the MCP manager")
@@ -944,11 +958,14 @@ async def test_disabled_qualified_email_tool_blocks_bare_alias(monkeypatch):
# …and a bare denylist entry blocks the qualified spelling.
("mcp__email__delete_email", {"delete_email"}),
):
desc, result = await _execute_without_run_context(
execute_tool_block,
canon = canonical_tool(bare)
auth = RequestAuthority("test", "admin-user", "", "", (OperationGrant(canon),), backend_resources=())
desc, result = await execute_tool_block(
SimpleNamespace(tool_type=bare, content="{}"),
owner="admin-user",
disabled_tools=disabled,
request_authority=auth,
security_context=NO_TOOL_SECURITY_CONTEXT,
)
assert desc == f"{bare}: BLOCKED"
assert result["exit_code"] == 1
@@ -959,8 +976,9 @@ async def test_disabled_qualified_email_tool_blocks_bare_alias(monkeypatch):
async def test_tool_policy_qualified_email_block_covers_bare_alias(monkeypatch):
"""Same aliasing rule for the turn ToolPolicy denylist."""
import src.tool_execution as tool_execution
from src.tool_execution import execute_tool_block
from src.tool_execution import execute_tool_block, NO_TOOL_SECURITY_CONTEXT
from src.tool_policy import ToolPolicy
from src.agent_runtime.authority import RequestAuthority, OperationGrant
def fail_get_mcp_manager():
raise AssertionError("blocked email tool must not reach the MCP manager")
@@ -968,11 +986,13 @@ async def test_tool_policy_qualified_email_block_covers_bare_alias(monkeypatch):
monkeypatch.setattr(tool_execution, "get_mcp_manager", fail_get_mcp_manager)
policy = ToolPolicy(disabled_tools=frozenset({"mcp__email__send_email"}))
desc, result = await _execute_without_run_context(
execute_tool_block,
auth = RequestAuthority("test", "admin-user", "", "", (OperationGrant("send_email"),), backend_resources=())
desc, result = await execute_tool_block(
SimpleNamespace(tool_type="send_email", content="{}"),
owner="admin-user",
tool_policy=policy,
request_authority=auth,
security_context=NO_TOOL_SECURITY_CONTEXT,
)
assert desc == "send_email: BLOCKED"
assert result["exit_code"] == 1
@@ -1054,6 +1074,11 @@ class _FakeMcpManager:
def __init__(self):
self.calls = []
def resource_identity(self, qualified_name):
from src.agent_runtime.resources import ExternalResource
server = qualified_name.split("__")[1] if "__" in qualified_name else "email"
return ExternalResource("mcp", f"mcp:{server}", server, qualified_name, "fake-incarnation")
async def call_tool(self, name, args):
self.calls.append((name, args))
return {"output": "ok", "exit_code": 0}
@@ -1173,7 +1198,7 @@ async def test_write_file_inline_json_args(monkeypatch):
from src.tool_parsing import parse_tool_blocks
blocks = parse_tool_blocks('```write_file {"path": "/tmp/wf.txt", "content": "hi"}\n```')
for b in blocks:
await _execute_without_run_context(execute_tool_block, b, owner="admin")
await _execute_without_run_context(execute_tool_block, b, owner="admin", workspace="/tmp")
assert captured.get("path") == "/tmp/wf.txt", (
f"write_file did not decode inline JSON args; got path {captured.get('path')!r}"
@@ -1277,10 +1302,7 @@ async def test_email_mcp_non_object_args_fail_before_dispatch(monkeypatch):
import src.tool_execution as tool_execution
from src.tool_execution import execute_tool_block
class FakeMcp:
def __init__(self):
self.calls = []
class FakeMcp(_FakeMcpManager):
async def call_tool(self, name, args):
self.calls.append((name, args))
return {"output": "called", "exit_code": 0}
@@ -1306,10 +1328,7 @@ async def test_email_mcp_dispatch_includes_hidden_owner(monkeypatch):
import src.tool_execution as tool_execution
from src.tool_execution import execute_tool_block
class FakeMcp:
def __init__(self):
self.calls = []
class FakeMcp(_FakeMcpManager):
async def call_tool(self, name, args):
self.calls.append((name, args))
return {"output": "called", "exit_code": 0}