refactor(tools): keep exec enable without configurable deny patterns
Made-with: Cursor
This commit is contained in:
@@ -1165,7 +1165,6 @@ MCP tools are automatically discovered and registered on startup. The LLM can us
|
|||||||
| `tools.restrictToWorkspace` | `false` | When `true`, restricts **all** agent tools (shell, file read/write/edit, list) to the workspace directory. Prevents path traversal and out-of-scope access. |
|
| `tools.restrictToWorkspace` | `false` | When `true`, restricts **all** agent tools (shell, file read/write/edit, list) to the workspace directory. Prevents path traversal and out-of-scope access. |
|
||||||
| `tools.exec.enable` | `true` | When `false`, the shell `exec` tool is not registered at all. Use this to completely disable shell command execution. |
|
| `tools.exec.enable` | `true` | When `false`, the shell `exec` tool is not registered at all. Use this to completely disable shell command execution. |
|
||||||
| `tools.exec.pathAppend` | `""` | Extra directories to append to `PATH` when running shell commands (e.g. `/usr/sbin` for `ufw`). |
|
| `tools.exec.pathAppend` | `""` | Extra directories to append to `PATH` when running shell commands (e.g. `/usr/sbin` for `ufw`). |
|
||||||
| `tools.exec.denyPatterns` | `null` | Optional regex blacklist for shell commands. Set this to override the default dangerous-command patterns used by `exec`. |
|
|
||||||
| `channels.*.allowFrom` | `[]` (deny all) | Whitelist of user IDs. Empty denies all; use `["*"]` to allow everyone. |
|
| `channels.*.allowFrom` | `[]` (deny all) | Whitelist of user IDs. Empty denies all; use `["*"]` to allow everyone. |
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,6 @@ class AgentLoop:
|
|||||||
timeout=self.exec_config.timeout,
|
timeout=self.exec_config.timeout,
|
||||||
restrict_to_workspace=self.restrict_to_workspace,
|
restrict_to_workspace=self.restrict_to_workspace,
|
||||||
path_append=self.exec_config.path_append,
|
path_append=self.exec_config.path_append,
|
||||||
deny_patterns=self.exec_config.deny_patterns,
|
|
||||||
))
|
))
|
||||||
self.tools.register(WebSearchTool(config=self.web_search_config, proxy=self.web_proxy))
|
self.tools.register(WebSearchTool(config=self.web_search_config, proxy=self.web_proxy))
|
||||||
self.tools.register(WebFetchTool(proxy=self.web_proxy))
|
self.tools.register(WebFetchTool(proxy=self.web_proxy))
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ExecTool(Tool):
|
|||||||
):
|
):
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.working_dir = working_dir
|
self.working_dir = working_dir
|
||||||
self.deny_patterns = deny_patterns if deny_patterns is not None else [
|
self.deny_patterns = deny_patterns or [
|
||||||
r"\brm\s+-[rf]{1,2}\b", # rm -r, rm -rf, rm -fr
|
r"\brm\s+-[rf]{1,2}\b", # rm -r, rm -rf, rm -fr
|
||||||
r"\bdel\s+/[fq]\b", # del /f, del /q
|
r"\bdel\s+/[fq]\b", # del /f, del /q
|
||||||
r"\brmdir\s+/s\b", # rmdir /s
|
r"\brmdir\s+/s\b", # rmdir /s
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ class ExecToolConfig(Base):
|
|||||||
enable: bool = True
|
enable: bool = True
|
||||||
timeout: int = 60
|
timeout: int = 60
|
||||||
path_append: str = ""
|
path_append: str = ""
|
||||||
deny_patterns: list[str] | None = None
|
|
||||||
|
|
||||||
class MCPServerConfig(Base):
|
class MCPServerConfig(Base):
|
||||||
"""MCP server connection configuration (stdio or HTTP)."""
|
"""MCP server connection configuration (stdio or HTTP)."""
|
||||||
|
|||||||
@@ -97,16 +97,6 @@ class TestDispatch:
|
|||||||
|
|
||||||
assert loop.tools.get("exec") is None
|
assert loop.tools.get("exec") is None
|
||||||
|
|
||||||
def test_exec_tool_receives_custom_deny_patterns(self):
|
|
||||||
from nanobot.agent.tools.shell import ExecTool
|
|
||||||
from nanobot.config.schema import ExecToolConfig
|
|
||||||
|
|
||||||
loop, _bus = _make_loop(exec_config=ExecToolConfig(deny_patterns=[r"\becho\b"]))
|
|
||||||
tool = loop.tools.get("exec")
|
|
||||||
|
|
||||||
assert isinstance(tool, ExecTool)
|
|
||||||
assert tool.deny_patterns == [r"\becho\b"]
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_dispatch_processes_and_publishes(self):
|
async def test_dispatch_processes_and_publishes(self):
|
||||||
from nanobot.bus.events import InboundMessage, OutboundMessage
|
from nanobot.bus.events import InboundMessage, OutboundMessage
|
||||||
|
|||||||
@@ -134,12 +134,6 @@ def test_exec_guard_blocks_quoted_home_path_outside_workspace(tmp_path) -> None:
|
|||||||
assert error == "Error: Command blocked by safety guard (path outside working dir)"
|
assert error == "Error: Command blocked by safety guard (path outside working dir)"
|
||||||
|
|
||||||
|
|
||||||
def test_exec_empty_deny_patterns_override_defaults() -> None:
|
|
||||||
tool = ExecTool(deny_patterns=[])
|
|
||||||
error = tool._guard_command("rm -rf /tmp/demo", "/tmp")
|
|
||||||
assert error is None
|
|
||||||
|
|
||||||
|
|
||||||
# --- cast_params tests ---
|
# --- cast_params tests ---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user