Repository navigation
Refuse a macOS bundle that is not sealed around its sidecars - #31
Conversation
The linker ad-hoc signs the executable on its own, which is not the same as sealing the bundle around it. Without a _CodeSignature covering the sidecars, macOS calls the app damaged and offers only the Trash — and nothing here opens the app, so that stays invisible until somebody downloads it. The sidecars are found rather than looked for at a fixed path: where Tauri puts an externalBin is Tauri's business, and a check that hard-codes it fails on a bundle that is fine. Tried against a bundle built for the purpose. It passes a sealed one, and fails each of: a sidecar outside the bundle, a sidecar replaced after signing, an unsigned executable added after signing, and a sidecar whose own signature has been stripped.
|
Warning Review limit reached
Next review available in: 90 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Two ways the first version could fail a build that was fine. It searched all of target, taking whichever Transcrate.app find reached first — so a stray one anywhere under there decided what got checked. It searches the folder the upload step already reads. It piped find into head, and under pipefail head leaving early sends find SIGPIPE and fails the step. That needs more output than a pipe buffer holds, which this search would not produce; the reason it would not is a count nobody is watching. `-print -quit` stops at the first match without a pipe. Both were found by running the step rather than by reading it. The SIGPIPE case reproduces at two thousand matches, exit 141 on a bundle that is sealed. Tried again against a bundle built for the purpose: it passes a sealed one, ignores a same-named folder elsewhere in target, and fails each of a missing app, a sidecar outside the bundle, a sidecar replaced after signing, an unsigned executable added after signing, and a sidecar whose own signature has been stripped.
The check accepted ffmpeg anywhere inside the bundle. The app does not: it resolves a tool against the directory holding the running executable and otherwise falls back to a bare name on the PATH, silently, on a machine that has no ffmpeg. A copy sealed under Contents/Resources passed — signed, verified, and never opened by the app that shipped with it. The reasoning behind the looser check was that where Tauri puts an externalBin is Tauri's business. It is not: tools::locate states where the tool has to be, so that is the assumption worth holding the build to. Contents/MacOS is that directory by the bundle format rather than by Tauri, so naming it is not a guess. When it fires it says where the file was found instead, which is the question anybody reading the failure will have. Tried against a bundle built for the purpose, under /bin/bash 3.2 as the runner has. It passes a correct bundle, and fails a sidecar sealed under Resources, a missing app, a sidecar outside the bundle, one replaced after signing, an unsigned executable added after signing, and one whose own signature has been stripped.
One step in the release workflow, on the job that builds the macOS app.
What it catches
The linker ad-hoc signs the executable on its own, which is not the same as
sealing the bundle around it. Without a
_CodeSignaturecovering the sidecars,macOS calls the app damaged and offers only the Trash — not the "open anyway"
setting the release notice walks people through.
Nothing in this workflow opens the app. It builds it, uploads it, and publishes
it, so a bundle in that state reaches the releases page looking exactly like a
good one and stays there until somebody downloads it and finds out.
How
The sidecars are required beside the executable, in
Contents/MacOS, becausethat is the only place the app looks:
tools::locateresolves a tool againstthe directory holding the running binary and otherwise falls back to a bare
name on the PATH — silently, on a machine with no ffmpeg on it. That directory
is fixed by the bundle format rather than by Tauri, so naming it is not a
guess.
Then
codesign --verify --deep --strict:--deepreaches the nestedexecutables,
--strictrefuses a seal that does not match what is there.What the drafts got wrong
Three things, each found by running the step rather than reading it:
codesign --verboseoutput looking for the sidecar's path. Thatreads a format nobody promises to keep.
targetand took whicheverTranscrate.appcame first,and piped
findintohead, which underpipefailfails the step whenheadleaves early. That needs more output than a pipe buffer holds —reproduced at two thousand matches, exit 141 on a bundle that is sealed.
Contents/Resourcesis signed, verifies, and is never opened by the appshipped with it. The reasoning had been that where Tauri puts an
externalBinis Tauri's business; it is not, becausetools::locatestateswhere the tool has to be.
Tried
Against a bundle built for the purpose — a minimal
.appwithffmpegandffprobein it, signed ad-hoc the waytauri-actionsigns the real one, rununder
/bin/bash3.2 as the runner has.targetContents/Resourcesnot beside the executable, and says where it isno Transcrate.app was builtnot beside the executablenested code is modified or invalida sealed resource is missing or invalidThe first attempt at the "replaced after signing" row removed the sidecar and
put the same bytes back, which leaves the seal matching and passes correctly.
It was rewritten to break the bundle for real.
Where it came from
This was written before the review branch and left behind on a branch cut from
an older
main, where the surrounding job has since changed. It is reinstatedhere rather than rebased, because the check itself was worth rewriting.
Worth having in before the next release rather than after it.