fix(sandbox): chặn mạng thật sự cho mọi tool NETWORK của agent
CI / test (pull_request) Canceled after 0s

block_network trước đây chỉ được run_command đọc tới, nên fetch_url,
jira_search, jira_get_issue và install_package vẫn ra internet bình thường
trong khi Monitoring báo Mạng - Bị chặn. Thêm cổng chặn ngay đầu bốn tool
đó, trước mọi lời gọi mạng, kèm test hồi quy và guardrail theo
BUILT_IN_CAPABILITIES.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-12 08:33:26 +09:00
co-authored by Claude Opus 5
parent d0df96c726
commit 57af508971
5 changed files with 175 additions and 9 deletions
@@ -128,6 +128,15 @@ def install_package(ctx: ToolContext, args: Dict[str, Any],
package = str(args.get("package", "")).strip()
if not package:
return {"ok": False, "output": "No package specified."}
# ``pip install`` bắt buộc phải ra internet, mà ``deps.pip_install`` chạy
# subprocess với ``os.environ`` nguyên vẹn — biến proxy hố đen của
# ``network_blocked_env`` không chạm tới nó. Từ chối thẳng ở đây (giống cách
# run_command chặn theo tên các công cụ không đi qua proxy) thay vì để pip
# thử 600 giây rồi báo một lỗi proxy khó hiểu.
if ctx.block_network:
return {"ok": False, "output": (
"install_package: network access is blocked by the Sandbox Security Layer "
"(\"Block network for agent-run commands\" is on in Settings).")}
python = _sandbox_python(ctx, cancel, on_output)
ok, detail = pip_install(package, cancel=cancel, on_output=on_output, python=python)
head = f"Installed {package}." if ok else f"Could not install {package}."
+25 -1
View File
@@ -6,11 +6,26 @@ tag added in R05-T01/domain/tools/tool_registry.py describes.
"""
from __future__ import annotations
from typing import Any, Dict
from typing import Any, Dict, Optional
from .tool_context import ToolContext
def _network_refusal(ctx: ToolContext, tool: str) -> Optional[Dict[str, Any]]:
"""Lời từ chối khi Sandbox Security Layer đang chặn mạng; None nếu được đi.
``block_network`` trước đây chỉ được đọc ở ``command_tools.py`` (lệnh shell),
nên ba tool mang ``ToolCapability.NETWORK`` ở file này vẫn ra internet bình
thường trong khi Monitoring báo "Mạng: Bị chặn". Kiểm ở đây, TRƯỚC mọi lời
gọi mạng, để công tắc chặn đúng thứ nó nói là chặn.
"""
if not ctx.block_network:
return None
return {"ok": False, "output": (
f"{tool}: network access is blocked by the Sandbox Security Layer "
"(\"Block network for agent-run commands\" is on in Settings).")}
def fetch_url(ctx: ToolContext, args: Dict[str, Any]) -> Dict[str, Any]:
"""Fetch a URL's text content (web page / online document / SharePoint-
OneDrive share link) via link_fetch — the same parser task-link attachments
@@ -20,6 +35,9 @@ def fetch_url(ctx: ToolContext, args: Dict[str, Any]) -> Dict[str, Any]:
return {"ok": False, "output": "fetch_url: 'url' is required."}
if not url.lower().startswith(("http://", "https://")):
return {"ok": False, "output": f"fetch_url: not an http(s) URL: {url}"}
blocked = _network_refusal(ctx, "fetch_url")
if blocked is not None:
return blocked
if not ctx.allow_url_fetch:
return {"ok": False,
"output": ("fetch_url: URL fetching is turned off in Settings → Security "
@@ -37,6 +55,9 @@ def fetch_url(ctx: ToolContext, args: Dict[str, Any]) -> Dict[str, Any]:
def jira_search(ctx: ToolContext, args: Dict[str, Any]) -> Dict[str, Any]:
"""Tìm issue trên Jira bằng JQL."""
blocked = _network_refusal(ctx, "jira_search")
if blocked is not None:
return blocked
from cowork_local.core import jira_tool
out = jira_tool.search(ctx.jira, str(args.get("jql", "")),
@@ -47,6 +68,9 @@ def jira_search(ctx: ToolContext, args: Dict[str, Any]) -> Dict[str, Any]:
def jira_get_issue(ctx: ToolContext, args: Dict[str, Any]) -> Dict[str, Any]:
"""Lấy chi tiết một issue Jira theo mã."""
blocked = _network_refusal(ctx, "jira_get_issue")
if blocked is not None:
return blocked
from cowork_local.core import jira_tool
out = jira_tool.get_issue(ctx.jira, str(args.get("key", "")))
+8 -4
View File
@@ -37,12 +37,16 @@ class ToolContext:
# None (default) = no limits, matching pre-existing behavior.
resource_limits: Optional[Dict[str, float]] = None
# Sandbox Security Layer — Settings' "Block network for agent commands"
# (policy-level, see deps.py::network_blocked_env). False (default) =
# — the proxy-env block for shell commands (deps.py::network_blocked_env)
# AND a flat refusal from every NETWORK-capability tool, which reaches the
# net in-process where proxy env vars mean nothing. False (default) =
# unrestricted, matching pre-existing behavior.
block_network: bool = False
# Whether the fetch_url tool may read URLs — SEPARATE from block_network
# (reading a web page/share link for info is safe; running networked shell
# commands is the risk). Defaults True; set from agent_security.allow_url_fetch.
# Whether the fetch_url tool may read URLs. Its own toggle, but NOT a way
# around block_network: with the network blocked every NETWORK-capability
# tool is refused first (fetch_tools.py::_network_refusal), so this flag only
# decides anything while the network is open. Defaults True; set from
# agent_security.allow_url_fetch.
allow_url_fetch: bool = True
# Jira read connector config (base_url/email/api_token) — None disables the
# jira_* tools' ability to connect. Populated from config.data["jira"].