mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(agents): block subagent spawning at the Claude Code level (disallow Task) (#377)
The fleet-wide subagent ban was implemented as an allowlist omission, but Task is a default-permitted Claude Code built-in — an allowlist auto-approves, it does not restrict. Under permission_mode="dontAsk" (intake/secretary SDK) and defaultMode="bypassPermissions" (fleet), Task ran regardless and can_use_tool was never invoked for it, so every Claude-path agent could still spawn subagents despite allows_subagent=False. Only the grok path blocked it. Explicitly disallow the subagent tool at every Claude-path spawn point: disallowed_tools=["Task"] on the intake and secretary SDK drivers, and "Task" in the fleet settings.json base_deny (an explicit deny applies even under bypassPermissions). This mirrors the grok path's --disallowed-tools Agent. Pins the ban in test_cc_lockdown.py (fleet settings deny Task) and a new test_sdk_driver_subagent_ban.py (intake + secretary options disallow Task). Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -106,6 +106,41 @@ class TestSharedClaudeCredentialsDenied:
|
||||
assert inner.startswith("//"), f"must use // absolute form: {entry}"
|
||||
|
||||
|
||||
class TestSubagentBanned:
|
||||
"""Every role's settings.json denies the `Task` subagent tool.
|
||||
|
||||
`Task` is a default-permitted Claude Code built-in; under
|
||||
defaultMode=bypassPermissions the manifest/allowlist omission does NOT
|
||||
remove it — only an explicit `permissions.deny` entry does. Without this
|
||||
the fleet-wide subagent ban (CEO, 2026-07-09) is unenforced on the Claude
|
||||
path (the grok path already blocks it via `--disallowed-tools Agent`).
|
||||
"""
|
||||
|
||||
def test_developer_settings_deny_task(self) -> None:
|
||||
orch = _orch()
|
||||
path = orch._generate_agent_settings(
|
||||
agent_id="be-dev-1",
|
||||
role="developer",
|
||||
workspace_path=_WS,
|
||||
cell_workspace_path=_CELL,
|
||||
)
|
||||
deny = json.loads(Path(path).read_text())["permissions"]["deny"]
|
||||
assert "Task" in deny, deny
|
||||
|
||||
def test_read_only_role_also_denies_task(self) -> None:
|
||||
"""A read-only role (pr_reviewer, the top prompt-injection target)
|
||||
gets the same base_deny → no subagent escape hatch."""
|
||||
orch = _orch()
|
||||
path = orch._generate_agent_settings(
|
||||
agent_id="be-pr-reviewer",
|
||||
role="pr_reviewer",
|
||||
workspace_path=_WS,
|
||||
cell_workspace_path=_CELL,
|
||||
)
|
||||
deny = json.loads(Path(path).read_text())["permissions"]["deny"]
|
||||
assert "Task" in deny, deny
|
||||
|
||||
|
||||
class TestSlashCommandsDisabled:
|
||||
"""--disable-slash-commands accompanies every container agent spawn."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user