bleam 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ import ReactAppDependencyProvider
4
4
  import UIKit
5
5
  import CryptoKit
6
6
  import Darwin
7
+ import Security
7
8
 
8
9
  func launchArgument(_ name: String) -> String? {
9
10
  let arguments = ProcessInfo.processInfo.arguments
@@ -75,43 +76,68 @@ private enum PlatformHelperInstaller {
75
76
  guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return }
76
77
 
77
78
  let serviceName = "PlatformHelper"
78
- let bundledService = Bundle.main.bundleURL
79
- .appendingPathComponent("Contents/XPCServices/\(serviceName).xpc", isDirectory: true)
80
- guard FileManager.default.fileExists(atPath: bundledService.path) else {
79
+ let bundledHelper = Bundle.main.bundleURL
80
+ .appendingPathComponent("Contents/Helpers/\(serviceName)")
81
+ guard FileManager.default.isExecutableFile(atPath: bundledHelper.path) else {
81
82
  NSLog("Bleam helper unavailable: bundled executable is missing")
82
83
  return
83
84
  }
85
+ guard
86
+ let teamIdentifier = Bundle.main.object(forInfoDictionaryKey: "BleamTeamIdentifier") as? String,
87
+ let platformVersion = Bundle.main.object(forInfoDictionaryKey: "BleamRuntimeVersion") as? String,
88
+ validSignature(at: bundledHelper, teamIdentifier: teamIdentifier)
89
+ else {
90
+ NSLog("Bleam helper unavailable: bundled executable has an invalid identity")
91
+ return
92
+ }
84
93
 
85
94
  let label = "\(bundleIdentifier).PlatformHelper"
86
95
  let domain = "gui/\(getuid())"
87
- _ = runLaunchctl(["bootout", "\(domain)/\(label)"])
88
-
89
96
  let helperRoot = homeDirectory
90
97
  .appendingPathComponent("Library/Application Support/Bleam/Helpers", isDirectory: true)
91
98
  .appendingPathComponent(bundleIdentifier, isDirectory: true)
92
- let installedService = helperRoot.appendingPathComponent("\(serviceName).xpc", isDirectory: true)
93
- let executable = installedService
94
- .appendingPathComponent("Contents/MacOS/\(serviceName)")
99
+ let installedHelper = helperRoot.appendingPathComponent(serviceName)
100
+ let stagedHelper = helperRoot.appendingPathComponent(".\(serviceName).\(UUID().uuidString)")
101
+ let backupHelper = helperRoot.appendingPathComponent(".\(serviceName).previous.\(UUID().uuidString)")
102
+ let failedHelper = helperRoot.appendingPathComponent(".\(serviceName).failed.\(UUID().uuidString)")
103
+ let pendingApply = homeDirectory
104
+ .appendingPathComponent("Library/Application Support/Bleam/Updates", isDirectory: true)
105
+ .appendingPathComponent(bundleIdentifier, isDirectory: true)
106
+ .appendingPathComponent("platform-apply.pending")
95
107
  let launchAgents = homeDirectory
96
108
  .appendingPathComponent("Library/LaunchAgents", isDirectory: true)
97
109
  let plistURL = launchAgents.appendingPathComponent("\(label).plist")
98
110
  let propertyList: [String: Any] = [
99
111
  "Label": label,
100
- "ProgramArguments": [executable.path],
101
- "RunAtLoad": true,
102
- "KeepAlive": true,
112
+ "ProgramArguments": [installedHelper.path],
103
113
  "ProcessType": "Background",
104
- "MachServices": ["\(bundleIdentifier).PlatformHelper": true],
105
- "EnvironmentVariables": ["XPC_SERVICE_NAME": "\(bundleIdentifier).PlatformHelper"],
114
+ "MachServices": [label: true],
115
+ "KeepAlive": [
116
+ "SuccessfulExit": false,
117
+ "PathState": [pendingApply.path: true],
118
+ ],
119
+ "EnvironmentVariables": [
120
+ "XPC_SERVICE_NAME": label,
121
+ "BLEAM_BUNDLE_IDENTIFIER": bundleIdentifier,
122
+ "BLEAM_TEAM_IDENTIFIER": teamIdentifier,
123
+ "BLEAM_HELPER_VERSION": platformVersion,
124
+ "BLEAM_PLATFORM_VERSION": platformVersion,
125
+ ],
106
126
  ]
107
127
 
108
128
  do {
129
+ defer { try? FileManager.default.removeItem(at: stagedHelper) }
109
130
  try FileManager.default.createDirectory(
110
131
  at: helperRoot,
111
132
  withIntermediateDirectories: true
112
133
  )
113
- try? FileManager.default.removeItem(at: installedService)
114
- try FileManager.default.copyItem(at: bundledService, to: installedService)
134
+ try FileManager.default.copyItem(at: bundledHelper, to: stagedHelper)
135
+ guard
136
+ FileManager.default.isExecutableFile(atPath: stagedHelper.path),
137
+ validSignature(at: stagedHelper, teamIdentifier: teamIdentifier)
138
+ else {
139
+ throw HelperInstallError.invalidSignature
140
+ }
115
141
  try FileManager.default.createDirectory(
116
142
  at: launchAgents,
117
143
  withIntermediateDirectories: true
@@ -121,18 +147,97 @@ private enum PlatformHelperInstaller {
121
147
  format: .xml,
122
148
  options: 0
123
149
  )
124
- try data.write(to: plistURL, options: .atomic)
150
+ let previousPlist = try? Data(contentsOf: plistURL)
151
+ let wasRegistered = runLaunchctl(["print", "\(domain)/\(label)"]) == 0
152
+ if wasRegistered, runLaunchctl(["bootout", "\(domain)/\(label)"]) != 0 {
153
+ throw HelperInstallError.registrationFailed
154
+ }
155
+
156
+ do {
157
+ if FileManager.default.fileExists(atPath: installedHelper.path),
158
+ rename(installedHelper.path, backupHelper.path) != 0
159
+ {
160
+ throw HelperInstallError.replacementFailed
161
+ }
162
+ guard rename(stagedHelper.path, installedHelper.path) == 0 else {
163
+ throw HelperInstallError.replacementFailed
164
+ }
165
+ try data.write(to: plistURL, options: .atomic)
166
+ let bootstrapStatus = runLaunchctl(["bootstrap", domain, plistURL.path])
167
+ guard
168
+ bootstrapStatus == 0 || runLaunchctl(["print", "\(domain)/\(label)"]) == 0
169
+ else {
170
+ throw HelperInstallError.registrationFailed
171
+ }
172
+ try? FileManager.default.removeItem(at: backupHelper)
173
+ try? FileManager.default.removeItem(
174
+ at: helperRoot.appendingPathComponent("PlatformHelper.xpc", isDirectory: true)
175
+ )
176
+ } catch let installError {
177
+ do {
178
+ let newJobRegistered = runLaunchctl(["print", "\(domain)/\(label)"]) == 0
179
+ if newJobRegistered,
180
+ runLaunchctl(["bootout", "\(domain)/\(label)"]) != 0
181
+ {
182
+ throw HelperInstallError.rollbackFailed
183
+ }
184
+ if FileManager.default.fileExists(atPath: installedHelper.path),
185
+ rename(installedHelper.path, failedHelper.path) != 0
186
+ {
187
+ throw HelperInstallError.rollbackFailed
188
+ }
189
+ if FileManager.default.fileExists(atPath: backupHelper.path),
190
+ rename(backupHelper.path, installedHelper.path) != 0
191
+ {
192
+ throw HelperInstallError.rollbackFailed
193
+ }
194
+ if let previousPlist {
195
+ try previousPlist.write(to: plistURL, options: .atomic)
196
+ if wasRegistered,
197
+ runLaunchctl(["bootstrap", domain, plistURL.path]) != 0
198
+ {
199
+ throw HelperInstallError.rollbackFailed
200
+ }
201
+ } else {
202
+ try? FileManager.default.removeItem(at: plistURL)
203
+ }
204
+ try? FileManager.default.removeItem(at: failedHelper)
205
+ } catch {
206
+ NSLog("Bleam helper rollback failed: %@", error.localizedDescription)
207
+ throw error
208
+ }
209
+ throw installError
210
+ }
125
211
  } catch {
126
212
  NSLog("Bleam helper install failed: %@", error.localizedDescription)
127
213
  return
128
214
  }
129
-
130
- if runLaunchctl(["bootstrap", domain, plistURL.path]) != 0 {
131
- NSLog("Bleam helper registration failed")
132
- }
133
215
  #endif
134
216
  }
135
217
 
218
+ private static func validSignature(at url: URL, teamIdentifier: String) -> Bool {
219
+ var staticCode: SecStaticCode?
220
+ guard
221
+ SecStaticCodeCreateWithPath(url as CFURL, [], &staticCode) == errSecSuccess,
222
+ let staticCode,
223
+ SecStaticCodeCheckValidity(
224
+ staticCode,
225
+ SecCSFlags(rawValue: kSecCSStrictValidate),
226
+ nil
227
+ ) == errSecSuccess
228
+ else { return false }
229
+
230
+ var signingInfo: CFDictionary?
231
+ guard
232
+ SecCodeCopySigningInformation(
233
+ staticCode,
234
+ SecCSFlags(rawValue: kSecCSSigningInformation),
235
+ &signingInfo
236
+ ) == errSecSuccess
237
+ else { return false }
238
+ return (signingInfo as? [String: Any])?[kSecCodeInfoTeamIdentifier as String] as? String == teamIdentifier
239
+ }
240
+
136
241
  private static func runLaunchctl(_ arguments: [String]) -> Int32 {
137
242
  let values = ["launchctl"] + arguments
138
243
  var argv = values.map { strdup($0) }
@@ -148,6 +253,10 @@ private enum PlatformHelperInstaller {
148
253
  }
149
254
  }
150
255
 
256
+ private enum HelperInstallError: Error {
257
+ case invalidSignature, registrationFailed, replacementFailed, rollbackFailed
258
+ }
259
+
151
260
  class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
152
261
  private let bundleOverrideURL: URL?
153
262
 
@@ -62,7 +62,7 @@
62
62
  F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
63
63
  F3E19B0AABF791129F5C2531 /* Flux2KVCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D1000122F00000000000012 /* Flux2KVCache.swift */; };
64
64
  A10000000000000000000001 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A10000000000000000000005 /* main.swift */; };
