Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

persist: re-sign mac app bundle #127

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

slice
Copy link

@slice slice commented Dec 16, 2024

This patch aims to implement proper persistence on Darwin (macOS) by reapplying the injector when an update finishes. It also forcibly signs the bundle to ensure that it has a stable Designated Requirement, which is important for e.g. screen sharing and keybinds to work consistently.

Currently, it's hardcoded to use a code signing identity named Moonlight. This should be made configurable at some point.

This doesn't work at the moment because it introduces some kind of race against the app becoming ready:

[INFO] [core/extension/loader] Loaded 10 extensions
[DEBUG] [core/persist] Inferred bundle path: /Applications/Discord.app
[DEBUG] [core/darwin] Invoking codesign with args: [ '--verify', '-vvv', '/Applications/Discord.app' ]
[DEBUG] [core/darwin] Spawned codesign (pid: 58755)
[DEBUG] [core/darwin] codesign stderr: --prepared:/Applications/Discord.app/Contents/Frameworks/Discord Helper (GPU).app
--validated:/Applications/Discord.app/Contents/Frameworks/Discord Helper (GPU).app
--prepared:/Applications/Discord.app/Contents/Frameworks/Discord Helper.app
--validated:/Applications/Discord.app/Contents/Frameworks/Discord Helper.app
--prepared:/Applications/Discord.app/Contents/Frameworks/ReactiveObjC.framework/Versions/Current/.
--validated:/Applications/Discord.app/Contents/Frameworks/ReactiveObjC.framework/Versions/Current/.
--prepared:/Applications/Discord.app/Contents/Frameworks/Mantle.framework/Versions/Current/.
--validated:/Applications/Discord.app/Contents/Frameworks/Mantle.framework/Versions/Current/.
--prepared:/Applications/Discord.app/Contents/Frameworks/Squirrel.framework/Versions/Current/.
--validated:/Applications/Discord.app/Contents/Frameworks/Squirrel.framework/Versions/Current/.
--prepared:/Applications/Discord.app/Contents/Frameworks/Discord Helper (Renderer).app
--validated:/Applications/Discord.app/Contents/Frameworks/Discord Helper (Renderer).app
--prepared:/Applications/Discord.app/Contents/Frameworks/Discord Helper (Plugin).app
--validated:/Applications/Discord.app/Contents/Frameworks/Discord Helper (Plugin).app
--prepared:/Applications/Discord.app/Contents/Frameworks/Electron Framework.framework/Versions/Current/.
--validated:/Applications/Discord.app/Contents/Frameworks/Electron Framework.framework/Versions/Current/.
/Applications/Discord.app: valid on disk
/Applications/Discord.app: satisfies its Designated Requirement

[DEBUG] [core/darwin] codesign peacefully exited
[WARN] [core/persist] Bundle is currently passing code signing, no need to sign
Discord 0.0.323
(node:58754) UnhandledPromiseRejectionWarning: Error: protocol.registerSchemesAsPrivileged should be called before app is ready
    at Object.beforeReadyProtocolRegistration (/Applications/Discord.app/Contents/Resources/_app.asar/app_bootstrap/protocols.js:10:22)
    at Object.<anonymous> (/Applications/Discord.app/Contents/Resources/_app.asar/app_bootstrap/bootstrap.js:213:20)
    at Module._compile (node:internal/modules/cjs/loader:1373:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1432:10)
    at Module.load (node:internal/modules/cjs/loader:1215:32)
    at Module._load (node:internal/modules/cjs/loader:1031:12)
    at c._load (node:electron/js2c/node_init:2:17025)
    at Module.require (node:internal/modules/cjs/loader:1240:19)
    at require (node:internal/modules/helpers:179:18)
    at Object.<anonymous> (/Applications/Discord.app/Contents/Resources/_app.asar/app_bootstrap/index.js:17:3)
    at emitUnhandledRejectionWarning (node:internal/process/promises:298:15)
    at warnWithErrorCodeUnhandledRejectionsMode (node:internal/process/promises:406:5)
    at processPromiseRejections (node:internal/process/promises:470:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:96:32)
(node:58754) Error: protocol.registerSchemesAsPrivileged should be called before app is ready
    at Object.beforeReadyProtocolRegistration (/Applications/Discord.app/Contents/Resources/_app.asar/app_bootstrap/protocols.js:10:22)
    at Object.<anonymous> (/Applications/Discord.app/Contents/Resources/_app.asar/app_bootstrap/bootstrap.js:213:20)
    at Module._compile (node:internal/modules/cjs/loader:1373:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1432:10)
    at Module.load (node:internal/modules/cjs/loader:1215:32)
    at Module._load (node:internal/modules/cjs/loader:1031:12)
    at c._load (node:electron/js2c/node_init:2:17025)
    at Module.require (node:internal/modules/cjs/loader:1240:19)
    at require (node:internal/modules/helpers:179:18)
    at Object.<anonymous> (/Applications/Discord.app/Contents/Resources/_app.asar/app_bootstrap/index.js:17:3)

The code signing process succeeds, but by the time registerSchemesAsPrivileged runs (a function in Discord's ASAR) the app is already ready for some reason.

As I was repeatedly re-running this I managed to get past the error and get this one:

2024-12-15 17:15:33.521 Discord[59145:2331708] Download completed to: file:///Users/skip/Library/Caches/com.hnc.Discord.ShipIt/update.DX5Y9Kz/Discord.zip
2024-12-16T01:15:34.140Z [Modules] Host update failed: Error: Code signature at URL file:///Users/skip/Library/Caches/com.hnc.Discord.ShipIt/update.DX5Y9Kz/Discord.app/ did not pass validation: code failed to satisfy specified code requirement(s)

According to Squirrel, the update bundle needs to match the DR of the target bundle, so we might need to actually sign Discord in moonlight's installer. Doing it from within core seems a bit precarious because macOS might not recognize the designated requirement changing on the fly like that.

packages/core/src/persist.ts Outdated Show resolved Hide resolved
packages/core/src/persist.ts Outdated Show resolved Hide resolved
packages/core/src/darwin.ts Outdated Show resolved Hide resolved
@slice slice force-pushed the skip/darwin-persist branch from 40593c9 to 8025fae Compare December 16, 2024 01:42
I missed that hookUpdaterForPersistence (FKA persistAsar) merely hooks
into the updater, so we need to run it inside of the hooked emit method.
Those are synchronous, so our shelling out to codesign(1) needs to be
synchronous, too. This blocks the updater. Not sure if this can somehow
be async instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants