Medium severity5.3NVD Advisory· Published Apr 28, 2026· Updated Apr 30, 2026
CVE-2026-41391
CVE-2026-41391
Description
OpenClaw before 2026.3.31 fails to properly sanitize PIP_INDEX_URL and UV_INDEX_URL environment variables in host execution contexts, allowing attackers to redirect Python package-index traffic. Attackers can exploit this bypass to intercept or manipulate package management operations by injecting malicious index URLs through unsanitized environment variables.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.31 | 2026.3.31 |
Affected products
1Patches
17ae1bb0c7799fix(host-env): block Python package index redirection env vars (#58011)
4 files changed · +58 −0
apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift+7 −0 modified@@ -82,6 +82,13 @@ enum HostEnvSecurityPolicy { "PHP_INI_SCAN_DIR", "DENO_DIR", "BUN_CONFIG_REGISTRY", + "PIP_INDEX_URL", + "PIP_PYPI_URL", + "PIP_EXTRA_INDEX_URL", + "UV_INDEX", + "UV_INDEX_URL", + "UV_DEFAULT_INDEX", + "UV_EXTRA_INDEX_URL", "LUA_PATH", "LUA_CPATH", "GEM_HOME",
CHANGELOG.md+1 −0 modified@@ -116,6 +116,7 @@ Docs: https://docs.openclaw.ai - Doctor/plugins: skip false Matrix legacy-helper warnings when no migration plans exist, and keep bundled `enabledByDefault` plugins in the gateway startup set. (#57931) Thanks @dinakars777. - Matrix/CLI send: start one-off Matrix send clients before outbound delivery so `openclaw message send --channel matrix` restores E2EE in encrypted rooms instead of sending plain events. (#57936) Thanks @gumadeiras. - Matrix/direct rooms: stop trusting remote `is_direct`, honor explicit local `is_direct: false` for discovered DM candidates, and avoid extra member-state lookups for shared rooms so DM routing and repair stay aligned. (#57124) Thanks @w-sss. +- Exec/env: block Python package index override variables from request-scoped host exec environment sanitization so package fetches cannot be redirected through a caller-supplied index. Thanks @nexrin and @vincentkoc. ## 2026.3.28
src/infra/host-env-security-policy.json+8 −0 modified@@ -75,6 +75,14 @@ "PHP_INI_SCAN_DIR", "DENO_DIR", "BUN_CONFIG_REGISTRY", + "PIP_INDEX_URL", + "PIP_PYPI_URL", + "PIP_EXTRA_INDEX_URL", + "UV_INDEX", + "UV_INDEX_URL", + "UV_EXTRA_INDEX_URL", + "UV_DEFAULT_INDEX", + "UV_EXTRA_INDEX_URL", "LUA_PATH", "LUA_CPATH", "GEM_HOME",
src/infra/host-env-security.test.ts+42 −0 modified@@ -233,6 +233,13 @@ describe("sanitizeHostExecEnv", () => { NPM_CONFIG_USERCONFIG: "/tmp/npmrc", GIT_CONFIG_GLOBAL: "/tmp/gitconfig", AWS_CONFIG_FILE: "/tmp/override-aws-config", + PIP_INDEX_URL: "https://example.invalid/simple", + PIP_PYPI_URL: "https://example.invalid/simple", + PIP_EXTRA_INDEX_URL: "https://example.invalid/simple", + 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", SHELLOPTS: "xtrace", PS4: "$(touch /tmp/pwned)", CLASSPATH: "/tmp/evil-classpath", @@ -267,6 +274,13 @@ describe("sanitizeHostExecEnv", () => { expect(env.GOFLAGS).toBeUndefined(); expect(env.PHPRC).toBeUndefined(); expect(env.XDG_CONFIG_HOME).toBeUndefined(); + expect(env.PIP_INDEX_URL).toBeUndefined(); + expect(env.PIP_PYPI_URL).toBeUndefined(); + expect(env.PIP_EXTRA_INDEX_URL).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.SAFE).toBe("ok"); expect(env.HOME).toBe("/tmp/trusted-home"); expect(env.ZDOTDIR).toBe("/tmp/trusted-zdotdir"); @@ -354,6 +368,13 @@ describe("isDangerousHostEnvOverrideVarName", () => { expect(isDangerousHostEnvOverrideVarName("git_config_global")).toBe(true); expect(isDangerousHostEnvOverrideVarName("GRADLE_USER_HOME")).toBe(true); expect(isDangerousHostEnvOverrideVarName("gradle_user_home")).toBe(true); + expect(isDangerousHostEnvOverrideVarName("PIP_INDEX_URL")).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("CLASSPATH")).toBe(true); expect(isDangerousHostEnvOverrideVarName("classpath")).toBe(true); expect(isDangerousHostEnvOverrideVarName("GOFLAGS")).toBe(true); @@ -380,6 +401,13 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => { CXX: "/tmp/evil-cxx", CMAKE_C_COMPILER: "/tmp/evil-c-compiler", CLASSPATH: "/tmp/evil-classpath", + PIP_INDEX_URL: "https://example.invalid/simple", + PIP_PYPI_URL: "https://example.invalid/simple", + PIP_EXTRA_INDEX_URL: "https://example.invalid/simple", + 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", SAFE_KEY: "ok", "BAD-KEY": "bad", }, @@ -390,13 +418,27 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => { "CMAKE_C_COMPILER", "CXX", "PATH", + "PIP_EXTRA_INDEX_URL", + "PIP_INDEX_URL", + "PIP_PYPI_URL", + "UV_DEFAULT_INDEX", + "UV_EXTRA_INDEX_URL", + "UV_INDEX", + "UV_INDEX_URL", ]); 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(); + 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.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(); }); 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
6- github.com/openclaw/openclaw/commit/7ae1bb0c7799fd0cbd2d4de7b0f5b8039837ab8dnvdPatchWEB
- github.com/advisories/GHSA-7ggg-pvrf-458vghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-7ggg-pvrf-458vnvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-41391ghsaADVISORY
- www.vulncheck.com/advisories/openclaw-environment-variable-bypass-in-package-index-url-handlingnvdThird Party AdvisoryWEB
- github.com/openclaw/openclaw/releases/tag/v2026.3.31ghsaWEB
News mentions
0No linked articles in our index yet.