fix(desktop): declare macOS Contacts and Apple Events usage strings

Signed, hardened-runtime builds without an NSContactsUsageDescription /
NSAppleEventsUsageDescription in mac.extendInfo are denied Contacts and
Apple Events access by TCC without ever showing a prompt — macOS requires
the usage string before it will even ask (#59482). Declare both next to
the sibling mic/camera/calendar strings, with a packaging test that holds
the contract at the config seam so the strings can't be lost in the next
config move.

Fixes #59482

Co-authored-by: Shashwat Gokhe <shashwatgokhe2@gmail.com>
This commit is contained in:
Hermes Agent
2026-09-25 10:56:24 -05:00
committed by brooklyn!
parent 85c7e87fc7
commit 58497395d3
2 changed files with 34 additions and 1 deletions

View File

@@ -163,7 +163,9 @@ module.exports = {
NSRemindersFullAccessUsageDescription: `${displayName} needs full access to Reminders to read and manage reminders when explicitly requested.`,
NSScreenCaptureUsageDescription: `${displayName} captures the screen when you ask the agent to screenshot or record it.`,
NSLocalNetworkUsageDescription: `${displayName} connects to devices on your local network when a plugin or feature you enable requests it.`,
NSAppleMusicUsageDescription: `${displayName} accesses your music library when a plugin or feature you enable requests it.`
NSAppleMusicUsageDescription: `${displayName} accesses your music library when a plugin or feature you enable requests it.`,
NSContactsUsageDescription: `${displayName} uses Contacts access when you ask it to read or update your address book.`,
NSAppleEventsUsageDescription: `${displayName} uses Apple Events to automate apps you explicitly ask it to control.`
},
target: ['dmg', 'zip'],
sign: createMacSigner({

View File

@@ -0,0 +1,31 @@
// TCC usage strings live in electron-builder.config.cjs's mac.extendInfo —
// without them a hardened-runtime (signed) build is denied Contacts / Apple
// Events access silently: macOS requires the usage description BEFORE it will
// even show the permission prompt (#59482). These tests hold that contract at
// the packaging seam so the strings can't silently disappear in a config move
// again (they were lost once already when the builder config moved out of
// package.json).
import assert from 'node:assert/strict'
import { createRequire } from 'node:module'
import { test } from 'vitest'
const require: NodeJS.Require = createRequire(import.meta.url)
const MAC_USAGE_STRINGS: readonly [string, string][] = [
['NSContactsUsageDescription', 'Contacts'],
['NSAppleEventsUsageDescription', 'Apple Events']
]
test('mac extendInfo declares a usage string for every TCC-gated desktop service', () => {
const config = require('../electron-builder.config.cjs')
const extendInfo = config?.mac?.extendInfo
assert.ok(extendInfo, 'electron-builder.config.cjs must define mac.extendInfo')
for (const [key, service] of MAC_USAGE_STRINGS) {
const value = extendInfo[key]
assert.ok(typeof value === 'string' && value.trim().length > 0, `mac.extendInfo.${key} must be a non-empty usage string; without it macOS denies ${service} access without showing a TCC prompt (#59482)`)
}
})