VYPR
Medium severity5.0NVD Advisory· Published May 11, 2026· Updated May 13, 2026

CVE-2026-44992

CVE-2026-44992

Description

OpenClaw versions 2026.4.5 before 2026.4.20 contain an environment variable injection vulnerability allowing workspace dotenv to override MINIMAX_API_HOST. Attackers can redirect credentialed MiniMax API requests to attacker-controlled origins, exposing the MiniMax API key in Authorization headers.

Affected products

1

Patches

1
2f06696579a1

fix(security): block MINIMAX_API_HOST workspace env injection and remove env-driven URL routing [AI-assisted] (#67300)

https://github.com/openclaw/openclawPavan Kumar GondhiApr 20, 2026via nvd-ref
5 files changed · +67 2
  • CHANGELOG.md+1 0 modified
    @@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
     
     ### Fixes
     
    +- fix(security): block MINIMAX_API_HOST workspace env injection and remove env-driven URL routing [AI-assisted]. (#67300) Thanks @pgondhi987.
     - Cron/delivery: treat explicit `delivery.mode: "none"` runs as not requested even if the runner reports `delivered: false`, so no-delivery cron jobs no longer persist false delivery failures or errors. (#69285) Thanks @matsuri1987.
     - Plugins/install: repair active and default-enabled bundled plugin runtime dependencies before import in packaged installs, so bundled Discord, WhatsApp, Slack, Telegram, and provider plugins work without putting their dependency trees in core.
     - BlueBubbles: raise the outbound `/api/v1/message/text` send timeout default from 10s to 30s, and add a configurable `channels.bluebubbles.sendTimeoutMs` (also per-account) so macOS 26 setups where Private API iMessage sends stall for 60+ seconds no longer silently lose messages at the 10s abort. Probes, chat lookups, and health checks keep the shorter 10s default. Fixes #67486. (#69193) Thanks @omarshahine.
    
  • extensions/minimax/speech-provider.test.ts+1 1 modified
    @@ -86,7 +86,7 @@ describe("buildMinimaxSpeechProvider", () => {
           expect(config.pitch).toBe(3);
         });
     
    -    it("reads from env vars as fallback", () => {
    +    it("keeps trusted MINIMAX_API_HOST fallback for TTS baseUrl", () => {
           process.env.MINIMAX_API_HOST = "https://env.api.com";
           process.env.MINIMAX_TTS_MODEL = "speech-01-240228";
           process.env.MINIMAX_TTS_VOICE_ID = "Chinese (Mandarin)_Gentle_Boy";
    
  • src/agents/minimax-vlm.normalizes-api-key.test.ts+30 0 modified
    @@ -10,13 +10,19 @@ beforeAll(async () => {
     
     describe("minimaxUnderstandImage apiKey normalization", () => {
       const priorFetch = global.fetch;
    +  const priorMinimaxApiHost = process.env.MINIMAX_API_HOST;
       const apiResponse = JSON.stringify({
         base_resp: { status_code: 0, status_msg: "ok" },
         content: "ok",
       });
     
       afterEach(() => {
         global.fetch = priorFetch;
    +    if (priorMinimaxApiHost === undefined) {
    +      delete process.env.MINIMAX_API_HOST;
    +    } else {
    +      process.env.MINIMAX_API_HOST = priorMinimaxApiHost;
    +    }
         vi.restoreAllMocks();
       });
     
    @@ -50,6 +56,30 @@ describe("minimaxUnderstandImage apiKey normalization", () => {
       it("drops non-Latin1 characters from apiKey before sending Authorization header", async () => {
         await runNormalizationCase("minimax-\u0417\u2502test-key");
       });
    +
    +  it("keeps trusted MINIMAX_API_HOST env fallback for VLM routing", async () => {
    +    process.env.MINIMAX_API_HOST = "https://api.minimaxi.com";
    +    const fetchSpy = vi.fn(async (input: RequestInfo | URL) => {
    +      const requestUrl =
    +        typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
    +      expect(requestUrl).toBe("https://api.minimaxi.com/v1/coding_plan/vlm");
    +      return new Response(apiResponse, {
    +        status: 200,
    +        headers: { "Content-Type": "application/json" },
    +      });
    +    });
    +    global.fetch = withFetchPreconnect(fetchSpy);
    +
    +    await expect(
    +      minimaxUnderstandImage({
    +        apiKey: "minimax-test-key",
    +        prompt: "hi",
    +        imageDataUrl: "data:image/png;base64,AAAA",
    +      }),
    +    ).resolves.toBe("ok");
    +
    +    expect(fetchSpy).toHaveBeenCalledOnce();
    +  });
     });
     
     describe("isMinimaxVlmModel", () => {
    
  • src/infra/dotenv.test.ts+33 0 modified
    @@ -197,6 +197,8 @@ describe("loadDotEnv", () => {
                 "OPENCLAW_STATE_DIR=./evil-state",
                 "OPENCLAW_CONFIG_PATH=./evil-config.json",
                 "ANTHROPIC_BASE_URL=https://evil.example.com/v1",
    +            "EXAMPLE_API_HOST=https://evil-api.example.com",
    +            "MINIMAX_API_HOST=https://evil.example.com",
                 "HTTP_PROXY=http://evil-proxy:8080",
                 "UV_PYTHON=./attacker-python",
                 "uv_python=./attacker-python-lower",
    @@ -209,6 +211,8 @@ describe("loadDotEnv", () => {
             delete process.env.NODE_OPTIONS;
             delete process.env.OPENCLAW_CONFIG_PATH;
             delete process.env.ANTHROPIC_BASE_URL;
    +        delete process.env.EXAMPLE_API_HOST;
    +        delete process.env.MINIMAX_API_HOST;
             delete process.env.HTTP_PROXY;
             delete process.env.UV_PYTHON;
             delete process.env.uv_python;
    @@ -221,6 +225,8 @@ describe("loadDotEnv", () => {
             expect(process.env.OPENCLAW_STATE_DIR).toBe(stateDir);
             expect(process.env.OPENCLAW_CONFIG_PATH).toBeUndefined();
             expect(process.env.ANTHROPIC_BASE_URL).toBeUndefined();
    +        expect(process.env.EXAMPLE_API_HOST).toBeUndefined();
    +        expect(process.env.MINIMAX_API_HOST).toBeUndefined();
             expect(process.env.HTTP_PROXY).toBeUndefined();
             expect(process.env.UV_PYTHON).toBeUndefined();
             expect(process.env.uv_python).toBeUndefined();
    @@ -613,6 +619,8 @@ describe("workspace .env blocklist completeness", () => {
               "OPENCLAW_DISABLE_BUNDLED_PLUGINS",
               "OPENCLAW_ALLOW_INSECURE_PRIVATE_WS",
               "OPENCLAW_BROWSER_EXECUTABLE_PATH",
    +          "EXAMPLE_API_HOST",
    +          "MINIMAX_API_HOST",
               "BROWSER_EXECUTABLE_PATH",
               "PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH",
               "OPENCLAW_SKIP_CHANNELS",
    @@ -671,4 +679,29 @@ describe("workspace .env blocklist completeness", () => {
           });
         });
       });
    +
    +  it("blocks generic endpoint-routing suffixes from workspace .env", async () => {
    +    await withIsolatedEnvAndCwd(async () => {
    +      await withDotEnvFixture(async ({ cwdDir }) => {
    +        await writeEnvFile(
    +          path.join(cwdDir, ".env"),
    +          [
    +            "FUTURE_PROVIDER_API_HOST=https://evil.example.com",
    +            "FUTURE_PROVIDER_BASE_URL=https://evil.example.com/v1",
    +            "SAFE_PROVIDER_URL=https://allowed.example.com",
    +          ].join("\n"),
    +        );
    +
    +        delete process.env.FUTURE_PROVIDER_API_HOST;
    +        delete process.env.FUTURE_PROVIDER_BASE_URL;
    +        delete process.env.SAFE_PROVIDER_URL;
    +
    +        loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
    +
    +        expect(process.env.FUTURE_PROVIDER_API_HOST).toBeUndefined();
    +        expect(process.env.FUTURE_PROVIDER_BASE_URL).toBeUndefined();
    +        expect(process.env.SAFE_PROVIDER_URL).toBe("https://allowed.example.com");
    +      });
    +    });
    +  });
     });
    
  • src/infra/dotenv.ts+2 1 modified
    @@ -21,6 +21,7 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([
       "CLAWHUB_URL",
       "HTTP_PROXY",
       "HTTPS_PROXY",
    +  "MINIMAX_API_HOST",
       "NODE_TLS_REJECT_UNAUTHORIZED",
       "NO_PROXY",
       "OPENAI_API_KEY",
    @@ -69,7 +70,7 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([
     ]);
     
     // Block endpoint redirection for any service without overfitting per-provider names.
    -const BLOCKED_WORKSPACE_DOTENV_SUFFIXES = ["_BASE_URL"];
    +const BLOCKED_WORKSPACE_DOTENV_SUFFIXES = ["_API_HOST", "_BASE_URL"];
     const BLOCKED_WORKSPACE_DOTENV_PREFIXES = [
       "ANTHROPIC_API_KEY_",
       "CLAWHUB_",
    

Vulnerability mechanics

AI mechanics synthesis has not run for this CVE yet.

References

3

News mentions

0

No linked articles in our index yet.