Skip to content

Commit 929c831

Browse files
committed
chore: ensure downloaded slim binary version matches server
1 parent e9e15db commit 929c831

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

Coder-Desktop/Coder-DesktopHelper/Manager.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ actor Manager {
7474
}
7575
pushProgress(stage: .validating)
7676
do {
77-
try Validator.validate(path: dest)
77+
try Validator.validateSignature(binaryPath: dest)
78+
try await Validator.validateVersion(binaryPath: dest, serverVersion: buildInfo.version)
7879
} catch {
7980
throw .validation(error)
8081
}

Coder-Desktop/VPNLib/Validate.swift

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import Subprocess
23

34
public enum ValidationError: Error {
45
case fileNotFound
@@ -7,7 +8,8 @@ public enum ValidationError: Error {
78
case unableToRetrieveSignature
89
case invalidIdentifier(identifier: String?)
910
case invalidTeamIdentifier(identifier: String?)
10-
case invalidVersion(version: String?)
11+
case unableToReadVersion(any Error)
12+
case binaryVersionMismatch(binaryVersion: String, serverVersion: String)
1113

1214
public var description: String {
1315
switch self {
@@ -21,10 +23,12 @@ public enum ValidationError: Error {
2123
"Unable to retrieve signing information."
2224
case let .invalidIdentifier(identifier):
2325
"Invalid identifier: \(identifier ?? "unknown")."
24-
case let .invalidVersion(version):
25-
"Invalid runtime version: \(version ?? "unknown")."
26+
case let .binaryVersionMismatch(binaryVersion, serverVersion):
27+
"Binary version does not match server. Binary: \(binaryVersion), Server: \(serverVersion)."
2628
case let .invalidTeamIdentifier(identifier):
2729
"Invalid team identifier: \(identifier ?? "unknown")."
30+
case .unableToReadVersion:
31+
"Unable to execute the binary to read version"
2832
}
2933
}
3034

@@ -41,13 +45,13 @@ public class Validator {
4145

4246
private static let signInfoFlags: SecCSFlags = .init(rawValue: kSecCSSigningInformation)
4347

44-
public static func validate(path: URL) throws(ValidationError) {
45-
guard FileManager.default.fileExists(atPath: path.path) else {
48+
public static func validateSignature(binaryPath: URL) throws(ValidationError) {
49+
guard FileManager.default.fileExists(atPath: binaryPath.path) else {
4650
throw .fileNotFound
4751
}
4852

4953
var staticCode: SecStaticCode?
50-
let status = SecStaticCodeCreateWithPath(path as CFURL, SecCSFlags(), &staticCode)
54+
let status = SecStaticCodeCreateWithPath(binaryPath as CFURL, SecCSFlags(), &staticCode)
5155
guard status == errSecSuccess, let code = staticCode else {
5256
throw .unableToCreateStaticCode
5357
}
@@ -78,6 +82,29 @@ public class Validator {
7882
}
7983
}
8084

85+
public static func validateVersion(binaryPath: URL, serverVersion: String) async throws(ValidationError) {
86+
guard FileManager.default.fileExists(atPath: binaryPath.path) else {
87+
throw .fileNotFound
88+
}
89+
90+
let version: String
91+
do {
92+
let versionOutput = try await Subprocess.data(for: [binaryPath.path, "version", "--output=json"])
93+
let parsed: VersionOutput = try JSONDecoder().decode(VersionOutput.self, from: versionOutput)
94+
version = parsed.version
95+
} catch {
96+
throw .unableToReadVersion(error)
97+
}
98+
99+
guard version == serverVersion else {
100+
throw .binaryVersionMismatch(binaryVersion: version, serverVersion: serverVersion)
101+
}
102+
}
103+
104+
struct VersionOutput: Codable {
105+
let version: String
106+
}
107+
81108
public static let xpcPeerRequirement = "anchor apple generic" + // Apple-issued certificate chain
82109
" and certificate leaf[subject.OU] = \"" + expectedTeamIdentifier + "\"" // Signed by the Coder team
83110
}

0 commit comments

Comments
 (0)