diff --git a/patches/squirrel.mac/.patches b/patches/squirrel.mac/.patches index a86478c8892a..4b47e7da493e 100644 --- a/patches/squirrel.mac/.patches +++ b/patches/squirrel.mac/.patches @@ -7,3 +7,4 @@ fix_abort_installation_attempt_at_the_final_mile_if_the_app_is.patch feat_add_ability_to_prevent_version_downgrades.patch refactor_use_non-deprecated_nskeyedarchiver_apis.patch chore_turn_off_launchapplicationaturl_deprecation_errors_in_squirrel.patch +fix_crash_when_process_to_extract_zip_cannot_be_launched.patch diff --git a/patches/squirrel.mac/fix_crash_when_process_to_extract_zip_cannot_be_launched.patch b/patches/squirrel.mac/fix_crash_when_process_to_extract_zip_cannot_be_launched.patch new file mode 100644 index 000000000000..a9438c7b068c --- /dev/null +++ b/patches/squirrel.mac/fix_crash_when_process_to_extract_zip_cannot_be_launched.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Niklas Wenzel +Date: Tue, 27 May 2025 02:03:54 +0200 +Subject: fix: crash when process to extract zip cannot be launched + +Fixes https://github.com/electron/electron/issues/47270 + +diff --git a/Squirrel/SQRLZipArchiver.m b/Squirrel/SQRLZipArchiver.m +index 68f5dac8e553638f41306956df9d38eeda18f8f2..a9cd676df63e19edf9e20473d27b85591c7cb49e 100644 +--- a/Squirrel/SQRLZipArchiver.m ++++ b/Squirrel/SQRLZipArchiver.m +@@ -153,7 +153,17 @@ - (RACSignal *)launchWithArguments:(NSArray *)arguments { + setNameWithFormat:@"-launchWithArguments: %@", arguments]; + + self.dittoTask.arguments = arguments; +- [self.dittoTask launch]; ++ ++ NSError *launchError = nil; ++ ++ if (![self.dittoTask launchAndReturnError:&launchError]) { ++ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; ++ userInfo[NSLocalizedDescriptionKey] = launchError.localizedDescription; ++ ++ NSLog(@"Starting ditto task failed with error: %@", launchError.localizedDescription); ++ ++ return [RACSignal error:[NSError errorWithDomain:SQRLZipArchiverErrorDomain code:SQRLZipArchiverShellTaskFailed userInfo:userInfo]]; ++ } + + return signal; + } diff --git a/spec/api-autoupdater-darwin-spec.ts b/spec/api-autoupdater-darwin-spec.ts index 24925c503c5b..24709ef21b91 100644 --- a/spec/api-autoupdater-darwin-spec.ts +++ b/spec/api-autoupdater-darwin-spec.ts @@ -42,6 +42,16 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () { return cp.spawn(path.resolve(appPath, 'Contents/MacOS/Electron'), args); }; + const launchAppSandboxed = (appPath: string, profilePath: string, args: string[] = []) => { + return spawn('/usr/bin/sandbox-exec', [ + '-f', + profilePath, + path.resolve(appPath, 'Contents/MacOS/Electron'), + ...args, + '--no-sandbox' + ]); + }; + const getRunningShipIts = async (appPath: string) => { const processes = await psList(); const activeShipIts = processes.filter(p => p.cmd?.includes('Squirrel.framework/Resources/ShipIt com.github.Electron.ShipIt') && p.cmd!.startsWith(appPath)); @@ -740,6 +750,41 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () { }); }); + it('should hit the download endpoint when an update is available and fail when the zip extraction process fails to launch', async () => { + await withUpdatableApp({ + nextVersion: '2.0.0', + startFixture: 'update', + endFixture: 'update' + }, async (appPath, updateZipPath) => { + server.get('/update-file', (req, res) => { + res.download(updateZipPath); + }); + server.get('/update-check', (req, res) => { + res.json({ + url: `http://localhost:${port}/update-file`, + name: 'My Release Name', + notes: 'Theses are some release notes innit', + pub_date: (new Date()).toString() + }); + }); + const launchResult = await launchAppSandboxed( + appPath, + path.resolve(__dirname, 'fixtures/auto-update/sandbox/block-ditto.sb'), + [`http://localhost:${port}/update-check`] + ); + logOnError(launchResult, () => { + expect(launchResult).to.have.property('code', 1); + expect(launchResult.out).to.include('Starting ditto task failed with error:'); + expect(launchResult.out).to.include('SQRLZipArchiverErrorDomain'); + expect(requests).to.have.lengthOf(2); + expect(requests[0]).to.have.property('url', '/update-check'); + expect(requests[1]).to.have.property('url', '/update-file'); + expect(requests[0].header('user-agent')).to.include('Electron/'); + expect(requests[1].header('user-agent')).to.include('Electron/'); + }); + }); + }); + it('should hit the download endpoint when an update is available and update successfully when the zip is provided with JSON update mode', async () => { await withUpdatableApp({ nextVersion: '2.0.0', diff --git a/spec/fixtures/auto-update/sandbox/block-ditto.sb b/spec/fixtures/auto-update/sandbox/block-ditto.sb new file mode 100644 index 000000000000..79194bdbd03a --- /dev/null +++ b/spec/fixtures/auto-update/sandbox/block-ditto.sb @@ -0,0 +1,5 @@ +(version 1) +(allow default) +(deny process-exec + (literal "/usr/bin/ditto") +)