Skip to content

fix(security): block plaintext WebSocket connections to non-loopback addresses#20803

Merged
vincentkoc merged 3 commits intoopenclaw:mainfrom
jscaldwell55:fix/cwe-319-websocket-transport-security
Feb 19, 2026
Merged

fix(security): block plaintext WebSocket connections to non-loopback addresses#20803
vincentkoc merged 3 commits intoopenclaw:mainfrom
jscaldwell55:fix/cwe-319-websocket-transport-security

Conversation

@jscaldwell55
Copy link
Contributor

@jscaldwell55 jscaldwell55 commented Feb 19, 2026

Summary

Fixes #12519 - CWE-319: Cleartext Transmission of Sensitive Information (CVSS 9.8)

This PR blocks ALL plaintext ws:// WebSocket connections to non-loopback addresses, protecting both credentials and chat data from network interception.

Security Policy

URL Type Result
wss:// any host ✅ Allowed
ws://127.0.0.1, ws://localhost, ws://[::1] ✅ Allowed
ws:// LAN/tailnet/remote Blocked

Key Changes

  • src/gateway/net.ts: Added isSecureWebSocketUrl() to validate WebSocket URL security
  • src/gateway/client.ts: Block insecure connections at connection time (catches device tokens loaded dynamically)
  • src/gateway/call.ts: Block insecure URLs during config/URL resolution (all code paths)
  • Malformed URLs handled gracefully without crashing

Why Block Without Checking Credentials?

  1. Device tokens bypass: Credentials can be loaded from persistent storage after initial validation
  2. Chat data exposure: Issue [Security][Third-Party] clawd Gateway: WebSocket Plaintext MITM and Credential Theft #12519 highlights MITM risk for conversation data, not just credentials
  3. Defense in depth: Block at the transport layer regardless of auth state

Test plan

  • New tests for GatewayClient security checks (4 tests)
  • New tests for buildGatewayConnectionDetails blocking (5 tests)
  • Updated existing tests to use wss:// for non-loopback URLs
  • Malformed URL handling test
  • All 450 gateway/security tests passing

Greptile Summary

This PR addresses a critical security vulnerability (CWE-319: Cleartext Transmission of Sensitive Information, CVSS 9.8) by blocking plaintext WebSocket connections to non-loopback addresses. The implementation adds isSecureWebSocketUrl() validation that allows wss:// to any host and ws:// only to loopback addresses (localhost, 127.x.x.x, ::1).

Key changes:

  • Added isSecureWebSocketUrl() function in src/gateway/net.ts with proper URL parsing and error handling
  • Enforced security checks at two critical points: GatewayClient.start() and buildGatewayConnectionDetails() to catch all code paths
  • Updated test suite comprehensively (9 new security tests across net, client, and call modules)
  • Updated existing tests to use wss:// for non-loopback URLs and enable TLS in config where needed
  • All security checks provide clear, actionable error messages directing users to use wss:// or SSH tunneling

The fix correctly implements defense in depth by blocking at both connection initialization and URL resolution, handles malformed URLs gracefully, and protects both credentials and chat data from interception.

Confidence Score: 5/5

  • This PR is safe to merge with no identified risks
  • The implementation is well-designed with comprehensive test coverage (9 new security tests), handles edge cases properly (malformed URLs, multiple code paths), and follows security best practices. The fix addresses a critical vulnerability (CVSS 9.8) correctly by implementing defense in depth at multiple layers. Error messages are clear and actionable. All existing tests were updated appropriately to maintain compatibility.
  • No files require special attention

Last reviewed commit: 9952d0e

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

@openclaw-barnacle openclaw-barnacle bot added gateway Gateway runtime commands Command implementations size: M labels Feb 19, 2026
@jscaldwell55 jscaldwell55 force-pushed the fix/cwe-319-websocket-transport-security branch from abbdbb3 to 126dfff Compare February 19, 2026 10:16
…addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519
Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
@jscaldwell55 jscaldwell55 force-pushed the fix/cwe-319-websocket-transport-security branch from 2395781 to a320b26 Compare February 19, 2026 10:34
@jscaldwell55
Copy link
Contributor Author

@thewilloftheshadow I think this should be good to merge

@vincentkoc vincentkoc merged commit 9edec67 into openclaw:main Feb 19, 2026
22 checks passed
kittipond2365 pushed a commit to kittipond2365/openclaw that referenced this pull request Feb 19, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
yneth-ray-openclaw pushed a commit to yneth-ray-openclaw/openclaw that referenced this pull request Feb 19, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
@HenryLoenwind
Copy link
Contributor

