From b8986f608ac9014219d11fbe78d9c8d1baf895d4 Mon Sep 17 00:00:00 2001 From: Zorg Date: Sat, 29 Jun 2024 15:15:30 -0700 Subject: [PATCH 1/3] Skip safe atomic swap if update has custom update security policy (#2593) Also emit a warning when checking if the updater is configured correctly. --- Autoupdate/SUPlainInstaller.m | 32 ++++++++++++++++++++++++-------- Sparkle/SPUUpdater.m | 9 +++++++++ Sparkle/SUHost.h | 2 ++ Sparkle/SUHost.m | 7 +++++++ 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Autoupdate/SUPlainInstaller.m b/Autoupdate/SUPlainInstaller.m index ba6cf9ce58..26303ed3ce 100644 --- a/Autoupdate/SUPlainInstaller.m +++ b/Autoupdate/SUPlainInstaller.m @@ -344,26 +344,42 @@ - (BOOL)performInitialInstallation:(NSError * __autoreleasing *)error SUFileManager *fileManager = [[SUFileManager alloc] init]; + BOOL updateHasCustomUpdateSecurityPolicy = NO; if (@available(macOS 13.0, *)) { + // If the new update is notarized / developer ID code signed and Autoupdate is not signed with the same Team ID as the new update, + // then we may run into Privacy & Security prompt issues from the OS + // which think we are modifying the update (but we're not) + // To avoid these, we skip the gatekeeper scan and skip performing an atomic swap during install + // If the update has a custom update security policy, the same team ID policy may not apply, + // so in that case we will also skip performing an atomic swap + NSURL *mainExecutableURL = NSBundle.mainBundle.executableURL; if (mainExecutableURL == nil) { // This shouldn't happen _canPerformSafeAtomicSwap = NO; } else { - NSString *installerTeamIdentifier = [SUCodeSigningVerifier teamIdentifierAtURL:mainExecutableURL]; - NSString *bundleTeamIdentifier = [SUCodeSigningVerifier teamIdentifierAtURL:bundle.bundleURL]; - - // If the new update is code signed and Autoupdate is not signed with the same Team ID as the new update, - // then we may run into Privacy & Security prompt issues from the OS - // To avoid these, we skip the gatekeeper scan and skip performing an atomic swap during install - _canPerformSafeAtomicSwap = (bundleTeamIdentifier == nil || (installerTeamIdentifier != nil && [installerTeamIdentifier isEqualToString:bundleTeamIdentifier])); + updateHasCustomUpdateSecurityPolicy = updateHost.hasUpdateSecurityPolicy; + if (updateHasCustomUpdateSecurityPolicy) { + // We don't handle working around a custom update security policy + _canPerformSafeAtomicSwap = NO; + } else { + NSString *installerTeamIdentifier = [SUCodeSigningVerifier teamIdentifierAtURL:mainExecutableURL]; + NSString *bundleTeamIdentifier = [SUCodeSigningVerifier teamIdentifierAtURL:bundle.bundleURL]; + + // If bundleTeamIdentifier is nil, then the update isn't code signed so atomic swap is okay + _canPerformSafeAtomicSwap = (bundleTeamIdentifier == nil || (installerTeamIdentifier != nil && [installerTeamIdentifier isEqualToString:bundleTeamIdentifier])); + } } } else { _canPerformSafeAtomicSwap = YES; } if (!_canPerformSafeAtomicSwap) { - SULog(SULogLevelDefault, @"Skipping atomic rename/swap and gatekeeper scan because Autoupdate is not signed with same identity as the new update %@", bundle.bundleURL.lastPathComponent); + if (updateHasCustomUpdateSecurityPolicy) { + SULog(SULogLevelDefault, @"Skipping atomic rename/swap and gatekeeper scan because new update %@ has a custom NSUpdateSecurityPolicy", bundle.bundleURL.lastPathComponent); + } else { + SULog(SULogLevelDefault, @"Skipping atomic rename/swap and gatekeeper scan because Autoupdate is not signed with same identity as the new update %@", bundle.bundleURL.lastPathComponent); + } } _newAndOldBundlesOnSameVolume = [fileManager itemAtURL:bundle.bundleURL isOnSameVolumeItemAsURL:_host.bundle.bundleURL]; diff --git a/Sparkle/SPUUpdater.m b/Sparkle/SPUUpdater.m index fc1193548d..a219e8b9b6 100644 --- a/Sparkle/SPUUpdater.m +++ b/Sparkle/SPUUpdater.m @@ -76,6 +76,7 @@ @implementation SPUUpdater BOOL _showingPermissionRequest; BOOL _loggedATSWarning; BOOL _loggedNoSecureKeyWarning; + BOOL _loggedUpdateSecurityPolicyWarning; BOOL _updatingMainBundle; } @@ -361,6 +362,14 @@ - (BOOL)checkIfConfiguredProperlyAndRequireFeedURL:(BOOL)requireFeedURL validate } } + if (_updatingMainBundle) { + if (!_loggedUpdateSecurityPolicyWarning && mainBundleHost.hasUpdateSecurityPolicy) { + SULog(SULogLevelDefault, @"Warning: %@ has a custom NSUpdateSecurityPolicy in its Info.plist. This may cause issues when installing updates. Please consider removing this key for your builds using Sparkle if you do not really require a custom update security policy.", hostName); + + _loggedUpdateSecurityPolicyWarning = YES; + } + } + return YES; } diff --git a/Sparkle/SUHost.h b/Sparkle/SUHost.h index 8f402d8969..f377d4f359 100644 --- a/Sparkle/SUHost.h +++ b/Sparkle/SUHost.h @@ -33,6 +33,8 @@ SPU_OBJC_DIRECT_MEMBERS @property (getter=isRunningTranslocated, nonatomic, readonly) BOOL runningTranslocated; @property (readonly, nonatomic, copy, nullable) NSString *publicDSAKeyFileKey; +@property (nonatomic, readonly) BOOL hasUpdateSecurityPolicy; + - (nullable id)objectForInfoDictionaryKey:(NSString *)key; - (BOOL)boolForInfoDictionaryKey:(NSString *)key; - (nullable id)objectForUserDefaultsKey:(NSString *)defaultName; diff --git a/Sparkle/SUHost.m b/Sparkle/SUHost.m index fff669ed13..b59da588e6 100644 --- a/Sparkle/SUHost.m +++ b/Sparkle/SUHost.m @@ -161,6 +161,13 @@ - (NSString *_Nullable)publicDSAKey SPU_OBJC_DIRECT return key; } +- (BOOL)hasUpdateSecurityPolicy +{ + NSDictionary *updateSecurityPolicy = [self objectForInfoDictionaryKey:@"NSUpdateSecurityPolicy"]; + + return (updateSecurityPolicy != nil); +} + - (SUPublicKeys *)publicKeys { return [[SUPublicKeys alloc] initWithEd:[self publicEDKey] From 6aa15e9933a2bbf4e4b100970229697e543539e1 Mon Sep 17 00:00:00 2001 From: Zorg Date: Sat, 29 Jun 2024 16:36:13 -0700 Subject: [PATCH 2/3] Update change log for 2.6.4 --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 21c6c59307..cc2d066853 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +# 2.6.4 + +* Fix app modification prompt from appearing when downloaded update overrides NSUpdateSecurityPolicy (#2593) + # 2.6.3 * Guard update timer update check against sessionInProgress to fix rare crash when checking for updates (#2561) From 0ef1ee0220239b3776f433314515fd849025673f Mon Sep 17 00:00:00 2001 From: Sparkle-Bot Date: Sat, 29 Jun 2024 23:44:15 +0000 Subject: [PATCH 3/3] Update Package management files for version 2.6.4 --- Carthage-dev.json | 2 +- Configurations/ConfigCommon.xcconfig | 4 ++-- Package.swift | 6 +++--- Sparkle.podspec | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Carthage-dev.json b/Carthage-dev.json index ac2dbb70b3..e7ba35f8a9 100644 --- a/Carthage-dev.json +++ b/Carthage-dev.json @@ -1 +1 @@ -{"1.27.1": "https://github.com/sparkle-project/Sparkle/releases/download/1.27.1/Sparkle-1.27.1.tar.xz", "2.0.0-beta.6": "https://github.com/sparkle-project/Sparkle/releases/download/2.0.0-beta.6/Sparkle-2.0.0-beta.6.tar.xz", "2.0.0-rc.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.0.0-rc.1/Sparkle-2.0.0-rc.1.tar.xz", "2.0.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.0.0/Sparkle-2.0.0.tar.xz", "2.1.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0-beta.1/Sparkle-2.1.0-beta.1.tar.xz", "2.1.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0-beta.2/Sparkle-2.1.0-beta.2.tar.xz", "2.1.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz", "2.2.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.0-beta.1/Sparkle-2.2.0-beta.1.tar.xz", "2.2.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.0-beta.2/Sparkle-2.2.0-beta.2.tar.xz", "2.2.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.0/Sparkle-2.2.0.tar.xz", "2.2.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.1/Sparkle-2.2.1.tar.xz", "2.2.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.2/Sparkle-2.2.2.tar.xz", "2.3.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.0-beta.1/Sparkle-2.3.0-beta.1.tar.xz", "2.3.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.0-beta.2/Sparkle-2.3.0-beta.2.tar.xz", "2.3.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.0/Sparkle-2.3.0.tar.xz", "2.3.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.1/Sparkle-2.3.1.tar.xz", "2.3.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.2/Sparkle-2.3.2.tar.xz", "2.4.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.0-beta.1/Sparkle-2.4.0-beta.1.tar.xz", "2.4.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.0-beta.2/Sparkle-2.4.0-beta.2.tar.xz", "2.4.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.0/Sparkle-2.4.0.tar.xz", "2.4.1-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.1-beta.1/Sparkle-2.4.1-beta.1.tar.xz", "2.4.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.1/Sparkle-2.4.1.tar.xz", "2.4.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.2/Sparkle-2.4.2.tar.xz", "2.5.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.0-beta.1/Sparkle-2.5.0-beta.1.tar.xz", "2.5.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.0-beta.2/Sparkle-2.5.0-beta.2.tar.xz", "2.5.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.0/Sparkle-2.5.0.tar.xz", "2.5.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.1/Sparkle-2.5.1.tar.xz", "2.5.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.2/Sparkle-2.5.2.tar.xz", "2.6.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.0-beta.1/Sparkle-2.6.0-beta.1.tar.xz", "2.6.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.0-beta.2/Sparkle-2.6.0-beta.2.tar.xz", "2.6.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.0/Sparkle-2.6.0.tar.xz", "2.6.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.1/Sparkle-2.6.1.tar.xz", "1.27.2": "https://github.com/sparkle-project/Sparkle/releases/download/1.27.2/Sparkle-1.27.2.tar.xz", "2.6.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.2/Sparkle-2.6.2.tar.xz", "1.27.3": "https://github.com/sparkle-project/Sparkle/releases/download/1.27.3/Sparkle-1.27.3.tar.xz", "2.6.3": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.3/Sparkle-2.6.3.tar.xz"} \ No newline at end of file +{"1.27.1": "https://github.com/sparkle-project/Sparkle/releases/download/1.27.1/Sparkle-1.27.1.tar.xz", "2.0.0-beta.6": "https://github.com/sparkle-project/Sparkle/releases/download/2.0.0-beta.6/Sparkle-2.0.0-beta.6.tar.xz", "2.0.0-rc.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.0.0-rc.1/Sparkle-2.0.0-rc.1.tar.xz", "2.0.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.0.0/Sparkle-2.0.0.tar.xz", "2.1.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0-beta.1/Sparkle-2.1.0-beta.1.tar.xz", "2.1.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0-beta.2/Sparkle-2.1.0-beta.2.tar.xz", "2.1.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz", "2.2.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.0-beta.1/Sparkle-2.2.0-beta.1.tar.xz", "2.2.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.0-beta.2/Sparkle-2.2.0-beta.2.tar.xz", "2.2.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.0/Sparkle-2.2.0.tar.xz", "2.2.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.1/Sparkle-2.2.1.tar.xz", "2.2.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.2.2/Sparkle-2.2.2.tar.xz", "2.3.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.0-beta.1/Sparkle-2.3.0-beta.1.tar.xz", "2.3.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.0-beta.2/Sparkle-2.3.0-beta.2.tar.xz", "2.3.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.0/Sparkle-2.3.0.tar.xz", "2.3.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.1/Sparkle-2.3.1.tar.xz", "2.3.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.3.2/Sparkle-2.3.2.tar.xz", "2.4.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.0-beta.1/Sparkle-2.4.0-beta.1.tar.xz", "2.4.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.0-beta.2/Sparkle-2.4.0-beta.2.tar.xz", "2.4.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.0/Sparkle-2.4.0.tar.xz", "2.4.1-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.1-beta.1/Sparkle-2.4.1-beta.1.tar.xz", "2.4.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.1/Sparkle-2.4.1.tar.xz", "2.4.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.4.2/Sparkle-2.4.2.tar.xz", "2.5.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.0-beta.1/Sparkle-2.5.0-beta.1.tar.xz", "2.5.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.0-beta.2/Sparkle-2.5.0-beta.2.tar.xz", "2.5.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.0/Sparkle-2.5.0.tar.xz", "2.5.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.1/Sparkle-2.5.1.tar.xz", "2.5.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.5.2/Sparkle-2.5.2.tar.xz", "2.6.0-beta.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.0-beta.1/Sparkle-2.6.0-beta.1.tar.xz", "2.6.0-beta.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.0-beta.2/Sparkle-2.6.0-beta.2.tar.xz", "2.6.0": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.0/Sparkle-2.6.0.tar.xz", "2.6.1": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.1/Sparkle-2.6.1.tar.xz", "1.27.2": "https://github.com/sparkle-project/Sparkle/releases/download/1.27.2/Sparkle-1.27.2.tar.xz", "2.6.2": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.2/Sparkle-2.6.2.tar.xz", "1.27.3": "https://github.com/sparkle-project/Sparkle/releases/download/1.27.3/Sparkle-1.27.3.tar.xz", "2.6.3": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.3/Sparkle-2.6.3.tar.xz", "2.6.4": "https://github.com/sparkle-project/Sparkle/releases/download/2.6.4/Sparkle-2.6.4.tar.xz"} \ No newline at end of file diff --git a/Configurations/ConfigCommon.xcconfig b/Configurations/ConfigCommon.xcconfig index fa561f6956..b1ab608e34 100644 --- a/Configurations/ConfigCommon.xcconfig +++ b/Configurations/ConfigCommon.xcconfig @@ -99,12 +99,12 @@ SPARKLE_ICON_NAME = AppIcon // These variables must have a space after the '=' too SPARKLE_VERSION_MAJOR = 2 SPARKLE_VERSION_MINOR = 6 -SPARKLE_VERSION_PATCH = 3 +SPARKLE_VERSION_PATCH = 4 // This should be in SemVer format or empty, ie. "-beta.1" // These variables must have a space after the '=' too SPARKLE_VERSION_SUFFIX = -CURRENT_PROJECT_VERSION = 2039 +CURRENT_PROJECT_VERSION = 2039.1 MARKETING_VERSION = $(SPARKLE_VERSION_MAJOR).$(SPARKLE_VERSION_MINOR).$(SPARKLE_VERSION_PATCH)$(SPARKLE_VERSION_SUFFIX) ALWAYS_SEARCH_USER_PATHS = NO diff --git a/Package.swift b/Package.swift index cd9fc4406c..602652baa5 100644 --- a/Package.swift +++ b/Package.swift @@ -2,10 +2,10 @@ import PackageDescription // Version is technically not required here, SPM doesn't check -let version = "2.6.3" +let version = "2.6.4" // Tag is required to point towards the right asset. SPM requires the tag to follow semantic versioning to be able to resolve it. -let tag = "2.6.3" -let checksum = "86a9a29529f7e19b02c6432771739b356caf06854ca3d9988fbed3ea4ae7e1f7" +let tag = "2.6.4" +let checksum = "eb3814726816ca2f09334ce562fff1b2cd0f7c16dd4e283256081f307accb9c4" let url = "https://github.com/sparkle-project/Sparkle/releases/download/\(tag)/Sparkle-for-Swift-Package-Manager.zip" let package = Package( diff --git a/Sparkle.podspec b/Sparkle.podspec index bde955a379..59c5f0a14e 100644 --- a/Sparkle.podspec +++ b/Sparkle.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Sparkle" - s.version = "2.6.3" + s.version = "2.6.4" s.summary = "A software update framework for macOS" s.description = "Sparkle is an easy-to-use software update framework for macOS." s.homepage = "https://sparkle-project.org"