Fix XPC validation

This commit is contained in:
世界
2026-01-05 15:42:56 +08:00
parent af9cfd0f59
commit 8a62a58ce7
12 changed files with 278 additions and 145 deletions
@@ -76,9 +76,15 @@ public struct AppView: View {
Section { Section {
if rootHelperRegistrationStatus == .enabled { if rootHelperRegistrationStatus == .enabled {
FormButton { FormButton {
performHelperAction { Task {
try HelperServiceManager.unregisterRootHelper() do {
try HelperServiceManager.registerRootHelper() try HelperServiceManager.unregisterRootHelper()
try await Task.sleep(for: .seconds(1))
try HelperServiceManager.registerRootHelper()
refreshHelperStatus()
} catch {
alert = AlertState(error: error)
}
} }
} label: { } label: {
Label("Update", systemImage: "arrow.down.doc.fill") Label("Update", systemImage: "arrow.down.doc.fill")
+13 -33
View File
@@ -8,46 +8,22 @@ class RootHelperService: NSObject {
private var listener: NSXPCListener? private var listener: NSXPCListener?
func start() { func start() {
setupLogging() listener = NSXPCListener(machServiceName: AppConfiguration.rootHelperMachService)
startXPCListener()
}
private func setupLogging() {
let basePath = "/var/log/sing-box"
try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true)
let logPath = basePath + "/roothelper.log"
freopen(logPath, "a", stderr)
}
private func startXPCListener() {
let machServiceName = getMachServiceName()
listener = NSXPCListener(machServiceName: machServiceName)
listener?.delegate = self listener?.delegate = self
listener?.resume() listener?.resume()
} }
private func getMachServiceName() -> String {
if let identifier = Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String {
return "\(identifier).helper"
}
fatalError("Missing AppGroupIdentifier in Info.plist")
}
} }
extension RootHelperService: NSXPCListenerDelegate { extension RootHelperService: NSXPCListenerDelegate {
func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
let allowedBundleIDs = [ let systemExtensionID = AppConfiguration.systemExtensionBundleID
AppConfiguration.systemExtensionBundleID, let standaloneID = AppConfiguration.packageName + ".standalone"
AppConfiguration.packageName + ".standalone", let teamID = AppConfiguration.teamID
] let requirement = "(identifier \"\(systemExtensionID)\" or identifier \"\(standaloneID)\") and anchor apple generic and certificate leaf[subject.OU] = \"\(teamID)\""
guard XPCConnectionValidator.validateConnection( do {
newConnection, try newConnection.setCodeSigningRequirement(requirement)
teamID: AppConfiguration.teamID, } catch {
allowedBundleIDs: allowedBundleIDs logger.warning("Rejected XPC connection: \(error.localizedDescription)")
) else {
let info = XPCConnectionValidator.getConnectionInfo(newConnection)
logger.warning("Rejected XPC connection: pid=\(info.pid), bundleID=\(info.bundleID ?? "unknown"), teamID=\(info.teamID ?? "unknown")")
return false return false
} }
@@ -106,4 +82,8 @@ extension RootHelperService: RootHelperProtocol {
reply(error as NSError) reply(error as NSError)
} }
} }
func getVersion(reply: @escaping (String) -> Void) {
reply(Bundle.main.version)
}
} }
+6 -8
View File
@@ -53,14 +53,12 @@
} }
func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
let allowedBundleIDs = [AppConfiguration.packageName + ".standalone"] let bundleID = AppConfiguration.packageName + ".standalone"
guard XPCConnectionValidator.validateConnection( let requirement = "identifier \"\(bundleID)\" and anchor apple generic and certificate leaf[subject.OU] = \"\(AppConfiguration.teamID)\""
newConnection, do {
teamID: AppConfiguration.teamID, try newConnection.setCodeSigningRequirement(requirement)
allowedBundleIDs: allowedBundleIDs } catch {
) else { logger.warning("Rejected XPC connection: \(error.localizedDescription)")
let info = XPCConnectionValidator.getConnectionInfo(newConnection)
logger.warning("Rejected XPC connection: pid=\(info.pid), bundleID=\(info.bundleID ?? "unknown"), teamID=\(info.teamID ?? "unknown")")
return false return false
} }
@@ -1,7 +1,10 @@
#if os(macOS) #if os(macOS)
import Foundation import Foundation
import os
import ServiceManagement import ServiceManagement
private let logger = Logger(category: "HelperServiceManager")
public enum HelperServiceManager { public enum HelperServiceManager {
private static var rootHelperService: SMAppService { private static var rootHelperService: SMAppService {
SMAppService.daemon(plistName: "\(AppConfiguration.rootHelperBundleID).plist") SMAppService.daemon(plistName: "\(AppConfiguration.rootHelperBundleID).plist")
@@ -21,5 +24,25 @@
public static func unregisterRootHelper() throws { public static func unregisterRootHelper() throws {
try rootHelperService.unregister() try rootHelperService.unregister()
} }
public static func updateRootHelperIfNeeded() async {
guard rootHelperStatus == .enabled else { return }
do {
let installedVersion = try RootHelperClient.shared.getVersion()
let currentVersion = Bundle.main.version
guard currentVersion != installedVersion else { return }
} catch {
logger.warning("Failed to get root helper version, updating: \(error.localizedDescription)")
}
do {
try unregisterRootHelper()
try await Task.sleep(for: .seconds(1))
try registerRootHelper()
} catch {
logger.error("Failed to update root helper: \(error.localizedDescription)")
}
}
} }
#endif #endif
+48
View File
@@ -42,6 +42,7 @@
func getWorkingDirectorySize(reply: @escaping (Int64, NSError?) -> Void) func getWorkingDirectorySize(reply: @escaping (Int64, NSError?) -> Void)
func cleanWorkingDirectory(reply: @escaping (NSError?) -> Void) func cleanWorkingDirectory(reply: @escaping (NSError?) -> Void)
func getVersion(reply: @escaping (String) -> Void)
} }
public enum RootHelperXPC { public enum RootHelperXPC {
@@ -217,5 +218,52 @@
proxy.cleanWorkingDirectory(reply: reply) proxy.cleanWorkingDirectory(reply: reply)
} }
} }
public func getVersion() throws -> String {
let semaphore = DispatchSemaphore(value: 0)
var result: String?
var resultError: NSError?
let conn = getConnection()
guard let proxy = conn.remoteObjectProxyWithErrorHandler({ error in
logger.error("getVersion XPC error: \(error.localizedDescription)")
resultError = error as NSError
semaphore.signal()
}) as? RootHelperProtocol else {
connectionLock.lock()
connection = nil
connectionLock.unlock()
conn.invalidate()
throw NSError(domain: "RootHelper", code: -1, userInfo: [
NSLocalizedDescriptionKey: "Failed to get RootHelper proxy",
])
}
proxy.getVersion { version in
result = version
semaphore.signal()
}
let timeout = DispatchTime.now() + .seconds(5)
if semaphore.wait(timeout: timeout) == .timedOut {
let error = NSError(domain: "RootHelper", code: -1, userInfo: [
NSLocalizedDescriptionKey: "getVersion request timeout",
])
logger.error("getVersion: timeout")
throw error
}
if let error = resultError {
throw error
}
guard let value = result else {
throw NSError(domain: "RootHelper", code: -1, userInfo: [
NSLocalizedDescriptionKey: "getVersion returned nil",
])
}
return value
}
} }
#endif #endif
@@ -46,14 +46,12 @@
} }
public func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { public func listener(_: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
let allowedBundleIDs = [AppConfiguration.systemExtensionBundleID] let bundleID = AppConfiguration.systemExtensionBundleID
guard XPCConnectionValidator.validateConnection( let requirement = "identifier \"\(bundleID)\" and anchor apple generic and certificate leaf[subject.OU] = \"\(AppConfiguration.teamID)\""
newConnection, do {
teamID: AppConfiguration.teamID, try newConnection.setCodeSigningRequirement(requirement)
allowedBundleIDs: allowedBundleIDs } catch {
) else { logger.warning("Rejected XPC connection: \(error.localizedDescription)")
let info = XPCConnectionValidator.getConnectionInfo(newConnection)
logger.warning("Rejected XPC connection: pid=\(info.pid), bundleID=\(info.bundleID ?? "unknown"), teamID=\(info.teamID ?? "unknown")")
return false return false
} }
@@ -1,80 +0,0 @@
#if os(macOS)
import Foundation
import Security
public struct XPCConnectionInfo {
public let pid: pid_t
public let bundleID: String?
public let teamID: String?
}
public enum XPCConnectionValidator {
private static func getSecCode(for connection: NSXPCConnection) -> SecCode? {
let pid = connection.processIdentifier
var code: SecCode?
let attributes = [kSecGuestAttributePid: pid] as CFDictionary
guard SecCodeCopyGuestWithAttributes(nil, attributes, [], &code) == errSecSuccess else {
return nil
}
return code
}
private static func getSigningInfo(_ code: SecCode) -> [String: Any]? {
var staticCode: SecStaticCode?
guard SecCodeCopyStaticCode(code, [], &staticCode) == errSecSuccess,
let staticCode
else {
return nil
}
var info: CFDictionary?
guard SecCodeCopySigningInformation(staticCode, [], &info) == errSecSuccess else {
return nil
}
return info as? [String: Any]
}
public static func getConnectionInfo(_ connection: NSXPCConnection) -> XPCConnectionInfo {
let pid = connection.processIdentifier
guard let secCode = getSecCode(for: connection),
let signingInfo = getSigningInfo(secCode)
else {
return XPCConnectionInfo(pid: pid, bundleID: nil, teamID: nil)
}
let bundleID = signingInfo[kSecCodeInfoIdentifier as String] as? String
let teamID = signingInfo[kSecCodeInfoTeamIdentifier as String] as? String
return XPCConnectionInfo(pid: pid, bundleID: bundleID, teamID: teamID)
}
public static func validateConnection(
_ connection: NSXPCConnection,
teamID: String,
allowedBundleIDs: [String]
) -> Bool {
guard let secCode = getSecCode(for: connection) else {
return false
}
let requirement = "anchor apple generic and certificate leaf[subject.OU] = \"\(teamID)\""
var secRequirement: SecRequirement?
guard SecRequirementCreateWithString(requirement as CFString, [], &secRequirement) == errSecSuccess,
let req = secRequirement,
SecCodeCheckValidity(secCode, [], req) == errSecSuccess
else {
return false
}
guard let signingInfo = getSigningInfo(secCode),
let bundleID = signingInfo[kSecCodeInfoIdentifier as String] as? String,
allowedBundleIDs.contains(bundleID)
else {
return false
}
return true
}
}
#endif
+1 -1
View File
@@ -1,6 +1,6 @@
import Foundation import Foundation
extension Bundle { public extension Bundle {
var version: String { var version: String {
infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown" infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown"
} }
+162 -11
View File
@@ -202,7 +202,14 @@
} }
}, },
"App Settings" : { "App Settings" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "应用设置"
}
}
}
}, },
"Append `0.0.0.0/31` and `::/127` to `route_exclude_address` if not exists." : { "Append `0.0.0.0/31` and `::/127` to `route_exclude_address` if not exists." : {
"localizations" : { "localizations" : {
@@ -562,6 +569,16 @@
} }
} }
}, },
"Context destroyed" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "上下文已销毁"
}
}
}
},
"Copy" : { "Copy" : {
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
@@ -984,7 +1001,14 @@
} }
}, },
"Enable" : { "Enable" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "启用"
}
}
}
}, },
"enforceRoutes" : { "enforceRoutes" : {
"shouldTranslate" : false "shouldTranslate" : false
@@ -1068,6 +1092,16 @@
} }
} }
}, },
"Failed to save rules" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "保存规则失败"
}
}
}
},
"false" : { "false" : {
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
@@ -1176,10 +1210,24 @@
} }
}, },
"Helper Service" : { "Helper Service" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "辅助服务"
}
}
}
}, },
"Helper Service Required" : { "Helper Service Required" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "需要辅助服务"
}
}
}
}, },
"Hide VPN Icon" : { "Hide VPN Icon" : {
"localizations" : { "localizations" : {
@@ -1355,7 +1403,14 @@
} }
}, },
"In the standalone version of SFM, reading WiFi state requires this app to be running. After you quit the SFM app, the sing-box service cannot continue to provide `wifi_ssid` and `wifi_bssid` routing rules." : { "In the standalone version of SFM, reading WiFi state requires this app to be running. After you quit the SFM app, the sing-box service cannot continue to provide `wifi_ssid` and `wifi_bssid` routing rules." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "在 SFM 独立版本中,读取 WiFi 状态需要应用保持运行。退出 SFM 应用后,sing-box 服务将无法继续提供 `wifi_ssid` 和 `wifi_bssid` 路由规则。"
}
}
}
}, },
"Inbound" : { "Inbound" : {
"localizations" : { "localizations" : {
@@ -1381,7 +1436,14 @@
"shouldTranslate" : false "shouldTranslate" : false
}, },
"Install" : { "Install" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "安装"
}
}
}
}, },
"Install Network Extension" : { "Install Network Extension" : {
"localizations" : { "localizations" : {
@@ -1424,6 +1486,7 @@
} }
}, },
"Invalid QR Code" : { "Invalid QR Code" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
@@ -1550,7 +1613,14 @@
} }
}, },
"Managing working directory requires Helper Service." : { "Managing working directory requires Helper Service." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "管理工作目录需要辅助服务。"
}
}
}
}, },
"Match Domains" : { "Match Domains" : {
"localizations" : { "localizations" : {
@@ -1585,6 +1655,26 @@
} }
} }
}, },
"Missing access to selected file" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "无法访问所选文件"
}
}
}
},
"Missing file" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "缺少文件"
}
}
}
},
"Missing path" : { "Missing path" : {
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
@@ -1897,6 +1987,26 @@
} }
} }
}, },
"Profile missing" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "配置丢失"
}
}
}
},
"Profile not found" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "未找到配置"
}
}
}
},
"Profile Override" : { "Profile Override" : {
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
@@ -2075,6 +2185,7 @@
} }
}, },
"Scanner Error" : { "Scanner Error" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
@@ -2115,6 +2226,7 @@
} }
}, },
"Service Error" : { "Service Error" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
@@ -2451,7 +2563,14 @@
} }
}, },
"The sing-box service requires Helper Service to provide process lookup functionality, which supports `process_name` and `process_path` routing rules." : { "The sing-box service requires Helper Service to provide process lookup functionality, which supports `process_name` and `process_path` routing rules." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "sing-box 服务需要辅助服务来提供进程查找功能,以支持 `process_name` 和 `process_path` 路由规则。"
}
}
}
}, },
"This app needs to be placed under the Applications folder to work." : { "This app needs to be placed under the Applications folder to work." : {
"localizations" : { "localizations" : {
@@ -2464,7 +2583,14 @@
} }
}, },
"This helper service provides process lookup for `process_name` and `process_path` routing rules, and manages the working directory." : { "This helper service provides process lookup for `process_name` and `process_path` routing rules, and manages the working directory." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此辅助服务为 `process_name` 和 `process_path` 路由规则提供进程查找功能,并管理工作目录。"
}
}
}
}, },
"To Clipboard" : { "To Clipboard" : {
"localizations" : { "localizations" : {
@@ -2537,7 +2663,24 @@
} }
}, },
"Unavailable" : { "Unavailable" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "不可用"
}
}
}
},
"Unexpected message type %lld" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "意外的消息类型 %lld"
}
}
}
}, },
"Uninstall" : { "Uninstall" : {
"localizations" : { "localizations" : {
@@ -2570,6 +2713,7 @@
} }
}, },
"Update Failed" : { "Update Failed" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"zh-Hans" : { "zh-Hans" : {
"stringUnit" : { "stringUnit" : {
@@ -2663,7 +2807,14 @@
} }
}, },
"WiFi State Access" : { "WiFi State Access" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "WiFi 状态访问"
}
}
}
}, },
"Working Directory" : { "Working Directory" : {
"localizations" : { "localizations" : {
+3
View File
@@ -1,3 +1,6 @@
SHELL := /bin/bash
.SHELLFLAGS := -o pipefail -c
build_all: build_ios build_macos build_tvos build_all: build_ios build_macos build_tvos
build_ios: build_ios:
@@ -12,6 +12,7 @@ class StandaloneApplicationDelegate: ApplicationDelegate {
UserServiceEndpointPublisher.shared.start() UserServiceEndpointPublisher.shared.start()
Task { Task {
await setupSystemExtension() await setupSystemExtension()
await HelperServiceManager.updateRootHelperIfNeeded()
} }
} }
+6 -1
View File
@@ -1836,6 +1836,7 @@
3AAAFB2F2EF5218F004C69AD /* Debug */ = { 3AAAFB2F2EF5218F004C69AD /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
APP_GROUP_IDENTIFIER = "group.$(BASE_PACKAGE_IDENTIFIER)";
CODE_SIGN_ENTITLEMENTS = FileProviderExtension/FileProviderExtension.entitlements; CODE_SIGN_ENTITLEMENTS = FileProviderExtension/FileProviderExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
@@ -1868,6 +1869,7 @@
3AAAFB302EF5218F004C69AD /* Release */ = { 3AAAFB302EF5218F004C69AD /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
APP_GROUP_IDENTIFIER = "group.$(BASE_PACKAGE_IDENTIFIER)";
CODE_SIGN_ENTITLEMENTS = FileProviderExtension/FileProviderExtension.entitlements; CODE_SIGN_ENTITLEMENTS = FileProviderExtension/FileProviderExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
@@ -2127,6 +2129,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "1.13.0-alpha.21";
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_PACKAGE_IDENTIFIER).helper"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_PACKAGE_IDENTIFIER).helper";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO; RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
@@ -2167,6 +2170,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = "1.13.0-alpha.21";
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_PACKAGE_IDENTIFIER).helper"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_PACKAGE_IDENTIFIER).helper";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO; RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO;
@@ -2513,6 +2517,7 @@
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx; SDKROOT = macosx;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
@@ -2536,7 +2541,6 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
STRIP_STYLE = "non-global";
DEVELOPMENT_TEAM = 287TTNZF8L; DEVELOPMENT_TEAM = 287TTNZF8L;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
@@ -2561,6 +2565,7 @@
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx; SDKROOT = macosx;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;