Skip to content

Commit 2f59a3c

Browse files
steipeteNarcooo
andcommitted
fix(gateway): land openclaw#39064 from @Narcooo
Co-authored-by: Narcooo <[email protected]>
1 parent 2ada1b7 commit 2f59a3c

File tree

3 files changed

+14
-4
lines changed

3 files changed

+14
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ Docs: https://docs.openclaw.ai
247247
- Discord/native slash command auth: honor `commands.allowFrom.discord` (and `commands.allowFrom["*"]`) in guild slash-command pre-dispatch authorization so allowlisted senders are no longer incorrectly rejected as unauthorized. (#38794) Thanks @jskoiz and @thewilloftheshadow.
248248
- Outbound/message target normalization: ignore empty legacy `to`/`channelId` fields when explicit `target` is provided so valid target-based sends no longer fail legacy-param validation; includes regression coverage. (#38944) Thanks @Narcooo.
249249
- Models/auth token prompts: guard cancelled manual token prompts so `Symbol(clack:cancel)` values cannot be persisted into auth profiles; adds regression coverage for cancelled `models auth paste-token`. (#38951) Thanks @MumuTW.
250+
- Gateway/loopback announce URLs: treat `http://` and `https://` aliases with the same loopback/private-network policy as websocket URLs so loopback cron announce delivery no longer fails secure URL validation. (#39064) Thanks @Narcooo.
250251

251252
## 2026.3.2
252253

src/gateway/net.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,10 @@ describe("isSecureWebSocketUrl", () => {
439439
// invalid URLs
440440
{ input: "not-a-url", expected: false },
441441
{ input: "", expected: false },
442-
{ input: "http://127.0.0.1:18789", expected: false },
443-
{ input: "https://127.0.0.1:18789", expected: false },
442+
{ input: "http://127.0.0.1:18789", expected: true },
443+
{ input: "https://127.0.0.1:18789", expected: true },
444+
{ input: "https://remote.example.com:18789", expected: true },
445+
{ input: "http://remote.example.com:18789", expected: false },
444446
] as const;
445447

446448
for (const testCase of cases) {
@@ -451,6 +453,7 @@ describe("isSecureWebSocketUrl", () => {
451453
it("allows private ws:// only when opt-in is enabled", () => {
452454
const allowedWhenOptedIn = [
453455
"ws://10.0.0.5:18789",
456+
"http://10.0.0.5:18789",
454457
"ws://172.16.0.1:18789",
455458
"ws://192.168.1.100:18789",
456459
"ws://100.64.0.1:18789",

src/gateway/net.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,11 +421,17 @@ export function isSecureWebSocketUrl(
421421
return false;
422422
}
423423

424-
if (parsed.protocol === "wss:") {
424+
// Node's ws client accepts http(s) URLs and normalizes them to ws(s).
425+
// Treat those aliases the same way here so loopback cron announce delivery
426+
// and TLS-backed https endpoints follow the same security policy.
427+
const protocol =
428+
parsed.protocol === "https:" ? "wss:" : parsed.protocol === "http:" ? "ws:" : parsed.protocol;
429+
430+
if (protocol === "wss:") {
425431
return true;
426432
}
427433

428-
if (parsed.protocol !== "ws:") {
434+
if (protocol !== "ws:") {
429435
return false;
430436
}
431437

0 commit comments

Comments
 (0)