diff --git a/Sources/Commands/PackageTools/PluginCommand.swift b/Sources/Commands/PackageTools/PluginCommand.swift index 648656ebc6a..61b8670390e 100644 --- a/Sources/Commands/PackageTools/PluginCommand.swift +++ b/Sources/Commands/PackageTools/PluginCommand.swift @@ -47,12 +47,72 @@ struct PluginCommand: SwiftCommand { ) var additionalAllowedWritableDirectories: [String] = [] - enum NetworkPermission: String, EnumerableFlag, ExpressibleByArgument { + enum NetworkPermission: EnumerableFlag, ExpressibleByArgument { + static var allCases: [PluginCommand.PluginOptions.NetworkPermission] { + return [.none, .local(ports: []), .all(ports: []), .docker, .unixDomainSocket] + } + case none - case local - case all + case local(ports: [Int]) + case all(ports: [Int]) case docker case unixDomainSocket + + init?(argument: String) { + let arg = argument.lowercased() + switch arg { + case "none": + self = .none + case "docker": + self = .docker + case "unixdomainsocket": + self = .unixDomainSocket + default: + if "all" == arg.prefix(3) { + let ports = Self.parsePorts(arg) + self = .all(ports: ports) + } else if "local" == arg.prefix(5) { + let ports = Self.parsePorts(arg) + self = .local(ports: ports) + } else { + return nil + } + } + } + + static func parsePorts(_ string: String) -> [Int] { + let parts = string.split(separator: ":") + guard parts.count == 2 else { + return [] + } + return parts[1] + .split(separator: ",") + .compactMap{ String($0).spm_chuzzle() } + .compactMap { Int($0) } + } + + var remedyDescription: String { + switch self { + case .none: + return "none" + case .local(let ports): + if ports.isEmpty { + return "local" + } else { + return "local:\(ports.map(String.init).joined(separator: ","))" + } + case .all(let ports): + if ports.isEmpty { + return "all" + } else { + return "all:\(ports.map(String.init).joined(separator: ","))" + } + case .docker: + return "docker" + case .unixDomainSocket: + return "unixDomainSocket" + } + } } @Option(name: .customLong("allow-network-connections")) @@ -211,7 +271,7 @@ struct PluginCommand: SwiftCommand { reasonString = reason remedyOption = - "--allow-network-connections \(PluginCommand.PluginOptions.NetworkPermission(scope).defaultValueDescription)" + "--allow-network-connections \(PluginCommand.PluginOptions.NetworkPermission(scope).remedyDescription)" } let problem = "Plugin ‘\(plugin.name)’ wants permission to \(permissionString)." @@ -377,8 +437,8 @@ extension PluginCommand.PluginOptions.NetworkPermission { case .unixDomainSocket: self = .unixDomainSocket case .docker: self = .docker case .none: self = .none - case .all: self = .all - case .local: self = .local + case .all(let ports): self = .all(ports: ports) + case .local(let ports): self = .local(ports: ports) } } } @@ -387,8 +447,8 @@ extension SandboxNetworkPermission { init(_ permission: PluginCommand.PluginOptions.NetworkPermission) { switch permission { case .none: self = .none - case .local: self = .local(ports: []) - case .all: self = .all(ports: []) + case .local(let ports): self = .local(ports: ports) + case .all(let ports): self = .all(ports: ports) case .docker: self = .docker case .unixDomainSocket: self = .unixDomainSocket } diff --git a/Tests/CommandsTests/PackageToolTests.swift b/Tests/CommandsTests/PackageToolTests.swift index cd88b393159..fe520dff7ac 100644 --- a/Tests/CommandsTests/PackageToolTests.swift +++ b/Tests/CommandsTests/PackageToolTests.swift @@ -1933,12 +1933,12 @@ final class PackageToolTests: CommandsTestCase { permissionsManifestFragment: "[.allowNetworkConnections(scope: .all(ports: [23, 42, 443, 8080]), reason: \"internet good\")]", permissionError: "all network connections on ports: 23, 42, 443, 8080", reason: "internet good", - remedy: ["--allow-network-connections", "all"]) + remedy: ["--allow-network-connections", "all:23,42,443,8080"]) try testCommandPluginNetworkingPermissions( permissionsManifestFragment: "[.allowNetworkConnections(scope: .all(ports: 1..<4), reason: \"internet good\")]", permissionError: "all network connections on ports: 1, 2, 3", reason: "internet good", - remedy: ["--allow-network-connections", "all"]) + remedy: ["--allow-network-connections", "all:1,2,3"]) try testCommandPluginNetworkingPermissions( permissionsManifestFragment: "[.allowNetworkConnections(scope: .local(), reason: \"localhost good\")]", @@ -1949,12 +1949,12 @@ final class PackageToolTests: CommandsTestCase { permissionsManifestFragment: "[.allowNetworkConnections(scope: .local(ports: [23, 42, 443, 8080]), reason: \"localhost good\")]", permissionError: "local network connections on ports: 23, 42, 443, 8080", reason: "localhost good", - remedy: ["--allow-network-connections", "local"]) + remedy: ["--allow-network-connections", "local:23,42,443,8080"]) try testCommandPluginNetworkingPermissions( permissionsManifestFragment: "[.allowNetworkConnections(scope: .local(ports: 1..<4), reason: \"localhost good\")]", permissionError: "local network connections on ports: 1, 2, 3", reason: "localhost good", - remedy: ["--allow-network-connections", "local"]) + remedy: ["--allow-network-connections", "local:1,2,3"]) try testCommandPluginNetworkingPermissions( permissionsManifestFragment: "[.allowNetworkConnections(scope: .docker, reason: \"docker good\")]",