From 27d94e395cc8d191a1b284dda2b42572ed9d4796 Mon Sep 17 00:00:00 2001 From: Zamil Majdy Date: Sun, 15 Feb 2026 10:51:25 +0400 Subject: [PATCH] feat(backend/sdk): enable WebSearch, block WebFetch, consolidate tool constants (#12108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Enable Claude Agent SDK built-in **WebSearch** tool (Brave Search via Anthropic API) for the CoPilot SDK agent - Explicitly **block WebFetch** via `SDK_DISALLOWED_TOOLS`. The agent uses the SSRF-protected `mcp__copilot__web_fetch` MCP tool instead - **Consolidate** all tool security constants (`BLOCKED_TOOLS`, `WORKSPACE_SCOPED_TOOLS`, `DANGEROUS_PATTERNS`, `SDK_DISALLOWED_TOOLS`) into `tool_adapter.py` as a single source of truth — previously scattered across `tool_adapter.py`, `security_hooks.py`, and inline in `service.py` ## Changes - `tool_adapter.py`: Add `WebSearch` to `_SDK_BUILTIN_TOOLS`, add `SDK_DISALLOWED_TOOLS`, move security constants here - `security_hooks.py`: Import constants from `tool_adapter.py` instead of defining locally - `service.py`: Use `SDK_DISALLOWED_TOOLS` instead of inline `["Bash"]` ## Test plan - [x] All 21 security hooks tests pass - [x] Ruff lint clean - [x] All pre-commit hooks pass - [ ] Verify WebSearch works in CoPilot chat (manual test)

Greptile Overview

Greptile Summary

Consolidates tool security constants into `tool_adapter.py` as single source of truth, enables WebSearch (Brave via Anthropic API), and explicitly blocks WebFetch to prevent SSRF attacks. The change improves security by ensuring the agent uses the SSRF-protected `mcp__copilot__web_fetch` tool instead of the built-in WebFetch which can access internal networks like `localhost:8006`.

Confidence Score: 5/5

- This PR is safe to merge with minimal risk - The changes improve security by blocking WebFetch (SSRF risk) while enabling safe WebSearch. The consolidation of constants into a single source of truth improves maintainability. All existing tests pass (21 security hooks tests), and the refactoring is straightforward with no behavioral changes to existing security logic. The only suggestions are minor improvements: adding a test for WebFetch blocking and considering a lowercase alias for consistency. - No files require special attention

Sequence Diagram

```mermaid sequenceDiagram participant Agent as SDK Agent participant Hooks as Security Hooks participant TA as tool_adapter.py participant MCP as MCP Tools Note over TA: SDK_DISALLOWED_TOOLS = ["Bash", "WebFetch"] Note over TA: _SDK_BUILTIN_TOOLS includes WebSearch Agent->>Hooks: Request WebSearch (Brave API) Hooks->>TA: Check BLOCKED_TOOLS TA-->>Hooks: Not blocked Hooks-->>Agent: Allowed ✓ Agent->>Agent: Execute via Anthropic API Agent->>Hooks: Request WebFetch (SSRF risk) Hooks->>TA: Check BLOCKED_TOOLS Note over TA: WebFetch in SDK_DISALLOWED_TOOLS TA-->>Hooks: Blocked Hooks-->>Agent: Denied ✗ Note over Agent: Use mcp__copilot__web_fetch instead Agent->>Hooks: Request mcp__copilot__web_fetch Hooks->>MCP: Validate (MCP tool, not SDK builtin) MCP-->>Hooks: Has SSRF protection Hooks-->>Agent: Allowed ✓ Agent->>MCP: Execute with SSRF checks ```
Last reviewed commit: 2d9975f --- .../api/features/chat/sdk/security_hooks.py | 42 +++--------------- .../backend/api/features/chat/sdk/service.py | 3 +- .../api/features/chat/sdk/tool_adapter.py | 43 ++++++++++++++++++- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py b/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py index 14efc6d459..89853402a3 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/security_hooks.py @@ -11,45 +11,15 @@ import re from collections.abc import Callable from typing import Any, cast -from backend.api.features.chat.sdk.tool_adapter import MCP_TOOL_PREFIX +from backend.api.features.chat.sdk.tool_adapter import ( + BLOCKED_TOOLS, + DANGEROUS_PATTERNS, + MCP_TOOL_PREFIX, + WORKSPACE_SCOPED_TOOLS, +) logger = logging.getLogger(__name__) -# Tools that are blocked entirely (CLI/system access). -# "Bash" (capital) is the SDK built-in — it's NOT in allowed_tools but blocked -# here as defence-in-depth. The agent uses mcp__copilot__bash_exec instead, -# which has kernel-level network isolation (unshare --net). -BLOCKED_TOOLS = { - "Bash", - "bash", - "shell", - "exec", - "terminal", - "command", -} - -# Tools allowed only when their path argument stays within the SDK workspace. -# The SDK uses these to handle oversized tool results (writes to tool-results/ -# files, then reads them back) and for workspace file operations. -WORKSPACE_SCOPED_TOOLS = {"Read", "Write", "Edit", "Glob", "Grep"} - -# Dangerous patterns in tool inputs -DANGEROUS_PATTERNS = [ - r"sudo", - r"rm\s+-rf", - r"dd\s+if=", - r"/etc/passwd", - r"/etc/shadow", - r"chmod\s+777", - r"curl\s+.*\|.*sh", - r"wget\s+.*\|.*sh", - r"eval\s*\(", - r"exec\s*\(", - r"__import__", - r"os\.system", - r"subprocess", -] - def _deny(reason: str) -> dict[str, Any]: """Return a hook denial response.""" diff --git a/autogpt_platform/backend/backend/api/features/chat/sdk/service.py b/autogpt_platform/backend/backend/api/features/chat/sdk/service.py index 65195b442c..65c4cebb06 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/service.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/service.py @@ -41,6 +41,7 @@ from .response_adapter import SDKResponseAdapter from .security_hooks import create_security_hooks from .tool_adapter import ( COPILOT_TOOL_NAMES, + SDK_DISALLOWED_TOOLS, LongRunningCallback, create_copilot_mcp_server, set_execution_context, @@ -543,7 +544,7 @@ async def stream_chat_completion_sdk( "system_prompt": system_prompt, "mcp_servers": {"copilot": mcp_server}, "allowed_tools": COPILOT_TOOL_NAMES, - "disallowed_tools": ["Bash"], + "disallowed_tools": SDK_DISALLOWED_TOOLS, "hooks": security_hooks, "cwd": sdk_cwd, "max_buffer_size": config.claude_agent_max_buffer_size, diff --git a/autogpt_platform/backend/backend/api/features/chat/sdk/tool_adapter.py b/autogpt_platform/backend/backend/api/features/chat/sdk/tool_adapter.py index d983d5e785..2d259730bf 100644 --- a/autogpt_platform/backend/backend/api/features/chat/sdk/tool_adapter.py +++ b/autogpt_platform/backend/backend/api/features/chat/sdk/tool_adapter.py @@ -310,7 +310,48 @@ def create_copilot_mcp_server(): # Bash is NOT included — use the sandboxed MCP bash_exec tool instead, # which provides kernel-level network isolation via unshare --net. # Task allows spawning sub-agents (rate-limited by security hooks). -_SDK_BUILTIN_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep", "Task"] +# WebSearch uses Brave Search via Anthropic's API — safe, no SSRF risk. +_SDK_BUILTIN_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep", "Task", "WebSearch"] + +# SDK built-in tools that must be explicitly blocked. +# Bash: dangerous — agent uses mcp__copilot__bash_exec with kernel-level +# network isolation (unshare --net) instead. +# WebFetch: SSRF risk — can reach internal network (localhost, 10.x, etc.). +# Agent uses the SSRF-protected mcp__copilot__web_fetch tool instead. +SDK_DISALLOWED_TOOLS = ["Bash", "WebFetch"] + +# Tools that are blocked entirely in security hooks (defence-in-depth). +# Includes SDK_DISALLOWED_TOOLS plus common aliases/synonyms. +BLOCKED_TOOLS = { + *SDK_DISALLOWED_TOOLS, + "bash", + "shell", + "exec", + "terminal", + "command", +} + +# Tools allowed only when their path argument stays within the SDK workspace. +# The SDK uses these to handle oversized tool results (writes to tool-results/ +# files, then reads them back) and for workspace file operations. +WORKSPACE_SCOPED_TOOLS = {"Read", "Write", "Edit", "Glob", "Grep"} + +# Dangerous patterns in tool inputs +DANGEROUS_PATTERNS = [ + r"sudo", + r"rm\s+-rf", + r"dd\s+if=", + r"/etc/passwd", + r"/etc/shadow", + r"chmod\s+777", + r"curl\s+.*\|.*sh", + r"wget\s+.*\|.*sh", + r"eval\s*\(", + r"exec\s*\(", + r"__import__", + r"os\.system", + r"subprocess", +] # List of tool names for allowed_tools configuration # Include MCP tools, the MCP Read tool for oversized results,