11import Foundation
2+ import Subprocess
23
34public 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