65
- A10000000000000000000002 /* PlatformHelper.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = A10000000000000000000006 /* PlatformHelper.xpc */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
65
+ A10000000000000000000002 /* PlatformHelper in Embed Platform Helper */ = {isa = PBXBuildFile; fileRef = A10000000000000000000006 /* PlatformHelper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
66
66
  /* End PBXBuildFile section */
67
67
 
68
68
  /* Begin PBXContainerItemProxy section */
@@ -104,11 +104,21 @@
104
104
  dstSubfolderSpec = 16;
105
105
  files = (
106
106
  8BEA0DE1098E9F3D9607D785 /* GenerationService.xpc in Embed XPC Services */,
107
- A10000000000000000000002 /* PlatformHelper.xpc in Embed XPC Services */,
108
107
  );
109
108
  name = "Embed XPC Services";
110
109
  runOnlyForDeploymentPostprocessing = 0;
111
110
  };
111
+ A10000000000000000000010 /* Embed Platform Helper */ = {
112
+ isa = PBXCopyFilesBuildPhase;
113
+ buildActionMask = 2147483647;
114
+ dstPath = "$(CONTENTS_FOLDER_PATH)/Helpers";
115
+ dstSubfolderSpec = 16;
116
+ files = (
117
+ A10000000000000000000002 /* PlatformHelper in Embed Platform Helper */,
118
+ );
119
+ name = "Embed Platform Helper";
120
+ runOnlyForDeploymentPostprocessing = 0;
121
+ };
112
122
  /* End PBXCopyFilesBuildPhase section */
