Skip to content

Commit 87cdd71

Browse files
tomerdMaxDesiatov
authored andcommitted
make it possible to specify permitted network port access at runtime (#7151)
motivation: The 'network' plugin permission in SwiftPM allows a plugin to request external network access to all or to a set of ports, but the command line flag does not allow to specify ports forcing users to allow all changes: * support passing ports to `local` and `all` --allow-network-connections flag * write parsing logic to extract ports from `local:port,port,port` and `all:port,port,port` * update remedy description to accurately specify the usage * update tests rdar://116241125
1 parent 3b1dfc1 commit 87cdd71

File tree

2 files changed

+72
-12
lines changed

2 files changed

+72
-12
lines changed

Sources/Commands/PackageTools/PluginCommand.swift

+68-8
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,72 @@ struct PluginCommand: SwiftCommand {
4747
)
4848
var additionalAllowedWritableDirectories: [String] = []
4949

50-
enum NetworkPermission: String, EnumerableFlag, ExpressibleByArgument {
50+
enum NetworkPermission: EnumerableFlag, ExpressibleByArgument {
51+
static var allCases: [PluginCommand.PluginOptions.NetworkPermission] {
52+
return [.none, .local(ports: []), .all(ports: []), .docker, .unixDomainSocket]
53+
}
54+
5155
case none
52-
case local
53-
case all
56+
case local(ports: [Int])
57+
case all(ports: [Int])
5458
case docker
5559
case unixDomainSocket
60+
61+
init?(argument: String) {
62+
let arg = argument.lowercased()
63+
switch arg {
64+
case "none":
65+
self = .none
66+
case "docker":
67+
self = .docker
68+
case "unixdomainsocket":
69+
self = .unixDomainSocket
70+
default:
71+
if "all" == arg.prefix(3) {
72+
let ports = Self.parsePorts(arg)
73+
self = .all(ports: ports)
74+
} else if "local" == arg.prefix(5) {
75+
let ports = Self.parsePorts(arg)
76+
self = .local(ports: ports)
77+
} else {
78+
return nil
79+
}
80+
}
81+
}
82+
83+
static func parsePorts(_ string: String) -> [Int] {
84+
let parts = string.split(separator: ":")
85+
guard parts.count == 2 else {
86+
return []
87+
}
88+
return parts[1]
89+
.split(separator: ",")
90+
.compactMap{ String($0).spm_chuzzle() }
91+
.compactMap { Int($0) }
92+
}
93+
94+
var remedyDescription: String {
95+
switch self {
96+
case .none:
97+
return "none"
98+
case .local(let ports):
99+
if ports.isEmpty {
100+
return "local"
101+
} else {
102+
return "local:\(ports.map(String.init).joined(separator: ","))"
103+
}
104+
case .all(let ports):
105+
if ports.isEmpty {
106+
return "all"
107+
} else {
108+
return "all:\(ports.map(String.init).joined(separator: ","))"
109+
}
110+
case .docker:
111+
return "docker"
112+
case .unixDomainSocket:
113+
return "unixDomainSocket"
114+
}
115+
}
56116
}
57117

58118
@Option(name: .customLong("allow-network-connections"))
@@ -211,7 +271,7 @@ struct PluginCommand: SwiftCommand {
211271

212272
reasonString = reason
213273
remedyOption =
214-
"--allow-network-connections \(PluginCommand.PluginOptions.NetworkPermission(scope).defaultValueDescription)"
274+
"--allow-network-connections \(PluginCommand.PluginOptions.NetworkPermission(scope).remedyDescription)"
215275
}
216276

217277
let problem = "Plugin ‘\(plugin.name)’ wants permission to \(permissionString)."
@@ -377,8 +437,8 @@ extension PluginCommand.PluginOptions.NetworkPermission {
377437
case .unixDomainSocket: self = .unixDomainSocket
378438
case .docker: self = .docker
379439
case .none: self = .none
380-
case .all: self = .all
381-
case .local: self = .local
440+
case .all(let ports): self = .all(ports: ports)
441+
case .local(let ports): self = .local(ports: ports)
382442
}
383443
}
384444
}
@@ -387,8 +447,8 @@ extension SandboxNetworkPermission {
387447
init(_ permission: PluginCommand.PluginOptions.NetworkPermission) {
388448
switch permission {
389449
case .none: self = .none
390-
case .local: self = .local(ports: [])
391-
case .all: self = .all(ports: [])
450+
case .local(let ports): self = .local(ports: ports)
451+
case .all(let ports): self = .all(ports: ports)
392452
case .docker: self = .docker
393453
case .unixDomainSocket: self = .unixDomainSocket
394454
}

Tests/CommandsTests/PackageToolTests.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -1936,12 +1936,12 @@ final class PackageToolTests: CommandsTestCase {
19361936
permissionsManifestFragment: "[.allowNetworkConnections(scope: .all(ports: [23, 42, 443, 8080]), reason: \"internet good\")]",
19371937
permissionError: "all network connections on ports: 23, 42, 443, 8080",
19381938
reason: "internet good",
1939-
remedy: ["--allow-network-connections", "all"])
1939+
remedy: ["--allow-network-connections", "all:23,42,443,8080"])
19401940
try testCommandPluginNetworkingPermissions(
19411941
permissionsManifestFragment: "[.allowNetworkConnections(scope: .all(ports: 1..<4), reason: \"internet good\")]",
19421942
permissionError: "all network connections on ports: 1, 2, 3",
19431943
reason: "internet good",
1944-
remedy: ["--allow-network-connections", "all"])
1944+
remedy: ["--allow-network-connections", "all:1,2,3"])
19451945

19461946
try testCommandPluginNetworkingPermissions(
19471947
permissionsManifestFragment: "[.allowNetworkConnections(scope: .local(), reason: \"localhost good\")]",
@@ -1952,12 +1952,12 @@ final class PackageToolTests: CommandsTestCase {
19521952
permissionsManifestFragment: "[.allowNetworkConnections(scope: .local(ports: [23, 42, 443, 8080]), reason: \"localhost good\")]",
19531953
permissionError: "local network connections on ports: 23, 42, 443, 8080",
19541954
reason: "localhost good",
1955-
remedy: ["--allow-network-connections", "local"])
1955+
remedy: ["--allow-network-connections", "local:23,42,443,8080"])
19561956
try testCommandPluginNetworkingPermissions(
19571957
permissionsManifestFragment: "[.allowNetworkConnections(scope: .local(ports: 1..<4), reason: \"localhost good\")]",
19581958
permissionError: "local network connections on ports: 1, 2, 3",
19591959
reason: "localhost good",
1960-
remedy: ["--allow-network-connections", "local"])
1960+
remedy: ["--allow-network-connections", "local:1,2,3"])
19611961

19621962
try testCommandPluginNetworkingPermissions(
19631963
permissionsManifestFragment: "[.allowNetworkConnections(scope: .docker, reason: \"docker good\")]",

0 commit comments

Comments
 (0)