cmux/Sources/SocketControlSettings.swift
Lawrence Chen 4c42bd8078 Fix migrateMode dropping allowAll to default cmuxOnly
migrateMode() had no case for "allowAll" rawValue, so it fell
through to the default branch which returned .cmuxOnly. This
silently downgraded any persisted allowAll setting.
2026-02-18 02:12:19 -08:00

116 lines
3.5 KiB
Swift

import Foundation
enum SocketControlMode: String, CaseIterable, Identifiable {
case off
case cmuxOnly
/// Allow any local process to connect (no ancestry check).
/// Only accessible via CMUX_SOCKET_MODE=allowAll env var not shown in the UI.
case allowAll
var id: String { rawValue }
/// Cases shown in the Settings UI. `allowAll` is intentionally excluded.
static var uiCases: [SocketControlMode] { [.off, .cmuxOnly] }
var displayName: String {
switch self {
case .off:
return "Off"
case .cmuxOnly:
return "cmux processes only"
case .allowAll:
return "Allow all processes"
}
}
var description: String {
switch self {
case .off:
return "Disable the local control socket."
case .cmuxOnly:
return "Only processes started inside cmux terminals can send commands."
case .allowAll:
return "Allow any local process to connect (no ancestry check)."
}
}
}
struct SocketControlSettings {
static let appStorageKey = "socketControlMode"
static let legacyEnabledKey = "socketControlEnabled"
/// Map old persisted rawValues to the new enum.
static func migrateMode(_ raw: String) -> SocketControlMode {
switch raw {
case "off": return .off
case "cmuxOnly": return .cmuxOnly
case "allowAll": return .allowAll
// Legacy values:
case "notifications", "full": return .cmuxOnly
default: return defaultMode
}
}
static var defaultMode: SocketControlMode {
return .cmuxOnly
}
static func socketPath() -> String {
if let override = ProcessInfo.processInfo.environment["CMUX_SOCKET_PATH"], !override.isEmpty {
return override
}
#if DEBUG
return "/tmp/cmux-debug.sock"
#else
return "/tmp/cmux.sock"
#endif
}
static func envOverrideEnabled() -> Bool? {
guard let raw = ProcessInfo.processInfo.environment["CMUX_SOCKET_ENABLE"], !raw.isEmpty else {
return nil
}
switch raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() {
case "1", "true", "yes", "on":
return true
case "0", "false", "no", "off":
return false
default:
return nil
}
}
static func envOverrideMode() -> SocketControlMode? {
guard let raw = ProcessInfo.processInfo.environment["CMUX_SOCKET_MODE"], !raw.isEmpty else {
return nil
}
let cleaned = raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
switch cleaned {
case "off": return .off
case "cmuxonly", "cmux_only", "cmux-only": return .cmuxOnly
case "allowall", "allow_all", "allow-all": return .allowAll
// Legacy env var values map to allowAll so existing test scripts keep working
case "notifications", "full": return .allowAll
default: return SocketControlMode(rawValue: cleaned)
}
}
static func effectiveMode(userMode: SocketControlMode) -> SocketControlMode {
if let overrideEnabled = envOverrideEnabled() {
if !overrideEnabled {
return .off
}
if let overrideMode = envOverrideMode() {
return overrideMode
}
return userMode == .off ? .cmuxOnly : userMode
}
if let overrideMode = envOverrideMode() {
return overrideMode
}
return userMode
}
}