Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions src/features/tlsnotary/proxyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,21 @@ export function extractDomainAndPort(targetUrl: string): {

/**
* Build a public WebSocket URL from a base HTTP(S)/WS(S) URL and a local port.
* Path mode: when the base URL has a path, route through a reverse proxy at
* `url.host` (port preserved) that maps `<path>/<port>` to the local port
* (single nginx rule). No-path mode: connect directly to `url.hostname` on
* the target port.
* Local bases keep direct ws://localhost:<port> URLs for development. Public
* bases always route through a TLS reverse proxy path, defaulting to
* /tlsn/<port>/ when no explicit path is provided.
*/
export function buildWsUrl(base: string, port: number | string): string {
const url = new URL(base)
const secure = url.protocol === "https:" || url.protocol === "wss:"
const wsScheme = secure ? "wss" : "ws"
const path = url.pathname.replace(/\/+$/, "")
return path
? `${wsScheme}://${url.host}${path}/${port}/`
: `${wsScheme}://${url.hostname}:${port}`
const isLocal =
url.hostname === "localhost" || url.hostname === "127.0.0.1"
Comment on lines +179 to +180
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The isLocal check omits the IPv6 loopback address ::1. When a base URL like http://[::1]:53550 is passed, url.hostname is ::1, which falls through to the public branch and produces wss://::1/tlsn/7047/. That string is an invalid URL (IPv6 literals in URLs require brackets), so any downstream new URL(...) call or WebSocket constructor on the result will throw.

Suggested change
const isLocal =
url.hostname === "localhost" || url.hostname === "127.0.0.1"
const isLocal =
url.hostname === "localhost" ||
url.hostname === "127.0.0.1" ||
url.hostname === "::1"


if (isLocal) {
return `ws://${url.hostname}:${port}`
}

const path = url.pathname.replace(/\/+$/, "") || "/tlsn"
return `wss://${url.hostname}${path}/${port}/`
}
Comment on lines +179 to 188
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Private RFC-1918 IPs routed through wss://

Any base URL whose hostname is a private-network address (e.g. 192.168.x.x, 10.x.x.x, 172.16–31.x.x) is not localhost/127.0.0.1, so the new code treats it as a public host and emits a wss:// path-routed URL. Without a TLS-terminating reverse proxy on those hosts the resulting WebSocket URL is unreachable, breaking LAN / on-premise development setups. Consider extending isLocal (or a separate isPrivate guard) for RFC-1918 ranges, or documenting that those deployments must also set up a reverse proxy.


/**
Expand Down
Loading