import Foundation /// URLSession delegate that bypasses certificate validation for **one host only** /// — the configured SparkControl backend. /// /// SparkControl sits behind a Start9 self-signed Root CA on the LAN. The supported /// path is to install that CA in the System keychain; default trust evaluation then /// succeeds and this delegate is never used. It exists only as an opt-in escape /// hatch (the "Skip TLS verification" setting, off by default) for a machine where /// the CA isn't installed. Even then it trusts a certificate only when the challenge /// host equals `allowedHost` — a server-trust challenge from any other host falls /// back to default validation, so the bypass can never become "trust any server". final class InsecureTrustDelegate: NSObject, URLSessionDelegate { /// The single host the bypass is scoped to (the configured backend host). When /// nil — only reachable via a malformed base URL — the gate never fires and every /// challenge falls back to default validation: the safe degenerate case. private let allowedHost: String? init(allowedHost: String?) { self.allowedHost = allowedHost } /// The security gate: the trust override may fire only for a server-trust /// challenge whose host matches `allowedHost`. Pure and synchronous so the /// host-scoping can be unit-tested without fabricating a `SecTrust`; the /// credential itself is built only when this is true *and* a serverTrust exists. func allowsTrustOverride(for space: URLProtectionSpace) -> Bool { guard let allowedHost else { return false } return space.authenticationMethod == NSURLAuthenticationMethodServerTrust && space.host == allowedHost } func urlSession( _ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) { guard allowsTrustOverride(for: challenge.protectionSpace), let serverTrust = challenge.protectionSpace.serverTrust else { completionHandler(.performDefaultHandling, nil) return } completionHandler(.useCredential, URLCredential(trust: serverTrust)) } }