VYPR
Medium severity4.4NVD Advisory· Published Apr 21, 2026· Updated Apr 27, 2026

CVE-2026-41330

CVE-2026-41330

Description

OpenClaw before 2026.3.31 contains an environment variable override vulnerability in host exec policy that fails to properly enforce proxy, TLS, Docker, and Git TLS controls. Attackers can bypass security controls by overriding environment variables to circumvent proxy settings, TLS verification, Docker restrictions, and Git TLS enforcement.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openclawnpm
< 2026.3.312026.3.31

Affected products

1
  • cpe:2.3:a:openclaw:openclaw:*:*:*:*:*:node.js:*:*
    Range: <2026.3.31

Patches

1
4d912e04519b

fix(exec): block proxy-style env overrides (#58202)

https://github.com/openclaw/openclawVincent KocMar 31, 2026via ghsa
7 files changed · +187 2
  • apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift+19 0 modified
    @@ -22,6 +22,9 @@ enum HostEnvSecurityPolicy {
             "GIT_EXEC_PATH",
             "GIT_SEQUENCE_EDITOR",
             "GIT_TEMPLATE_DIR",
    +        "GIT_SSL_NO_VERIFY",
    +        "GIT_SSL_CAINFO",
    +        "GIT_SSL_CAPATH",
             "CC",
             "CXX",
             "CARGO_BUILD_RUSTC",
    @@ -54,6 +57,9 @@ enum HostEnvSecurityPolicy {
             "GIT_SSH",
             "GIT_PROXY_COMMAND",
             "GIT_ASKPASS",
    +        "GIT_SSL_NO_VERIFY",
    +        "GIT_SSL_CAINFO",
    +        "GIT_SSL_CAPATH",
             "SSH_ASKPASS",
             "LESSOPEN",
             "LESSCLOSE",
    @@ -82,6 +88,19 @@ enum HostEnvSecurityPolicy {
             "PHP_INI_SCAN_DIR",
             "DENO_DIR",
             "BUN_CONFIG_REGISTRY",
    +        "HTTP_PROXY",
    +        "HTTPS_PROXY",
    +        "ALL_PROXY",
    +        "NO_PROXY",
    +        "NODE_TLS_REJECT_UNAUTHORIZED",
    +        "NODE_EXTRA_CA_CERTS",
    +        "SSL_CERT_FILE",
    +        "SSL_CERT_DIR",
    +        "REQUESTS_CA_BUNDLE",
    +        "CURL_CA_BUNDLE",
    +        "DOCKER_HOST",
    +        "DOCKER_TLS_VERIFY",
    +        "DOCKER_CERT_PATH",
             "PIP_INDEX_URL",
             "PIP_PYPI_URL",
             "PIP_EXTRA_INDEX_URL",
    
  • CHANGELOG.md+1 0 modified
    @@ -91,6 +91,7 @@ Docs: https://docs.openclaw.ai
     - Agents/Kimi: preserve already-valid Anthropic-compatible tool call argument objects while still clearing cached repairs when later trailing junk exceeds the repair allowance. (#54491) Thanks @yuanaichi.
     - Docker/setup: force BuildKit for local image builds (including sandbox image builds) so `./docker-setup.sh` no longer fails on `RUN --mount=...` when hosts default to Docker's legacy builder. (#56681) Thanks @zhanghui-china.
     - Control UI/agents: auto-load agent workspace files on initial Files panel open, and populate overview model/workspace/fallbacks from effective runtime agent metadata so defaulted models no longer show as `Not set`. (#56637) Thanks @dxsx84.
    +- Exec/env: block proxy, TLS, and Docker endpoint env overrides in host execution so request-scoped commands cannot silently reroute outbound traffic or trust attacker-supplied certificate settings. Thanks @AntAISecurityLab.
     - Control UI/slash commands: make `/steer` and `/redirect` work from the chat command palette with visible pending state for active-run `/steer`, correct redirected-run tracking, and a single canonical `/steer` entry in the command menu. (#54625) Thanks @fuller-stack-dev.
     - Exec/approvals: keep `awk` and `sed` family binaries out of the low-risk `safeBins` fast path, and stop doctor profile scaffolding from treating them like ordinary custom filters. Thanks @vincentkoc.
     - Exec/runtime: default implicit exec to `host=auto`, resolve that target to sandbox only when a sandbox runtime exists, keep explicit `host=sandbox` fail-closed without sandbox, and show `/exec` effective host state in runtime status/docs.
    
  • src/agents/bash-tools.exec.path.test.ts+16 0 modified
    @@ -228,6 +228,22 @@ describe("exec host env validation", () => {
         ).rejects.toThrow(/Security Violation: Environment variable 'LD_DEBUG' is forbidden/);
       });
     
    +  it("blocks proxy and TLS override env vars on host execution", async () => {
    +    const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });
    +
    +    await expect(
    +      tool.execute("call1", {
    +        command: "echo ok",
    +        env: {
    +          HTTPS_PROXY: "http://proxy.example.test:8080",
    +          NODE_TLS_REJECT_UNAUTHORIZED: "0",
    +        },
    +      }),
    +    ).rejects.toThrow(
    +      /Security Violation: blocked override keys: HTTPS_PROXY, NODE_TLS_REJECT_UNAUTHORIZED\./,
    +    );
    +  });
    +
       it("strips dangerous inherited env vars from host execution", async () => {
         if (isWin) {
           return;
    
  • src/agents/skills/env-overrides.ts+7 2 modified
    @@ -1,6 +1,9 @@
     import type { OpenClawConfig } from "../../config/config.js";
     import { normalizeResolvedSecretInputString } from "../../config/types.secrets.js";
    -import { isDangerousHostEnvVarName } from "../../infra/host-env-security.js";
    +import {
    +  isDangerousHostEnvOverrideVarName,
    +  isDangerousHostEnvVarName,
    +} from "../../infra/host-env-security.js";
     import { createSubsystemLogger } from "../../logging/subsystem.js";
     import { sanitizeEnvVars, validateEnvVarValue } from "../sandbox/sanitize-env-vars.js";
     import { resolveSkillConfig } from "./config.js";
    @@ -86,7 +89,9 @@ function matchesAnyPattern(value: string, patterns: readonly RegExp[]): boolean
     
     function isAlwaysBlockedSkillEnvKey(key: string): boolean {
       return (
    -    isDangerousHostEnvVarName(key) || matchesAnyPattern(key, SKILL_ALWAYS_BLOCKED_ENV_PATTERNS)
    +    isDangerousHostEnvVarName(key) ||
    +    isDangerousHostEnvOverrideVarName(key) ||
    +    matchesAnyPattern(key, SKILL_ALWAYS_BLOCKED_ENV_PATTERNS)
       );
     }
     
    
  • src/agents/skills.test.ts+44 0 modified
    @@ -511,6 +511,50 @@ describe("applySkillEnvOverrides", () => {
         });
       });
     
    +  it("blocks override-only host env overrides in skill config", async () => {
    +    const workspaceDir = await makeWorkspace();
    +    const skillDir = path.join(workspaceDir, "skills", "override-env-skill");
    +    await writeSkill({
    +      dir: skillDir,
    +      name: "override-env-skill",
    +      description: "Needs env",
    +      metadata:
    +        '{"openclaw":{"requires":{"env":["HTTPS_PROXY","NODE_TLS_REJECT_UNAUTHORIZED","DOCKER_HOST"]}}}',
    +    });
    +
    +    const entries = loadWorkspaceSkillEntries(workspaceDir, resolveTestSkillDirs(workspaceDir));
    +
    +    withClearedEnv(["HTTPS_PROXY", "NODE_TLS_REJECT_UNAUTHORIZED", "DOCKER_HOST"], () => {
    +      const restore = applySkillEnvOverrides({
    +        skills: entries,
    +        config: {
    +          skills: {
    +            entries: {
    +              "override-env-skill": {
    +                env: {
    +                  HTTPS_PROXY: "http://proxy.example.test:8080",
    +                  NODE_TLS_REJECT_UNAUTHORIZED: "0",
    +                  DOCKER_HOST: "tcp://docker.example.test:2376",
    +                },
    +              },
    +            },
    +          },
    +        },
    +      });
    +
    +      try {
    +        expect(process.env.HTTPS_PROXY).toBeUndefined();
    +        expect(process.env.NODE_TLS_REJECT_UNAUTHORIZED).toBeUndefined();
    +        expect(process.env.DOCKER_HOST).toBeUndefined();
    +      } finally {
    +        restore();
    +        expect(process.env.HTTPS_PROXY).toBeUndefined();
    +        expect(process.env.NODE_TLS_REJECT_UNAUTHORIZED).toBeUndefined();
    +        expect(process.env.DOCKER_HOST).toBeUndefined();
    +      }
    +    });
    +  });
    +
       it("allows required env overrides from snapshots", async () => {
         const workspaceDir = await makeWorkspace();
         const skillDir = path.join(workspaceDir, "skills", "snapshot-env-skill");
    
  • src/infra/host-env-security-policy.json+19 0 modified
    @@ -16,6 +16,9 @@
         "GIT_EXEC_PATH",
         "GIT_SEQUENCE_EDITOR",
         "GIT_TEMPLATE_DIR",
    +    "GIT_SSL_NO_VERIFY",
    +    "GIT_SSL_CAINFO",
    +    "GIT_SSL_CAPATH",
         "CC",
         "CXX",
         "CARGO_BUILD_RUSTC",
    @@ -47,6 +50,9 @@
         "GIT_SSH",
         "GIT_PROXY_COMMAND",
         "GIT_ASKPASS",
    +    "GIT_SSL_NO_VERIFY",
    +    "GIT_SSL_CAINFO",
    +    "GIT_SSL_CAPATH",
         "SSH_ASKPASS",
         "LESSOPEN",
         "LESSCLOSE",
    @@ -75,6 +81,19 @@
         "PHP_INI_SCAN_DIR",
         "DENO_DIR",
         "BUN_CONFIG_REGISTRY",
    +    "HTTP_PROXY",
    +    "HTTPS_PROXY",
    +    "ALL_PROXY",
    +    "NO_PROXY",
    +    "NODE_TLS_REJECT_UNAUTHORIZED",
    +    "NODE_EXTRA_CA_CERTS",
    +    "SSL_CERT_FILE",
    +    "SSL_CERT_DIR",
    +    "REQUESTS_CA_BUNDLE",
    +    "CURL_CA_BUNDLE",
    +    "DOCKER_HOST",
    +    "DOCKER_TLS_VERIFY",
    +    "DOCKER_CERT_PATH",
         "PIP_INDEX_URL",
         "PIP_PYPI_URL",
         "PIP_EXTRA_INDEX_URL",
    
  • src/infra/host-env-security.test.ts+81 0 modified
    @@ -174,6 +174,21 @@ describe("isDangerousHostEnvVarName", () => {
         expect(isDangerousHostEnvVarName("gradle_opts")).toBe(true);
         expect(isDangerousHostEnvVarName("ANT_OPTS")).toBe(true);
         expect(isDangerousHostEnvVarName("ant_opts")).toBe(true);
    +    expect(isDangerousHostEnvVarName("HTTPS_PROXY")).toBe(false);
    +    expect(isDangerousHostEnvVarName("https_proxy")).toBe(false);
    +    expect(isDangerousHostEnvVarName("HTTP_PROXY")).toBe(false);
    +    expect(isDangerousHostEnvVarName("http_proxy")).toBe(false);
    +    expect(isDangerousHostEnvVarName("ALL_PROXY")).toBe(false);
    +    expect(isDangerousHostEnvVarName("no_proxy")).toBe(false);
    +    expect(isDangerousHostEnvVarName("NODE_TLS_REJECT_UNAUTHORIZED")).toBe(false);
    +    expect(isDangerousHostEnvVarName("node_extra_ca_certs")).toBe(false);
    +    expect(isDangerousHostEnvVarName("SSL_CERT_FILE")).toBe(false);
    +    expect(isDangerousHostEnvVarName("SSL_CERT_DIR")).toBe(false);
    +    expect(isDangerousHostEnvVarName("requests_ca_bundle")).toBe(false);
    +    expect(isDangerousHostEnvVarName("CURL_CA_BUNDLE")).toBe(false);
    +    expect(isDangerousHostEnvVarName("DOCKER_HOST")).toBe(false);
    +    expect(isDangerousHostEnvVarName("docker_cert_path")).toBe(false);
    +    expect(isDangerousHostEnvVarName("DOCKER_TLS_VERIFY")).toBe(false);
         expect(isDangerousHostEnvVarName("AWS_CONFIG_FILE")).toBe(false);
         expect(isDangerousHostEnvVarName("aws_config_file")).toBe(false);
         expect(isDangerousHostEnvVarName("PATH")).toBe(false);
    @@ -257,6 +272,9 @@ describe("sanitizeHostExecEnv", () => {
             SSL_CERT_DIR: "/tmp/evil-cert-dir",
             REQUESTS_CA_BUNDLE: "/tmp/evil-requests-ca.pem",
             CURL_CA_BUNDLE: "/tmp/evil-curl-ca.pem",
    +        GIT_SSL_NO_VERIFY: "1",
    +        GIT_SSL_CAINFO: "/tmp/evil-git-ca.pem",
    +        GIT_SSL_CAPATH: "/tmp/evil-git-ca-dir",
             GOPROXY: "https://example.invalid/proxy",
             GONOSUMCHECK: "example.invalid/*",
             GONOSUMDB: "example.invalid/*",
    @@ -338,6 +356,54 @@ describe("sanitizeHostExecEnv", () => {
         expect(env.ZDOTDIR).toBe("/tmp/trusted-zdotdir");
       });
     
    +  it("keeps trusted inherited proxy, TLS, and Docker env while blocking overrides", () => {
    +    const env = sanitizeHostExecEnv({
    +      baseEnv: {
    +        PATH: "/usr/bin:/bin",
    +        HTTP_PROXY: "http://trusted-proxy.example.test:8080",
    +        HTTPS_PROXY: "http://trusted-proxy.example.test:8443",
    +        NODE_TLS_REJECT_UNAUTHORIZED: "0",
    +        SSL_CERT_DIR: "/etc/ssl/certs",
    +        CURL_CA_BUNDLE: "/etc/ssl/cert.pem",
    +        DOCKER_TLS_VERIFY: "1",
    +      },
    +      overrides: {
    +        HTTP_PROXY: "http://evil-proxy.example.test:8080",
    +        NODE_TLS_REJECT_UNAUTHORIZED: "1",
    +        DOCKER_TLS_VERIFY: "0",
    +      },
    +    });
    +
    +    expect(env).toEqual({
    +      OPENCLAW_CLI: OPENCLAW_CLI_ENV_VALUE,
    +      PATH: "/usr/bin:/bin",
    +      HTTP_PROXY: "http://trusted-proxy.example.test:8080",
    +      HTTPS_PROXY: "http://trusted-proxy.example.test:8443",
    +      NODE_TLS_REJECT_UNAUTHORIZED: "0",
    +      SSL_CERT_DIR: "/etc/ssl/certs",
    +      CURL_CA_BUNDLE: "/etc/ssl/cert.pem",
    +      DOCKER_TLS_VERIFY: "1",
    +    });
    +  });
    +
    +  it("blocks proxy, TLS, and Docker override values explicitly", () => {
    +    expect(isDangerousHostEnvOverrideVarName("HTTPS_PROXY")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("https_proxy")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("HTTP_PROXY")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("http_proxy")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("ALL_PROXY")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("no_proxy")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("NODE_TLS_REJECT_UNAUTHORIZED")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("node_extra_ca_certs")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("SSL_CERT_FILE")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("SSL_CERT_DIR")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("requests_ca_bundle")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("CURL_CA_BUNDLE")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("DOCKER_HOST")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("docker_cert_path")).toBe(true);
    +    expect(isDangerousHostEnvOverrideVarName("DOCKER_TLS_VERIFY")).toBe(true);
    +  });
    +
       it("drops dangerous inherited shell trace keys", () => {
         const env = sanitizeHostExecEnv({
           baseEnv: {
    @@ -503,6 +569,11 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => {
             GOPATH: "/tmp/evil-go",
             PYTHONUSERBASE: "/tmp/evil-python-userbase",
             VIRTUAL_ENV: "/tmp/evil-venv",
    +        HTTPS_PROXY: "http://proxy.example.test:8080",
    +        GIT_SSL_NO_VERIFY: "1",
    +        GIT_SSL_CAINFO: "/tmp/evil-git-ca.pem",
    +        GIT_SSL_CAPATH: "/tmp/evil-git-capath",
    +        NODE_TLS_REJECT_UNAUTHORIZED: "0",
             SAFE_KEY: "ok",
             "BAD-KEY": "bad",
           },
    @@ -520,15 +591,20 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => {
           "DOCKER_CONTEXT",
           "DOCKER_HOST",
           "DOCKER_TLS_VERIFY",
    +      "GIT_SSL_CAINFO",
    +      "GIT_SSL_CAPATH",
    +      "GIT_SSL_NO_VERIFY",
           "GOENV",
           "GONOPROXY",
           "GONOSUMCHECK",
           "GONOSUMDB",
           "GOPATH",
           "GOPRIVATE",
           "GOPROXY",
    +      "HTTPS_PROXY",
           "LIBRARY_PATH",
           "NODE_EXTRA_CA_CERTS",
    +      "NODE_TLS_REJECT_UNAUTHORIZED",
           "OBJC_INCLUDE_PATH",
           "PATH",
           "PIP_CONFIG_FILE",
    @@ -563,6 +639,9 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => {
         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.GIT_SSL_NO_VERIFY).toBeUndefined();
    +    expect(result.env.GIT_SSL_CAINFO).toBeUndefined();
    +    expect(result.env.GIT_SSL_CAPATH).toBeUndefined();
         expect(result.env.DOCKER_HOST).toBeUndefined();
         expect(result.env.DOCKER_TLS_VERIFY).toBeUndefined();
         expect(result.env.DOCKER_CERT_PATH).toBeUndefined();
    @@ -584,6 +663,8 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => {
         expect(result.env.GOPRIVATE).toBeUndefined();
         expect(result.env.GOENV).toBeUndefined();
         expect(result.env.GOPATH).toBeUndefined();
    +    expect(result.env.HTTPS_PROXY).toBeUndefined();
    +    expect(result.env.NODE_TLS_REJECT_UNAUTHORIZED).toBeUndefined();
         expect(result.env.PYTHONUSERBASE).toBeUndefined();
         expect(result.env.VIRTUAL_ENV).toBeUndefined();
       });
    

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

News mentions

0

No linked articles in our index yet.