feat(security): add allowedTools — universal tool restriction across …#125
Conversation
…all providers Add role-based tool restrictions that translate CAO's unified tool vocabulary (execute_bash, fs_read, fs_write, fs_*, @cao-mcp-server) to each provider's native enforcement mechanism: - Q CLI / Kiro CLI: allowedTools in agent JSON (install time) - Claude Code: --disallowedTools flags alongside --dangerously-skip-permissions - Copilot CLI: --deny-tool flags override --allow-all - Gemini CLI: Policy Engine TOML deny rules in ~/.gemini/policies/ - Kimi CLI / Codex: Security system prompt (soft enforcement) Key changes: - AgentProfile: add role field (supervisor/developer/reviewer) - Constants: ROLE_TOOL_DEFAULTS with per-role defaults - launch.py: --allowed-tools and --yolo CLI flags, confirmation prompt - New utils/tool_mapping.py: CAO-to-native tool name translation - All 7 providers: native restriction flags in command builders - Database: allowed_tools column for cross-provider inheritance - MCP server: allowed_tools inheritance for handoff/assign - Built-in profiles: role + allowedTools frontmatter + security constraints - SECURITY.md: full documentation of tool restriction system - E2E tests: file-based bash execution proof across all providers Gemini CLI enforcement uses Policy Engine deny rules (not deprecated excludeTools) which work even in --yolo mode by completely excluding denied tools from the model's memory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- README.md: add Tool Restrictions section with role defaults, usage examples, and provider enforcement table - docs/tool-restrictions.md: comprehensive guide covering unified CLI (--allowed-tools, --yolo), role-based defaults, CAO tool vocabulary, resolution order, per-provider behavior matrix, cross-provider inheritance, and security recommendations - docs/agent-profile.md: add role field, allowedTools documentation, tool vocabulary reference, and resolution order - docs/gemini-cli.md: add Tool Restrictions section explaining Policy Engine TOML deny rules and why excludeTools was replaced Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move `from pathlib import Path` to stdlib import group. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merges the intent of PR #105. CAO agents run in non-interactive tmux sessions where Codex's interactive approval prompts block handoff/assign flows. The --yolo flag (alias for --dangerously-bypass-approvals-and-sandbox) mirrors Claude Code's --dangerously-skip-permissions and Gemini CLI's --yolo flags. E2E results: 5/8 passed. 3 failures are pre-existing Codex model behavior issues (timeouts, supervisor not calling MCP tools) unrelated to this change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agent profiles launch cao-mcp-server via uvx --from git+..., which downloads ~80 packages on a cold cache (~20s). This exceeds Codex's default 10s MCP startup timeout, causing flaky test failures. Add a session-scoped fixture that runs uvx once before tests to populate the cache (<3s on subsequent runs). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
hi! as a user this is a much-needed feature! however I feel this feature seems a bit too restrictive in some cases...
Hope these items could be considered, thank you~ a few more points on the change itself:
(full disclaimer that this comment is based off of my comprehension of the added tool restrictions md section) |
| # All CAO tool categories and what they map to in each provider. | ||
| # Keys are provider names, values map CAO tool names to lists of native tool names. | ||
| TOOL_MAPPING: Dict[str, Dict[str, List[str]]] = { | ||
| "claude_code": { |
There was a problem hiding this comment.
for claude code, missing webfetch, mcp, subagent tools (https://code.claude.com/docs/en/permissions#webfetch). If not adding support for these tools for now, should add this to documentation. It's also unclear how @cao-mcp-server inclusion in allowedTools would affect the tool enforcement behavior (at least from reading the added documentation)
There was a problem hiding this comment.
@patricka3125 great point. please help to review the latest changes. for this point we documented in in docs/tool-restrictions.md
1. Missing WebFetch/Agent/MCP tools: Documented as Known Limitation #1. The current CAO vocabulary maps 6 Claude Code tools (Bash, Read, Edit, Write, Glob, Grep). WebFetch, Agent (subagent), and MCP tools are not yet mapped — they remain unrestricted even when allowedTools is set. Claude Code's --disallowedTools could block them, but we need to add web_fetch and subagent to the CAO vocabulary first. Planned for a follow-up.
2. How @cao-mcp-server affects enforcement:
Updated Known Limitation #2 to be explicit about this. @cao-mcp-server is a pass-through marker — it signals intent ("this agent should orchestrate") but does not translate to any native enforcement flag. In get_disallowed_tools() (tool_mapping.py L136-138), @-prefixed refs are skipped:
if cao_tool.startswith("@"):
# MCP server references don't map to native tools
continueThis means MCP tools (handoff, assign, send_message) are always available to the agent regardless of allowedTools. None of the 7 providers currently support blocking individual MCP server tools. Including or excluding @cao-mcp-server from allowedTools has no effect on what the agent can actually call — it's purely declarative for documentation and the confirmation prompt display.
- Role is a high-level abstraction (named bundle of allowedTools) - allowedTools is the low-level fine-grained control - No role + no allowedTools = unrestricted (backward compatible) - Support custom roles via settings.json "roles" key - Fix supervisor defaults: add fs_read and fs_list - Remove DEFAULT_ROLE (no role = unrestricted, not developer) - Rewrite docs/tool-restrictions.md with clear concept hierarchy - Update tests for new defaults and opt-in behavior Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, deduplicate docs - Change default when no role/allowedTools set from unrestricted ["*"] to developer defaults — secure by default while preserving backward compat - Add --yolo warning prompt (visible but non-blocking) - Add "To grant all permissions, re-run with --yolo" hint to normal prompt - Add "no role set" reminder with docs link when profile lacks role/allowedTools - Change confirmation text from "Do you trust all the actions in this folder?" to "Proceed?" — clearer that it confirms the shown restrictions, not blanket trust - Deduplicate agent-profile.md: replace duplicated roles/vocabulary/resolution tables with brief summary linking to tool-restrictions.md - Add allowedTools-only example (no role needed) to tool-restrictions.md - Add "Launch Confirmation Prompt" section with Confirmation vs --yolo comparison - Add "Example Profiles" section linking to examples directory - Fix stale supervisor defaults in README.md (add fs_read, fs_list) - Add "Tool restrictions" bullet to Key Features in README.md - Add role/allowedTools to all example profiles missing them - Add Codex allowed_tools e2e tests with xfail for soft enforcement Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Run black on launch.py to fix formatting - Update test_launch.py assertions: old "Do you trust all the actions in this folder?" → new "Proceed?" prompt text - Add WARNING assertion for --yolo test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… enforced Address PR #125 feedback: make explicit that @cao-mcp-server in allowedTools is a declarative marker, not translated to native enforcement flags. MCP tools remain always available. Also add WebFetch reference link. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@patricka3125 pls also review the documentation to check whether this looks good |
Replace dual role + allowedTools with role-only in all 14 example profiles. Add inline comment showing the role's default permissions and pointing to docs/tool-restrictions.md for fine-grained control. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Same cleanup as examples/ — built-in profiles now use role-only with inline comment showing default permissions. Also fixes stale supervisor defaults (was missing fs_read, fs_list). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace detailed tables and examples with a brief summary and link to docs/tool-restrictions.md for the comprehensive reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Hey @haofeif , the updated documentation looks good to me. However still curious on you and the team's perspective regarding the unadressed points:
I think the added support for custom roles is in the right direction~ Appreciate the updated changes! |
@patricka3125 please see the response below from my opinion
|
Summary
Universal tool restriction mechanism (
allowedTools) across all 6 CAO providers. Agents now get only the tools they need based on their role, enforced through each provider's native mechanism.Problem: All agents launched with unrestricted access — supervisors could run
rm -rf /,aws iam, orcurlto exfiltrate data, with no way to limit tool access per role.Solution: Two-layer control system:
role(high-level preset) maps toallowedTools(fine-grained tool list). CAO translates to each provider's native restriction mechanism automatically. Profiles withoutroleorallowedToolsdefault todeveloperpermissions with a prompt reminder.Changes
Core Mechanism
utils/tool_mapping.py(new): CAO vocabulary → provider-native tool name mapping withresolve_allowed_tools()andget_disallowed_tools()constants.py: Role defaults (ROLE_TOOL_DEFAULTS), security prompt (SECURITY_PROMPT)models/agent_profile.py: Addrolefieldproviders/base.py: Add_allowed_toolstoBaseProviderapi/main.py→terminal_service.py→providers/manager.py→ each providerclients/database.py: Persistallowed_tools(JSON column) for child terminal inheritancemcp_server/server.py: Pass parent'sallowed_toolsto child terminals viahandoff/assignProvider Enforcement (7 providers)
--disallowedToolsflags alongside--dangerously-skip-permissionsallowedToolsin agent JSON (install-time)allowedToolsin agent JSON (install-time)--deny-toolflags override--allow-all~/.gemini/policies/CLI (
launch.py,install.py)--allowed-tools: Override allowedTools per launch--yolo: Unrestricted access + skip confirmation (setsallowedTools: ["*"]), shows warning--yolo→--allowed-toolsCLI → profileallowedTools→ role defaults → developer defaults--yolohint--yolowarning prompt (visible but non-blocking)role/allowedToolsset, with link to docsRole Defaults
allowedToolssupervisor@cao-mcp-server, fs_read, fs_listdeveloper@builtin, fs_*, execute_bash, @cao-mcp-serverreviewer@builtin, fs_read, fs_list, @cao-mcp-serverdeveloperCustom roles can be defined in
settings.jsonunder"roles".Launch Prompts
Normal launch — shows restrictions + yolo hint, requires confirmation:
--yolo— shows warning, no confirmation:[WARNING] --yolo mode enabled Agent 'code_supervisor' launching UNRESTRICTED on claude_code. Agent can execute ANY command (aws, rm, curl, read credentials). Directory: /home/user/my-projectNo role set — adds reminder:
Documentation
docs/tool-restrictions.md(new): Full reference — roles, tool vocabulary, custom roles, resolution order, launch prompts, confirmation vs--yolo, provider enforcement,known limitations, example profilesREADME.md: Added "Tool restrictions" to Key Features, added Tool Restrictions sectiondocs/agent-profile.md: Deduplicated — brief summary linking totool-restrictions.mddocs/gemini-cli.md: Updated with Policy Engine approachSECURITY.md(new): Security model documentationexamples/updated withroleandallowedToolsTests
test/utils/test_tool_mapping.py(new): resolve, disallow, format teststest/e2e/test_allowed_tools.py(new): E2E tests — restricted/unrestricted/metadata across providerstest/cli/commands/test_launch.py: Updated prompt assertionstest/e2e/conftest.py: Pre-warm uvx cache to prevent MCP startup timeoutsE2E Results
Known Limitations
WebFetch,Agent(subagent), MCP tools not yet mapped@cao-mcp-serveris all-or-nothingTest Plan