113
123
 
114
124
  /* Begin PBXFileReference section */
@@ -172,9 +182,8 @@
172
182
  F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = bleam/AppDelegate.swift; sourceTree = "<group>"; };
173
183
  F11748442D0722820044C1D9 /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Bridging-Header.h"; path = "bleam/Bridging-Header.h"; sourceTree = "<group>"; };
174
184
  FB433ED9D990D1F5BEAC2C93 /* GenerationService.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; name = GenerationService.xpc; path = GenerationService.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
175
- A10000000000000000000004 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
176
185
  A10000000000000000000005 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
177
- A10000000000000000000006 /* PlatformHelper.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = PlatformHelper.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
186
+ A10000000000000000000006 /* PlatformHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PlatformHelper; sourceTree = BUILT_PRODUCTS_DIR; };
178
187
  /* End PBXFileReference section */
179
188
 
180
189
  /* Begin PBXFrameworksBuildPhase section */
@@ -280,7 +289,7 @@
280
289
  children = (
281
290
  13B07F961A680F5B00A75B9A /* bleam.app */,
282
291
  FB433ED9D990D1F5BEAC2C93 /* GenerationService.xpc */,
283
- A10000000000000000000006 /* PlatformHelper.xpc */,
292
+ A10000000000000000000006 /* PlatformHelper */,
284
293
  );
