1
1
import Foundation
2
+ import Subprocess
2
3
3
4
public enum ValidationError : Error {
4
5
case fileNotFound
@@ -7,7 +8,8 @@ public enum ValidationError: Error {
7
8
case unableToRetrieveSignature
8
9
case invalidIdentifier( identifier: String ? )
9
10
case invalidTeamIdentifier( identifier: String ? )
10
- case invalidVersion( version: String ? )
11
+ case unableToReadVersion( any Error )
12
+ case binaryVersionMismatch( binaryVersion: String , serverVersion: String )
11
13
12
14
public var description : String {
13
15
switch self {
@@ -21,10 +23,12 @@ public enum ValidationError: Error {
21
23
" Unable to retrieve signing information. "
22
24
case let . invalidIdentifier( identifier) :
23
25
" 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 ) . "
26
28
case let . invalidTeamIdentifier( identifier) :
27
29
" Invalid team identifier: \( identifier ?? " unknown " ) . "
30
+ case . unableToReadVersion:
31
+ " Unable to execute the binary to read version "
28
32
}
29
33
}
30
34
@@ -41,13 +45,13 @@ public class Validator {
41
45
42
46
private static let signInfoFlags : SecCSFlags = . init( rawValue: kSecCSSigningInformation)
43
47
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 {
46
50
throw . fileNotFound
47
51
}
48
52
49
53
var staticCode : SecStaticCode ?
50
- let status = SecStaticCodeCreateWithPath ( path as CFURL , SecCSFlags ( ) , & staticCode)
54
+ let status = SecStaticCodeCreateWithPath ( binaryPath as CFURL , SecCSFlags ( ) , & staticCode)
51
55
guard status == errSecSuccess, let code = staticCode else {
52
56
throw . unableToCreateStaticCode
53
57
}
@@ -78,6 +82,29 @@ public class Validator {
78
82
}
79
83
}
80
84
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
+
81
108
public static let xpcPeerRequirement = " anchor apple generic " + // Apple-issued certificate chain
82
109
" and certificate leaf[subject.OU] = \" " + expectedTeamIdentifier + " \" " // Signed by the Coder team
83
110
}
0 commit comments