feat(tools): gate network egress behind a web_fetch tool category (closes #310)#311
Conversation
CAO's tool-restriction vocabulary did not map providers' network tools, so they stayed unrestricted even when allowedTools was set — a read-only reviewer or orchestration-only supervisor could still reach the network. Add a web_fetch category so network access is governable across providers. - claude_code: web_fetch -> [WebFetch, WebSearch]; gemini_cli: web_fetch -> [web_fetch, google_web_search]. A profile/role without web_fetch now blocks those tools. - developer role gains web_fetch (full-access role and the no-role default keep network access — no silent regression); supervisor/reviewer stay off the network, removing their egress channel entirely (they also lack execute_bash, i.e. curl). - subagent (Task) is intentionally NOT a separate category: it stays folded into execute_bash, since a Task subagent spawns with its own full toolset and can run shell — a standalone subagent grant would re-open that escape. - Launch-time guard: kimi_cli/codex have no native tool-blocking (soft/prompt enforcement only), so creating a restricted terminal on them logs a loud warning to route restricted/write-capable roles to hard-enforcement providers. - web_fetch is a no-op for providers without a network entry (copilot), keeping the vocabulary universal without changing their behavior. - Update docs/tool-restrictions.md vocabulary/translation tables and Known Limitation #1; extend test/utils/test_tool_mapping.py. Closes awslabs#310
70fbf35 to
ccca369
Compare
web_fetch category (closes #310)web_fetch tool category (closes #310)
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #311 +/- ##
=======================================
Coverage ? 87.23%
=======================================
Files ? 92
Lines ? 10976
Branches ? 0
=======================================
Hits ? 9575
Misses ? 1401
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Introduces a new CAO tool category (web_fetch) to make provider network-egress tools governable under CAO’s allowedTools/role restriction system, closing an enforcement gap where some providers’ web tools stayed unrestricted.
Changes:
- Add
web_fetchto the CAO vocabulary and map it to provider-native network tools for Claude Code and Gemini CLI. - Update built-in role defaults so
developerretains network access whilesupervisor/reviewerremain network-disabled. - Add a launch-time warning when attempting restricted policies on providers that only support prompt-level (soft) enforcement, and update docs/tests accordingly.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| test/utils/test_tool_mapping.py | Adds unit tests for web_fetch mapping and updates developer expectations. |
| test/e2e/test_allowed_tools.py | Updates e2e metadata test to include web_fetch in the developer allowed-tools string. |
| src/cli_agent_orchestrator/utils/tool_mapping.py | Adds web_fetch mappings for claude_code and gemini_cli provider tool translation. |
| src/cli_agent_orchestrator/services/terminal_service.py | Adds soft-enforcement provider warning when restricted allowed_tools is requested. |
| src/cli_agent_orchestrator/constants.py | Extends developer role defaults to include web_fetch and documents vocabulary intent. |
| docs/tool-restrictions.md | Documents web_fetch vocabulary, role defaults, and updated known limitations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # only), so a restricted policy on them is advisory, not enforced. | ||
| # Surface that loudly at launch so operators route restricted or | ||
| # write-capable roles to hard-enforcement providers instead. | ||
| if provider in SOFT_ENFORCEMENT_PROVIDERS and allowed_tools and "*" not in allowed_tools: |
What & why
Closes #310.
CAO's tool-restriction vocabulary did not map providers' network tools (Claude Code's
WebFetch/WebSearch, Gemini'sweb_fetch/google_web_search) to any CAO category. As documented indocs/tool-restrictions.mdKnown Limitation #1, they stayed unrestricted even whenallowedToolswas set — a read-onlyrevieweror orchestration-onlysupervisorcould still fetch arbitrary URLs, an exfiltration / SSRF surface the restriction system otherwise closes.This adds a
web_fetchcategory so network access becomes governable.Changes
tool_mapping.py—claude_code:web_fetch → [WebFetch, WebSearch];gemini_cli:web_fetch → [web_fetch, google_web_search]. A profile/role withoutweb_fetchnow emits the native disallow flags for those tools.constants.py—developerrole default gainsweb_fetch, so the full-access role (and the no-role/no-allowedToolsdefault, which resolves todeveloper) keeps network access.supervisor/reviewerstay off the network — and since they also lackexecute_bash(curl), they have no egress channel at all.terminal_service.py— launch-time guard:kimi_cli/codexhave no native tool-blocking (soft/prompt-level only), so creating a restricted terminal on them now logs a loud warning to route restricted or write-capable roles to a hard-enforcement provider.docs/tool-restrictions.md— vocabulary + translation tables, role table, default-behavior text, sample confirmation prompt, and a rewritten Known Limitation Initial Launch #1.web_fetchcases (claude + gemini mapping, egress-channel blocking, copilot no-op).subagent/Task— intentionally not a separate categoryThe original limitation note also mentioned
subagent.Taskis already gated: it's deliberately folded intoexecute_bash(see the comment block intool_mapping.pyandTestClaudeCodeSubagentEscape) because aTasksubagent spawns with its own full toolset and can run shell. Exposingsubagentas an independently-grantable category would let a profile grant it withoutexecute_bashand re-open that privilege-escalation escape. So this PR leavesTaskbundled intoexecute_bash.Behavior change / compatibility
Profiles that hard-code the old developer list (
["@builtin", "fs_*", "execute_bash", "@cao-mcp-server"]) rather than usingrole: developerwill now have the network tools blocked. They opt back in by addingweb_fetch(or*). This is the intended consequence of making web access governable. Therole: developerpath and the unrestricted default are unaffected. For providers without a network entry (Copilot), an unknownweb_fetchtoken is a harmless no-op.Testing
uv run pytest test/utils/test_tool_mapping.py— passes (includes the newweb_fetchcases).uv run pytest test/services/test_terminal_service*.py test/mcp_server/test_resolve_child_allowed_tools.py test/cli/commands/test_launch.py— passes (covers the launch guard + tool resolution).--disallowedTools WebFetchon Claude Code is best verified with the live-CLI e2e (test/e2e/test_allowed_tools.py).