From 17f6b96f8932c7cc791850a892e51da33a6af2a9 Mon Sep 17 00:00:00 2001 From: "Mars.P" Date: Mon, 27 Jul 2026 18:54:10 +0800 Subject: [PATCH] Fix macOS microphone release entitlement Add the hardened-runtime audio-input entitlement and gate both CI configuration and the final release DMG so macOS cannot silently deny microphone access again. --- .github/workflows/ci.yml | 4 +++ .github/workflows/release.yml | 10 ++++++ app/src-tauri/Entitlements.plist | 3 ++ scripts/verify-macos-entitlements.sh | 50 ++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100755 scripts/verify-macos-entitlements.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0a5de4..ef207a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,6 +125,10 @@ jobs: run: | npm ci npm run build + - name: Verify macOS entitlement config + run: | + test "$(plutil -extract 'com\.apple\.security\.device\.audio-input' raw app/src-tauri/Entitlements.plist)" = "true" + test "$(plutil -extract 'com\.apple\.security\.cs\.disable-library-validation' raw app/src-tauri/Entitlements.plist)" = "true" # Build sherpa in RELEASE so its prebuilt dylibs land in target/release — the exact path # tauri.conf.json's `bundle.macOS.frameworks` points at, which tauri_build validates at compile # time. The app itself still builds in debug (fast); only the dylib *location* needs release. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b1b175..63c009d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,6 +109,12 @@ jobs: working-directory: app run: npm ci + - name: Verify macOS entitlement config + if: runner.os == 'macOS' + run: | + test "$(plutil -extract 'com\.apple\.security\.device\.audio-input' raw app/src-tauri/Entitlements.plist)" = "true" + test "$(plutil -extract 'com\.apple\.security\.cs\.disable-library-validation' raw app/src-tauri/Entitlements.plist)" = "true" + # Stamp the version from the tag into the manifests so installer FILENAMES and the embedded # version (macOS About box, Windows MSI ProductVersion) match the release. The repo's checked-in # 0.1.0 stays as the working version; the tag is the source of truth at release time. @@ -145,3 +151,7 @@ jobs: releaseDraft: true prerelease: false args: ${{ matrix.args }} + + - name: Verify bundled macOS entitlements + if: runner.os == 'macOS' + run: ./scripts/verify-macos-entitlements.sh app/src-tauri/target/release/bundle/dmg/Wisp_*.dmg diff --git a/app/src-tauri/Entitlements.plist b/app/src-tauri/Entitlements.plist index bdcd6ae..c090c67 100644 --- a/app/src-tauri/Entitlements.plist +++ b/app/src-tauri/Entitlements.plist @@ -15,5 +15,8 @@ --> com.apple.security.cs.disable-library-validation + + com.apple.security.device.audio-input + diff --git a/scripts/verify-macos-entitlements.sh b/scripts/verify-macos-entitlements.sh new file mode 100755 index 0000000..5bbb028 --- /dev/null +++ b/scripts/verify-macos-entitlements.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 /path/to/Wisp.app-or.dmg" >&2 + exit 2 +fi + +artifact_path="$1" +mount_point="" + +cleanup() { + if [[ -n "$mount_point" ]]; then + hdiutil detach "$mount_point" >/dev/null + rmdir "$mount_point" + fi +} + +trap cleanup EXIT + +if [[ -d "$artifact_path" ]]; then + app_path="$artifact_path" +elif [[ -f "$artifact_path" && "$artifact_path" == *.dmg ]]; then + mount_point="$(mktemp -d "${TMPDIR:-/tmp}/wisp-dmg.XXXXXX")" + hdiutil attach -readonly -nobrowse -noautoopen -mountpoint "$mount_point" "$artifact_path" >/dev/null + app_path="$mount_point/Wisp.app" +else + echo "App bundle or DMG not found: $artifact_path" >&2 + exit 2 +fi + +if [[ ! -d "$app_path" ]]; then + echo "Wisp.app not found in artifact: $artifact_path" >&2 + exit 2 +fi + +entitlements="$(codesign -d --entitlements :- "$app_path" 2>/dev/null)" + +if ! microphone_access="$(printf '%s' "$entitlements" | plutil -extract 'com\.apple\.security\.device\.audio-input' raw - 2>/dev/null)" || [[ "$microphone_access" != "true" ]]; then + echo "Missing com.apple.security.device.audio-input entitlement: $app_path" >&2 + exit 1 +fi + +if ! library_validation="$(printf '%s' "$entitlements" | plutil -extract 'com\.apple\.security\.cs\.disable-library-validation' raw - 2>/dev/null)" || [[ "$library_validation" != "true" ]]; then + echo "Missing com.apple.security.cs.disable-library-validation entitlement: $app_path" >&2 + exit 1 +fi + +echo "Verified macOS audio-input and library-validation entitlements: $artifact_path"