From 278331c49cc2d62db464ae600d157a99e0490d1a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 23 Feb 2026 01:48:09 +0100 Subject: [PATCH] fix(exec): restore sandbox as implicit host default --- CHANGELOG.md | 2 +- docs/tools/exec.md | 4 ++-- src/agents/bash-tools.exec.path.test.ts | 14 ++++++++++---- src/agents/bash-tools.exec.ts | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a556752f909..540600bc44d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,7 +211,7 @@ Docs: https://docs.openclaw.ai - Security/Config: make parsed chat allowlist checks fail closed when `allowFrom` is empty, restoring expected DM/pairing gating. - Security/Exec: in non-default setups that manually add `sort` to `tools.exec.safeBins`, block `sort --compress-program` so allowlist-mode safe-bin checks cannot bypass approval. Thanks @tdjackey for reporting. - Security/Exec approvals: when users choose `allow-always` for shell-wrapper commands (for example `/bin/zsh -lc ...`), persist allowlist patterns for the inner executable(s) instead of the wrapper shell binary, preventing accidental broad shell allowlisting in moderate mode. (#23276) Thanks @xrom2863. -- Security/Exec: fail closed when `tools.exec.host=sandbox` is configured/requested but sandbox runtime is unavailable, and default implicit exec host routing to `gateway` when no sandbox runtime exists. (#23398) Thanks @bmendonca3. +- Security/Exec: fail closed when `tools.exec.host=sandbox` is configured/requested but sandbox runtime is unavailable. (#23398) Thanks @bmendonca3. - Security/macOS app beta: enforce path-only `system.run` allowlist matching (drop basename matches like `echo`), migrate legacy basename entries to last resolved paths when available, and harden shell-chain handling to fail closed on unsafe parse/control syntax (including quoted command substitution/backticks). This is an optional allowlist-mode feature; default installs remain deny-by-default. This ships in the next npm release. Thanks @tdjackey for reporting. - Security/Agents: auto-generate and persist a dedicated `commands.ownerDisplaySecret` when `commands.ownerDisplay=hash`, remove gateway token fallback from owner-ID prompt hashing across CLI and embedded agent runners, and centralize owner-display secret resolution in one shared helper. This ships in the next npm release. Thanks @aether-ai-agent for reporting. - Security/SSRF: expand IPv4 fetch guard blocking to include RFC special-use/non-global ranges (including benchmarking, TEST-NET, multicast, and reserved/broadcast blocks), centralize range checks into a single CIDR policy table, and reuse one shared host/IP classifier across literal + DNS checks to reduce classifier drift. This ships in the next npm release. Thanks @princeeismond-dot for reporting. diff --git a/docs/tools/exec.md b/docs/tools/exec.md index 181a7610432..1123d3068d2 100644 --- a/docs/tools/exec.md +++ b/docs/tools/exec.md @@ -29,7 +29,7 @@ Background sessions are scoped per agent; `process` only sees sessions from the Notes: -- `host` defaults to `sandbox` when sandbox runtime is active, and defaults to `gateway` otherwise. +- `host` defaults to `sandbox`. - `elevated` is ignored when sandboxing is off (exec already runs on the host). - `gateway`/`node` approvals are controlled by `~/.openclaw/exec-approvals.json`. - `node` requires a paired node (companion app or headless node host). @@ -49,7 +49,7 @@ Notes: - `tools.exec.notifyOnExit` (default: true): when true, backgrounded exec sessions enqueue a system event and request a heartbeat on exit. - `tools.exec.approvalRunningNoticeMs` (default: 10000): emit a single “running” notice when an approval-gated exec runs longer than this (0 disables). -- `tools.exec.host` (default: runtime-aware: `sandbox` when sandbox runtime is active, `gateway` otherwise) +- `tools.exec.host` (default: `sandbox`) - `tools.exec.security` (default: `deny` for sandbox, `allowlist` for gateway + node when unset) - `tools.exec.ask` (default: `on-miss`) - `tools.exec.node` (default: unset) diff --git a/src/agents/bash-tools.exec.path.test.ts b/src/agents/bash-tools.exec.path.test.ts index f5f57ec7a3d..9bdbe07524c 100644 --- a/src/agents/bash-tools.exec.path.test.ts +++ b/src/agents/bash-tools.exec.path.test.ts @@ -126,19 +126,25 @@ describe("exec host env validation", () => { ).rejects.toThrow(/Security Violation: Environment variable 'LD_DEBUG' is forbidden/); }); - it("defaults to gateway when sandbox runtime is unavailable", async () => { + it("defaults to sandbox when sandbox runtime is unavailable", async () => { const tool = createExecTool({ security: "full", ask: "off" }); + const result = await tool.execute("call1", { + command: "echo ok", + }); + const text = normalizeText(result.content.find((c) => c.type === "text")?.text); + expect(text).toContain("ok"); + const err = await tool - .execute("call1", { + .execute("call2", { command: "echo ok", - host: "sandbox", + host: "gateway", }) .then(() => null) .catch((error: unknown) => (error instanceof Error ? error : new Error(String(error)))); expect(err).toBeTruthy(); expect(err?.message).toMatch(/exec host not allowed/); - expect(err?.message).toMatch(/tools\.exec\.host=gateway/); + expect(err?.message).toMatch(/tools\.exec\.host=sandbox/); }); it("fails closed when sandbox host is explicitly configured without sandbox runtime", async () => { diff --git a/src/agents/bash-tools.exec.ts b/src/agents/bash-tools.exec.ts index 1d0b416a6b2..7fd16e36eaf 100644 --- a/src/agents/bash-tools.exec.ts +++ b/src/agents/bash-tools.exec.ts @@ -300,7 +300,7 @@ export function createExecTool( if (elevatedRequested) { logInfo(`exec: elevated command ${truncateMiddle(params.command, 120)}`); } - const configuredHost = defaults?.host ?? (defaults?.sandbox ? "sandbox" : "gateway"); + const configuredHost = defaults?.host ?? "sandbox"; const sandboxHostConfigured = defaults?.host === "sandbox"; const requestedHost = normalizeExecHost(params.host) ?? null; let host: ExecHost = requestedHost ?? configuredHost;