mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 23:34:34 +00:00
fix(gateway): allow ws:// to private network addresses (#28670)
* fix(gateway): allow ws:// to RFC 1918 private network addresses resolve ws-private-network conflicts * gateway: keep ws security strict-by-default with private opt-in * gateway: apply private ws opt-in in connection detail guard * gateway: apply private ws opt-in in websocket client * onboarding: gate private ws urls behind explicit opt-in * gateway tests: enforce strict ws defaults with private opt-in * onboarding tests: validate private ws opt-in behavior * gateway client tests: cover private ws env override * gateway call tests: cover private ws env override * changelog: add ws strict-default security entry for pr 28670 * docs(onboard): document private ws break-glass env * docs(gateway): add private ws env to remote guide * docs(docker): add private ws break-glass env var * docs(security): add private ws break-glass guidance * docs(config): document OPENCLAW_ALLOW_PRIVATE_WS * Update CHANGELOG.md * gateway: normalize private-ws host classification * test(gateway): cover non-unicast ipv6 private-ws edges * changelog: rename insecure private ws break-glass env * docs(onboard): rename insecure private ws env * docs(gateway): rename insecure private ws env in config reference * docs(gateway): rename insecure private ws env in remote guide * docs(security): rename insecure private ws env * docs(docker): rename insecure private ws env * test(onboard): rename insecure private ws env * onboard: rename insecure private ws env * test(gateway): rename insecure private ws env in call tests * gateway: rename insecure private ws env in call flow * test(gateway): rename insecure private ws env in client tests * gateway: rename insecure private ws env in client * docker: pass insecure private ws env to services * docker-setup: persist insecure private ws env --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { GatewayBonjourBeacon } from "../infra/bonjour-discovery.js";
|
||||
import { captureEnv } from "../test-utils/env.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import { createWizardPrompter } from "./test-wizard-helpers.js";
|
||||
|
||||
@@ -27,8 +28,11 @@ function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
|
||||
}
|
||||
|
||||
describe("promptRemoteGatewayConfig", () => {
|
||||
const envSnapshot = captureEnv(["OPENCLAW_ALLOW_INSECURE_PRIVATE_WS"]);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
envSnapshot.restore();
|
||||
detectBinary.mockResolvedValue(false);
|
||||
discoverGatewayBeacons.mockResolvedValue([]);
|
||||
resolveWideAreaDiscoveryDomain.mockReturnValue(undefined);
|
||||
@@ -88,9 +92,12 @@ describe("promptRemoteGatewayConfig", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("validates insecure ws:// remote URLs and allows loopback ws://", async () => {
|
||||
it("validates insecure ws:// remote URLs and allows only loopback ws:// by default", async () => {
|
||||
const text: WizardPrompter["text"] = vi.fn(async (params) => {
|
||||
if (params.message === "Gateway WebSocket URL") {
|
||||
// ws:// to public IPs is rejected
|
||||
expect(params.validate?.("ws://203.0.113.10:18789")).toContain("Use wss://");
|
||||
// ws:// to private IPs remains blocked by default
|
||||
expect(params.validate?.("ws://10.0.0.8:18789")).toContain("Use wss://");
|
||||
expect(params.validate?.("ws://127.0.0.1:18789")).toBeUndefined();
|
||||
expect(params.validate?.("wss://remote.example.com:18789")).toBeUndefined();
|
||||
@@ -119,4 +126,34 @@ describe("promptRemoteGatewayConfig", () => {
|
||||
expect(next.gateway?.remote?.url).toBe("wss://remote.example.com:18789");
|
||||
expect(next.gateway?.remote?.token).toBeUndefined();
|
||||
});
|
||||
|
||||
it("allows private ws:// only when OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1", async () => {
|
||||
process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS = "1";
|
||||
|
||||
const text: WizardPrompter["text"] = vi.fn(async (params) => {
|
||||
if (params.message === "Gateway WebSocket URL") {
|
||||
expect(params.validate?.("ws://10.0.0.8:18789")).toBeUndefined();
|
||||
return "ws://10.0.0.8:18789";
|
||||
}
|
||||
return "";
|
||||
}) as WizardPrompter["text"];
|
||||
|
||||
const select: WizardPrompter["select"] = vi.fn(async (params) => {
|
||||
if (params.message === "Gateway auth") {
|
||||
return "off" as never;
|
||||
}
|
||||
return (params.options[0]?.value ?? "") as never;
|
||||
});
|
||||
|
||||
const cfg = {} as OpenClawConfig;
|
||||
const prompter = createPrompter({
|
||||
confirm: vi.fn(async () => false),
|
||||
select,
|
||||
text,
|
||||
});
|
||||
|
||||
const next = await promptRemoteGatewayConfig(cfg, prompter);
|
||||
|
||||
expect(next.gateway?.remote?.url).toBe("ws://10.0.0.8:18789");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user