commit 72261ba3f7764c20a041a7c64820b972dff046cf
parent 2a54c07c49946496aaf2f657e98d2c5ecd082d09
Author: Michael Camilleri <[email protected]>
Date: Tue, 26 May 2026 01:50:42 +0900
Ensure protocol in URLs is wss
Diffstat:
2 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/Crossmate/Services/EngagementHost.swift b/Crossmate/Services/EngagementHost.swift
@@ -78,12 +78,16 @@ final class EngagementHost: NSObject {
}
}
- private static func socketURL(
+ static func socketURL(
room: EngagementRoomCredentials,
authorID: String,
- deviceID: String
+ deviceID: String,
+ baseURL: URL? = endpointURL
) throws -> URL? {
- guard let baseURL = endpointURL else { return nil }
+ guard let baseURL else { return nil }
+ guard baseURL.scheme == "ws" || baseURL.scheme == "wss" else {
+ throw EngagementHostError.invalidEndpoint
+ }
let timestamp = String(Int(Date().timeIntervalSince1970))
let nonce = UUID().uuidString
let signaturePayload = EngagementSocketAuthenticator.signaturePayload(
@@ -156,6 +160,7 @@ extension EngagementHost: URLSessionWebSocketDelegate {
enum EngagementHostError: LocalizedError {
case missingEndpoint
case missingSocket
+ case invalidEndpoint
case invalidSecret
var errorDescription: String? {
@@ -164,6 +169,8 @@ enum EngagementHostError: LocalizedError {
"CrossmateEngagementSocketURL is not configured."
case .missingSocket:
"The engagement socket is not connected."
+ case .invalidEndpoint:
+ "CrossmateEngagementSocketURL must use ws or wss."
case .invalidSecret:
"The engagement room secret is invalid."
}
diff --git a/Tests/Unit/Sync/EngagementCoordinatorTests.swift b/Tests/Unit/Sync/EngagementCoordinatorTests.swift
@@ -50,6 +50,30 @@ struct EngagementCoordinatorTests {
#expect(!first.isEmpty)
}
+ @Test("socket URL preserves configured WebSocket endpoint")
+ func socketURL() throws {
+ let room = EngagementRoomCredentials(
+ roomID: UUID(uuidString: "23232323-2323-2323-2323-232323232323")!,
+ secret: Data(repeating: 2, count: 32).base64URLEncodedString(),
+ createdAt: Date(timeIntervalSince1970: 100),
+ expiresAt: Date(timeIntervalSince1970: 700)
+ )
+
+ let url = try #require(EngagementHost.socketURL(
+ room: room,
+ authorID: "alice",
+ deviceID: "deviceA",
+ baseURL: URL(string: "wss://example.org")
+ ))
+
+ #expect(url.scheme == "wss")
+ #expect(url.host() == "example.org")
+ #expect(url.path == "/rooms/23232323-2323-2323-2323-232323232323/socket")
+ #expect(URLComponents(url: url, resolvingAgainstBaseURL: false)?
+ .queryItems?
+ .contains(URLQueryItem(name: "authorID", value: "alice")) == true)
+ }
+
@Test("debug message envelope round trips")
func debugMessageRoundTrip() throws {
let sentAt = Date(timeIntervalSince1970: 123)