Summary
The companion WebSocket URL is always constructed with the plaintext `ws://` scheme, even when the intercom frontend itself is served over HTTPS. This means:
- Mixed-content blocking — modern browsers block `ws://` WebSocket connections initiated from an `https://` page, so the companion connection silently fails in production
- Security — even where the browser does not block it, the companion WebSocket traffic (call state, control messages) is transmitted unencrypted and is vulnerable to passive eavesdropping and MITM
Location
`src/utils/call-url.ts` lines 45–47:
```ts
// companion parameter always gets ws:// regardless of page origin
return ws://\${param};
```
Also affects:
- `src/components/calls-page/connect-to-ws-modal.tsx` line 56
- `src/components/calls-page/save-preset-modal.tsx` line 166
(Both strip the existing protocol and re-prepend `ws://`, preventing a user from ever specifying a `wss://` URL via the UI or URL parameter.)
Recommendation
In `parseCompanionParam` (and any other place that constructs the WebSocket URL):
- If the companion param already includes a scheme (`ws://` or `wss://`), preserve it as-is
- If no scheme is present and `window.location.protocol === 'https:'`, default to `wss://`
- If no scheme is present and on HTTP, default to `ws://` (current behaviour)
Example:
```ts
function parseCompanionParam(param: string): string {
if (param.startsWith('ws://') || param.startsWith('wss://')) return param;
const scheme = window.location.protocol === 'https:' ? 'wss' : 'ws';
return `${scheme}://${param}`;
}
```
Additionally, update the companion URL input in the modal to accept and preserve `wss://` prefixes rather than stripping and replacing the scheme.
References
Summary
The companion WebSocket URL is always constructed with the plaintext `ws://` scheme, even when the intercom frontend itself is served over HTTPS. This means:
Location
`src/utils/call-url.ts` lines 45–47:
```ts
// companion parameter always gets ws:// regardless of page origin
return
ws://\${param};```
Also affects:
(Both strip the existing protocol and re-prepend `ws://`, preventing a user from ever specifying a `wss://` URL via the UI or URL parameter.)
Recommendation
In `parseCompanionParam` (and any other place that constructs the WebSocket URL):
Example:
```ts
function parseCompanionParam(param: string): string {
if (param.startsWith('ws://') || param.startsWith('wss://')) return param;
const scheme = window.location.protocol === 'https:' ? 'wss' : 'ws';
return `${scheme}://${param}`;
}
```
Additionally, update the companion URL input in the modal to accept and preserve `wss://` prefixes rather than stripping and replacing the scheme.
References