123set -euo pipefail45die() {6 printf 'Portal signature verification failed: %s\n' "$*" >&27 exit 18}910usage() {11 printf 'Usage: %s --dmg /absolute/path/Portal-VERSION-arm64.dmg --expected-team-id TEAMID [--expected-version VERSION]\n' "$0"12}1314dmg=''15expected_version=''16expected_team_id=''17while (($#)); do18 case "$1" in19 --dmg) [[ $# -ge 2 ]] || die "$1 requires a value"; dmg="$2"; shift 2 ;;20 --expected-version) [[ $# -ge 2 ]] || die "$1 requires a value"; expected_version="$2"; shift 2 ;;21 --expected-team-id) [[ $# -ge 2 ]] || die "$1 requires a value"; expected_team_id="$2"; shift 2 ;;22 --help|-h) usage; exit 0 ;;23 *) die "unknown argument: $1" ;;24 esac25done2627[[ "$(uname -s)" == Darwin ]] || die 'verification must run on macOS'28[[ "$dmg" == /* && -f "$dmg" && ! -L "$dmg" ]] || die 'DMG must be an absolute regular file'29[[ "$expected_team_id" =~ ^[A-Z0-9]{10}$ ]] || die 'expected Apple TeamIdentifier is required'30for command_name in codesign find grep hdiutil lipo plutil sed spctl xcrun; do31 command -v "$command_name" >/dev/null 2>&1 || die "$command_name is required"32done3334temporary_base="${TMPDIR:-/tmp}"35verification_root="$(mktemp -d "${temporary_base%/}/goq-portal-verify.XXXXXX")"36mount_path="$verification_root/mount"37mkdir -m 0700 "$mount_path"38mounted=false39cleanup() {40 local status=$?41 trap - EXIT INT TERM HUP42 if $mounted; then43 hdiutil detach "$mount_path" -force >/dev/null 2>&1 || true44 fi45 rmdir "$mount_path" "$verification_root" >/dev/null 2>&1 || true46 exit "$status"47}48trap cleanup EXIT INT TERM HUP4950hdiutil verify "$dmg" >/dev/null51xcrun stapler validate "$dmg"52spctl --assess --type open --context context:primary-signature --verbose=4 "$dmg"53hdiutil attach "$dmg" -readonly -nobrowse -mountpoint "$mount_path" >/dev/null54mounted=true5556app_count="$(find "$mount_path" -maxdepth 2 -type d -name 'Portal.app' -print | wc -l | tr -d ' ')"57[[ "$app_count" == 1 ]] || die 'DMG must contain exactly one Portal.app'58app_path="$(find "$mount_path" -maxdepth 2 -type d -name 'Portal.app' -print)"59codesign --verify --deep --strict --verbose=4 "$app_path"60signature_details="$(codesign -d --verbose=4 "$app_path" 2>&1)"61grep -Eq '^Authority=Developer ID Application:' <<<"$signature_details" \62 || die 'application is not signed by a Developer ID Application identity'63team_identifier="$(sed -n 's/^TeamIdentifier=//p' <<<"$signature_details")"64[[ "$team_identifier" == "$expected_team_id" ]] \65 || die 'application TeamIdentifier does not match the pinned Portal signer'66grep -Eq '^flags=.*runtime' <<<"$signature_details" \67 || die 'application signature does not enable hardened runtime'68spctl --assess --type execute --verbose=4 "$app_path"69xcrun stapler validate "$app_path"7071identifier="$(plutil -extract CFBundleIdentifier raw "$app_path/Contents/Info.plist")"72[[ "$identifier" == sh.goq.portal ]] || die "unexpected bundle identifier: $identifier"73version="$(plutil -extract CFBundleShortVersionString raw "$app_path/Contents/Info.plist")"74[[ -z "$expected_version" || "$version" == "$expected_version" ]] \75 || die "app version $version does not match $expected_version"76executable="$app_path/Contents/MacOS/portal"77[[ -x "$executable" ]] || die 'Portal executable is missing'78[[ "$(lipo -archs "$executable")" == arm64 ]] || die 'Portal executable is not arm64-only'79if find "$app_path" -type f \( -name sigil -o -name sigil-host -o -name sigil-probe \80 -o -name host.toml -o -name '*.key' \) | grep -q .81then82 die 'Portal contains a forbidden host or credential artifact'83fi8485printf 'portal_signature_verification=ok\nportal_version=%s\nportal_architecture=arm64\n' "$version"