From 1df084f36b012dc44d5f42b25e0c4a4ed0f944a8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 24 Feb 2026 23:46:29 +0000 Subject: [PATCH] fix(exec): harden inherited host env sanitization (#25755) (thanks @bmendonca3) --- CHANGELOG.md | 2 +- src/agents/bash-tools.exec-runtime.ts | 7 ++++++- src/agents/bash-tools.exec.path.test.ts | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a3547bab04..8a577427894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -597,7 +597,7 @@ Docs: https://docs.openclaw.ai - Security/Media: harden local media ingestion against TOCTOU/symlink swap attacks by pinning reads to a single file descriptor with symlink rejection and inode/device verification in `saveMediaSource`. Thanks @dorjoos for reporting. - Security/Lobster (Windows): for the next npm release, remove shell-based fallback when launching Lobster wrappers (`.cmd`/`.bat`) and switch to explicit argv execution with wrapper entrypoint resolution, preventing command injection while preserving Windows wrapper compatibility. Thanks @allsmog for reporting. - Security/Exec: require `tools.exec.safeBins` binaries to resolve from trusted bin directories (system defaults plus gateway startup `PATH`) so PATH-hijacked trojan binaries cannot bypass allowlist checks. Thanks @jackhax for reporting. -- Security/Exec: sanitize inherited host execution environment before merge and strip dangerous keys (`LD_*`, `DYLD_*`, `SSLKEYLOGFILE`, and related injection vectors) from non-sandboxed exec runs. (#9792) +- Security/Exec: sanitize inherited host execution environment before merge, canonicalize inherited PATH handling, and strip dangerous keys (`LD_*`, `DYLD_*`, `SSLKEYLOGFILE`, and related injection vectors) from non-sandboxed exec runs. (#25755) Thanks @bmendonca3. - Security/Exec: remove file-existence oracle behavior from `tools.exec.safeBins` by using deterministic argv-only stdin-safe validation and blocking file-oriented flags (for example `sort -o`, `jq -f`, `grep -f`) so allow/deny results no longer disclose host file presence. Thanks @nedlir for reporting. - Security/Browser: route browser URL navigation through one SSRF-guarded validation path for tab-open/CDP-target/Playwright navigation flows and block private/metadata destinations by default (configurable via `browser.ssrfPolicy`). Thanks @dorjoos for reporting. - Security/Exec: for the next npm release, harden safe-bin stdin-only enforcement by blocking output/recursive flags (`sort -o/--output`, grep recursion) and tightening default safe bins to remove `sort`/`grep`, preventing safe-bin allowlist bypass for file writes/recursive reads. Thanks @nedlir for reporting. diff --git a/src/agents/bash-tools.exec-runtime.ts b/src/agents/bash-tools.exec-runtime.ts index 05973993cff..2d9ae426576 100644 --- a/src/agents/bash-tools.exec-runtime.ts +++ b/src/agents/bash-tools.exec-runtime.ts @@ -33,10 +33,12 @@ import { getShellConfig, sanitizeBinaryOutput } from "./shell-utils.js"; // are not propagated into non-sandboxed executions. export function sanitizeHostBaseEnv(env: Record): Record { const sanitized: Record = {}; + let hostPath: string | undefined; for (const [key, value] of Object.entries(env)) { const upperKey = key.toUpperCase(); if (upperKey === "PATH") { - sanitized[key] = value; + // Canonicalize PATH casing so downstream PATH merges always hit one key. + hostPath ??= value; continue; } if (isDangerousHostEnvVarName(upperKey)) { @@ -44,6 +46,9 @@ export function sanitizeHostBaseEnv(env: Record): Record { }); describe("exec host env validation", () => { + it("sanitizes inherited host env without mutating source object", () => { + const inherited = { + PATH: "/usr/bin", + Path: "/should-not-win", + SAFE_KEY: "ok", + LD_PRELOAD: "bad", + sslkeylogfile: "/tmp/keys.log", + }; + const sanitized = sanitizeHostBaseEnv(inherited); + expect(sanitized).toEqual({ + PATH: "/usr/bin", + SAFE_KEY: "ok", + }); + expect(inherited).toEqual({ + PATH: "/usr/bin", + Path: "/should-not-win", + SAFE_KEY: "ok", + LD_PRELOAD: "bad", + sslkeylogfile: "/tmp/keys.log", + }); + }); + it("blocks LD_/DYLD_ env vars on host execution", async () => { const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });