-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidate_ios_ipa.sh
More file actions
executable file
·149 lines (133 loc) · 4.92 KB
/
Copy pathvalidate_ios_ipa.sh
File metadata and controls
executable file
·149 lines (133 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env bash
# SPDX-License-Identifier: MIT
# Part of PyBLE (https://pyble.dev) — see /LICENSE.
#
# Validate the exported iOS payload, including the script-like Flutter asset
# classification that codesign --deep does not detect but App Store validation
# rejects as unsigned nested code.
set -euo pipefail
readonly REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
readonly IPA_PATH="${1:-$REPO_ROOT/app/build/ios/ipa/PyBLE.ipa}"
fail() {
printf 'validate_ios_ipa: %s\n' "$*" >&2
exit 1
}
[ -f "$IPA_PATH" ] || fail "IPA does not exist: $IPA_PATH"
command -v codesign >/dev/null 2>&1 || fail 'codesign is required'
command -v ditto >/dev/null 2>&1 || fail 'ditto is required'
command -v file >/dev/null 2>&1 || fail 'file is required'
command -v plutil >/dev/null 2>&1 || fail 'plutil is required'
command -v xcrun >/dev/null 2>&1 || fail 'xcrun is required'
SWINFO_PATH="$(xcrun --find swinfo 2>/dev/null)" ||
fail 'Xcode swinfo is unavailable'
readonly SWINFO_PATH
[ -x "$SWINFO_PATH" ] || fail "Xcode swinfo is unavailable: $SWINFO_PATH"
VERIFY_DIR="$(mktemp -d /tmp/pyble-ios-ipa.XXXXXX)"
readonly VERIFY_DIR
cleanup() {
if [ -n "${VERIFY_DIR:-}" ] && [ -d "$VERIFY_DIR" ]; then
rm -R "$VERIFY_DIR"
fi
}
trap cleanup EXIT
ditto -x -k "$IPA_PATH" "$VERIFY_DIR"
app_count="$(
find "$VERIFY_DIR/Payload" -mindepth 1 -maxdepth 1 -type d -name '*.app' |
wc -l |
tr -d '[:space:]'
)"
[ "$app_count" = '1' ] ||
fail "expected one application in Payload, found $app_count"
APP_PATH="$(find "$VERIFY_DIR/Payload" -mindepth 1 -maxdepth 1 -type d -name '*.app')"
readonly APP_PATH
readonly FLUTTER_ASSETS="$APP_PATH/Frameworks/App.framework/flutter_assets"
[ -d "$FLUTTER_ASSETS" ] ||
fail "Flutter assets are missing: $FLUTTER_ASSETS"
codesign --verify --deep --strict --verbose=4 "$APP_PATH"
script_like_files=0
while IFS= read -r -d '' file_path; do
file_kind="$(file -b "$file_path")"
lowercase_kind="$(printf '%s' "$file_kind" | tr '[:upper:]' '[:lower:]')"
case "$lowercase_kind" in
*script*executable*)
printf 'validate_ios_ipa: unsigned script-like file: %s (%s)\n' \
"${file_path#"$APP_PATH"/}" \
"$file_kind" >&2
script_like_files=$((script_like_files + 1))
;;
esac
done < <(find "$APP_PATH" -type f -print0)
[ "$script_like_files" = '0' ] ||
fail "$script_like_files file(s) would be treated as unsigned nested code"
invalid_executables=0
while IFS= read -r -d '' file_path; do
file_kind="$(file -b "$file_path")"
case "$file_kind" in
Mach-O*)
;;
*)
printf 'validate_ios_ipa: non-Mach-O file has executable mode: %s (%s)\n' \
"${file_path#"$APP_PATH"/}" \
"$file_kind" >&2
invalid_executables=$((invalid_executables + 1))
;;
esac
done < <(find "$APP_PATH" -type f -perm +111 -print0)
[ "$invalid_executables" = '0' ] ||
fail "$invalid_executables non-Mach-O file(s) have executable mode"
readonly SWINFO_PLIST="$VERIFY_DIR/swinfo.plist"
readonly SWINFO_ERRORS="$VERIFY_DIR/swinfo-errors.json"
if ! "$SWINFO_PATH" -f "$IPA_PATH" -prettyprint false \
>"$SWINFO_PLIST" 2>"$VERIFY_DIR/swinfo.stderr"; then
cat "$VERIFY_DIR/swinfo.stderr" >&2
fail 'Xcode swinfo could not analyze the IPA'
fi
if plutil -extract product-errors json -o "$SWINFO_ERRORS" \
"$SWINFO_PLIST" 2>/dev/null; then
swinfo_error_count="$(
plutil -extract product-errors raw -o - "$SWINFO_PLIST"
)"
unexpected_swinfo_errors=0
ignored_resource_bundles=0
swinfo_error_index=0
while [ "$swinfo_error_index" -lt "$swinfo_error_count" ]; do
swinfo_error_path="$(
plutil -extract "product-errors.$swinfo_error_index.path" raw -o - \
"$SWINFO_PLIST"
)"
swinfo_error_code="$(
plutil -extract "product-errors.$swinfo_error_index.code-string" raw \
-o - "$SWINFO_PLIST"
)"
swinfo_error_status="$(
plutil -extract "product-errors.$swinfo_error_index.osstatus" raw -o - \
"$SWINFO_PLIST"
)"
case "$swinfo_error_path" in
Runner.app/*.bundle)
if [ "$swinfo_error_code" = \
'ITunesSoftwareServiceUnableToAnalyzeSigningInformation' ] &&
[ "$swinfo_error_status" = '-67062' ]; then
ignored_resource_bundles=$((ignored_resource_bundles + 1))
else
unexpected_swinfo_errors=$((unexpected_swinfo_errors + 1))
fi
;;
*)
unexpected_swinfo_errors=$((unexpected_swinfo_errors + 1))
;;
esac
swinfo_error_index=$((swinfo_error_index + 1))
done
if [ "$unexpected_swinfo_errors" != '0' ]; then
plutil -extract product-errors xml1 -o - "$SWINFO_PLIST" |
plutil -p - >&2
fail "Xcode swinfo reported $unexpected_swinfo_errors unexpected error(s)"
fi
if [ "$ignored_resource_bundles" != '0' ]; then
printf \
'validate_ios_ipa: ignored %s expected unsigned resource-bundle record(s)\n' \
"$ignored_resource_bundles"
fi
fi
printf 'validate_ios_ipa: valid App Store payload: %s\n' "$IPA_PATH"