fix(macos/plist): add Bluetooth + privacy keys to prevent Gmeet sign-in crash (#1288)#1297
Conversation
…eet sign-in crash (tinyhumansai#1288) Embedded Chromium probes CoreBluetooth via WebAuthn caBLE / hybrid transport when Google sign-in offers cross-device passkey authentication (phone-as- authenticator). macOS TCC enforces hard SIGABRT (not graceful denial) when a non-sandboxed Hardened Runtime app accesses a privacy-sensitive resource without the matching usage-description key. The same Bluetooth probe is also reachable via getUserMedia() / enumerateDevices() audio peripheral listing on later macOS versions, so the description covers both surfaces. Adding NSBluetoothAlwaysUsageDescription so the first probe prompts the user instead of crashing the production app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ew safety (tinyhumansai#1288) Audit revealed six sibling privacy keys exposed to the same SIGABRT class as NSBluetoothAlwaysUsageDescription: any embedded webview probe (Gmeet/Slack/ Discord/Telegram) into Location, Documents/Downloads/Desktop folders, Contacts, or Calendars on macOS 11+ would crash a non-sandboxed Hardened Runtime app without the matching usage-description key. macOS only renders the system prompt when the resource is actually probed, so adding the keys does NOT pre-emptively annoy users — the prompt path exists only when a webview triggers it. Keys added: - NSLocationWhenInUseUsageDescription - NSDocumentsFolderUsageDescription - NSDownloadsFolderUsageDescription - NSDesktopFolderUsageDescription - NSContactsUsageDescription - NSCalendarsUsageDescription Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dened Runtime (tinyhumansai#1288) Belt-and-braces companion to NSBluetoothAlwaysUsageDescription in Info.plist. Harmless under non-sandboxed Hardened Runtime (the current bundle config), future-safe if Apple tightens the sandbox model in a later macOS release. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#1288) Vitest unit test reads `app/src-tauri/Info.plist`, parses XML via DOMParser, asserts all 10 required NS*UsageDescription keys exist with non-empty descriptions ≥ 30 chars (cheap placeholder check). Catches accidental removal of any privacy key in future PRs — would have caught tinyhumansai#1288 had it been in place pre-incident. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…yhumansai#1288) Add three new line items under the macOS section covering the privacy prompts gated on the keys added in tinyhumansai#1288: - Bluetooth prompt on first Gmeet sign-in or audio device enumeration (regression watch — hard fail mode is SIGABRT before prompt renders). - Location no-crash invariant on Gmeet room-finder probe. - File picker no-crash invariant for Documents/Downloads/Desktop. These line items make the issue's "Regression safety" acceptance criterion concrete in the release-cut checklist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds missing macOS privacy usage descriptions (Bluetooth, Location, file access, Contacts, Calendars) to the app's Info.plist, enables Bluetooth device entitlement, and introduces validation tests to prevent regression of missing privacy declarations. ChangesmacOS Privacy Permissions & Validation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
@0xpremgs — your repro on macOS 12.7.6 surfaced this. Local smoke on my machine couldn't deterministically reproduce the crash because Google offered local TouchID/platform authenticator instead of cross-device passkey, so the BLE probe path didn't fire during my sign-in. Could you install the build from this PR and re-run the original Gmeet sign-in on the same Monterey machine? Specifically:
Confirming the prompt copy renders + no crash closes the regression-safety acceptance criterion. Thanks! |
|
Smoke pass — fix verified end-to-end on the same machine that just reproduced the crash. Crash (prod v0.53.17, before fix)Triggered Google sign-in inside Gmeet, picked cross-device passkey path. Hard SIGABRT, exact crash text from the macOS report:
Faulting stack: Called from Environment: macOS 26.2 (25C56), Apple M5 (Mac17,2), bundle id After PR build (this branch, same machine)Replaced Result: macOS rendered the standard TCC consent dialog instead of SIGABRT.
This closes the regression-safety acceptance criterion and confirms the user-facing copy renders correctly. (Screenshot of the prompt available locally; happy to drop it inline in the GitHub web UI if helpful.) |
Summary
NSBluetoothAlwaysUsageDescriptionto the macOS bundleInfo.plistso embedded Chromium's WebAuthn caBLE / hybrid-transport probe (andenumerateDevices()Bluetooth audio listing) no longer SIGABRTs the production app on Google Meet sign-in.com.apple.security.device.bluetoothentitlement under the Hardened Runtime as belt-and-braces.NS*UsageDescriptionkeys are present with non-empty descriptions — catches accidental removal.docs/RELEASE-MANUAL-SMOKE.mdwith three macOS line items covering the new privacy prompts.Problem
Production OpenHuman desktop app on macOS 12.7.6 crashes on Google Meet web login (#1288). The crash report names the missing key:
Root cause: macOS TCC enforces a hard
SIGABRT(not graceful denial) when a non-sandboxed Hardened Runtime app probes a privacy-sensitive resource without the matchingNS*UsageDescriptionkey. Embedded Chromium walks CoreBluetooth via the WebAuthn caBLE/hybrid transport when Google sign-in offers cross-device passkey authentication ("use your phone as an authenticator"). The same probe is also reachable viagetUserMedia()/enumerateDevices()audio peripheral listing on later macOS versions, so the description covers both surfaces.Audit revealed six sibling privacy keys exposed to the same crash class — any embedded webview probe (Gmeet/Slack/Discord/Telegram) into Location, Documents/Downloads/Desktop folders, Contacts, or Calendars on macOS 11+ would SIGABRT a non-sandboxed Hardened Runtime app without the matching key. Fixing them in one PR prevents next-week QA crashes on a sibling key.
Solution
app/src-tauri/Info.plistnow declares 10 privacy keys (was 3):NSMicrophoneUsageDescriptionNSCameraUsageDescriptionNSAppleEventsUsageDescriptionNSBluetoothAlwaysUsageDescriptionNSLocationWhenInUseUsageDescriptionNSDocumentsFolderUsageDescriptionNSDownloadsFolderUsageDescriptionNSDesktopFolderUsageDescriptionNSContactsUsageDescriptionNSCalendarsUsageDescriptionapp/src-tauri/entitlements.sidecar.plistaddscom.apple.security.device.bluetooth(harmless under non-sandboxed Hardened Runtime, future-safe if Apple tightens the sandbox model).app/test/info-plist-required-keys.test.ts(new): readsapp/src-tauri/Info.plist, parses XML viaDOMParser, asserts the 10 required keys exist with non-empty descriptions ≥ 30 chars (cheap placeholder check). Would have caught #1288 had it been in place pre-incident.docs/RELEASE-MANUAL-SMOKE.md: three new macOS line items — Bluetooth prompt on first Gmeet sign-in (regression watch — hard fail mode is SIGABRT), Location no-crash invariant on Gmeet room-finder probe, file picker no-crash invariant for Documents/Downloads/Desktop.Note on copy: macOS only fires the system prompt when the resource is actually probed — adding the keys does NOT pre-emptively annoy users.
Verification
plutil -lintboth plists OK.APPLE_SIGNING_IDENTITY="OpenHuman Dev Signer"(perfeedback_macos_release_build_signing.md). Bundle confirms:flags=0x10000(runtime)— Hardened Runtime activeAuthority=OpenHuman Dev SignerNS*UsageDescriptionkeys embedded in bundledInfo.plistcom.apple.security.device.bluetoothentitlement embeddedTermination Reason: Namespace TCC, Code 0with the exact missing-key error text; faulting stack pinned to+[IOBluetoothDevice pairedDevices]→-[IOBluetoothCoreBluetoothCoordinator init]from the CEF WebAuthn caBLE / passkey path during Google sign-in. Bug class is NOT Monterey-only — current macOS still hard-crashes on missing key./Applications/OpenHuman.app, BLE probe fired the proper TCC consent dialog instead of SIGABRT. Prompt rendered with title"OpenHuman" would like to use Bluetooth.and the body copy from the newNSBluetoothAlwaysUsageDescriptionstring verbatim. Allow / Don't Allow both complete normally — no crash.Submission Checklist
docs/TESTING-STRATEGY.mdlcovdoes not instrument plist/markdown.TEST-COVERAGE-MATRIX.md.## Related— no matrix row touched.docs/TESTING-STRATEGY.md)docs/RELEASE-MANUAL-SMOKE.md)Closes #NNNin the## RelatedsectionImpact
NS*UsageDescriptionkeys — no behavioural change on those platforms..appat build time, no Rust/TS code path changes. macOS only renders prompts when a resource is probed, so users see no surprise dialogs unless an embedded webview triggers the corresponding probe.Related
NSAppleEventsUsageDescription+com.apple.security.automation.apple-events) — same Hardened Runtime / TCC crash class, different resource.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
fix/1288-macos-bluetooth-privacy-keysSummary by CodeRabbit
New Features
Tests
Documentation