Spaces:
Paused
Paused
| """Tests for delegate_tool toolset scoping. | |
| Verifies that subagents cannot gain tools that the parent does not have. | |
| The LLM controls the `toolsets` parameter — without intersection with the | |
| parent's enabled_toolsets, it can escalate privileges by requesting | |
| arbitrary toolsets. | |
| """ | |
| from unittest.mock import MagicMock, patch | |
| from types import SimpleNamespace | |
| from tools.delegate_tool import _strip_blocked_tools | |
| class TestToolsetIntersection: | |
| """Subagent toolsets must be a subset of parent's enabled_toolsets.""" | |
| def test_requested_toolsets_intersected_with_parent(self): | |
| """LLM requests toolsets parent doesn't have — extras are dropped.""" | |
| parent = SimpleNamespace(enabled_toolsets=["terminal", "file"]) | |
| # Simulate the intersection logic from _build_child_agent | |
| parent_toolsets = set(parent.enabled_toolsets) | |
| requested = ["terminal", "file", "web", "browser", "rl"] | |
| scoped = [t for t in requested if t in parent_toolsets] | |
| assert sorted(scoped) == ["file", "terminal"] | |
| assert "web" not in scoped | |
| assert "browser" not in scoped | |
| assert "rl" not in scoped | |
| def test_all_requested_toolsets_available_on_parent(self): | |
| """LLM requests subset of parent tools — all pass through.""" | |
| parent = SimpleNamespace(enabled_toolsets=["terminal", "file", "web", "browser"]) | |
| parent_toolsets = set(parent.enabled_toolsets) | |
| requested = ["terminal", "web"] | |
| scoped = [t for t in requested if t in parent_toolsets] | |
| assert sorted(scoped) == ["terminal", "web"] | |
| def test_no_toolsets_requested_inherits_parent(self): | |
| """When toolsets is None/empty, child inherits parent's set.""" | |
| parent_toolsets = ["terminal", "file", "web"] | |
| child = _strip_blocked_tools(parent_toolsets) | |
| assert "terminal" in child | |
| assert "file" in child | |
| assert "web" in child | |
| def test_strip_blocked_removes_delegation(self): | |
| """Blocked toolsets (delegation, clarify, etc.) are always removed.""" | |
| child = _strip_blocked_tools(["terminal", "delegation", "clarify", "memory"]) | |
| assert "delegation" not in child | |
| assert "clarify" not in child | |
| assert "memory" not in child | |
| assert "terminal" in child | |
| def test_empty_intersection_yields_empty_toolsets(self): | |
| """If parent has no overlap with requested, child gets nothing extra.""" | |
| parent = SimpleNamespace(enabled_toolsets=["terminal"]) | |
| parent_toolsets = set(parent.enabled_toolsets) | |
| requested = ["web", "browser"] | |
| scoped = [t for t in requested if t in parent_toolsets] | |
| assert scoped == [] | |