Medium severity6.5NVD Advisory· Published Apr 28, 2026· Updated Apr 28, 2026
CVE-2026-41369
CVE-2026-41369
Description
OpenClaw before 2026.3.31 contains insufficient environment variable sanitization in host exec operations, failing to filter package, registry, Docker, compiler, and TLS override variables. Attackers can exploit this by injecting malicious environment variables to override critical system configurations and compromise host execution integrity.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.31 | 2026.3.31 |
Affected products
1Patches
1eb8de6715f02fix(exec): block risky host env overrides (#58209)
4 files changed · +200 −0
apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift+26 −0 modified@@ -85,10 +85,36 @@ enum HostEnvSecurityPolicy { "PIP_INDEX_URL", "PIP_PYPI_URL", "PIP_EXTRA_INDEX_URL", + "PIP_CONFIG_FILE", + "PIP_FIND_LINKS", + "PIP_TRUSTED_HOST", "UV_INDEX", "UV_INDEX_URL", "UV_EXTRA_INDEX_URL", "UV_DEFAULT_INDEX", + "DOCKER_HOST", + "DOCKER_TLS_VERIFY", + "DOCKER_CERT_PATH", + "DOCKER_CONTEXT", + "LIBRARY_PATH", + "CPATH", + "C_INCLUDE_PATH", + "CPLUS_INCLUDE_PATH", + "OBJC_INCLUDE_PATH", + "NODE_EXTRA_CA_CERTS", + "SSL_CERT_FILE", + "SSL_CERT_DIR", + "REQUESTS_CA_BUNDLE", + "CURL_CA_BUNDLE", + "GOPROXY", + "GONOSUMCHECK", + "GONOSUMDB", + "GONOPROXY", + "GOPRIVATE", + "GOENV", + "GOPATH", + "PYTHONUSERBASE", + "VIRTUAL_ENV", "LUA_PATH", "LUA_CPATH", "GEM_HOME",
CHANGELOG.md+1 −0 modified@@ -53,6 +53,7 @@ Docs: https://docs.openclaw.ai - Agents/sandbox: honor `tools.sandbox.tools.alsoAllow`, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman. - Memory/QMD: preserve explicit `start_line` and `end_line` metadata from mcporter query results so `memory search` hits keep the real snippet offsets instead of falling back to the snippet header. (#47960) Thanks @vincentkoc. - LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn ... --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels. +- Host exec/env: block additional request-scoped env overrides that can redirect Docker endpoints, trust roots, compiler include paths, package resolution, or Python environment roots during approved host runs. Thanks @tdjackey and @vincentkoc. - LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997. - TTS/Microsoft: auto-switch the default Edge voice to Chinese for CJK-dominant text without overriding explicitly selected Microsoft voices. (#52355) Thanks @extrasmall0. - Agents/context pruning: count supplementary-plane CJK characters with the shared code-point-aware estimator so context pruning stops underestimating Japanese and Chinese text that uses Extension B ideographs. (#39985) Thanks @Edward-Qiang-2024.
src/infra/host-env-security-policy.json+26 −0 modified@@ -78,10 +78,36 @@ "PIP_INDEX_URL", "PIP_PYPI_URL", "PIP_EXTRA_INDEX_URL", + "PIP_CONFIG_FILE", + "PIP_FIND_LINKS", + "PIP_TRUSTED_HOST", "UV_INDEX", "UV_INDEX_URL", "UV_EXTRA_INDEX_URL", "UV_DEFAULT_INDEX", + "DOCKER_HOST", + "DOCKER_TLS_VERIFY", + "DOCKER_CERT_PATH", + "DOCKER_CONTEXT", + "LIBRARY_PATH", + "CPATH", + "C_INCLUDE_PATH", + "CPLUS_INCLUDE_PATH", + "OBJC_INCLUDE_PATH", + "NODE_EXTRA_CA_CERTS", + "SSL_CERT_FILE", + "SSL_CERT_DIR", + "REQUESTS_CA_BUNDLE", + "CURL_CA_BUNDLE", + "GOPROXY", + "GONOSUMCHECK", + "GONOSUMDB", + "GONOPROXY", + "GOPRIVATE", + "GOENV", + "GOPATH", + "PYTHONUSERBASE", + "VIRTUAL_ENV", "LUA_PATH", "LUA_CPATH", "GEM_HOME",
src/infra/host-env-security.test.ts+147 −0 modified@@ -236,10 +236,36 @@ describe("sanitizeHostExecEnv", () => { PIP_INDEX_URL: "https://example.invalid/simple", PIP_PYPI_URL: "https://example.invalid/simple", PIP_EXTRA_INDEX_URL: "https://example.invalid/simple", + PIP_CONFIG_FILE: "/tmp/evil-pip.conf", + PIP_FIND_LINKS: "https://example.invalid/wheels", + PIP_TRUSTED_HOST: "example.invalid", UV_INDEX: "https://example.invalid/simple", UV_INDEX_URL: "https://example.invalid/simple", UV_DEFAULT_INDEX: "https://example.invalid/simple", UV_EXTRA_INDEX_URL: "https://example.invalid/simple", + DOCKER_HOST: "tcp://example.invalid:2376", + DOCKER_TLS_VERIFY: "1", + DOCKER_CERT_PATH: "/tmp/evil-docker-certs", + DOCKER_CONTEXT: "evil-remote", + LIBRARY_PATH: "/tmp/evil-lib", + CPATH: "/tmp/evil-headers", + C_INCLUDE_PATH: "/tmp/evil-c-headers", + CPLUS_INCLUDE_PATH: "/tmp/evil-cpp-headers", + OBJC_INCLUDE_PATH: "/tmp/evil-objc-headers", + NODE_EXTRA_CA_CERTS: "/tmp/evil-ca.pem", + SSL_CERT_FILE: "/tmp/evil-cert.pem", + SSL_CERT_DIR: "/tmp/evil-cert-dir", + REQUESTS_CA_BUNDLE: "/tmp/evil-requests-ca.pem", + CURL_CA_BUNDLE: "/tmp/evil-curl-ca.pem", + GOPROXY: "https://example.invalid/proxy", + GONOSUMCHECK: "example.invalid/*", + GONOSUMDB: "example.invalid/*", + GONOPROXY: "example.invalid/*", + GOPRIVATE: "example.invalid/*", + GOENV: "/tmp/evil-goenv", + GOPATH: "/tmp/evil-go", + PYTHONUSERBASE: "/tmp/evil-python-userbase", + VIRTUAL_ENV: "/tmp/evil-venv", SHELLOPTS: "xtrace", PS4: "$(touch /tmp/pwned)", CLASSPATH: "/tmp/evil-classpath", @@ -277,10 +303,36 @@ describe("sanitizeHostExecEnv", () => { expect(env.PIP_INDEX_URL).toBeUndefined(); expect(env.PIP_PYPI_URL).toBeUndefined(); expect(env.PIP_EXTRA_INDEX_URL).toBeUndefined(); + expect(env.PIP_CONFIG_FILE).toBeUndefined(); + expect(env.PIP_FIND_LINKS).toBeUndefined(); + expect(env.PIP_TRUSTED_HOST).toBeUndefined(); expect(env.UV_INDEX).toBeUndefined(); expect(env.UV_INDEX_URL).toBeUndefined(); expect(env.UV_DEFAULT_INDEX).toBeUndefined(); expect(env.UV_EXTRA_INDEX_URL).toBeUndefined(); + expect(env.DOCKER_HOST).toBeUndefined(); + expect(env.DOCKER_TLS_VERIFY).toBeUndefined(); + expect(env.DOCKER_CERT_PATH).toBeUndefined(); + expect(env.DOCKER_CONTEXT).toBeUndefined(); + expect(env.LIBRARY_PATH).toBeUndefined(); + expect(env.CPATH).toBeUndefined(); + expect(env.C_INCLUDE_PATH).toBeUndefined(); + expect(env.CPLUS_INCLUDE_PATH).toBeUndefined(); + expect(env.OBJC_INCLUDE_PATH).toBeUndefined(); + expect(env.NODE_EXTRA_CA_CERTS).toBeUndefined(); + expect(env.SSL_CERT_FILE).toBeUndefined(); + expect(env.SSL_CERT_DIR).toBeUndefined(); + expect(env.REQUESTS_CA_BUNDLE).toBeUndefined(); + expect(env.CURL_CA_BUNDLE).toBeUndefined(); + expect(env.GOPROXY).toBeUndefined(); + expect(env.GONOSUMCHECK).toBeUndefined(); + expect(env.GONOSUMDB).toBeUndefined(); + expect(env.GONOPROXY).toBeUndefined(); + expect(env.GOPRIVATE).toBeUndefined(); + expect(env.GOENV).toBeUndefined(); + expect(env.GOPATH).toBeUndefined(); + expect(env.PYTHONUSERBASE).toBeUndefined(); + expect(env.VIRTUAL_ENV).toBeUndefined(); expect(env.SAFE).toBe("ok"); expect(env.HOME).toBe("/tmp/trusted-home"); expect(env.ZDOTDIR).toBe("/tmp/trusted-zdotdir"); @@ -369,12 +421,29 @@ describe("isDangerousHostEnvOverrideVarName", () => { expect(isDangerousHostEnvOverrideVarName("GRADLE_USER_HOME")).toBe(true); expect(isDangerousHostEnvOverrideVarName("gradle_user_home")).toBe(true); expect(isDangerousHostEnvOverrideVarName("PIP_INDEX_URL")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("pip_config_file")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("PIP_FIND_LINKS")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("pip_trusted_host")).toBe(true); expect(isDangerousHostEnvOverrideVarName("pip_pypi_url")).toBe(true); expect(isDangerousHostEnvOverrideVarName("PIP_EXTRA_INDEX_URL")).toBe(true); expect(isDangerousHostEnvOverrideVarName("UV_INDEX")).toBe(true); expect(isDangerousHostEnvOverrideVarName("UV_INDEX_URL")).toBe(true); expect(isDangerousHostEnvOverrideVarName("uv_default_index")).toBe(true); expect(isDangerousHostEnvOverrideVarName("UV_EXTRA_INDEX_URL")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("DOCKER_HOST")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("docker_context")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("NODE_EXTRA_CA_CERTS")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("ssl_cert_file")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("REQUESTS_CA_BUNDLE")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("curl_ca_bundle")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("LIBRARY_PATH")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("c_include_path")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("GOPROXY")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("gonosumdb")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("GOPRIVATE")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("goenv")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("PYTHONUSERBASE")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("virtual_env")).toBe(true); expect(isDangerousHostEnvOverrideVarName("CLASSPATH")).toBe(true); expect(isDangerousHostEnvOverrideVarName("classpath")).toBe(true); expect(isDangerousHostEnvOverrideVarName("GOFLAGS")).toBe(true); @@ -404,27 +473,79 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => { PIP_INDEX_URL: "https://example.invalid/simple", PIP_PYPI_URL: "https://example.invalid/simple", PIP_EXTRA_INDEX_URL: "https://example.invalid/simple", + PIP_CONFIG_FILE: "/tmp/evil-pip.conf", + PIP_FIND_LINKS: "https://example.invalid/wheels", + PIP_TRUSTED_HOST: "example.invalid", UV_INDEX: "https://example.invalid/simple", UV_INDEX_URL: "https://example.invalid/simple", UV_DEFAULT_INDEX: "https://example.invalid/simple", UV_EXTRA_INDEX_URL: "https://example.invalid/simple", + DOCKER_HOST: "tcp://example.invalid:2376", + DOCKER_TLS_VERIFY: "1", + DOCKER_CERT_PATH: "/tmp/evil-docker-certs", + DOCKER_CONTEXT: "evil-remote", + LIBRARY_PATH: "/tmp/evil-lib", + CPATH: "/tmp/evil-headers", + C_INCLUDE_PATH: "/tmp/evil-c-headers", + CPLUS_INCLUDE_PATH: "/tmp/evil-cpp-headers", + OBJC_INCLUDE_PATH: "/tmp/evil-objc-headers", + NODE_EXTRA_CA_CERTS: "/tmp/evil-ca.pem", + SSL_CERT_FILE: "/tmp/evil-cert.pem", + SSL_CERT_DIR: "/tmp/evil-cert-dir", + REQUESTS_CA_BUNDLE: "/tmp/evil-requests-ca.pem", + CURL_CA_BUNDLE: "/tmp/evil-curl-ca.pem", + GOPROXY: "https://example.invalid/proxy", + GONOSUMCHECK: "example.invalid/*", + GONOSUMDB: "example.invalid/*", + GONOPROXY: "example.invalid/*", + GOPRIVATE: "example.invalid/*", + GOENV: "/tmp/evil-goenv", + GOPATH: "/tmp/evil-go", + PYTHONUSERBASE: "/tmp/evil-python-userbase", + VIRTUAL_ENV: "/tmp/evil-venv", SAFE_KEY: "ok", "BAD-KEY": "bad", }, }); expect(result.rejectedOverrideBlockedKeys).toEqual([ + "C_INCLUDE_PATH", "CLASSPATH", "CMAKE_C_COMPILER", + "CPATH", + "CPLUS_INCLUDE_PATH", + "CURL_CA_BUNDLE", "CXX", + "DOCKER_CERT_PATH", + "DOCKER_CONTEXT", + "DOCKER_HOST", + "DOCKER_TLS_VERIFY", + "GOENV", + "GONOPROXY", + "GONOSUMCHECK", + "GONOSUMDB", + "GOPATH", + "GOPRIVATE", + "GOPROXY", + "LIBRARY_PATH", + "NODE_EXTRA_CA_CERTS", + "OBJC_INCLUDE_PATH", "PATH", + "PIP_CONFIG_FILE", "PIP_EXTRA_INDEX_URL", + "PIP_FIND_LINKS", "PIP_INDEX_URL", "PIP_PYPI_URL", + "PIP_TRUSTED_HOST", + "PYTHONUSERBASE", + "REQUESTS_CA_BUNDLE", + "SSL_CERT_DIR", + "SSL_CERT_FILE", "UV_DEFAULT_INDEX", "UV_EXTRA_INDEX_URL", "UV_INDEX", "UV_INDEX_URL", + "VIRTUAL_ENV", ]); expect(result.rejectedOverrideInvalidKeys).toEqual(["BAD-KEY"]); expect(result.env.SAFE_KEY).toBe("ok"); @@ -435,10 +556,36 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => { expect(result.env.PIP_INDEX_URL).toBeUndefined(); expect(result.env.PIP_PYPI_URL).toBeUndefined(); expect(result.env.PIP_EXTRA_INDEX_URL).toBeUndefined(); + expect(result.env.PIP_CONFIG_FILE).toBeUndefined(); + expect(result.env.PIP_FIND_LINKS).toBeUndefined(); + expect(result.env.PIP_TRUSTED_HOST).toBeUndefined(); expect(result.env.UV_INDEX).toBeUndefined(); expect(result.env.UV_INDEX_URL).toBeUndefined(); expect(result.env.UV_DEFAULT_INDEX).toBeUndefined(); expect(result.env.UV_EXTRA_INDEX_URL).toBeUndefined(); + expect(result.env.DOCKER_HOST).toBeUndefined(); + expect(result.env.DOCKER_TLS_VERIFY).toBeUndefined(); + expect(result.env.DOCKER_CERT_PATH).toBeUndefined(); + expect(result.env.DOCKER_CONTEXT).toBeUndefined(); + expect(result.env.LIBRARY_PATH).toBeUndefined(); + expect(result.env.CPATH).toBeUndefined(); + expect(result.env.C_INCLUDE_PATH).toBeUndefined(); + expect(result.env.CPLUS_INCLUDE_PATH).toBeUndefined(); + expect(result.env.OBJC_INCLUDE_PATH).toBeUndefined(); + expect(result.env.NODE_EXTRA_CA_CERTS).toBeUndefined(); + expect(result.env.SSL_CERT_FILE).toBeUndefined(); + expect(result.env.SSL_CERT_DIR).toBeUndefined(); + expect(result.env.REQUESTS_CA_BUNDLE).toBeUndefined(); + expect(result.env.CURL_CA_BUNDLE).toBeUndefined(); + expect(result.env.GOPROXY).toBeUndefined(); + expect(result.env.GONOSUMCHECK).toBeUndefined(); + expect(result.env.GONOSUMDB).toBeUndefined(); + expect(result.env.GONOPROXY).toBeUndefined(); + expect(result.env.GOPRIVATE).toBeUndefined(); + expect(result.env.GOENV).toBeUndefined(); + expect(result.env.GOPATH).toBeUndefined(); + expect(result.env.PYTHONUSERBASE).toBeUndefined(); + expect(result.env.VIRTUAL_ENV).toBeUndefined(); }); it("allows Windows-style override names while still rejecting invalid keys", () => {
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
5- github.com/openclaw/openclaw/commit/eb8de6715f02949c21c4e895fffc8a6dcb00975cnvdPatchWEB
- github.com/advisories/GHSA-cg7q-fg22-4g98ghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-cg7q-fg22-4g98nvdVendor AdvisoryWEB
- www.vulncheck.com/advisories/openclaw-insufficient-environment-variable-sanitization-in-host-executionnvdThird Party AdvisoryWEB
- github.com/openclaw/openclaw/releases/tag/v2026.3.31ghsaWEB
News mentions
0No linked articles in our index yet.