mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-27 09:02:15 +00:00
Exec: harden host env override handling across gateway and node (#51207)
* Exec: harden host env override enforcement and fail closed * Node host: enforce env override diagnostics before shell filtering * Env overrides: align Windows key handling and mac node rejection
This commit is contained in:
@@ -3,6 +3,7 @@ import path from "node:path";
|
||||
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import { type ExecHost, loadExecApprovals, maxAsk, minSecurity } from "../infra/exec-approvals.js";
|
||||
import { resolveExecSafeBinRuntimePolicy } from "../infra/exec-safe-bin-runtime-policy.js";
|
||||
import { sanitizeHostExecEnvWithDiagnostics } from "../infra/host-env-security.js";
|
||||
import {
|
||||
getShellPathFromLoginShell,
|
||||
resolveShellEnvFallbackTimeoutMs,
|
||||
@@ -25,9 +26,7 @@ import {
|
||||
renderExecHostLabel,
|
||||
resolveApprovalRunningNoticeMs,
|
||||
runExecProcess,
|
||||
sanitizeHostBaseEnv,
|
||||
execSchema,
|
||||
validateHostEnv,
|
||||
} from "./bash-tools.exec-runtime.js";
|
||||
import type {
|
||||
ExecElevatedDefaults,
|
||||
@@ -362,24 +361,58 @@ export function createExecTool(
|
||||
}
|
||||
|
||||
const inheritedBaseEnv = coerceEnv(process.env);
|
||||
const baseEnv = host === "sandbox" ? inheritedBaseEnv : sanitizeHostBaseEnv(inheritedBaseEnv);
|
||||
|
||||
// Logic: Sandbox gets raw env. Host (gateway/node) must pass validation.
|
||||
// We validate BEFORE merging to prevent any dangerous vars from entering the stream.
|
||||
if (host !== "sandbox" && params.env) {
|
||||
validateHostEnv(params.env);
|
||||
const hostEnvResult =
|
||||
host === "sandbox"
|
||||
? null
|
||||
: sanitizeHostExecEnvWithDiagnostics({
|
||||
baseEnv: inheritedBaseEnv,
|
||||
overrides: params.env,
|
||||
blockPathOverrides: true,
|
||||
});
|
||||
if (
|
||||
hostEnvResult &&
|
||||
params.env &&
|
||||
(hostEnvResult.rejectedOverrideBlockedKeys.length > 0 ||
|
||||
hostEnvResult.rejectedOverrideInvalidKeys.length > 0)
|
||||
) {
|
||||
const blockedKeys = hostEnvResult.rejectedOverrideBlockedKeys;
|
||||
const invalidKeys = hostEnvResult.rejectedOverrideInvalidKeys;
|
||||
const pathBlocked = blockedKeys.includes("PATH");
|
||||
if (pathBlocked && blockedKeys.length === 1 && invalidKeys.length === 0) {
|
||||
throw new Error(
|
||||
"Security Violation: Custom 'PATH' variable is forbidden during host execution.",
|
||||
);
|
||||
}
|
||||
if (blockedKeys.length === 1 && invalidKeys.length === 0) {
|
||||
throw new Error(
|
||||
`Security Violation: Environment variable '${blockedKeys[0]}' is forbidden during host execution.`,
|
||||
);
|
||||
}
|
||||
const details: string[] = [];
|
||||
if (blockedKeys.length > 0) {
|
||||
details.push(`blocked override keys: ${blockedKeys.join(", ")}`);
|
||||
}
|
||||
if (invalidKeys.length > 0) {
|
||||
details.push(`invalid non-portable override keys: ${invalidKeys.join(", ")}`);
|
||||
}
|
||||
const suffix = details.join("; ");
|
||||
if (pathBlocked) {
|
||||
throw new Error(
|
||||
`Security Violation: Custom 'PATH' variable is forbidden during host execution (${suffix}).`,
|
||||
);
|
||||
}
|
||||
throw new Error(`Security Violation: ${suffix}.`);
|
||||
}
|
||||
|
||||
const mergedEnv = params.env ? { ...baseEnv, ...params.env } : baseEnv;
|
||||
|
||||
const env = sandbox
|
||||
? buildSandboxEnv({
|
||||
defaultPath: DEFAULT_PATH,
|
||||
paramsEnv: params.env,
|
||||
sandboxEnv: sandbox.env,
|
||||
containerWorkdir: containerWorkdir ?? sandbox.containerWorkdir,
|
||||
})
|
||||
: mergedEnv;
|
||||
const env =
|
||||
sandbox && host === "sandbox"
|
||||
? buildSandboxEnv({
|
||||
defaultPath: DEFAULT_PATH,
|
||||
paramsEnv: params.env,
|
||||
sandboxEnv: sandbox.env,
|
||||
containerWorkdir: containerWorkdir ?? sandbox.containerWorkdir,
|
||||
})
|
||||
: (hostEnvResult?.env ?? inheritedBaseEnv);
|
||||
|
||||
if (!sandbox && host === "gateway" && !params.env?.PATH) {
|
||||
const shellPath = getShellPathFromLoginShell({
|
||||
|
||||
Reference in New Issue
Block a user