feat(runtime): bind remote and owned resources to authority

This commit is contained in:
Alexandre Teixeira
2026-10-02 03:00:46 +01:00
parent ba3e631d58
commit 571f685ad5
23 changed files with 2491 additions and 74 deletions
+6 -2
View File
@@ -14,16 +14,20 @@ def server_authorized_executor(executor):
from src.agent_runtime.authority import OperationGrant, RequestAuthority
from src.tool_policy import known_tool_names
from src.turn_contract import canonical_tool
from src.agent_runtime.remote_resources import seal_backends
call_signature = signature(executor)
@wraps(executor)
async def execute(*args, **kwargs):
bound = call_signature.bind(*args, **kwargs)
parameters = bound.arguments
grants = tuple(OperationGrant(name) for name in sorted(
{canonical_tool(n) for n in known_tool_names()} | {"list_dir", "find_files"}))
kwargs.setdefault("request_authority", RequestAuthority(
"standalone-test-request", str(parameters.get("owner") or "").strip().casefold(),
str(parameters.get("session_id") or ""), str(parameters.get("workspace") or ""),
tuple(OperationGrant(name) for name in sorted(
{canonical_tool(n) for n in known_tool_names()} | {"list_dir", "find_files"})),
grants,
backend_resources=seal_backends((g.tool for g in grants), context=parameters.get("client_runtime_context"),
owner=str(parameters.get("owner") or "").strip().casefold()),
))
return await executor(*args, **kwargs)
return execute
+429
View File
@@ -0,0 +1,429 @@
"""Server resolution pins record aliases before approval and dispatch."""
import asyncio
from dataclasses import replace
from datetime import datetime, timedelta
import json
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from src.agent_runtime.authority import ExactOperation, OperationGrant, RequestAuthority, bind_request_authority
from src.agent_runtime.owned_resources import (
active_owned_operation, admit_owned_operation, bind_owned_operation,
bound_attachment_path, resolve_owned_operation,
observe_vault_records,
)
from src.agent_runtime.remote_resources import active_backend_operation
from src.agent_runtime.resources import OwnedScope, ResourceIdentityError
from src.tool_approvals import ToolApprovalStore
from src.tool_capabilities import ToolRunSecurityContext, capabilities_for_action
from src.tool_types import ToolBlock
@pytest.fixture(autouse=True)
def fresh_vault_observations(monkeypatch):
from src.agent_runtime import owned_resources
monkeypatch.setattr(owned_resources, "_VAULT_RECORDS", {})
def grant(*tools, scopes=None, owner="alice", thread="s"):
return RequestAuthority("owned-request", owner, thread, "",
tuple(OperationGrant(t) for t in tools), owned_scopes=scopes)
async def dispatch(authority, tool, content, **kwargs):
from src import tool_execution as execution
return await execution.execute_tool_block(ToolBlock(tool, content), owner=authority.owner,
session_id=authority.session_id, request_authority=authority,
security_context=kwargs.pop("security_context", execution.NO_TOOL_SECURITY_CONTEXT), **kwargs)
def approval(authority, tool, content, **kwargs):
store = ToolApprovalStore()
pending = store.create(owner=authority.owner, session_id=authority.session_id, origin_run_id="run",
tool_name=tool, content=content, workspace=None, request_authority=authority,
external_untrusted_context_seen=True, capabilities=capabilities_for_action(tool, content), **kwargs)
return store.consume(pending.approval_id, decision="approve", owner=authority.owner, session_id=authority.session_id)
@pytest.fixture
def records(monkeypatch):
import core.database as db
import src.database as compatibility
from src.agent_tools import document_tools
engine = create_engine("sqlite:///:memory:")
db.Base.metadata.create_all(engine)
factory = sessionmaker(bind=engine)
monkeypatch.setattr(db, "SessionLocal", factory)
monkeypatch.setattr(compatibility, "SessionLocal", factory)
from src import tool_execution as execution
monkeypatch.setattr(execution, "_owner_is_admin", lambda owner: True)
now = datetime(2026, 1, 1)
with factory() as connection:
for identifier, owner in (("s", "alice"), ("other", "alice"), ("foreign", "bob")):
connection.add(db.Session(id=identifier, owner=owner, name=identifier, endpoint_url="https://model.test", model="test"))
for identifier, owner, thread, offset in (("d1", "alice", "s", 1), ("d2", "alice", "other", 2), ("private", "bob", "foreign", 3)):
connection.add(db.Document(id=identifier, owner=owner, session_id=thread, title=identifier,
current_content=identifier + " original", language="text", version_count=1,
created_at=now, updated_at=now + timedelta(days=offset)))
connection.add(db.Note(id="note-one", owner="alice", title="first", content="original"))
connection.add(db.Note(id="note-two", owner="alice", title="second", content="original"))
connection.add(db.Note(id="note-foreign", owner="bob", title="private", content="private"))
connection.commit()
monkeypatch.setattr(document_tools, "_active_document_id", None)
yield factory
engine.dispose()
@pytest.mark.parametrize("selector", ["active", "current", "latest"])
async def test_document_alias_resolves_once_and_does_not_follow_new_active_or_latest(records, monkeypatch, selector):
from src import tool_execution as execution
authority = grant("manage_documents")
content = json.dumps({"action": "read", "document_id": selector})
exact = approval(authority, "manage_documents", content, document_id="d1")
bound = exact.pending.owned_operation
expected = "d2" if selector == "latest" else "d1"
assert bound.document_id == expected
import core.database as db
with records() as connection:
connection.add(db.Document(id="newest", owner="alice", session_id="s", title="newest",
current_content="newest content", version_count=1, updated_at=datetime(2030, 1, 1)))
connection.commit()
seen = []
async def implementation(block, **kwargs):
seen.append((json.loads(block.content)["document_id"], kwargs["approved_document_id"], active_owned_operation()))
return "read", {"exit_code": 0}
monkeypatch.setattr(execution, "_execute_tool_block_impl", implementation)
_, result = await dispatch(authority, "manage_documents", content, active_document_id="newest",
exact_approval=exact, security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["exit_code"] == 0
assert seen[0][:2] == (expected, expected)
assert seen[0][2] is bound
assert active_owned_operation() is None
async def test_document_runtime_executes_captured_id_not_process_global_alias(records):
from src.agent_tools import document_tools
authority = grant("update_document")
exact = approval(authority, "update_document", "replacement", document_id="d1")
document_tools.set_active_document("d2")
_, result = await dispatch(authority, "update_document", "replacement", active_document_id="d2",
exact_approval=exact, security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result.get("exit_code", 0) == 0 and not result.get("error")
import core.database as db
with records() as connection:
assert connection.get(db.Document, "d1").current_content == "replacement"
assert connection.get(db.Document, "d2").current_content == "d2 original"
@pytest.mark.parametrize("change", ["revision", "owner", "thread", "deleted", "request", "invocation_thread"])
async def test_stale_or_rebound_document_approval_fails_before_effect(records, monkeypatch, change):
import core.database as db
from src import tool_execution as execution
authority = grant("update_document")
exact = approval(authority, "update_document", "replacement", document_id="d1")
if change in {"request", "invocation_thread"}:
authority = replace(authority, **({"request_id": "other"} if change == "request" else
{"session_id": "other", "owned_scopes": (OwnedScope("documents", "alice", "other"),)}))
else:
with records() as connection:
row = connection.get(db.Document, "d1")
if change == "revision":
row.current_content = "changed"
row.version_count += 1
elif change == "owner":
row.owner = "bob"
elif change == "thread":
row.session_id = "other"
else:
connection.delete(row)
connection.commit()
implementation = AsyncMock()
monkeypatch.setattr(execution, "_execute_tool_block_impl", implementation)
_, result = await dispatch(authority, "update_document", "replacement", exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["failure_kind"] == "resource_identity_denied"
implementation.assert_not_awaited()
assert not exact._claimed
@pytest.mark.parametrize("owner,thread", [("", "s"), ("alice", ""), ("bob", "s")])
async def test_owner_and_invocation_thread_are_mandatory(records, owner, thread):
_, result = await dispatch(grant("manage_documents", owner=owner, thread=thread),
"manage_documents", '{"action":"read","id":"d1"}')
assert result["failure_kind"] == "resource_identity_denied"
async def test_child_record_scope_is_intersection_and_exact_approval_cannot_widen(records, monkeypatch):
parent = grant("manage_documents", scopes=(OwnedScope("documents", "alice", "s", frozenset({"d1"})),))
child = grant("manage_documents")
assert parent.intersect(child).owned_scopes == parent.owned_scopes
exact = approval(child, "manage_documents", '{"action":"read","id":"d2"}')
from src import tool_execution as execution
handler = AsyncMock(return_value=("read", {"exit_code": 0}))
monkeypatch.setattr(execution, "_execute_tool_block_impl", handler)
with bind_request_authority(parent):
_, denied = await dispatch(child, "manage_documents", exact.pending.content, exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
_, allowed = await dispatch(child, "manage_documents", '{"action":"read","id":"d1"}')
_, collection = await dispatch(child, "manage_documents", '{"action":"list"}')
assert denied["failure_kind"] == collection["failure_kind"] == "resource_identity_denied"
assert allowed["exit_code"] == 0 and handler.await_count == 1
async def test_legacy_owned_approval_is_exact_one_use_not_reconstructed_scope(records, monkeypatch):
snapshot = grant("manage_documents").to_dict()
snapshot["version"] = 2
authority = RequestAuthority.from_dict(snapshot)
exact = approval(authority, "manage_documents", '{"action":"read","id":"d1"}')
from src import tool_execution as execution
handler = AsyncMock(return_value=("read", {"exit_code": 0}))
monkeypatch.setattr(execution, "_execute_tool_block_impl", handler)
_, denied = await dispatch(authority, "manage_documents", exact.pending.content)
assert denied["failure_kind"] == "resource_identity_denied"
security = ToolRunSecurityContext(external_untrusted_context_seen=True)
_, allowed = await dispatch(authority, "manage_documents", exact.pending.content, exact_approval=exact, security_context=security)
_, replay = await dispatch(authority, "manage_documents", exact.pending.content, exact_approval=exact, security_context=security)
assert allowed["exit_code"] == 0 and replay["exit_code"] == 1
assert authority.owned_scopes == () and handler.await_count == 1
@pytest.mark.parametrize("tool,content", [("manage_session", '{"action":"rename","session":"current","value":"new"}'),
("manage_session", "rename\ncurrent\nnew"), ("send_to_session", "current\nhello")])
def test_thread_current_alias_becomes_exact_owned_identity(records, tool, content):
bound = resolve_owned_operation(ExactOperation.normalize(tool, content), owner="alice", thread_id="s")
assert bound.resources[0].record_id == bound.resources[0].record_thread_id == "s"
assert "current" not in bound.execution_input
with pytest.raises(ResourceIdentityError):
resolve_owned_operation(ExactOperation.normalize(tool, content.replace("current", "foreign")), owner="alice", thread_id="s")
@pytest.mark.parametrize("selector", ["note-o", "first"])
def test_note_alias_resolves_once_and_prefix_ambiguity_fails_closed(records, selector):
content = {"action": "update", "content": "replacement", "id" if selector == "note-o" else "title": selector}
bound = resolve_owned_operation(ExactOperation.normalize("manage_notes", json.dumps(content)), owner="alice", thread_id="s")
assert bound.resources[0].record_id == json.loads(bound.execution_input)["id"] == "note-one"
with pytest.raises(ResourceIdentityError):
resolve_owned_operation(ExactOperation.normalize("manage_notes", '{"action":"view","id":"note-"}'), owner="alice", thread_id="s")
@pytest.fixture
def attachment_store(tmp_path, monkeypatch):
from src import tool_utils
file = tmp_path / "image.png"
file.write_bytes(b"test image")
row = {"id": "upload.png", "owner": "alice", "path": str(file), "hash": "observed-hash"}
handler = SimpleNamespace(upload_dir=str(tmp_path), resolve_upload=lambda identifier, *, owner, allow_admin:
dict(row) if identifier == row.get("id") and owner == row.get("owner") and not allow_admin else None)
monkeypatch.setattr(tool_utils, "get_upload_handler", lambda: handler)
return row, file
@pytest.mark.parametrize("change", ["owner", "path", "file", "missing"])
def test_attachment_ownership_index_and_file_identity_are_pinned(attachment_store, change):
row, file = attachment_store
bound = resolve_owned_operation(ExactOperation.normalize("extract_text", '{"path":"odysseus://attachment/upload.png"}'), owner="alice", thread_id="s")
with bind_owned_operation(bound):
assert bound_attachment_path("alice", "odysseus://attachment/upload.png") == str(file)
with pytest.raises(ResourceIdentityError):
bound_attachment_path("bob", "odysseus://attachment/upload.png")
if change == "owner":
row["owner"] = "bob"
elif change == "path":
other = file.with_name("other.png")
other.write_bytes(b"other")
row["path"] = str(other)
elif change == "file":
file.rename(file.with_name("old.png"))
file.write_bytes(b"replacement")
else:
row.clear()
with pytest.raises(ResourceIdentityError):
bound.validate()
def test_memory_prefix_is_owner_scoped_exact_and_revision_sensitive(monkeypatch):
from src import ai_interaction
rows = [{"id": "memory-one", "owner": "alice", "text": "secret", "timestamp": 1},
{"id": "memory-other", "owner": "bob", "text": "private", "timestamp": 1}]
monkeypatch.setattr(ai_interaction, "_memory_manager", SimpleNamespace(load=lambda owner: rows))
bound = resolve_owned_operation(ExactOperation.normalize("manage_memory", "edit\nmemory-o\nreplacement"), owner="alice", thread_id="s")
assert bound.resources[0].record_id == "memory-one"
assert bound.execution_input == "edit\nmemory-one\nreplacement"
assert "secret" not in json.dumps(bound.to_dict())
rows[0]["text"] = "changed in same second"
with pytest.raises(ResourceIdentityError):
bound.validate()
@pytest.mark.parametrize("change", ["owner", "endpoint", "session"])
def test_private_vault_identity_binds_owner_endpoint_and_exact_uuid_without_credentials(monkeypatch, change):
from src.tools import vault
cfg = {"owner": "alice", "server_url": "https://vault.test", "session": "SECRET_SESSION", "unlocked_at": "observed"}
monkeypatch.setattr(vault, "_load_vault_config", lambda: cfg)
observe_vault_records("alice", cfg, [{"id": "12345678-1234-1234-1234-123456789abc", "name": "bank", "login": {"password": "PRIVATE_PASSWORD"}}])
tool = ExactOperation.normalize("vault_get", '{"item_id":"12345678-1234-1234-1234-123456789abc","reason":"requested"}')
bound = resolve_owned_operation(tool, owner="alice", thread_id="s")
assert "SECRET_SESSION" not in json.dumps(bound.to_dict()) and "PRIVATE_PASSWORD" not in json.dumps(bound.to_dict())
cfg.update({"owner": "bob"} if change == "owner" else {"server_url": "https://other.test"} if change == "endpoint" else {"session": "OTHER_SECRET"})
with pytest.raises(ResourceIdentityError):
bound.validate()
def test_legacy_vault_and_model_name_alias_do_not_create_private_identity(monkeypatch):
from src.tools import vault
monkeypatch.setattr(vault, "_load_vault_config", lambda: {"server_url": "https://vault.test", "session": "secret"})
with pytest.raises(ResourceIdentityError):
resolve_owned_operation(ExactOperation.normalize("vault_search", '{"query":"bank"}'), owner="alice", thread_id="s")
with pytest.raises(ResourceIdentityError):
resolve_owned_operation(ExactOperation.normalize("vault_get", '{"item_id":"latest","reason":"requested"}'), owner="alice", thread_id="s")
@pytest.mark.parametrize("error", [None, RuntimeError, asyncio.CancelledError])
async def test_owned_and_backend_context_restore_after_success_error_cancel_and_nested_call(records, monkeypatch, error):
from src import tool_execution as execution
authority = grant("manage_documents")
parent = admit_owned_operation(authority, ExactOperation.normalize("manage_documents", '{"action":"read","id":"d1"}'))
async def implementation(block, **kwargs):
assert active_owned_operation().resources[0].record_id == "d2"
assert active_backend_operation() is not None
if error:
raise error("stop")
return "read", {"exit_code": 0}
monkeypatch.setattr(execution, "_execute_tool_block_impl", implementation)
with bind_owned_operation(parent):
if error:
with pytest.raises(error):
await dispatch(authority, "manage_documents", '{"action":"read","id":"d2"}')
else:
await dispatch(authority, "manage_documents", '{"action":"read","id":"d2"}')
assert active_owned_operation() is parent
assert active_backend_operation() is None
assert active_owned_operation() is None
@pytest.mark.parametrize("path", ["/api/document/d1", "/api/history/s", "/api/vault/config", "/api/memory", "/api/notes",
"/api/upload/upload.png", "/api/%64ocument/d1", "/api/cookbook/../document/d1"])
async def test_generic_internal_bridge_cannot_bypass_owned_resource_adapter(monkeypatch, path):
from src import tool_execution as execution
handler = AsyncMock()
monkeypatch.setattr(execution, "_execute_tool_block_impl", handler)
_, result = await dispatch(grant("app_api"), "app_api", json.dumps({"path": path}))
assert result["failure_kind"] == "resource_identity_denied"
handler.assert_not_awaited()
def test_owned_snapshot_roundtrip_and_malformed_scopes_fail_closed(records):
authority = grant("manage_documents", scopes=(OwnedScope("documents", "alice", "s", frozenset({"d1"})),))
snapshot = json.loads(json.dumps(authority.to_dict()))
assert RequestAuthority.from_dict(snapshot) == authority
for mutation in ({"owner": "bob"}, {"thread_id": "other"}, {"record_ids": ["*"]}, {"record_ids": [1]}):
changed = json.loads(json.dumps(snapshot))
changed["owned_scopes"][0].update(mutation)
with pytest.raises((ValueError, TypeError)):
RequestAuthority.from_dict(changed)
def test_vault_config_owner_is_produced_by_authenticated_request_and_drops_legacy_session():
from routes.vault.vault_routes import _bind_config_owner
from fastapi import HTTPException
request = SimpleNamespace(state=SimpleNamespace(current_user="alice", api_token=False))
cfg = {"session": "legacy-secret", "unlocked_at": "legacy"}
_bind_config_owner(cfg, request)
assert cfg == {"owner": "alice"}
request.state.current_user = "bob"
with pytest.raises(HTTPException):
_bind_config_owner(cfg, request)
def test_missing_proposal_record_is_not_reconstructed_after_it_appears(records):
authority = grant("manage_documents")
exact = approval(authority, "manage_documents", '{"action":"read","id":"not-yet"}')
assert exact.pending.owned_operation is None
import core.database as db
with records() as connection:
connection.add(db.Document(id="not-yet", owner="alice", title="appeared", current_content="content", version_count=1))
connection.commit()
_, result = asyncio.run(dispatch(authority, "manage_documents", exact.pending.content, exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True)))
assert result["failure_kind"] == "resource_identity_denied" and not exact._claimed
def test_malformed_record_identity_and_normalized_approval_tampering_fail_closed(records):
authority = grant("manage_documents")
exact = approval(authority, "manage_documents", '{"action":"read","id":"d1"}')
bound = exact.pending.owned_operation
with pytest.raises(ValueError):
replace(bound, resources=(replace(bound.resources[0], revision=""),))
with pytest.raises(ValueError):
replace(bound, document_id="d2")
exact.pending = replace(exact.pending, owned_operation=replace(bound, execution_input='{"action":"read","document_id":"d2"}'))
assert not exact.matches(owner="alice", session_id="s", workspace=None,
tool_name="manage_documents", content=exact.pending.content)
def test_note_prefix_wildcards_cannot_create_selector_authority(records):
with pytest.raises(ResourceIdentityError):
resolve_owned_operation(ExactOperation.normalize("manage_notes", '{"action":"view","id":"note-o%"}'), owner="alice", thread_id="s")
@pytest.mark.parametrize("content", ['{"action":"read"}', '{"action":"read","id":"active"}', '{"action":"read","id":"current"}', '{"action":"read","id":42}', '{"action":"read","id":"d1","uid":"d2"}'])
def test_missing_malformed_and_conflicting_document_selectors_fail_closed(records, content):
with pytest.raises(ResourceIdentityError):
resolve_owned_operation(ExactOperation.normalize("manage_documents", content), owner="alice", thread_id="s")
async def test_resumed_child_approval_cannot_restore_excluded_record(records):
parent = grant("manage_documents", scopes=(OwnedScope("documents", "alice", "s", frozenset({"d1"})),))
child = parent.intersect(grant("manage_documents"))
exact = approval(child, "manage_documents", '{"action":"read","id":"d2"}')
assert exact.pending.owned_operation is None
_, result = await dispatch(replace(child, inherited=False), "manage_documents", exact.pending.content, exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["failure_kind"] == "resource_identity_denied" and not exact._claimed
async def test_vault_search_producer_supplies_exact_owned_item_identity_and_alias_binding(monkeypatch):
from src.tools import vault
from src import tool_execution as execution
cfg = {"owner": "alice", "server_url": "https://vault.test", "session": "SECRET_SESSION", "unlocked_at": "observed"}
item = {"id": "12345678-1234-1234-1234-123456789abc", "name": "bank", "login": {"password": "PRIVATE_PASSWORD"}}
monkeypatch.setattr(vault, "_load_vault_config", lambda: cfg)
monkeypatch.setattr(execution, "_owner_is_admin", lambda owner: True)
cli = AsyncMock(side_effect=[(json.dumps([item]), "", 0), (json.dumps(item), "", 0)])
monkeypatch.setattr(vault, "_run_bw", cli)
authority = grant("vault_search", "vault_get")
_, missing = await dispatch(authority, "vault_get", json.dumps({"item_id": item["id"], "reason": "requested"}))
assert missing["failure_kind"] == "resource_identity_denied"
cli.assert_not_awaited()
_, search = await dispatch(authority, "vault_search", '{"query":"bank"}')
assert search["exit_code"] == 0 and item["id"] in search["output"]
exact = approval(authority, "vault_get", '{"item_id":"bank","reason":"requested"}')
assert exact.pending.owned_operation.resources[0].record_id == item["id"]
# A later producer result with the same alias cannot change the approved ID.
other = {"id": "87654321-1234-1234-1234-123456789abc", "name": "bank", "login": {"password": "OTHER_PASSWORD"}}
observe_vault_records("alice", cfg, [other])
_, result = await dispatch(authority, "vault_get", exact.pending.content, exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["exit_code"] == 0 and "PRIVATE_PASSWORD" in result["output"] and "OTHER_PASSWORD" not in result["output"]
assert cli.await_args.args[0] == ["get", "item", item["id"]]
with pytest.raises(ResourceIdentityError):
resolve_owned_operation(ExactOperation.normalize("vault_get", exact.pending.content), owner="alice", thread_id="s")
def test_vault_producer_revision_change_invalidates_sealed_item_and_cannot_cross_owner(monkeypatch):
from src.tools import vault
cfg = {"owner": "alice", "server_url": "https://vault.test", "session": "SECRET"}
item = {"id": "12345678-1234-1234-1234-123456789abc", "name": "bank", "revisionDate": "one"}
monkeypatch.setattr(vault, "_load_vault_config", lambda: cfg)
observe_vault_records("alice", cfg, [item])
bound = resolve_owned_operation(ExactOperation.normalize("vault_get", '{"item_id":"12345678","reason":"requested"}'), owner="alice", thread_id="s")
assert json.loads(bound.execution_input)["item_id"] == item["id"]
with pytest.raises(ResourceIdentityError):
observe_vault_records("bob", cfg, [item])
observe_vault_records("alice", cfg, [{**item, "revisionDate": "two"}])
with pytest.raises(ResourceIdentityError):
bound.validate()
+397
View File
@@ -0,0 +1,397 @@
"""Backend selection is resolution, never an operation or resource grant."""
import asyncio
from dataclasses import replace
import json
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from src.agent_runtime.authority import ExactOperation, OperationGrant, RequestAuthority, bind_request_authority
from src.agent_runtime.remote_resources import (
active_backend_operation, bind_backend_operation, bind_backend_for_operation,
configuration_incarnation, endpoint_identity, integration_resource, seal_backends,
)
from src.agent_runtime.resources import ExternalResource, NativeBackendResource, ResourceIdentityError
from src.mcp_manager import McpManager
from src.tool_approvals import ToolApprovalStore
from src.tool_capabilities import ToolRunSecurityContext, capabilities_for_action
from src.tool_types import ToolBlock
def grant(*tools, resources=None):
return RequestAuthority("remote-request", "alice", "s", "",
tuple(OperationGrant(t) for t in tools), backend_resources=resources)
async def dispatch(authority, tool, content="{}", **kwargs):
from src import tool_execution as execution
return await execution.execute_tool_block(ToolBlock(tool, content), owner=authority.owner,
session_id=authority.session_id, request_authority=authority,
security_context=kwargs.pop("security_context", execution.NO_TOOL_SECURITY_CONTEXT), **kwargs)
def approval(authority, tool, content="{}", **kwargs):
store = ToolApprovalStore()
pending = store.create(owner=authority.owner, session_id=authority.session_id, origin_run_id="run",
tool_name=tool, content=content, workspace=None, request_authority=authority,
external_untrusted_context_seen=True, capabilities=capabilities_for_action(tool, content), **kwargs)
return store.consume(pending.approval_id, decision="approve", owner=authority.owner, session_id=authority.session_id)
@pytest.fixture
def manager(monkeypatch):
from src import tool_execution as execution
value = McpManager()
monkeypatch.setattr(execution, "get_mcp_manager", lambda: value)
monkeypatch.setattr(execution, "_owner_is_admin", lambda owner: True)
return value
def connect(manager, server="alpha", tools=("read", "write"), url="https://example.test/mcp?token=SECRET"):
session = SimpleNamespace(call_tool=AsyncMock(return_value=SimpleNamespace(
content=[SimpleNamespace(text="remote result")], isError=False)))
manager._sessions[server] = session
manager._tools[server] = [{"name": tool} for tool in tools]
manager._resource_endpoints[server] = (endpoint_identity(url), configuration_incarnation(url))
manager._register_resource_connection(server, session)
return session
@pytest.mark.parametrize("kind", ["availability", "selection", "model_name", "legacy"])
async def test_remote_availability_does_not_create_resource_authority(manager, kind):
session = connect(manager)
authority = grant("mcp__alpha__read", resources=()) if kind != "model_name" else grant()
if kind == "legacy":
snapshot = grant("mcp__alpha__read").to_dict()
snapshot["version"] = 2
authority = RequestAuthority.from_dict(snapshot)
_, result = await dispatch(authority, "mcp__alpha__read")
assert result["exit_code"] == 1
session.call_tool.assert_not_awaited()
async def test_qualified_mcp_binds_exact_tool_and_backend(manager):
session = connect(manager)
authority = grant("mcp__alpha__read")
_, allowed = await dispatch(authority, "mcp__alpha__read", '{"record":"one"}')
assert allowed["exit_code"] == 0
session.call_tool.assert_awaited_once_with("read", {"record": "one"})
_, denied = await dispatch(replace(authority, grants=(OperationGrant("mcp__alpha__write"),)), "mcp__alpha__write")
assert denied["failure_kind"] == "resource_identity_denied"
assert active_backend_operation() is None
@pytest.mark.parametrize("change", ["session", "endpoint", "path", "query", "discovery"])
async def test_remote_identity_changes_invalidate_admission_and_exact_approval(manager, change):
old = connect(manager)
authority = grant("mcp__alpha__read")
exact = approval(authority, "mcp__alpha__read", '{"resource":"one"}')
assert exact.pending.backend_operation is not None
if change == "session":
new = connect(manager)
elif change == "discovery":
manager._tools["alpha"] = [{"name": "write"}]
else:
url = {"endpoint": "https://other.test/mcp", "path": "https://example.test/other",
"query": "https://example.test/mcp?token=OTHER"}[change]
manager._resource_endpoints["alpha"] = (endpoint_identity(url), configuration_incarnation(url))
for extra in ({}, {"exact_approval": exact, "security_context": ToolRunSecurityContext(external_untrusted_context_seen=True)}):
_, result = await dispatch(authority, "mcp__alpha__read", '{"resource":"one"}', **extra)
assert result["failure_kind"] == "resource_identity_denied"
old.call_tool.assert_not_awaited()
if change == "session":
new.call_tool.assert_not_awaited()
assert not exact._claimed
@pytest.mark.parametrize("change", ["tool", "selector", "request", "owner", "session"])
async def test_remote_approval_is_bound_to_operation_and_request(manager, change):
session = connect(manager)
authority = grant("mcp__alpha__read", "mcp__alpha__write")
exact = approval(authority, "mcp__alpha__read", '{"record":"one"}')
tool, content = "mcp__alpha__read", '{"record":"one"}'
if change == "tool":
tool = "mcp__alpha__write"
elif change == "selector":
content = '{"record":"two"}'
else:
authority = replace(authority, **{"request": {"request_id": "other"}, "owner": {"owner": "bob"},
"session": {"session_id": "other"}}[change])
_, result = await dispatch(authority, tool, content, exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["exit_code"] == 1
session.call_tool.assert_not_awaited()
assert not exact._claimed
async def test_legacy_exact_remote_approval_is_one_use_and_does_not_mint_backend_scope(manager):
session = connect(manager)
authority = grant(resources=())
exact = approval(authority, "mcp__alpha__read")
security = ToolRunSecurityContext(external_untrusted_context_seen=True)
_, result = await dispatch(authority, "mcp__alpha__read", exact_approval=exact, security_context=security)
assert result["exit_code"] == 0
_, replay = await dispatch(authority, "mcp__alpha__read", exact_approval=exact, security_context=security)
assert replay["exit_code"] == 1
assert session.call_tool.await_count == 1 and authority.backend_resources == ()
assert "backend_operation" not in exact.pending.public_payload()
async def test_child_cannot_use_parent_ungranted_backend_or_exact_approval(manager):
session = connect(manager)
parent = grant("mcp__alpha__read", resources=())
child = grant("mcp__alpha__read")
exact = approval(child, "mcp__alpha__read")
with bind_request_authority(parent):
_, denied = await dispatch(child, "mcp__alpha__read", exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert denied["failure_kind"] == "resource_identity_denied"
session.call_tool.assert_not_awaited()
def test_remote_snapshots_exclude_credentials_and_cannot_claim_containment(manager):
connect(manager, url="https://user:PASSWORD@example.test/SECRET_PATH?token=TOKEN")
authority = grant("mcp__alpha__read")
snapshot = json.dumps(authority.to_dict())
assert all(secret not in snapshot for secret in ("PASSWORD", "SECRET_PATH", "TOKEN", "user:"))
resource = authority.backend_resources[0]
assert resource.endpoint_id == "https://example.test"
assert resource.external is True and resource.contained is False
assert RequestAuthority.from_dict(json.loads(snapshot)) == authority
with pytest.raises(ValueError):
replace(resource, contained=True)
async def test_mcp_revalidates_at_transport_and_never_retries_bound_calls(manager):
manager._resource_owners["memory"] = "alice"
session = connect(manager, server="memory")
authority = grant("mcp__memory__read")
operation = ExactOperation.normalize("mcp__memory__read", "{}")
bound = bind_backend_for_operation(authority, operation)
reconnect = AsyncMock()
manager._reconnect_builtin = reconnect
session.call_tool.side_effect = RuntimeError("disconnected")
with bind_backend_operation(bound):
result = await manager.call_tool(operation.tool, {})
assert result["exit_code"] == 1
replacement = connect(manager, server="memory")
result = await manager.call_tool(operation.tool, {})
assert result["failure_kind"] == "resource_identity_denied"
replacement.call_tool.assert_not_awaited()
reconnect.assert_not_awaited()
@pytest.mark.parametrize("owner", ["", "bob"])
async def test_builtin_memory_backend_requires_its_configured_owner(manager, owner):
manager._resource_owners["memory"] = owner
session = connect(manager, server="memory")
_, result = await dispatch(grant("mcp__memory__read"), "mcp__memory__read")
assert result["failure_kind"] == "resource_identity_denied"
session.call_tool.assert_not_awaited()
async def test_native_filesystem_cannot_be_redirected_through_mcp(manager, tmp_path):
session = connect(manager, server="filesystem", tools=("read_file",))
(tmp_path / "a").write_text("native contents")
authority = RequestAuthority("request", "alice", "s", str(tmp_path), (OperationGrant("read_file"),))
from src import tool_execution as execution
_, result = await execution.execute_tool_block(ToolBlock("read_file", "a"), owner="alice", session_id="s",
workspace=str(tmp_path), request_authority=authority, security_context=execution.NO_TOOL_SECURITY_CONTEXT)
assert result["output"] == "native contents"
assert isinstance(authority.backend_resources[0], NativeBackendResource)
session.call_tool.assert_not_awaited()
@pytest.mark.parametrize("change", ["alias", "endpoint", "secret_path"])
async def test_integration_alias_and_configuration_cannot_retarget_approval(monkeypatch, change):
from src import integrations
rows = [{"id": "one", "name": "service", "base_url": "https://service.test/SECRET", "enabled": True}]
monkeypatch.setattr(integrations, "load_integrations", lambda: rows)
authority = grant("api_call", resources=(integration_resource(rows[0]),))
exact = approval(authority, "api_call", '{"integration":"service","path":"/record/one"}')
assert exact.pending.backend_operation.resource.server_id == "one"
assert "SECRET" not in json.dumps(authority.to_dict())
if change == "alias":
rows[:] = [{**rows[0], "id": "two"}]
else:
rows[0]["base_url"] = "https://other.test/SECRET" if change == "endpoint" else "https://service.test/OTHER"
from src import tool_execution as execution
handler = AsyncMock()
monkeypatch.setattr(execution, "_execute_tool_block_impl", handler)
_, result = await dispatch(authority, "api_call", exact.pending.content, exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["failure_kind"] == "resource_identity_denied"
handler.assert_not_awaited()
@pytest.mark.parametrize("error", [None, RuntimeError, asyncio.CancelledError])
async def test_scoped_bridge_context_restores_and_replacement_is_ungranted(monkeypatch, error):
from src import tool_execution as execution
seen = []
async def route(*args):
seen.append(active_backend_operation().resource)
if error:
raise error("stop")
return "bridge", {"exit_code": 0}
bridge = execution.AgentExecutionBridge(route, frozenset({"host_shell"}), name="test")
monkeypatch.setattr(execution, "_owner_is_admin", lambda owner: True)
with execution.bind_execution_bridge(bridge):
authority = grant("host_shell")
parent_bound = bind_backend_for_operation(authority, ExactOperation.normalize("host_shell", "parent"))
with bind_backend_operation(parent_bound):
if error is asyncio.CancelledError:
with pytest.raises(error):
await dispatch(authority, "host_shell", "pwd")
else:
await dispatch(authority, "host_shell", "pwd")
assert active_backend_operation() is parent_bound
assert active_backend_operation() is None
assert seen[0].external and not seen[0].contained
with execution.bind_execution_bridge(replace(bridge)):
_, denied = await dispatch(authority, "host_shell", "pwd")
assert denied["failure_kind"] == "resource_identity_denied"
async def test_tui_endpoint_is_registered_only_at_trusted_admission(monkeypatch):
from src import tool_execution as execution
context = {"surface": "odysseus-tui", "host_shell_bridge": {"url": "http://127.0.0.1:17654/run", "token": "TOKEN"}}
authority = grant("bash", resources=(NativeBackendResource("bash"),))
_, denied = await dispatch(authority, "bash", "pwd", client_runtime_context=context)
assert denied["failure_kind"] == "resource_identity_denied"
authority = replace(authority, backend_resources=seal_backends(["bash"], context=context, owner="alice"))
monkeypatch.setattr(execution, "_bridge_post", AsyncMock(return_value={"exit_code": 0, "stdout": "external", "stderr": ""}))
monkeypatch.setattr(execution, "_owner_is_admin", lambda owner: True)
_, allowed = await dispatch(authority, "bash", "pwd", client_runtime_context=context)
assert allowed["exit_code"] == 0
context["host_shell_bridge"]["url"] = "http://127.0.0.1:17655/run"
_, denied = await dispatch(authority, "bash", "pwd", client_runtime_context=context)
assert denied["failure_kind"] == "resource_identity_denied"
@pytest.mark.parametrize("change", [None, "url", "token"])
async def test_http_bridge_factory_and_admission_share_config_identity(monkeypatch, change):
from src import tool_execution as execution
from routes.chat_routes import _external_execution_bridge
context = {"external_execution_bridge": {"url": "http://127.0.0.1:17654/execute",
"token": "SECRET_TOKEN", "supported_tools": ["host_shell"]}}
authority = grant("host_shell", resources=seal_backends(["host_shell"], context=context, owner="alice"))
if change:
context["external_execution_bridge"][change] = "http://127.0.0.1:17655/execute" if change == "url" else "OTHER_TOKEN"
bridge = _external_execution_bridge(context)
route = AsyncMock(return_value=("bridge", {"exit_code": 0}))
bridge = replace(bridge, route_tool=route)
monkeypatch.setattr(execution, "_owner_is_admin", lambda owner: True)
with execution.bind_execution_bridge(bridge):
_, result = await dispatch(authority, "host_shell", "pwd", client_runtime_context=context)
if change:
assert result["failure_kind"] == "resource_identity_denied"
route.assert_not_awaited()
else:
assert result["exit_code"] == 0
route.assert_awaited_once()
assert "SECRET_TOKEN" not in json.dumps(authority.to_dict())
async def test_backend_alias_cannot_retarget_a_legacy_tool_after_approval(manager, monkeypatch):
from src import tool_execution as execution
first = connect(manager, server="web_fetch", tools=("web_fetch",))
second = connect(manager, server="other", tools=("fetch",))
authority = grant("web_fetch")
exact = approval(authority, "web_fetch", "https://page.test/one")
monkeypatch.setitem(execution._MCP_TOOL_MAP, "web_fetch", ("other", "fetch"))
_, result = await dispatch(authority, "web_fetch", exact.pending.content, exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["failure_kind"] == "resource_identity_denied"
first.call_tool.assert_not_awaited()
second.call_tool.assert_not_awaited()
async def test_native_backend_is_pinned_when_mcp_becomes_available(manager, monkeypatch):
from src import tool_execution as execution
authority = grant("web_fetch")
session = connect(manager, server="web_fetch", tools=("web_fetch",))
fallback = AsyncMock(return_value={"output": "native", "exit_code": 0})
monkeypatch.setattr(execution, "_direct_fallback", fallback)
_, result = await dispatch(authority, "web_fetch", "https://page.test/one")
assert result["output"] == "native"
session.call_tool.assert_not_awaited()
async def test_integration_inventory_does_not_supply_backend_scope(monkeypatch):
from src import integrations
rows = [{"id": "one", "name": "service", "base_url": "https://service.test", "enabled": True}]
monkeypatch.setattr(integrations, "load_integrations", lambda: rows)
authority = grant("api_call")
assert authority.backend_resources == ()
_, denied = await dispatch(authority, "api_call", '{"integration":"service"}')
assert denied["failure_kind"] == "resource_identity_denied"
async def test_external_bash_marker_cannot_switch_to_local_background_execution(manager, monkeypatch):
from src import bg_jobs
session = connect(manager, server="bash", tools=("bash",))
launch = AsyncMock()
monkeypatch.setattr(bg_jobs, "launch", launch)
_, result = await dispatch(grant("bash"), "bash", "#!bg\npwd")
assert result["exit_code"] == 0
assert session.call_tool.await_count == 1
launch.assert_not_called()
@pytest.mark.parametrize("alias", ["host_shell_bridge", "hostShellBridge"])
def test_host_shell_bridge_aliases_resolve_to_one_external_identity(alias):
context = {"surface": "odysseus-tui", alias: {"url": "http://127.0.0.1:17654/run", "token": "TOKEN"}}
resources = seal_backends(["host_shell"], context=context, owner="alice")
assert len(resources) == 1 and isinstance(resources[0], ExternalResource)
assert resources[0].external and not resources[0].contained
async def test_host_shell_cannot_reconstruct_an_unsealed_external_backend(monkeypatch):
from src import tool_execution as execution
handler = AsyncMock()
monkeypatch.setattr(execution, "_execute_tool_block_impl", handler)
_, result = await dispatch(grant("host_shell"), "host_shell", "pwd")
assert result["failure_kind"] == "resource_identity_denied"
handler.assert_not_awaited()
async def test_http_backend_without_bound_producer_cannot_fall_back_to_native(monkeypatch):
from src import tool_execution as execution
context = {"external_execution_bridge": {"url": "http://127.0.0.1:17654/execute",
"token": "TOKEN", "supported_tools": ["bash"]}}
authority = grant("bash", resources=seal_backends(["bash"], context=context, owner="alice"))
fallback = AsyncMock()
monkeypatch.setattr(execution, "_direct_fallback", fallback)
_, denied = await dispatch(authority, "bash", "pwd", client_runtime_context=context)
assert denied["failure_kind"] == "resource_identity_denied"
fallback.assert_not_awaited()
async def test_resumed_child_approval_cannot_restore_excluded_backend(manager):
session = connect(manager)
child = grant("mcp__alpha__read", resources=()).intersect(grant("mcp__alpha__read"))
exact = approval(child, "mcp__alpha__read")
assert exact.pending.backend_operation is None
_, result = await dispatch(replace(child, inherited=False), "mcp__alpha__read", exact_approval=exact,
security_context=ToolRunSecurityContext(external_untrusted_context_seen=True))
assert result["failure_kind"] == "resource_identity_denied"
session.call_tool.assert_not_awaited()
async def test_integration_executes_server_resolved_id_and_revalidates_loaded_configuration(monkeypatch):
from src import integrations, tool_execution as execution
row = {"id": "one", "name": "service", "base_url": "https://service.test", "enabled": True}
monkeypatch.setattr(integrations, "load_integrations", lambda: [dict(row)])
monkeypatch.setattr(execution, "_owner_is_admin", lambda owner: True)
authority = grant("api_call", resources=(integration_resource(row),))
producer = AsyncMock(return_value={"output": "remote", "exit_code": 0})
original = integrations.execute_api_call
monkeypatch.setattr(integrations, "execute_api_call", producer)
_, allowed = await dispatch(authority, "api_call", '{"integration":"service","method":"GET","path":"/record/one"}')
assert allowed["exit_code"] == 0
assert producer.await_args.args == ("one", "GET", "/record/one")
monkeypatch.setattr(integrations, "execute_api_call", original)
monkeypatch.setattr(integrations, "_find_integration", lambda identifier: {**row, "base_url": "https://other.test"})
_, denied = await dispatch(authority, "api_call", '{"integration":"service","path":"/record/one"}')
assert denied["failure_kind"] == "resource_identity_denied"
+214 -3
View File
@@ -81,6 +81,31 @@ def test_symlink_escape_is_not_a_resource(tmp_path):
resolve(authority(workspace, "read_file"), "read_file", "alias")
@pytest.mark.parametrize("alias", ["direct", "relative", "symlink", "hardlink"])
@pytest.mark.parametrize("must_exist", [True, False])
def test_media_workspace_paths_cannot_address_control_state(tmp_path, monkeypatch, alias, must_exist):
from src import constants, tool_execution
from src.agent_tools.media_tools import _resolve_workspace_path
control = tmp_path / "receipts.json"
control.write_text("private execution state")
monkeypatch.setattr(constants, "CONTAINMENT_STATE_FILE", str(control))
monkeypatch.setattr(tool_execution, "get_active_workspace", lambda: str(tmp_path))
if alias == "direct":
selector = str(control)
elif alias == "relative":
selector = "./receipts.json"
else:
target = tmp_path / "image.png"
if alias == "symlink":
target.symlink_to(control)
else:
os.link(control, target)
selector = "/workspace/image.png"
with pytest.raises(ValueError, match="execution-control"):
_resolve_workspace_path(selector, must_exist=must_exist)
assert control.read_text() == "private execution state"
def test_destination_binds_absence_and_existing_ancestors(tmp_path):
parent = tmp_path / "existing"
parent.mkdir()
@@ -143,6 +168,93 @@ async def test_user_filesystem_scope_cannot_write_server_execution_state(tmp_pat
assert not (tmp_path / target).exists()
@pytest.mark.parametrize("state", ["authority", "jobs", "containment", "result", "exit", "database", "vault", "uploads"])
@pytest.mark.parametrize("alias", ["direct", "relative", "symlink", "hardlink"])
async def test_control_files_cannot_be_read_or_written_through_aliases(tmp_path, monkeypatch, state, alias):
import src.constants as constants
jobs = tmp_path / "jobs"
jobs.mkdir()
monkeypatch.setattr(constants, "BG_JOBS_DIR", str(jobs))
monkeypatch.setattr(constants, "DATA_DIR", str(tmp_path))
monkeypatch.setattr(constants, "UPLOAD_DIR", str(tmp_path / "uploads"))
for name, filename in (("BG_JOBS_FILE", "jobs.json"), ("CONTAINMENT_STATE_FILE", "receipts.json"),
("APP_DB", "private.db"), ("VAULT_FILE", "vault.json")):
monkeypatch.setattr(constants, name, str(tmp_path / filename))
filename = {"authority": "jobs/job.authority.json", "jobs": "jobs.json", "containment": "receipts.json",
"result": "jobs/job.result.json", "exit": "jobs/job.exit", "database": "private.db",
"vault": "vault.json", "uploads": "uploads/uploads.json"}[state]
target = tmp_path / filename
target.parent.mkdir(exist_ok=True)
target.write_text("control-secret")
selector = str(target)
if alias == "relative":
selector = "./" + filename
elif alias in {"symlink", "hardlink"}:
link = tmp_path / "ordinary.txt"
try:
link.symlink_to(target) if alias == "symlink" else os.link(target, link)
except OSError as error:
pytest.skip(f"Platform cannot create {alias}: {error}")
selector = str(link)
grant = authority(tmp_path, "read_file", "write_file")
for tool, content in (("read_file", selector), ("write_file", selector + "\nforged")):
_, result = await dispatch(grant, tool, content)
assert result["failure_kind"] == "resource_identity_denied"
assert target.read_text() == "control-secret"
async def test_directory_grep_does_not_scan_control_state_or_hardlinks(tmp_path, monkeypatch):
import src.constants as constants
control = tmp_path / "jobs.json"
control.write_text("UNIQUE_CONTROL_SECRET")
(tmp_path / "ordinary").write_text("visible text")
os.link(control, tmp_path / "innocent.txt")
monkeypatch.setattr(constants, "BG_JOBS_FILE", str(control))
_, result = await dispatch(authority(tmp_path, "grep"), "grep", '{"pattern":"UNIQUE_CONTROL_SECRET","path":"."}')
assert result["exit_code"] == 0
assert "No matches" in result["output"]
@pytest.mark.parametrize("tool,content", [("glob", '{"pattern":"*.json","path":"."}'), ("ls", ".")])
async def test_directory_enumeration_does_not_address_control_files(tmp_path, monkeypatch, tool, content):
import src.constants as constants
control = tmp_path / "jobs.json"
control.write_text("control")
monkeypatch.setattr(constants, "BG_JOBS_FILE", str(control))
_, result = await dispatch(authority(tmp_path, tool), tool, content)
assert result["exit_code"] == 0
assert "jobs.json" not in result["output"]
@pytest.mark.parametrize("producer", ["database", "containment", "jobs", "uploads"])
@pytest.mark.parametrize("alias", ["direct", "hardlink"])
async def test_configured_control_producer_paths_are_protected(tmp_path, monkeypatch, producer, alias):
target = tmp_path / "custom" / "state"
target.parent.mkdir()
if producer == "database":
import core.database as database
monkeypatch.setattr(database, "engine", SimpleNamespace(url=SimpleNamespace(
get_backend_name=lambda: "sqlite", database=str(target))))
elif producer == "containment":
from src import containment
monkeypatch.setattr(containment, "_store_path", lambda: target)
elif producer == "jobs":
from src import bg_jobs
monkeypatch.setattr(bg_jobs, "_STORE", target)
else:
from src import tool_utils
target = target.parent / "uploads.json"
monkeypatch.setattr(tool_utils, "get_upload_handler", lambda: SimpleNamespace(upload_dir=str(target.parent)))
target.write_text("server state")
selector = str(target)
if alias == "hardlink":
link = tmp_path / "ordinary"
os.link(target, link)
selector = str(link)
_, result = await dispatch(authority(tmp_path, "read_file"), "read_file", selector)
assert result["failure_kind"] == "resource_identity_denied"
@pytest.mark.parametrize("roots", [(), None])
async def test_nonworkspace_allowlist_and_operation_do_not_grant_resources(tmp_path, monkeypatch, roots):
from src import tool_execution as execution
@@ -217,6 +329,43 @@ def test_child_cannot_renew_replaced_parent_root(tmp_path):
assert parent.intersect(child).resource_roots == ()
@pytest.mark.parametrize("caller", ["intersection", "context", "task"])
@pytest.mark.parametrize("child_location", ["root", "subtree"])
def test_replaced_parent_cannot_be_renewed_by_new_child_observation(tmp_path, caller, child_location):
root = tmp_path / "root"
root.mkdir()
parent = authority(root, "read_file")
root.rename(tmp_path / "old")
root.mkdir()
sub = root / "sub"
sub.mkdir()
(sub / "a").write_text("replacement")
child_root = FilesystemRoot.seal(root if child_location == "root" else sub, owner="alice")
child = authority(root, "read_file", roots=(child_root,))
if caller == "intersection":
effective = parent.intersect(child)
elif caller == "context":
with bind_request_authority(parent), bind_request_authority(child) as effective:
assert effective.resource_roots == ()
else:
with bind_request_authority(parent):
sealed = seal_task_authority("Read files in the workspace", "llm", None, owner="alice")
effective = restore_task_authority(sealed, "Read files in the workspace", "llm", None,
owner="alice", session_id="continuation")
assert effective.resource_roots == ()
with pytest.raises(ValueError):
resolve(effective, "read_file", "sub/a")
def test_equal_stale_roots_are_revalidated(tmp_path):
root = tmp_path / "root"
root.mkdir()
parent = authority(root, "read_file")
root.rename(tmp_path / "old")
root.mkdir()
assert parent.intersect(parent).resource_roots == ()
@pytest.mark.parametrize("legacy", [False, True])
async def test_snapshot_preserves_incarnation_and_never_reconstructs_legacy(tmp_path, legacy):
root = tmp_path / "root"
@@ -336,7 +485,7 @@ async def test_last_dispatch_validation_refuses_replacement_and_resets_context(t
assert execution.get_active_workspace() is None
@pytest.mark.parametrize("error_type", [RuntimeError, asyncio.CancelledError])
@pytest.mark.parametrize("error_type", [None, RuntimeError, asyncio.CancelledError])
async def test_nested_resource_context_restores_on_failure_or_cancellation(tmp_path, monkeypatch, error_type):
from src import tool_execution as execution
for name in ("parent", "child"):
@@ -345,10 +494,15 @@ async def test_nested_resource_context_restores_on_failure_or_cancellation(tmp_p
parent = resolve(grant, "read_file", "parent")
async def implementation(block, **kwargs):
assert active_resource_operation().bindings[0].resource.path == str(tmp_path / "child")
raise error_type("stop")
if error_type:
raise error_type("stop")
return "read", {"exit_code": 0}
monkeypatch.setattr(execution, "_execute_tool_block_impl", implementation)
with bind_resource_operation(parent):
with pytest.raises(error_type):
if error_type:
with pytest.raises(error_type):
await dispatch(grant, "read_file", "child")
else:
await dispatch(grant, "read_file", "child")
assert active_resource_operation() is parent
assert active_resource_operation() is None
@@ -480,6 +634,63 @@ async def test_exact_user_approval_binds_only_one_missing_destination(tmp_path):
assert not (tmp_path / "other.txt").exists()
@pytest.mark.parametrize("version", [1, 2])
async def test_restored_empty_roots_approval_is_exact_and_never_restores_generic_scope(tmp_path, version):
(tmp_path / "approved").write_text("approved content")
(tmp_path / "sibling").write_text("private sibling")
snapshot = authority(tmp_path, "read_file", "write_file", "ls").to_dict()
snapshot["version"] = version
snapshot["resource_roots"] = []
restored = RequestAuthority.from_dict(snapshot)
exact, security = approval(restored, "read_file", "approved")
assert exact.pending.resource_operation is not None
for tool, content in (("read_file", "sibling"), ("ls", "."), ("write_file", "sibling\nx")):
_, blocked = await dispatch(restored, tool, content, exact_approval=exact, security_context=security)
assert blocked["exit_code"] == 1
_, unapproved = await dispatch(restored, tool, content)
assert unapproved["failure_kind"] == "resource_identity_denied"
_, allowed = await dispatch(restored, "read_file", "approved", exact_approval=exact, security_context=security)
assert allowed["output"] == "approved content"
_, replay = await dispatch(restored, "read_file", "approved", exact_approval=exact, security_context=security)
assert replay["exit_code"] == 1
assert restored.resource_roots == () and restored.backend_resources == ()
assert (tmp_path / "sibling").read_text() == "private sibling"
@pytest.mark.parametrize("change", ["alias", "request", "session", "owner"])
async def test_restored_exact_filesystem_binding_rejects_retarget_and_rebinding(tmp_path, change):
(tmp_path / "a").write_text("a")
(tmp_path / "b").write_text("b")
(tmp_path / "alias").symlink_to(tmp_path / "a")
snapshot = authority(tmp_path, "read_file").to_dict()
snapshot["version"] = 1
restored = RequestAuthority.from_dict(snapshot)
exact, security = approval(restored, "read_file", "alias")
if change == "alias":
(tmp_path / "alias").unlink()
(tmp_path / "alias").symlink_to(tmp_path / "b")
else:
restored = replace(restored, **{"request": {"request_id": "other"},
"session": {"session_id": "other"}, "owner": {"owner": "bob"}}[change])
_, result = await dispatch(restored, "read_file", "alias", exact_approval=exact, security_context=security)
assert result["exit_code"] == 1
assert exact.matches(owner="alice", session_id="s", workspace=str(tmp_path), tool_name="read_file", content="alias")
async def test_resumed_child_approval_cannot_renew_replaced_parent_root(tmp_path):
root = tmp_path / "root"
root.mkdir()
parent = authority(root, "read_file")
root.rename(tmp_path / "old")
root.mkdir()
(root / "new").write_text("replacement")
child = parent.intersect(authority(root, "read_file"))
exact, security = approval(child, "read_file", "new")
assert child.resource_roots == () and exact.pending.resource_operation is None
_, result = await dispatch(replace(child, inherited=False), "read_file", "new", exact_approval=exact, security_context=security)
assert result["failure_kind"] == "resource_identity_denied" and not exact._claimed
@pytest.mark.parametrize("request_text,denied", [
("Transcribe /workspace/audio.wav", "read_file"),
("OCR extract exact text from /workspace/image.png", "write_file"),
+10
View File
@@ -204,6 +204,16 @@ async def test_dispatcher_claims_approval_immediately_before_execution(monkeypat
@pytest.mark.asyncio
async def test_dispatcher_uses_sealed_document_target(monkeypatch):
import src.tool_execution as tool_execution
from datetime import datetime
from types import SimpleNamespace
from src.agent_runtime import owned_resources
# This dispatcher fixture seals an observed owned row, as production does;
# model/document text alone cannot stand in for a resource identity.
row = SimpleNamespace(id="document-7", owner="alice", session_id="session-1",
version_count=4, current_content="original", created_at=datetime(2026, 1, 1),
updated_at=datetime(2026, 1, 2))
monkeypatch.setattr(owned_resources, "_row", lambda namespace, identifier, owner: row
if (namespace, identifier, owner) == ("documents", "document-7", "alice") else None)
store = ToolApprovalStore()
content = '{"content":"replacement"}'