You're using the wrong check.

"localhost" can point to anything, it's just a hostname that's resolved like any other. The real check you want is if the IP address the hostname resolves to is that of a local interface. And in that case, any interface will do. Traffic to a host's own addresses will never leave the machine. Traffic to whatever "localhost" resolves could travel around the world twice.


Aside from that, in my personal opinion, by default a check whether the address is in the broadcast domain of any interface would be the more sensible default. Most network setups won't use a prehistoric network hub but a switch---i.e. direct traffic within a broadcast domain is only visible to the two machines communicating and the switch(es).

As installations also are very likely to run in a LAN that is either controlled or trusted by the owner of the machine, packet sniffing by the first switch is a very rare and special attack vector---who plugs a machine into a network port when they don't trust the operator? People who are aware enough to properly secure their machine.

Side note: It is a major pain to set up SSL certs for machines that only have an address in the private IP address range. And not only that, it opens up the SSL connections to outside attacks---the DNS provider (and there are not many that allow private IPs and access to the records needed to get a cert) can easily change their address resolution and then someone else easily can get a cert for that hostname. With that, they can redirect local traffic over the internet and even keep it SSL secured. This makes SSL to private addresses less secure than unencrypted local connections that cannot be affected by outside forces.

vignesh07 pushed a commit to pahdo/openclaw that referenced this pull request Feb 20, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
usedhonda added a commit to usedhonda/openclaw that referenced this pull request Feb 20, 2026
When bind=lan or bind=tailnet, buildGatewayConnectionDetails() was
generating localUrl with the LAN/tailnet IP (e.g. ws://192.168.x.x).
The security check added in openclaw#20803 then rejected these as plaintext
ws:// to non-loopback addresses.

Root cause: bind mode controls which interface the *server* listens on,
but was incorrectly also driving the *client* connection URL for agents
running on the same host.

Fix: localUrl now always uses ws://127.0.0.1 (loopback). LAN and
tailnet IPs remain available in lanIPv4/tailnetIPv4 for display
purposes (QR codes, onboarding hints, urlSource labels) but are no
longer used as the actual connection target.

Fixes openclaw#22047
rodrigogs pushed a commit to rodrigogs/openclaw that referenced this pull request Feb 20, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
mmyyfirstb pushed a commit to mmyyfirstb/openclaw that referenced this pull request Feb 21, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
obviyus pushed a commit to guirguispierre/openclaw that referenced this pull request Feb 22, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.

(cherry picked from commit 9edec67)

# Conflicts:
#	src/commands/gateway-status.test.ts
#	src/gateway/call.test.ts
#	src/gateway/client.test.ts
#	src/gateway/net.test.ts
#	src/tui/gateway-chat.test.ts
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.

(cherry picked from commit 9edec67)

# Conflicts:
#	src/commands/gateway-status.test.ts
#	src/gateway/call.test.ts
#	src/gateway/call.ts
#	src/gateway/client.test.ts
#	src/gateway/net.test.ts
#	src/tui/gateway-chat.test.ts
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
…addresses (openclaw#20803)

* fix(security): block plaintext WebSocket connections to non-loopback addresses

Addresses CWE-319 (Cleartext Transmission of Sensitive Information).

Previously, ws:// connections to remote hosts were allowed, exposing
both credentials and chat data to network interception. This change
blocks ALL plaintext ws:// connections to non-loopback addresses,
regardless of whether explicit credentials are configured (device
tokens may be loaded dynamically).

Security policy:
- wss:// allowed to any host
- ws:// allowed only to loopback (127.x.x.x, localhost, ::1)
- ws:// to LAN/tailnet/remote hosts now requires TLS

Changes:
- Add isSecureWebSocketUrl() validation in net.ts
- Block insecure connections in GatewayClient.start()
- Block insecure URLs in buildGatewayConnectionDetails()
- Handle malformed URLs gracefully without crashing
- Update tests to use wss:// for non-loopback URLs

Fixes openclaw#12519

* fix(test): update gateway-chat mock to preserve net.js exports

Use importOriginal to spread actual module exports and mock only
the functions needed for testing. This ensures isSecureWebSocketUrl
and other exports remain available to the code under test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations gateway Gateway runtime size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security][Third-Party] clawd Gateway: WebSocket Plaintext MITM and Credential Theft

3 participants