fix(exec): harden inherited host env sanitization (#25755) (thanks @bmendonca3)

This commit is contained in:
Peter Steinberger
2026-02-24 23:46:29 +00:00
parent 9687f81237
commit 1df084f36b
3 changed files with 30 additions and 2 deletions

View File

@@ -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.

View File

@@ -33,10 +33,12 @@ import { getShellConfig, sanitizeBinaryOutput } from "./shell-utils.js";
// are not propagated into non-sandboxed executions.
export function sanitizeHostBaseEnv(env: Record<string, string>): Record<string, string> {
const sanitized: Record<string, string> = {};
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<string, string>): Record<string,
}
sanitized[key] = value;
}
if (hostPath !== undefined) {
sanitized.PATH = hostPath;
}
return sanitized;
}
// Centralized sanitization helper.

View File

@@ -4,6 +4,7 @@ import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { ExecApprovalsResolved } from "../infra/exec-approvals.js";
import { captureEnv } from "../test-utils/env.js";
import { sanitizeHostBaseEnv } from "./bash-tools.exec-runtime.js";
import { sanitizeBinaryOutput } from "./shell-utils.js";
const isWin = process.platform === "win32";
@@ -155,6 +156,28 @@ describe("exec PATH login shell merge", () => {
});
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" });