285
294
  name = Products;
286
295
  sourceTree = "<group>";
@@ -338,7 +347,6 @@
338
347
  A1000000000000000000000F /* PlatformHelper */ = {
339
348
  isa = PBXGroup;
340
349
  children = (
341
- A10000000000000000000004 /* Info.plist */,
342
350
  A10000000000000000000005 /* main.swift */,
343
351
  );
344
352
  path = PlatformHelper;
@@ -414,6 +422,7 @@
414
422
  84C420C0881684F1F1EB77FB /* [bleam] Normalize Catalyst prebuilt frameworks */,
415
423
  BEC473809D63946EDB256C9A /* [CP] Embed Pods Frameworks */,
416
424
  CA12AFD569B8A5A458B4F36A /* Embed XPC Services */,
425
+ A10000000000000000000010 /* Embed Platform Helper */,
417
426
  );
418
427
  buildRules = (
419
428
  );
@@ -458,8 +467,8 @@
458
467
  );
459
468
  name = PlatformHelper;
460
469
  productName = PlatformHelper;
461
- productReference = A10000000000000000000006 /* PlatformHelper.xpc */;
462
- productType = "com.apple.product-type.xpc-service";
470
+ productReference = A10000000000000000000006 /* PlatformHelper */;
471
+ productType = "com.apple.product-type.tool";
463
472
  };
464
473
  /* End PBXNativeTarget section */
465
474
 
@@ -1061,17 +1070,13 @@
1061
1070
  A1000000000000000000000C /* Debug */ = {
1062
1071
  isa = XCBuildConfiguration;
1063
1072
  buildSettings = {
1073
+ ARCHS = arm64;
1064
1074
  CODE_SIGN_STYLE = Automatic;
1065
- CURRENT_PROJECT_VERSION = 1;
1066
1075
  DEAD_CODE_STRIPPING = YES;
1067
1076
  DEVELOPMENT_TEAM = 5SZ3H5P69M;
1068
1077
  ENABLE_APP_SANDBOX = NO;
1069
1078
  ENABLE_HARDENED_RUNTIME = YES;
1070
- GENERATE_INFOPLIST_FILE = NO;
1071
- INFOPLIST_FILE = PlatformHelper/Info.plist;
1072
1079
  MACOSX_DEPLOYMENT_TARGET = 13.4;
1073
- MARKETING_VERSION = 1.0;
1074
- PRODUCT_BUNDLE_IDENTIFIER = dev.bleam.app.PlatformHelper;
1075
1080
  PRODUCT_NAME = "$(TARGET_NAME)";
1076
1081
  SDKROOT = macosx;
1077
1082
  SKIP_INSTALL = YES;
@@ -1083,17 +1088,13 @@
1083
1088
  A1000000000000000000000D /* Release */ = {
1084
1089
  isa = XCBuildConfiguration;
1085
1090
  buildSettings = {
1091
+ ARCHS = arm64;
1086
1092
  CODE_SIGN_STYLE = Automatic;
1087
- CURRENT_PROJECT_VERSION = 1;
1088
1093
  DEAD_CODE_STRIPPING = YES;
1089
1094
  DEVELOPMENT_TEAM = 5SZ3H5P69M;
1090
1095
  ENABLE_APP_SANDBOX = NO;
1091
1096
  ENABLE_HARDENED_RUNTIME = YES;
1092
- GENERATE_INFOPLIST_FILE = NO;
1093
- INFOPLIST_FILE = PlatformHelper/Info.plist;
1094
1097
  MACOSX_DEPLOYMENT_TARGET = 13.4;
1095
- MARKETING_VERSION = 1.0;
1096
- PRODUCT_BUNDLE_IDENTIFIER = dev.bleam.app.PlatformHelper;
1097
1098
  PRODUCT_NAME = "$(TARGET_NAME)";
1098
1099
  SDKROOT = macosx;
1099
1100
  SKIP_INSTALL = YES;