@@ -95,7 +95,104 @@ enum Signer: Signing {
9595
9696 return items
9797 }
98-
98+
99+ ///
100+ /// Check whether the given file is an native executable binary or not.
101+ ///
102+ private static func isExecutable( _ file: URL ) async throws -> Bool {
103+ let outPipe = Pipe ( )
104+ let errPipe = Pipe ( )
105+ let task = Process ( )
106+ task. standardOutput = outPipe
107+ task. standardError = errPipe
108+
109+ let command = " file \" \( file. path) \" "
110+
111+ guard await run ( " /bin/zsh " , [ " -c " , command] , task: task) == 0 else {
112+ throw MacCrafterError . signing ( " Failed to determine if \( file. path) is an executable. " )
113+ }
114+
115+ let outputFileHandle = outPipe. fileHandleForReading
116+ let outputData = outputFileHandle. readDataToEndOfFile ( )
117+ try outputFileHandle. close ( )
118+ let output = String ( data: outputData, encoding: . utf8) ?? " "
119+
120+ return output. contains ( " Mach-O 64-bit executable " )
121+ }
122+
123+ ///
124+ /// Find and sign the Qt web engine helper app inside the QtWebEngineCore framework.
125+ ///
126+ /// This needs explicit treatment because codesign does not automatically sign it when signing the upstream framework bundle.
127+ ///
128+ private static func signQtWebEngineProcessApp( in bundle: URL , with codeSignIdentity: String ) async {
129+ let location = bundle
130+ . appendingPathComponent ( " Contents " )
131+ . appendingPathComponent ( " Frameworks " )
132+ . appendingPathComponent ( " QtWebEngineCore.framework " )
133+ . appendingPathComponent ( " Versions " )
134+ . appendingPathComponent ( " A " )
135+ . appendingPathComponent ( " Helpers " )
136+ . appendingPathComponent ( " QtWebEngineProcess.app " )
137+
138+ await sign ( at: location, with: codeSignIdentity, entitlements: nil )
139+ }
140+
141+ ///
142+ /// Find and sign the Sparkle downloader inside the Sparkle framework.
143+ ///
144+ /// This needs explicit treatment because codesign does not automatically sign it when signing the upstream framework bundle.
145+ ///
146+ private static func signSparkleDownloader( in bundle: URL , with codeSignIdentity: String ) async {
147+ let location = bundle
148+ . appendingPathComponent ( " Contents " )
149+ . appendingPathComponent ( " Frameworks " )
150+ . appendingPathComponent ( " Sparkle.framework " )
151+ . appendingPathComponent ( " Versions " )
152+ . appendingPathComponent ( " B " )
153+ . appendingPathComponent ( " XPCServices " )
154+ . appendingPathComponent ( " Downloader.xpc " )
155+
156+ await sign ( at: location, with: codeSignIdentity, entitlements: nil )
157+ }
158+
159+ ///
160+ /// Find and sign the Sparkle updater app inside the Sparkle framework.
161+ ///
162+ /// This needs explicit treatment because codesign does not automatically sign it when signing the upstream framework bundle.
163+ ///
164+ private static func signSparkleUpdaterApp( in bundle: URL , with codeSignIdentity: String ) async {
165+ let location = bundle
166+ . appendingPathComponent ( " Contents " )
167+ . appendingPathComponent ( " Frameworks " )
168+ . appendingPathComponent ( " Sparkle.framework " )
169+ . appendingPathComponent ( " Versions " )
170+ . appendingPathComponent ( " B " )
171+ . appendingPathComponent ( " Updater.app " )
172+
173+ await sign ( at: location, with: codeSignIdentity, entitlements: nil )
174+ }
175+
176+ ///
177+ /// There may be additional executables in the binaries directory which also need to be signed.
178+ ///
179+ private static func signAdditionalBinaries( in bundle: URL , with codeSignIdentity: String ) async throws {
180+ let location = bundle
181+ . appendingPathComponent ( " Contents " )
182+ . appendingPathComponent ( " MacOS " )
183+
184+ let candidates = try FileManager . default. contentsOfDirectory ( at: location, includingPropertiesForKeys: nil )
185+
186+ for candidate in candidates {
187+ if try await isExecutable ( candidate) {
188+ await sign ( at: candidate, with: codeSignIdentity, entitlements: nil )
189+ }
190+ }
191+
192+ await sign ( at: location. appendingPathComponent ( " nextcloudcmd " ) , with: codeSignIdentity, entitlements: nil )
193+ await sign ( at: location. appendingPathComponent ( " nextclouddevcmd " ) , with: codeSignIdentity, entitlements: nil )
194+ }
195+
99196 private static func verify( at location: URL ) async throws {
100197 Log . info ( " Verifying: \( location. path) " )
101198 let code = await shell ( " codesign --verify --deep --strict --verbose=2 \" \( location. path) \" " )
@@ -132,7 +229,11 @@ enum Signer: Signing {
132229
133230 await sign ( at: extensionInMainBundle, with: codeSignIdentity, entitlements: extensionEntitlements)
134231 }
135-
232+
233+ await signQtWebEngineProcessApp ( in: location, with: codeSignIdentity)
234+ await signSparkleDownloader ( in: location, with: codeSignIdentity)
235+ await signSparkleUpdaterApp ( in: location, with: codeSignIdentity)
236+
136237 let frameworksInsideMainBundle = try findFrameworks ( at: location)
137238
138239 try await withThrowingTaskGroup ( of: Void . self) { group in
@@ -163,8 +264,7 @@ enum Signer: Signing {
163264 await sign ( at: dynamicLibrary, with: codeSignIdentity, entitlements: nil )
164265 }
165266
166- await sign ( at: binariesLocation. appendingPathComponent ( " nextcloudcmd " ) , with: codeSignIdentity, entitlements: nil )
167- await sign ( at: binariesLocation. appendingPathComponent ( " nextclouddevcmd " ) , with: codeSignIdentity, entitlements: nil )
267+ try await signAdditionalBinaries ( in: location, with: codeSignIdentity)
168268
169269 guard let mainAppEntitlements = entitlements [ location. lastPathComponent] else {
170270 throw MacCrafterError . signing ( " No entitlements provided for: \( location. path) " )
@@ -188,10 +288,11 @@ enum Signer: Signing {
188288 " codesign " ,
189289 location. path,
190290 " --timestamp " ,
191- " --verbose= 4 " ,
192- " --preserve-metadata= entitlements " ,
291+ " --verbose 4 " ,
292+ " --preserve-metadata entitlements " ,
193293 " --force " ,
194- " --sign= \" \( codeSignIdentity) \" "
294+ " --options runtime " ,
295+ " --sign \" \( codeSignIdentity) \" "
195296 ]
196297
197298 if let entitlements {
0 commit comments