#!/bin/bash # # notarize.sh # Zyquo Agent # # Author: Simon-Pierre Boucher # Mail: contact@spboucher.ai # # Developer ID signing, notarization, stapling, and verification for # Zyquo Agent. Identity, Team ID, and the notarytool keychain profile are the # same ones the Zyquo family already uses (see ~/Desktop/other/OTHER/zyquo-term). # # Usage: notarize.sh # notarize.sh dmg # # Signs nested code first, then the bundle, with the Hardened Runtime and a # secure timestamp; submits a ditto zip to Apple, waits, staples the ticket, # and fails loudly unless `spctl` reports "Notarized Developer ID". # # Entitlements posture (Resources/ZyquoAgent.entitlements): Hardened Runtime # ON, App Sandbox deliberately OFF (Zyquo Agent must spawn /bin/bash and # /usr/bin/osascript to do its job, like Terminal.app or the sibling Zyquo # Term); the only declared entitlement is # com.apple.security.automation.apple-events, paired with # NSAppleEventsUsageDescription in Info.plist, for AppleScript automation. # Keep that file pure ASCII with no XML comments: the AMFI parser codesign # uses rejects both. # set -euo pipefail APP_DIR="${1:?app bundle path required}" IDENTITY="${2:?signing identity required}" KEYCHAIN_PROFILE="${3:?notarytool keychain profile required}" ENTITLEMENTS="${4:?entitlements plist required}" MAKE_DMG="${5:-}" APP_NAME="$(basename "$APP_DIR" .app)" DIST="$(dirname "$APP_DIR")" EXEC_NAME="ZyquoAgent" ZIP_PATH="$DIST/${EXEC_NAME}.zip" DMG_PATH="$DIST/${EXEC_NAME}.dmg" [ -d "$APP_DIR" ] || { echo "ERROR: $APP_DIR missing — run make release/bundle first" >&2; exit 1; } [ -f "$ENTITLEMENTS" ] || { echo "ERROR: $ENTITLEMENTS missing" >&2; exit 1; } # ---------------------------------------------------------------- signing -- echo "=== Signing (Developer ID, hardened runtime) ===" # Nested code first (frameworks/bundles/helpers), then the main executable, # then the bundle itself — codesign requires inside-out order. while IFS= read -r -d '' nested; do echo " nested: $nested" codesign --force --options runtime --timestamp \ --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" "$nested" done < <(find "$APP_DIR/Contents" \ \( -name '*.framework' -o -name '*.dylib' -o -name '*.bundle' \) -print0 2>/dev/null) codesign --force --options runtime --timestamp \ --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" \ "$APP_DIR/Contents/MacOS/$EXEC_NAME" codesign --force --options runtime --timestamp \ --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" "$APP_DIR" echo "=== Verifying signature ===" codesign --verify --deep --strict --verbose=2 "$APP_DIR" codesign --display --entitlements - --verbose=2 "$APP_DIR" 2>&1 | sed -n '1,12p' # ------------------------------------------------------------ notarizing -- echo "=== Submitting to Apple notary service (profile: $KEYCHAIN_PROFILE) ===" rm -f "$ZIP_PATH" ditto -c -k --keepParent "$APP_DIR" "$ZIP_PATH" xcrun notarytool submit "$ZIP_PATH" --keychain-profile "$KEYCHAIN_PROFILE" --wait echo "=== Stapling ===" xcrun stapler staple "$APP_DIR" xcrun stapler validate "$APP_DIR" echo "=== Gatekeeper assessment ===" assessment="$(spctl -a -vv "$APP_DIR" 2>&1)" echo "$assessment" case "$assessment" in *"source=Notarized Developer ID"*) echo "✅ spctl: accepted, source=Notarized Developer ID" ;; *) echo "❌ spctl did not report a notarized Developer ID signature" >&2 exit 1 ;; esac # Re-zip the stapled bundle so the distributed archive carries the ticket. rm -f "$ZIP_PATH" ditto -c -k --keepParent "$APP_DIR" "$ZIP_PATH" echo "Archive: $ZIP_PATH" # ------------------------------------------------------------- optional dmg -- if [ "$MAKE_DMG" = "dmg" ]; then echo "=== Building signed + notarized DMG ===" rm -f "$DMG_PATH" hdiutil create -volname "$APP_NAME" -srcfolder "$APP_DIR" \ -ov -format UDZO "$DMG_PATH" codesign --force --timestamp --sign "$IDENTITY" "$DMG_PATH" xcrun notarytool submit "$DMG_PATH" --keychain-profile "$KEYCHAIN_PROFILE" --wait xcrun stapler staple "$DMG_PATH" spctl -a -vv -t open --context context:primary-signature "$DMG_PATH" 2>&1 | tail -3 echo "DMG: $DMG_PATH" fi echo "=== Done: $APP_DIR is signed, notarized, and stapled ==="