mobile: don't downgrade wss:// relay URLs to plaintext ws://

RelayConfig.wsUrl derived the WebSocket scheme with
`uri.scheme == 'https' ? 'wss' : 'ws'`, mapping every non-https scheme
(including wss) to plaintext ws, so a base URL already using wss:// was
silently downgraded to ws://.

Invite-joined communities store their relay as wss://, so invited members'
connections lost transport encryption and failed outright against TLS-only
relays, making the community unusable for them.

Map the scheme explicitly (https/wss -> wss, http/ws -> ws, other schemes
pass through unchanged); only the wss -> ws downgrade changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Max Lampert <maxwell@squareup.com>
This commit is contained in:
Max Lampert
2026-07-28 16:02:43 -07:00
co-authored by Claude Opus 4.8
parent 9227bdf58a
commit 51be21da74
2 changed files with 41 additions and 2 deletions
+10 -2
View File
@@ -17,10 +17,18 @@ class RelayConfig {
const RelayConfig({required this.baseUrl, this.nsec});
/// Derive the websocket URL from the HTTP base URL.
/// Derive the websocket URL from the base URL.
///
/// A base that is already `ws`/`wss` (e.g. an invite-joined community's
/// `wss://` relay) passes through unchanged — mapping it to `ws` would be a
/// silent TLS downgrade that also fails against TLS-only relays.
String get wsUrl {
final uri = Uri.parse(baseUrl);
final scheme = uri.scheme == 'https' ? 'wss' : 'ws';
final scheme = switch (uri.scheme) {
'https' || 'wss' => 'wss',
'http' || 'ws' => 'ws',
_ => uri.scheme,
};
return uri.replace(scheme: scheme).toString();
}
}
@@ -0,0 +1,31 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:buzz/shared/relay/relay_provider.dart';
void main() {
group('RelayConfig.wsUrl', () {
test('maps http-family schemes to their websocket equivalent', () {
expect(
const RelayConfig(baseUrl: 'https://relay.example').wsUrl,
'wss://relay.example',
);
expect(
const RelayConfig(baseUrl: 'http://localhost:3000').wsUrl,
'ws://localhost:3000',
);
});
test('passes ws-family schemes through without downgrading', () {
// An invite-joined community stores relayUrl as wss://…; downgrading it to
// plaintext ws:// is a TLS downgrade and fails against TLS-only relays.
expect(
const RelayConfig(baseUrl: 'wss://relay.example').wsUrl,
'wss://relay.example',
);
expect(
const RelayConfig(baseUrl: 'ws://localhost:3000').wsUrl,
'ws://localhost:3000',
);
});
});
}