fix: harden host exec env validation (#4896) (thanks @HassanFleyah)
This commit is contained in:
parent
0a5821a811
commit
a87a07ec8a
4 changed files with 28 additions and 5 deletions
|
|
@ -29,6 +29,7 @@ Docs: https://docs.openclaw.ai
|
||||||
- Browser: secure Chrome extension relay CDP sessions.
|
- Browser: secure Chrome extension relay CDP sessions.
|
||||||
- Docker: use container port for gateway command instead of host port. (#5110) Thanks @mise42.
|
- Docker: use container port for gateway command instead of host port. (#5110) Thanks @mise42.
|
||||||
- fix(lobster): block arbitrary exec via lobsterPath/cwd injection (GHSA-4mhr-g7xj-cg8j). (#5335) Thanks @vignesh07.
|
- fix(lobster): block arbitrary exec via lobsterPath/cwd injection (GHSA-4mhr-g7xj-cg8j). (#5335) Thanks @vignesh07.
|
||||||
|
- Security: block LD_/DYLD_ env overrides for host exec. (#4896) Thanks @HassanFleyah.
|
||||||
- Security: harden web tool content wrapping + file parsing safeguards. (#4058) Thanks @VACInc.
|
- Security: harden web tool content wrapping + file parsing safeguards. (#4058) Thanks @VACInc.
|
||||||
|
|
||||||
## 2026.1.30
|
## 2026.1.30
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ Notes:
|
||||||
- If multiple nodes are available, set `exec.node` or `tools.exec.node` to select one.
|
- If multiple nodes are available, set `exec.node` or `tools.exec.node` to select one.
|
||||||
- On non-Windows hosts, exec uses `SHELL` when set; if `SHELL` is `fish`, it prefers `bash` (or `sh`)
|
- On non-Windows hosts, exec uses `SHELL` when set; if `SHELL` is `fish`, it prefers `bash` (or `sh`)
|
||||||
from `PATH` to avoid fish-incompatible scripts, then falls back to `SHELL` if neither exists.
|
from `PATH` to avoid fish-incompatible scripts, then falls back to `SHELL` if neither exists.
|
||||||
|
- Host execution (`gateway`/`node`) rejects `env.PATH` and loader overrides (`LD_*`/`DYLD_*`) to
|
||||||
|
prevent binary hijacking or injected code.
|
||||||
- Important: sandboxing is **off by default**. If sandboxing is off, `host=sandbox` runs directly on
|
- Important: sandboxing is **off by default**. If sandboxing is off, `host=sandbox` runs directly on
|
||||||
the gateway host (no container) and **does not require approvals**. To require approvals, run with
|
the gateway host (no container) and **does not require approvals**. To require approvals, run with
|
||||||
`host=gateway` and configure exec approvals (or enable sandboxing).
|
`host=gateway` and configure exec approvals (or enable sandboxing).
|
||||||
|
|
@ -65,16 +67,16 @@ Example:
|
||||||
|
|
||||||
### PATH handling
|
### PATH handling
|
||||||
|
|
||||||
- `host=gateway`: merges your login-shell `PATH` into the exec environment (unless the exec call
|
- `host=gateway`: merges your login-shell `PATH` into the exec environment. `env.PATH` overrides are
|
||||||
already sets `env.PATH`). The daemon itself still runs with a minimal `PATH`:
|
rejected for host execution. The daemon itself still runs with a minimal `PATH`:
|
||||||
- macOS: `/opt/homebrew/bin`, `/usr/local/bin`, `/usr/bin`, `/bin`
|
- macOS: `/opt/homebrew/bin`, `/usr/local/bin`, `/usr/bin`, `/bin`
|
||||||
- Linux: `/usr/local/bin`, `/usr/bin`, `/bin`
|
- Linux: `/usr/local/bin`, `/usr/bin`, `/bin`
|
||||||
- `host=sandbox`: runs `sh -lc` (login shell) inside the container, so `/etc/profile` may reset `PATH`.
|
- `host=sandbox`: runs `sh -lc` (login shell) inside the container, so `/etc/profile` may reset `PATH`.
|
||||||
OpenClaw prepends `env.PATH` after profile sourcing via an internal env var (no shell interpolation);
|
OpenClaw prepends `env.PATH` after profile sourcing via an internal env var (no shell interpolation);
|
||||||
`tools.exec.pathPrepend` applies here too.
|
`tools.exec.pathPrepend` applies here too.
|
||||||
- `host=node`: only env overrides you pass are sent to the node. `tools.exec.pathPrepend` only applies
|
- `host=node`: only non-blocked env overrides you pass are sent to the node. `env.PATH` overrides are
|
||||||
if the exec call already sets `env.PATH`. Headless node hosts accept `PATH` only when it prepends
|
rejected for host execution. Headless node hosts accept `PATH` only when it prepends the node host
|
||||||
the node host PATH (no replacement). macOS nodes drop `PATH` overrides entirely.
|
PATH (no replacement). macOS nodes drop `PATH` overrides entirely.
|
||||||
|
|
||||||
Per-agent node binding (use the agent list index in config):
|
Per-agent node binding (use the agent list index in config):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,3 +109,17 @@ describe("exec PATH login shell merge", () => {
|
||||||
expect(shellPathMock).not.toHaveBeenCalled();
|
expect(shellPathMock).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("exec host env validation", () => {
|
||||||
|
it("blocks LD_/DYLD_ env vars on host execution", async () => {
|
||||||
|
const { createExecTool } = await import("./bash-tools.exec.js");
|
||||||
|
const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
tool.execute("call1", {
|
||||||
|
command: "echo ok",
|
||||||
|
env: { LD_DEBUG: "1" },
|
||||||
|
}),
|
||||||
|
).rejects.toThrow(/Security Violation: Environment variable 'LD_DEBUG' is forbidden/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ const DANGEROUS_HOST_ENV_VARS = new Set([
|
||||||
"IFS",
|
"IFS",
|
||||||
"SSLKEYLOGFILE",
|
"SSLKEYLOGFILE",
|
||||||
]);
|
]);
|
||||||
|
const DANGEROUS_HOST_ENV_PREFIXES = ["DYLD_", "LD_"];
|
||||||
|
|
||||||
// Centralized sanitization helper.
|
// Centralized sanitization helper.
|
||||||
// Throws an error if dangerous variables or PATH modifications are detected on the host.
|
// Throws an error if dangerous variables or PATH modifications are detected on the host.
|
||||||
|
|
@ -84,6 +85,11 @@ function validateHostEnv(env: Record<string, string>): void {
|
||||||
const upperKey = key.toUpperCase();
|
const upperKey = key.toUpperCase();
|
||||||
|
|
||||||
// 1. Block known dangerous variables (Fail Closed)
|
// 1. Block known dangerous variables (Fail Closed)
|
||||||
|
if (DANGEROUS_HOST_ENV_PREFIXES.some((prefix) => upperKey.startsWith(prefix))) {
|
||||||
|
throw new Error(
|
||||||
|
`Security Violation: Environment variable '${key}' is forbidden during host execution.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
if (DANGEROUS_HOST_ENV_VARS.has(upperKey)) {
|
if (DANGEROUS_HOST_ENV_VARS.has(upperKey)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Security Violation: Environment variable '${key}' is forbidden during host execution.`,
|
`Security Violation: Environment variable '${key}' is forbidden during host execution.`,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue