fix(macos): validate remote ports
This commit is contained in:
@@ -124,17 +124,28 @@ enum ClawdbotConfigFile {
|
||||
}
|
||||
|
||||
static func remoteGatewayPort() -> Int? {
|
||||
let root = self.loadDict()
|
||||
guard let gateway = root["gateway"] as? [String: Any],
|
||||
let remote = gateway["remote"] as? [String: Any],
|
||||
let raw = remote["url"] as? String
|
||||
guard let url = self.remoteGatewayUrl(),
|
||||
let port = url.port,
|
||||
port > 0
|
||||
else { return nil }
|
||||
return port
|
||||
}
|
||||
|
||||
static func remoteGatewayPort(matchingHost sshHost: String) -> Int? {
|
||||
let trimmedSshHost = sshHost.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmedSshHost.isEmpty,
|
||||
let url = self.remoteGatewayUrl(),
|
||||
let port = url.port,
|
||||
port > 0,
|
||||
let urlHost = url.host?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!urlHost.isEmpty
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty, let url = URL(string: trimmed), let port = url.port, port > 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let sshKey = Self.hostKey(trimmedSshHost)
|
||||
let urlKey = Self.hostKey(urlHost)
|
||||
guard !sshKey.isEmpty, !urlKey.isEmpty, sshKey == urlKey else { return nil }
|
||||
return port
|
||||
}
|
||||
|
||||
@@ -152,6 +163,30 @@ enum ClawdbotConfigFile {
|
||||
}
|
||||
}
|
||||
|
||||
private static func remoteGatewayUrl() -> URL? {
|
||||
let root = self.loadDict()
|
||||
guard let gateway = root["gateway"] as? [String: Any],
|
||||
let remote = gateway["remote"] as? [String: Any],
|
||||
let raw = remote["url"] as? String
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty, let url = URL(string: trimmed) else { return nil }
|
||||
return url
|
||||
}
|
||||
|
||||
private static func hostKey(_ host: String) -> String {
|
||||
let trimmed = host.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||
guard !trimmed.isEmpty else { return "" }
|
||||
if trimmed.contains(":") { return trimmed }
|
||||
let digits = CharacterSet(charactersIn: "0123456789.")
|
||||
if trimmed.rangeOfCharacter(from: digits.inverted) == nil {
|
||||
return trimmed
|
||||
}
|
||||
return trimmed.split(separator: ".").first.map(String.init) ?? trimmed
|
||||
}
|
||||
|
||||
private static func parseConfigData(_ data: Data) -> [String: Any]? {
|
||||
if let root = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
||||
return root
|
||||
|
||||
27
apps/macos/Sources/Clawdbot/GatewayAgentChannel.swift
Normal file
27
apps/macos/Sources/Clawdbot/GatewayAgentChannel.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
import Foundation
|
||||
|
||||
enum GatewayAgentChannel: String, CaseIterable, Sendable {
|
||||
case last
|
||||
case webchat
|
||||
case whatsapp
|
||||
case telegram
|
||||
|
||||
init(raw: String?) {
|
||||
let trimmed = raw?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.lowercased() ?? ""
|
||||
self = GatewayAgentChannel(rawValue: trimmed) ?? .last
|
||||
}
|
||||
|
||||
func shouldDeliver(_ isLast: Bool) -> Bool {
|
||||
switch self {
|
||||
case .webchat:
|
||||
return false
|
||||
case .last:
|
||||
return isLast
|
||||
case .whatsapp, .telegram:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,11 @@ final class MacNodeModeCoordinator {
|
||||
|
||||
static func remoteBridgePort() -> Int {
|
||||
let fallback = Int(Self.loopbackBridgePort() ?? 18790)
|
||||
let base = ClawdbotConfigFile.remoteGatewayPort() ?? GatewayEnvironment.gatewayPort()
|
||||
let settings = CommandResolver.connectionSettings()
|
||||
let sshHost = CommandResolver.parseSSHTarget(settings.target)?.host ?? ""
|
||||
let base =
|
||||
ClawdbotConfigFile.remoteGatewayPort(matchingHost: sshHost) ??
|
||||
GatewayEnvironment.gatewayPort()
|
||||
guard base > 0 else { return fallback }
|
||||
return Self.derivePort(base: base, offset: 1, fallback: fallback)
|
||||
}
|
||||
|
||||
@@ -131,42 +131,9 @@ final class RemotePortTunnel {
|
||||
}
|
||||
|
||||
private static func resolveRemotePortOverride(for sshHost: String) -> Int? {
|
||||
let root = ClawdbotConfigFile.loadDict()
|
||||
guard let gateway = root["gateway"] as? [String: Any],
|
||||
let remote = gateway["remote"] as? [String: Any],
|
||||
let urlRaw = remote["url"] as? String
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
let trimmed = urlRaw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty, let url = URL(string: trimmed), let port = url.port else {
|
||||
return nil
|
||||
}
|
||||
guard let host = url.host?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!host.isEmpty
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
let sshKey = Self.hostKey(sshHost)
|
||||
let urlKey = Self.hostKey(host)
|
||||
guard !sshKey.isEmpty, !urlKey.isEmpty else { return nil }
|
||||
guard sshKey == urlKey else {
|
||||
Self.logger.debug(
|
||||
"remote url host mismatch sshHost=\(sshHost, privacy: .public) urlHost=\(host, privacy: .public)")
|
||||
return nil
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
private static func hostKey(_ host: String) -> String {
|
||||
let trimmed = host.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||
guard !trimmed.isEmpty else { return "" }
|
||||
if trimmed.contains(":") { return trimmed }
|
||||
let digits = CharacterSet(charactersIn: "0123456789.")
|
||||
if trimmed.rangeOfCharacter(from: digits.inverted) == nil {
|
||||
return trimmed
|
||||
}
|
||||
return trimmed.split(separator: ".").first.map(String.init) ?? trimmed
|
||||
let trimmed = sshHost.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { return nil }
|
||||
return ClawdbotConfigFile.remoteGatewayPort(matchingHost: trimmed)
|
||||
}
|
||||
|
||||
private static func findPort(preferred: UInt16?) async throws -> UInt16 {
|
||||
|
||||
Reference in New Issue
Block a user