Medium severity6.1NVD Advisory· Published Apr 28, 2026· Updated May 1, 2026
CVE-2026-41373
CVE-2026-41373
Description
OpenClaw before 2026.3.31 contains an incomplete host-env-security-policy.json that fails to restrict compiler binary environment variables, allowing untrusted models to substitute CC, CXX, CARGO_BUILD_RUSTC, and CMAKE_C_COMPILER via environment overrides. Attackers with approved host-exec requests can override compiler binaries to execute arbitrary code during build processes.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.31 | 2026.3.31 |
Affected products
1Patches
1e277a37f896bInfra: block compiler env overrides (#57832)
3 files changed · +126 −4
apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift+5 −0 modified@@ -22,6 +22,11 @@ enum HostEnvSecurityPolicy { "GIT_EXEC_PATH", "GIT_SEQUENCE_EDITOR", "GIT_TEMPLATE_DIR", + "CC", + "CXX", + "CARGO_BUILD_RUSTC", + "CMAKE_C_COMPILER", + "CMAKE_CXX_COMPILER", "SHELL", "SHELLOPTS", "PS4",
src/infra/host-env-security-policy.json+5 −0 modified@@ -16,6 +16,11 @@ "GIT_EXEC_PATH", "GIT_SEQUENCE_EDITOR", "GIT_TEMPLATE_DIR", + "CC", + "CXX", + "CARGO_BUILD_RUSTC", + "CMAKE_C_COMPILER", + "CMAKE_CXX_COMPILER", "SHELL", "SHELLOPTS", "PS4",
src/infra/host-env-security.test.ts+116 −4 modified@@ -13,12 +13,28 @@ import { } from "./host-env-security.js"; import { OPENCLAW_CLI_ENV_VALUE } from "./openclaw-exec-env.js"; -function getSystemGitPath() { +function findSystemCommandPath(command: string) { if (process.platform === "win32") { return null; } - const gitPath = "/usr/bin/git"; - return fs.existsSync(gitPath) ? gitPath : null; + for (const dir of (process.env.PATH ?? "/usr/bin:/bin").split(path.delimiter)) { + if (!dir) { + continue; + } + const candidate = path.join(dir, command); + if (fs.existsSync(candidate)) { + return candidate; + } + } + return null; +} + +function getSystemGitPath() { + return findSystemCommandPath("git"); +} + +function getSystemMakePath() { + return findSystemCommandPath("make"); } function clearMarker(marker: string) { @@ -96,6 +112,18 @@ async function initGitRepoWithCommits(gitPath: string, repoDir: string, commitCo } } +async function runMakeCommand(makePath: string, cwd: string, env: NodeJS.ProcessEnv) { + await new Promise<void>((resolve) => { + const child = spawn(makePath, ["all"], { + cwd, + env, + stdio: "ignore", + }); + child.once("error", () => resolve()); + child.once("close", () => resolve()); + }); +} + describe("isDangerousHostEnvVarName", () => { it("matches dangerous keys and prefixes case-insensitively", () => { expect(isDangerousHostEnvVarName("BASH_ENV")).toBe(true); @@ -111,6 +139,14 @@ describe("isDangerousHostEnvVarName", () => { expect(isDangerousHostEnvVarName("git_sequence_editor")).toBe(true); expect(isDangerousHostEnvVarName("GIT_TEMPLATE_DIR")).toBe(true); expect(isDangerousHostEnvVarName("git_template_dir")).toBe(true); + expect(isDangerousHostEnvVarName("CC")).toBe(true); + expect(isDangerousHostEnvVarName("cxx")).toBe(true); + expect(isDangerousHostEnvVarName("CARGO_BUILD_RUSTC")).toBe(true); + expect(isDangerousHostEnvVarName("cargo_build_rustc")).toBe(true); + expect(isDangerousHostEnvVarName("CMAKE_C_COMPILER")).toBe(true); + expect(isDangerousHostEnvVarName("cmake_c_compiler")).toBe(true); + expect(isDangerousHostEnvVarName("CMAKE_CXX_COMPILER")).toBe(true); + expect(isDangerousHostEnvVarName("cmake_cxx_compiler")).toBe(true); expect(isDangerousHostEnvVarName("SHELLOPTS")).toBe(true); expect(isDangerousHostEnvVarName("ps4")).toBe(true); expect(isDangerousHostEnvVarName("DYLD_INSERT_LIBRARIES")).toBe(true); @@ -184,6 +220,11 @@ describe("sanitizeHostExecEnv", () => { ZDOTDIR: "/tmp/evil-zdotdir", BASH_ENV: "/tmp/pwn.sh", BROWSER: "/tmp/browser", + CC: "/tmp/evil-cc", + CXX: "/tmp/evil-cxx", + CARGO_BUILD_RUSTC: "/tmp/evil-rustc", + CMAKE_C_COMPILER: "/tmp/evil-c-compiler", + CMAKE_CXX_COMPILER: "/tmp/evil-cxx-compiler", GIT_SSH_COMMAND: "touch /tmp/pwned", GIT_EDITOR: "/tmp/git-editor", GIT_EXEC_PATH: "/tmp/git-exec-path", @@ -207,6 +248,11 @@ describe("sanitizeHostExecEnv", () => { expect(env.BASH_ENV).toBeUndefined(); expect(env.BROWSER).toBeUndefined(); expect(env.GIT_EDITOR).toBeUndefined(); + expect(env.CC).toBeUndefined(); + expect(env.CXX).toBeUndefined(); + expect(env.CARGO_BUILD_RUSTC).toBeUndefined(); + expect(env.CMAKE_C_COMPILER).toBeUndefined(); + expect(env.CMAKE_CXX_COMPILER).toBeUndefined(); expect(env.GIT_TEMPLATE_DIR).toBeUndefined(); expect(env.GIT_SEQUENCE_EDITOR).toBeUndefined(); expect(env.AWS_CONFIG_FILE).toBeUndefined(); @@ -331,17 +377,26 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => { }, overrides: { PATH: "/tmp/evil", + CXX: "/tmp/evil-cxx", + CMAKE_C_COMPILER: "/tmp/evil-c-compiler", CLASSPATH: "/tmp/evil-classpath", SAFE_KEY: "ok", "BAD-KEY": "bad", }, }); - expect(result.rejectedOverrideBlockedKeys).toEqual(["CLASSPATH", "PATH"]); + expect(result.rejectedOverrideBlockedKeys).toEqual([ + "CLASSPATH", + "CMAKE_C_COMPILER", + "CXX", + "PATH", + ]); expect(result.rejectedOverrideInvalidKeys).toEqual(["BAD-KEY"]); expect(result.env.SAFE_KEY).toBe("ok"); expect(result.env.PATH).toBe("/usr/bin:/bin"); expect(result.env.CLASSPATH).toBeUndefined(); + expect(result.env.CXX).toBeUndefined(); + expect(result.env.CMAKE_C_COMPILER).toBeUndefined(); }); it("allows Windows-style override names while still rejecting invalid keys", () => { @@ -696,3 +751,60 @@ describe("git env exploit regression", () => { expect(fs.existsSync(marker)).toBe(false); }); }); + +describe("compiler override exploit regression", () => { + it("blocks CC overrides so make cannot execute a substituted compiler", async () => { + const makePath = getSystemMakePath(); + if (!makePath) { + return; + } + + const tempDir = fs.mkdtempSync( + path.join(os.tmpdir(), `openclaw-compiler-override-${process.pid}-${Date.now()}-`), + ); + const exploitPath = path.join(tempDir, "evil-cc"); + const marker = path.join( + os.tmpdir(), + `openclaw-compiler-override-marker-${process.pid}-${Date.now()}`, + ); + + try { + // `CC` is a representative proof for the whole class because all compiler override keys + // flow through the same host env sanitization boundary; unit tests cover the sibling keys. + clearMarker(marker); + fs.writeFileSync( + path.join(tempDir, "Makefile"), + "all:\n\t@$(CC) --version >/dev/null 2>&1 || true\n", + "utf8", + ); + fs.writeFileSync(exploitPath, `#!/bin/sh\ntouch ${JSON.stringify(marker)}\nexit 1\n`, "utf8"); + fs.chmodSync(exploitPath, 0o755); + + const baseEnv = { + PATH: process.env.PATH ?? "/usr/bin:/bin", + }; + + await runMakeCommand(makePath, tempDir, { + ...baseEnv, + CC: exploitPath, + }); + + expect(fs.existsSync(marker)).toBe(true); + clearMarker(marker); + + const safeEnv = sanitizeHostExecEnv({ + baseEnv, + overrides: { + CC: exploitPath, + }, + }); + + await runMakeCommand(makePath, tempDir, safeEnv); + + expect(fs.existsSync(marker)).toBe(false); + } finally { + fs.rmSync(tempDir, { recursive: true, force: true }); + fs.rmSync(marker, { force: true }); + } + }); +});
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/openclaw/openclaw/commit/e277a37f896b5011a1df06e6490c6630074d0afanvdPatchWEB
- github.com/advisories/GHSA-g8xp-qx39-9jq9ghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-g8xp-qx39-9jq9nvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-41373ghsaADVISORY
- www.vulncheck.com/advisories/openclaw-compiler-binary-substitution-via-environment-variable-override-in-host-execution-policynvdThird Party AdvisoryWEB
- github.com/openclaw/openclaw/releases/tag/v2026.3.31ghsaWEB
News mentions
0No linked articles in our index yet.