OpenClaw Gateway tool allowed unrestricted gatewayUrl override
Description
OpenClaw is a personal AI assistant. Prior to OpenClaw version 2026.2.14, the Gateway tool accepted a tool-supplied gatewayUrl without sufficient restrictions, which could cause the OpenClaw host to attempt outbound WebSocket connections to user-specified targets. This requires the ability to invoke tools that accept gatewayUrl overrides (directly or indirectly). In typical setups this is limited to authenticated operators, trusted automation, or environments where tool calls are exposed to non-operators. In other words, this is not a drive-by issue for arbitrary internet users unless a deployment explicitly allows untrusted users to trigger these tool calls. Some tool call paths allowed gatewayUrl overrides to flow into the Gateway WebSocket client without validation or allowlisting. This meant the host could be instructed to attempt connections to non-gateway endpoints (for example, localhost services, private network addresses, or cloud metadata IPs). In the common case, this results in an outbound connection attempt from the OpenClaw host (and corresponding errors/timeouts). In environments where the tool caller can observe the results, this can also be used for limited network reachability probing. If the target speaks WebSocket and is reachable, further interaction may be possible. Starting in version 2026.2.14, tool-supplied gatewayUrl overrides are restricted to loopback (on the configured gateway port) or the configured gateway.remote.url. Disallowed protocols, credentials, query/hash, and non-root paths are rejected.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.2.14 | 2026.2.14 |
Affected products
1Patches
1c5406e1d2434fix(security): prevent gatewayUrl SSRF
4 files changed · +61 −2
CHANGELOG.md+1 −0 modified@@ -25,6 +25,7 @@ Docs: https://docs.openclaw.ai - Security/Exec approvals: prevent safeBins allowlist bypass via shell expansion (host exec allowlist mode only; not enabled by default). Thanks @christos-eth. - Security/Gateway: block `system.execApprovals.*` via `node.invoke` (use `exec.approvals.node.*` instead). Thanks @christos-eth. - Security/Exec: harden PATH handling by disabling project-local `node_modules/.bin` bootstrapping by default, disallowing node-host `PATH` overrides, and spawning ACP servers via the current executable by default. Thanks @akhmittra. +- Security/Gateway: prevent SSRF by ignoring user-provided `gatewayUrl` tool inputs (gateway URL must come from config). Thanks @p80n-sec. - CLI: fix lazy core command registration so top-level maintenance commands (`doctor`, `dashboard`, `reset`, `uninstall`) resolve correctly instead of exposing a non-functional `maintenance` placeholder command. - Telegram: when `channels.telegram.commands.native` is `false`, exclude plugin commands from `setMyCommands` menu registration while keeping plugin slash handlers callable. (#15132) Thanks @Glucksberg. - Security/Agents: scope CLI process cleanup to owned child PIDs to avoid killing unrelated processes on shared hosts. Thanks @aether-ai-agent.
src/agents/tools/gateway.e2e.test.ts+1 −1 modified@@ -20,7 +20,7 @@ describe("gateway tool defaults", () => { expect(opts.url).toBeUndefined(); }); - it("passes through explicit overrides", async () => { + it("accepts allowlisted gatewayUrl overrides (SSRF hardening)", async () => { callGatewayMock.mockResolvedValueOnce({ ok: true }); await callGatewayTool( "health",
src/infra/outbound/message.e2e.test.ts+51 −0 modified@@ -3,6 +3,7 @@ import type { ChannelOutboundAdapter, ChannelPlugin } from "../../channels/plugi import { setActivePluginRegistry } from "../../plugins/runtime.js"; import { createTestRegistry } from "../../test-utils/channel-plugins.js"; import { createIMessageTestPlugin } from "../../test-utils/imessage-test-plugin.js"; +import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js"; import { sendMessage, sendPoll } from "./message.js"; const setRegistry = (registry: ReturnType<typeof createTestRegistry>) => { @@ -172,6 +173,56 @@ describe("sendPoll channel normalization", () => { }); }); +describe("gateway url override hardening", () => { + beforeEach(() => { + callGatewayMock.mockReset(); + setRegistry(emptyRegistry); + }); + + afterEach(() => { + setRegistry(emptyRegistry); + }); + + it("drops gateway url overrides in backend mode (SSRF hardening)", async () => { + setRegistry( + createTestRegistry([ + { + pluginId: "mattermost", + source: "test", + plugin: { + ...createMattermostLikePlugin({ onSendText: () => {} }), + outbound: { deliveryMode: "gateway" }, + }, + }, + ]), + ); + + callGatewayMock.mockResolvedValueOnce({ messageId: "m1" }); + await sendMessage({ + cfg: {}, + to: "channel:town-square", + content: "hi", + channel: "mattermost", + gateway: { + url: "ws://169.254.169.254:80/latest/meta-data/", + token: "t", + timeoutMs: 5000, + clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT, + clientDisplayName: "agent", + mode: GATEWAY_CLIENT_MODES.BACKEND, + }, + }); + + expect(callGatewayMock).toHaveBeenCalledWith( + expect.objectContaining({ + url: undefined, + token: "t", + timeoutMs: 5000, + }), + ); + }); +}); + const emptyRegistry = createTestRegistry([]); const createMSTeamsOutbound = (opts?: { includePoll?: boolean }): ChannelOutboundAdapter => ({
src/infra/outbound/message.ts+8 −1 modified@@ -102,8 +102,15 @@ export type MessagePollResult = { }; function resolveGatewayOptions(opts?: MessageGatewayOptions) { + // Security: backend callers (tools/agents) must not accept user-controlled gateway URLs. + // Use config-derived gateway target only. + const url = + opts?.mode === GATEWAY_CLIENT_MODES.BACKEND || + opts?.clientName === GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT + ? undefined + : opts?.url; return { - url: opts?.url, + url, token: opts?.token, timeoutMs: typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs)
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/advisories/GHSA-g6q9-8fvw-f7rfghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-26322ghsaADVISORY
- github.com/openclaw/openclaw/commit/c5406e1d2434be2ef6eb4d26d8f1798d718713f4ghsax_refsource_MISCWEB
- github.com/openclaw/openclaw/releases/tag/v2026.2.14ghsax_refsource_MISCWEB
- github.com/openclaw/openclaw/security/advisories/GHSA-g6q9-8fvw-f7rfghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.