@hasna/recordings 0.2.10 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +350 -35
  2. package/bun.lock +3 -0
  3. package/dist/__tests__/helpers/native-fs-guard.d.ts +2 -0
  4. package/dist/__tests__/helpers/native-fs-guard.d.ts.map +1 -0
  5. package/dist/cli/index.js +1728 -320
  6. package/dist/cli/macos-permissions.d.ts +13 -0
  7. package/dist/cli/macos-permissions.d.ts.map +1 -0
  8. package/dist/cli/options.d.ts +12 -0
  9. package/dist/cli/options.d.ts.map +1 -1
  10. package/dist/db/pg-migrations.d.ts.map +1 -1
  11. package/dist/db/recordings.d.ts +2 -1
  12. package/dist/db/recordings.d.ts.map +1 -1
  13. package/dist/db/remote-storage.d.ts +5 -1
  14. package/dist/db/remote-storage.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +628 -147
  18. package/dist/lib/bun-runtime.d.ts +11 -0
  19. package/dist/lib/bun-runtime.d.ts.map +1 -0
  20. package/dist/lib/config.d.ts +8 -1
  21. package/dist/lib/config.d.ts.map +1 -1
  22. package/dist/lib/enhancer.d.ts +10 -2
  23. package/dist/lib/enhancer.d.ts.map +1 -1
  24. package/dist/lib/install-maintenance.d.ts +24 -0
  25. package/dist/lib/install-maintenance.d.ts.map +1 -0
  26. package/dist/lib/machine.d.ts +2 -0
  27. package/dist/lib/machine.d.ts.map +1 -0
  28. package/dist/lib/recorder.d.ts +2 -1
  29. package/dist/lib/recorder.d.ts.map +1 -1
  30. package/dist/lib/recording-create-identity.d.ts +13 -0
  31. package/dist/lib/recording-create-identity.d.ts.map +1 -0
  32. package/dist/lib/release-install-policy.d.ts +31 -0
  33. package/dist/lib/release-install-policy.d.ts.map +1 -0
  34. package/dist/lib/transcriber.d.ts.map +1 -1
  35. package/dist/mcp/index.d.ts.map +1 -1
  36. package/dist/mcp/index.js +977 -194
  37. package/dist/sdk/index.js +5 -1
  38. package/dist/sdk/v1.generated.d.ts +4 -0
  39. package/dist/sdk/v1.generated.d.ts.map +1 -1
  40. package/dist/server/cloud-config.d.ts +9 -0
  41. package/dist/server/cloud-config.d.ts.map +1 -0
  42. package/dist/server/cloud-readiness.d.ts +10 -0
  43. package/dist/server/cloud-readiness.d.ts.map +1 -0
  44. package/dist/server/cloud.d.ts +9 -18
  45. package/dist/server/cloud.d.ts.map +1 -1
  46. package/dist/server/index.js +1654 -304
  47. package/dist/server/migrate-command.d.ts +11 -0
  48. package/dist/server/migrate-command.d.ts.map +1 -0
  49. package/dist/server/openapi.d.ts +22 -0
  50. package/dist/server/openapi.d.ts.map +1 -1
  51. package/dist/server/repo.d.ts +8 -1
  52. package/dist/server/repo.d.ts.map +1 -1
  53. package/dist/server/serve.d.ts +6 -1
  54. package/dist/server/serve.d.ts.map +1 -1
  55. package/dist/server/v1.d.ts.map +1 -1
  56. package/dist/storage.js +515 -80
  57. package/dist/store.d.ts +3 -1
  58. package/dist/store.d.ts.map +1 -1
  59. package/dist/types/index.d.ts +10 -0
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/version.d.ts +1 -1
  62. package/package.json +7 -4
  63. package/packaging/macos/Empty.entitlements +5 -0
  64. package/packaging/macos/Library/LaunchDaemons/com.hasna.recordings.updater.plist +34 -0
  65. package/packaging/macos/Verifier.entitlements +10 -0
  66. package/packaging/macos/artifact-verifier.sb +33 -0
  67. package/packaging/macos/build_release_pkg.sh +872 -0
  68. package/packaging/macos/managed_bootstrap.sh +623 -0
  69. package/packaging/macos/pkgutil_fingerprint.awk +37 -0
  70. package/packaging/macos/release_lifecycle.ts +463 -0
  71. package/packaging/macos/scripts/postinstall +565 -0
  72. package/packaging/macos/scripts/preinstall +297 -0
  73. package/scripts/build_companion_cli.sh +337 -0
  74. package/scripts/build_native_fs_guard.sh +59 -0
  75. package/scripts/generate-sdk.ts +19 -1
  76. package/scripts/install_macos_app.sh +1922 -77
  77. package/scripts/macos_artifact.ts +5316 -0
  78. package/scripts/migrate.ts +38 -10
  79. package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
  80. package/scripts/native/recordings_fs_guard.c +1167 -0
  81. package/scripts/native_fs_guard.ts +158 -0
  82. package/scripts/release-guard.ts +20 -1
  83. package/scripts/resolve_tailscale_cli.sh +389 -0
  84. package/scripts/smoke_macos_app.sh +573 -0
  85. package/src/native/Recordings/App/ContentView.swift +83 -0
  86. package/src/native/Recordings/App/MenuBarStatusView.swift +102 -0
  87. package/src/native/Recordings/App/RecordWorkspaceView.swift +468 -0
  88. package/src/native/Recordings/App/RecordingDetailView.swift +138 -0
  89. package/src/native/Recordings/App/RecordingsApp.swift +367 -44
  90. package/src/native/Recordings/App/RecordingsListView.swift +130 -0
  91. package/src/native/Recordings/App/RecordingsStore.swift +206 -0
  92. package/src/native/Recordings/App/RuntimeSmoke.swift +158 -0
  93. package/src/native/Recordings/App/SidebarView.swift +212 -0
  94. package/src/native/Recordings/App/Theme.swift +87 -0
  95. package/src/native/Recordings/Package.resolved +5 -5
  96. package/src/native/Recordings/Package.swift +62 -2
  97. package/src/native/Recordings/RecordingsLib/AccessibilityPromptGate.swift +104 -0
  98. package/src/native/Recordings/RecordingsLib/BrandAssets.swift +0 -32
  99. package/src/native/Recordings/RecordingsLib/ChromeSurface.swift +17 -0
  100. package/src/native/Recordings/RecordingsLib/Info.plist +7 -3
  101. package/src/native/Recordings/RecordingsLib/MenuBarPresentation.swift +37 -0
  102. package/src/native/Recordings/RecordingsLib/NativeAppDiagnostics.swift +32 -2
  103. package/src/native/Recordings/RecordingsLib/NativeMachineIdentity.swift +24 -0
  104. package/src/native/Recordings/RecordingsLib/NativePCMRecorder.swift +262 -23
  105. package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +56 -0
  106. package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +56 -0
  107. package/src/native/Recordings/RecordingsLib/ProjectStore.swift +321 -25
  108. package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +605 -90
  109. package/src/native/Recordings/RecordingsLib/Recording.swift +141 -0
  110. package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +3704 -376
  111. package/src/native/Recordings/RecordingsLib/RecordingStartControlPresentation.swift +29 -0
  112. package/src/native/Recordings/RecordingsLib/RecordingsCLI.entitlements +10 -0
  113. package/src/native/Recordings/RecordingsLib/RecordingsCLI.swift +222 -0
  114. package/src/native/Recordings/RecordingsLib/SettingsView.swift +45 -11
  115. package/src/native/Recordings/RecordingsLib/SpeechIntent.swift +331 -0
  116. package/src/native/Recordings/RecordingsLib/SpeechIntentClassifier.swift +232 -0
  117. package/src/native/Recordings/RecordingsLib/VoiceShortcuts.swift +17 -7
  118. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +1343 -0
  119. package/src/native/Recordings/RecordingsTests/MenuBarPresentationTests.swift +98 -0
  120. package/src/native/Recordings/RecordingsTests/NativeAppDiagnosticsTests.swift +20 -0
  121. package/src/native/Recordings/RecordingsTests/NativePCMRecorderTests.swift +370 -1
  122. package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +987 -1
  123. package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +385 -0
  124. package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +835 -8
  125. package/src/native/Recordings/RecordingsTests/RecordingBridgeTests.swift +180 -0
  126. package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +760 -0
  127. package/src/native/Recordings/RecordingsTests/RecordingStartControlPresentationTests.swift +42 -0
  128. package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +264 -0
  129. package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +129 -0
  130. package/src/native/Recordings/RecordingsTests/SpeechIntentTests.swift +600 -0
  131. package/src/native/Recordings/RecordingsTests/TranscriptResolutionTests.swift +62 -1
  132. package/src/native/Recordings/RecordingsTests/TranscriptionResultIdentityTests.swift +21 -0
  133. package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +739 -0
  134. package/src/native/Recordings/Updater/Broker/ActivationRecoveryPolicy.swift +254 -0
  135. package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +463 -0
  136. package/src/native/Recordings/Updater/Broker/ApplicationProcessQuiescence.swift +55 -0
  137. package/src/native/Recordings/Updater/Broker/ArtifactIngest.swift +341 -0
  138. package/src/native/Recordings/Updater/Broker/AtomicActivation.swift +477 -0
  139. package/src/native/Recordings/Updater/Broker/BrokerMain.swift +624 -0
  140. package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +63 -0
  141. package/src/native/Recordings/Updater/Broker/CodeValidation.swift +561 -0
  142. package/src/native/Recordings/Updater/Broker/DarwinACLValidator.swift +66 -0
  143. package/src/native/Recordings/Updater/Broker/HostOSProductVersion.swift +50 -0
  144. package/src/native/Recordings/Updater/Broker/InstallJournal.swift +362 -0
  145. package/src/native/Recordings/Updater/Broker/InstallRecovery.swift +662 -0
  146. package/src/native/Recordings/Updater/Broker/MonotonicState.swift +456 -0
  147. package/src/native/Recordings/Updater/Broker/PeerIdentity.swift +274 -0
  148. package/src/native/Recordings/Updater/Broker/VerifierRunner.swift +81 -0
  149. package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +259 -0
  150. package/src/native/Recordings/Updater/BrokerTests/CanonicalReleaseOrderTests.swift +21 -0
  151. package/src/native/Recordings/Updater/Client/ClientMain.swift +119 -0
  152. package/src/native/Recordings/Updater/Protocol/BoundedProcess.swift +159 -0
  153. package/src/native/Recordings/Updater/Protocol/CandidateMetadataPolicy.swift +214 -0
  154. package/src/native/Recordings/Updater/Protocol/HostOSVersionPolicy.swift +55 -0
  155. package/src/native/Recordings/Updater/Protocol/MonotonicReleasePolicy.swift +152 -0
  156. package/src/native/Recordings/Updater/Protocol/ReleaseEnvelope.swift +229 -0
  157. package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +119 -0
  158. package/src/native/Recordings/Updater/ProtocolTests/BoundedProcessRunnerTests.swift +60 -0
  159. package/src/native/Recordings/Updater/ProtocolTests/CandidateMetadataPolicyTests.swift +168 -0
  160. package/src/native/Recordings/Updater/ProtocolTests/HostOSVersionPolicyTests.swift +62 -0
  161. package/src/native/Recordings/Updater/ProtocolTests/MonotonicReleasePolicyTests.swift +172 -0
  162. package/src/native/Recordings/Updater/ProtocolTests/ReleaseEnvelopeValidationTests.swift +65 -0
  163. package/src/native/Recordings/Updater/Signer/SignerMain.swift +210 -0
  164. package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +725 -0
  165. package/src/native/Recordings/Updater/VerifierLauncher/include/RecordingsVerifierLauncher.h +33 -0
  166. package/src/native/Recordings/build.sh +1990 -29
  167. package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +0 -380
@@ -0,0 +1,623 @@
1
+ #!/bin/bash
2
+ # Noninteractive MDM/base-image entry point for bootstrap and exact crash recovery.
3
+
4
+ set -euo pipefail
5
+ umask 077
6
+ export PATH=/usr/bin:/bin:/usr/sbin:/sbin
7
+ export LC_ALL=C
8
+ export LANG=C
9
+ export TZ=UTC0
10
+
11
+ PKG=""
12
+ MANIFEST=""
13
+ ENVELOPE=""
14
+ EXPECTED_PACKAGE_SHA256=""
15
+ EXPECTED_INSTALLER_TEAM_ID=""
16
+ EXPECTED_INSTALLER_CERTIFICATE_SHA256=""
17
+ while [ "$#" -gt 0 ]; do
18
+ case "$1" in
19
+ --artifact) PKG="${2:-}"; shift 2 ;;
20
+ --manifest) MANIFEST="${2:-}"; shift 2 ;;
21
+ --envelope) ENVELOPE="${2:-}"; shift 2 ;;
22
+ --expected-package-sha256) EXPECTED_PACKAGE_SHA256="${2:-}"; shift 2 ;;
23
+ --expected-installer-team-id) EXPECTED_INSTALLER_TEAM_ID="${2:-}"; shift 2 ;;
24
+ --expected-installer-certificate-sha256) EXPECTED_INSTALLER_CERTIFICATE_SHA256="${2:-}"; shift 2 ;;
25
+ *) echo "Unknown argument: $1" >&2; exit 2 ;;
26
+ esac
27
+ done
28
+
29
+ [[ "$EXPECTED_PACKAGE_SHA256" =~ ^[0-9a-f]{64}$ ]] || {
30
+ echo "Managed bootstrap requires an out-of-band pinned package SHA-256." >&2
31
+ exit 2
32
+ }
33
+ [[ "$EXPECTED_INSTALLER_TEAM_ID" =~ ^[A-Z0-9]{10}$ ]] || {
34
+ echo "Managed bootstrap requires an exact out-of-band Installer Team ID." >&2
35
+ exit 2
36
+ }
37
+ [[ "$EXPECTED_INSTALLER_CERTIFICATE_SHA256" =~ ^[0-9a-f]{64}$ ]] || {
38
+ echo "Managed bootstrap requires an exact out-of-band Installer certificate SHA-256." >&2
39
+ exit 2
40
+ }
41
+ [ "$(/usr/bin/id -u)" = "0" ] || { echo "Managed bootstrap must run as root through MDM." >&2; exit 1; }
42
+
43
+ path_exists_or_symlink() {
44
+ [ -e "$1" ] || [ -L "$1" ]
45
+ }
46
+
47
+ require_no_extended_acl() {
48
+ local path="$1"
49
+ local acl_listing acl_status
50
+ acl_listing="$(/bin/ls -lade "$path")" || {
51
+ echo "Managed bootstrap could not inspect extended ACLs on: $path" >&2
52
+ exit 1
53
+ }
54
+ if /usr/bin/printf '%s\n' "$acl_listing" | /usr/bin/awk '
55
+ NR == 1 && length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
56
+ NR > 1 && $1 ~ /^[0-9]+:$/ { found = 1 }
57
+ END { exit found ? 0 : 1 }
58
+ '; then
59
+ echo "Managed bootstrap trust path has an unexpected extended ACL: $path" >&2
60
+ exit 1
61
+ else
62
+ acl_status=$?
63
+ [ "$acl_status" -eq 1 ] || {
64
+ echo "Managed bootstrap could not evaluate extended ACLs on: $path" >&2
65
+ exit 1
66
+ }
67
+ fi
68
+ }
69
+
70
+ require_tree_without_extended_acl() {
71
+ local tree="$1"
72
+ local acl_listing acl_status
73
+ require_no_extended_acl "$tree"
74
+ [ -d "$tree" ] || return 0
75
+ acl_listing="$(/bin/ls -laeR "$tree")" || {
76
+ echo "Managed bootstrap could not recursively inspect extended ACLs on: $tree" >&2
77
+ exit 1
78
+ }
79
+ if /usr/bin/printf '%s\n' "$acl_listing" | /usr/bin/awk '
80
+ length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
81
+ $1 ~ /^[0-9]+:$/ { found = 1 }
82
+ END { exit found ? 0 : 1 }
83
+ '; then
84
+ echo "Managed bootstrap tree has an unexpected extended ACL: $tree" >&2
85
+ exit 1
86
+ else
87
+ acl_status=$?
88
+ [ "$acl_status" -eq 1 ] || {
89
+ echo "Managed bootstrap could not evaluate recursive extended ACLs on: $tree" >&2
90
+ exit 1
91
+ }
92
+ fi
93
+ }
94
+
95
+ require_root_owned_nonwritable_directory() {
96
+ local directory="$1"
97
+ local mode
98
+ [ -d "$directory" ] && [ ! -L "$directory" ] && \
99
+ [ "$(/usr/bin/stat -f '%u' "$directory")" = 0 ] || {
100
+ echo "Managed bootstrap target ancestry is missing, linked, or not root-owned: $directory" >&2
101
+ exit 1
102
+ }
103
+ mode="$(/usr/bin/stat -f '%Lp' "$directory")"
104
+ [ $((8#$mode & 8#022)) -eq 0 ] || {
105
+ echo "Managed bootstrap target ancestry is group/other writable: $directory" >&2
106
+ exit 1
107
+ }
108
+ require_no_extended_acl "$directory"
109
+ }
110
+
111
+ # Standard macOS permits the admin group to install app bundles. Executable
112
+ # authority remains the pinned Developer ID/audit-token policy, so accept only
113
+ # that exact OS topology and reject every ACL or broader mode.
114
+ require_managed_applications_directory() {
115
+ local mode group_id
116
+ [ -d "/Applications" ] && [ ! -L "/Applications" ] && \
117
+ [ "$(/usr/bin/stat -f '%u' "/Applications")" = 0 ] || {
118
+ echo "Managed bootstrap requires a root-owned canonical /Applications directory." >&2
119
+ exit 1
120
+ }
121
+ require_no_extended_acl "/Applications"
122
+ mode="$(/usr/bin/stat -f '%Lp' "/Applications")"
123
+ group_id="$(/usr/bin/stat -f '%g' "/Applications")"
124
+ case "$mode:$group_id" in
125
+ 755:*|775:80) ;;
126
+ *)
127
+ echo "Managed bootstrap requires ACL-free /Applications mode 0755 or root:admin mode 0775." >&2
128
+ exit 1
129
+ ;;
130
+ esac
131
+ }
132
+
133
+ require_expected_var_link() {
134
+ local var_target
135
+ [ -L "/var" ] || {
136
+ echo "Managed bootstrap requires the canonical macOS /var system link." >&2
137
+ exit 1
138
+ }
139
+ var_target="$(/usr/bin/readlink "/var")" || {
140
+ echo "Managed bootstrap could not inspect the macOS /var system link." >&2
141
+ exit 1
142
+ }
143
+ [ "$var_target" = "private/var" ] && \
144
+ [ "$(cd "/var" && /bin/pwd -P)" = "/private/var" ] || {
145
+ echo "Managed bootstrap found a noncanonical macOS /var system link." >&2
146
+ exit 1
147
+ }
148
+ }
149
+
150
+ validate_target_ancestry() {
151
+ local required_target_ancestor optional_target_ancestor
152
+ require_expected_var_link
153
+ require_managed_applications_directory
154
+ for required_target_ancestor in \
155
+ "/" \
156
+ "/Library" \
157
+ "/Library/Application Support" \
158
+ "/Library/PrivilegedHelperTools" \
159
+ "/Library/LaunchDaemons" \
160
+ "/private" \
161
+ "/private/var" \
162
+ "/private/var/db"; do
163
+ require_root_owned_nonwritable_directory "$required_target_ancestor"
164
+ done
165
+ for optional_target_ancestor in \
166
+ "/Library/Application Support/Hasna" \
167
+ "/Library/Application Support/Hasna/Recordings"; do
168
+ if path_exists_or_symlink "$optional_target_ancestor"; then
169
+ require_root_owned_nonwritable_directory "$optional_target_ancestor"
170
+ fi
171
+ done
172
+ }
173
+
174
+ validate_target_ancestry
175
+
176
+ CLIENT="/Applications/Recordings.app/Contents/Helpers/recordings-update-client"
177
+ STATE="/var/db/com.hasna.recordings.updater/release-state.json"
178
+ AUTHORIZATION_ROOT="/private/var/db/com.hasna.recordings.bootstrap-authorization"
179
+ AUTHORIZATION_JOURNAL="${AUTHORIZATION_ROOT}/journal"
180
+ AUTHORIZATION_NEXT="${AUTHORIZATION_ROOT}/.journal.next"
181
+ COHORT_PATHS=(
182
+ "/Library/PrivilegedHelperTools/com.hasna.recordings.updater"
183
+ "/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier"
184
+ "/Library/LaunchDaemons/com.hasna.recordings.updater.plist"
185
+ "/Applications/Recordings.app"
186
+ "$CLIENT"
187
+ "/Library/Application Support/Hasna/Recordings/Trust"
188
+ "/Library/Application Support/Hasna/Recordings/Bootstrap"
189
+ "/Library/Application Support/Hasna/Recordings/Updates"
190
+ "/var/db/com.hasna.recordings.updater"
191
+ )
192
+
193
+ validate_managed_input() {
194
+ local input="$1"
195
+ local parent canonical_parent leaf expected relative current component mode
196
+ case "$input" in
197
+ /*) ;;
198
+ *) echo "Managed bootstrap inputs must be absolute." >&2; exit 2 ;;
199
+ esac
200
+ case "$input" in
201
+ *$'\n'*|*'//'*|*'/./'*|*'/../'*|*/.|*/..) echo "Managed bootstrap input path is not canonical." >&2; exit 1 ;;
202
+ esac
203
+ parent="$(/usr/bin/dirname "$input")"
204
+ leaf="$(/usr/bin/basename "$input")"
205
+ canonical_parent="$(cd "$parent" && /bin/pwd -P)" || {
206
+ echo "Managed bootstrap input parent is unavailable." >&2
207
+ exit 1
208
+ }
209
+ if [ "$canonical_parent" = / ]; then expected="/$leaf"; else expected="$canonical_parent/$leaf"; fi
210
+ [ "$input" = "$expected" ] || { echo "Managed bootstrap input path is not canonical." >&2; exit 1; }
211
+ require_root_owned_nonwritable_directory "/"
212
+ relative="${canonical_parent#/}"
213
+ current=""
214
+ IFS='/' read -r -a input_components <<<"$relative"
215
+ for component in "${input_components[@]}"; do
216
+ [ -n "$component" ] || continue
217
+ current="$current/$component"
218
+ require_root_owned_nonwritable_directory "$current"
219
+ done
220
+ [ -f "$input" ] && [ ! -L "$input" ] && [ "$(/usr/bin/stat -f '%u' "$input")" = 0 ] || {
221
+ echo "Managed bootstrap input is missing, linked, or not root-owned." >&2
222
+ exit 1
223
+ }
224
+ mode="$(/usr/bin/stat -f '%Lp' "$input")"
225
+ [ $((8#$mode & 8#022)) -eq 0 ] || {
226
+ echo "Managed bootstrap inputs must not be group/other writable." >&2
227
+ exit 1
228
+ }
229
+ require_no_extended_acl "$input"
230
+ }
231
+
232
+ for input in "$PKG" "$MANIFEST" "$ENVELOPE"; do validate_managed_input "$input"; done
233
+
234
+ SNAPSHOT_PARENT="/private/var/db"
235
+ require_root_owned_nonwritable_directory "$SNAPSHOT_PARENT"
236
+ WORK_DIR="$(/usr/bin/mktemp -d "$SNAPSHOT_PARENT/recordings-bootstrap.XXXXXX")"
237
+ /bin/chmod -N "$WORK_DIR"
238
+ /bin/chmod 0700 "$WORK_DIR"
239
+ require_no_extended_acl "$WORK_DIR"
240
+ cleanup() {
241
+ local status=$?
242
+ trap - EXIT
243
+ /bin/rm -rf "$WORK_DIR"
244
+ exit "$status"
245
+ }
246
+ trap cleanup EXIT
247
+
248
+ snapshot_input() {
249
+ local source="$1"
250
+ local target="$2"
251
+ local before after copied
252
+ before="$(/usr/bin/shasum -a 256 "$source" | /usr/bin/awk '{ print $1 }')"
253
+ /bin/cp "$source" "$target"
254
+ /bin/chmod -N "$target"
255
+ /bin/chmod 0400 "$target"
256
+ require_no_extended_acl "$target"
257
+ copied="$(/usr/bin/shasum -a 256 "$target" | /usr/bin/awk '{ print $1 }')"
258
+ after="$(/usr/bin/shasum -a 256 "$source" | /usr/bin/awk '{ print $1 }')"
259
+ [ "$before" = "$copied" ] && [ "$before" = "$after" ] || {
260
+ echo "Managed bootstrap input changed while taking a root-private snapshot." >&2
261
+ exit 1
262
+ }
263
+ }
264
+
265
+ SNAPSHOT_PKG="$WORK_DIR/bootstrap.pkg"
266
+ SNAPSHOT_MANIFEST="$WORK_DIR/manifest.json"
267
+ SNAPSHOT_ENVELOPE="$WORK_DIR/envelope.json"
268
+ snapshot_input "$PKG" "$SNAPSHOT_PKG"
269
+ snapshot_input "$MANIFEST" "$SNAPSHOT_MANIFEST"
270
+ snapshot_input "$ENVELOPE" "$SNAPSHOT_ENVELOPE"
271
+ ACTUAL_PKG_SHA256="$(/usr/bin/shasum -a 256 "$SNAPSHOT_PKG" | /usr/bin/awk '{ print $1 }')"
272
+ [ "$EXPECTED_PACKAGE_SHA256" = "$ACTUAL_PKG_SHA256" ] || {
273
+ echo "Supplied PKG does not match the management-pinned digest." >&2
274
+ exit 1
275
+ }
276
+
277
+ /usr/bin/env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin HOME=/var/empty TMPDIR="$WORK_DIR" \
278
+ /usr/sbin/spctl --assess --type install --verbose=2 "$SNAPSHOT_PKG"
279
+ PKG_SIGNATURE="$(/usr/sbin/pkgutil --check-signature "$SNAPSHOT_PKG")"
280
+ /usr/bin/grep -E "^[[:space:]]*1\\. Developer ID Installer: .+ \\(${EXPECTED_INSTALLER_TEAM_ID}\\)$" \
281
+ <<<"$PKG_SIGNATURE" >/dev/null
282
+ SCRIPT_DIR="$(cd "$(/usr/bin/dirname "$0")" && /bin/pwd -P)"
283
+ PKGUTIL_FINGERPRINT_PARSER="$SCRIPT_DIR/pkgutil_fingerprint.awk"
284
+ [ -f "$PKGUTIL_FINGERPRINT_PARSER" ] && [ ! -L "$PKGUTIL_FINGERPRINT_PARSER" ] || {
285
+ echo "Managed bootstrap fingerprint parser is missing or unsafe." >&2
286
+ exit 1
287
+ }
288
+ ACTUAL_INSTALLER_CERTIFICATE_SHA256="$(printf '%s\n' "$PKG_SIGNATURE" | \
289
+ /usr/bin/awk -f "$PKGUTIL_FINGERPRINT_PARSER")" || {
290
+ echo "Could not parse the Installer certificate SHA-256 fingerprint." >&2
291
+ exit 1
292
+ }
293
+ [ "$ACTUAL_INSTALLER_CERTIFICATE_SHA256" = "$EXPECTED_INSTALLER_CERTIFICATE_SHA256" ] || {
294
+ echo "Supplied PKG has the wrong Installer certificate fingerprint." >&2
295
+ exit 1
296
+ }
297
+
298
+ EXPANDED="$WORK_DIR/expanded"
299
+ /usr/bin/env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin HOME=/var/empty TMPDIR="$WORK_DIR" \
300
+ /usr/sbin/pkgutil --expand-full "$SNAPSHOT_PKG" "$EXPANDED"
301
+ /bin/chmod -RN "$EXPANDED"
302
+ require_tree_without_extended_acl "$EXPANDED"
303
+ PAYLOAD="$EXPANDED/Payload"
304
+ PREFLIGHT="$EXPANDED/Scripts/recordings-bootstrap-preflight"
305
+ EXPANDED_PREINSTALL="$EXPANDED/Scripts/preinstall"
306
+ EXPANDED_POSTINSTALL="$EXPANDED/Scripts/postinstall"
307
+ PAYLOAD_CLIENT="$PAYLOAD/Applications/Recordings.app/Contents/Helpers/recordings-update-client"
308
+ [ -d "$PAYLOAD" ] && [ ! -L "$PAYLOAD" ] && \
309
+ [ -x "$PREFLIGHT" ] && [ ! -L "$PREFLIGHT" ] && \
310
+ [ -x "$EXPANDED_PREINSTALL" ] && [ ! -L "$EXPANDED_PREINSTALL" ] && \
311
+ [ -x "$EXPANDED_POSTINSTALL" ] && [ ! -L "$EXPANDED_POSTINSTALL" ] && \
312
+ [ -x "$PAYLOAD_CLIENT" ] && [ ! -L "$PAYLOAD_CLIENT" ] || {
313
+ echo "Verified PKG does not contain the expected expanded payload and preflight verifier." >&2
314
+ exit 1
315
+ }
316
+
317
+ extract_package_bootstrap_id() {
318
+ local script="$1"
319
+ local identity
320
+ identity="$(/usr/bin/sed -n 's/^PACKAGE_BOOTSTRAP_ID="\([0-9a-f-]*\)"$/\1/p' "$script")"
321
+ [[ "$identity" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]] || {
322
+ echo "Verified PKG script does not carry one valid package bootstrap identity." >&2
323
+ exit 1
324
+ }
325
+ printf '%s\n' "$identity"
326
+ }
327
+
328
+ PREINSTALL_BOOTSTRAP_ID="$(extract_package_bootstrap_id "$EXPANDED_PREINSTALL")"
329
+ POSTINSTALL_BOOTSTRAP_ID="$(extract_package_bootstrap_id "$EXPANDED_POSTINSTALL")"
330
+ [ "$PREINSTALL_BOOTSTRAP_ID" = "$POSTINSTALL_BOOTSTRAP_ID" ] || {
331
+ echo "Verified PKG scripts carry conflicting bootstrap identities." >&2
332
+ exit 1
333
+ }
334
+ PACKAGE_BOOTSTRAP_ID="$PREINSTALL_BOOTSTRAP_ID"
335
+
336
+ verify_preflight_identity() {
337
+ local details authority team identifier timestamp flags
338
+ /usr/bin/codesign --verify --strict --all-architectures --verbose=2 "$PREFLIGHT"
339
+ details="$(/usr/bin/codesign -d --verbose=4 "$PREFLIGHT" 2>&1)"
340
+ authority="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^Authority=/ { print $2; exit }')"
341
+ team="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^TeamIdentifier=/ { print $2; exit }')"
342
+ identifier="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^Identifier=/ { print $2; exit }')"
343
+ timestamp="$(printf '%s\n' "$details" | /usr/bin/awk -F= '/^Timestamp=/ { print $2; exit }')"
344
+ flags="$(printf '%s\n' "$details" | /usr/bin/sed -n 's/^CodeDirectory .*flags=[^(]*(\([^)]*\)).*/\1/p' | /usr/bin/head -n 1)"
345
+ [[ "$authority" == "Developer ID Application:"* ]] && \
346
+ [ "$team" = "$EXPECTED_INSTALLER_TEAM_ID" ] && \
347
+ [ "$identifier" = "com.hasna.recordings.bootstrap-preflight" ] || {
348
+ echo "Bootstrap preflight verifier has the wrong signed identity." >&2
349
+ exit 1
350
+ }
351
+ case ",$flags," in *,runtime,*) ;; *) echo "Bootstrap preflight verifier lacks hardened runtime." >&2; exit 1 ;; esac
352
+ case "$timestamp" in ''|none|None|NONE) echo "Bootstrap preflight verifier lacks a trusted timestamp." >&2; exit 1 ;; esac
353
+ }
354
+ verify_preflight_identity
355
+
356
+ run_preflight() {
357
+ local require_committed="${1:-false}"
358
+ local args=(
359
+ --package "$SNAPSHOT_PKG"
360
+ --manifest "$SNAPSHOT_MANIFEST"
361
+ --envelope "$SNAPSHOT_ENVELOPE"
362
+ --payload-root "$PAYLOAD"
363
+ --expected-team-id "$EXPECTED_INSTALLER_TEAM_ID"
364
+ --installer-certificate-sha256 "$ACTUAL_INSTALLER_CERTIFICATE_SHA256"
365
+ )
366
+ if path_exists_or_symlink "$STATE"; then
367
+ [ -f "$STATE" ] && [ ! -L "$STATE" ] || {
368
+ echo "Installed release state is missing or unsafe." >&2
369
+ exit 1
370
+ }
371
+ require_no_extended_acl "$STATE"
372
+ args+=(--release-state "$STATE")
373
+ fi
374
+ if [ "$require_committed" = true ]; then args+=(--require-committed-state); fi
375
+ /usr/bin/env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin HOME=/var/empty TMPDIR="$WORK_DIR" \
376
+ LC_ALL=C LANG=C TZ=UTC0 "$PREFLIGHT" "${args[@]}"
377
+ }
378
+
379
+ read_journal_value() {
380
+ local key="$1"
381
+ /usr/bin/awk -F= -v key="$key" '
382
+ $1 == key { count += 1; value = substr($0, length(key) + 2) }
383
+ END { if (count != 1) exit 1; print value }
384
+ ' "$AUTHORIZATION_JOURNAL"
385
+ }
386
+
387
+ require_authorization_journal() {
388
+ path_exists_or_symlink "$AUTHORIZATION_ROOT" && \
389
+ [ -d "$AUTHORIZATION_ROOT" ] && [ ! -L "$AUTHORIZATION_ROOT" ] && \
390
+ [ "$(/usr/bin/stat -f '%u:%g:%Lp' "$AUTHORIZATION_ROOT")" = "0:0:700" ] || {
391
+ echo "Managed bootstrap authorization evidence is missing or unsafe." >&2
392
+ exit 1
393
+ }
394
+ [ -f "$AUTHORIZATION_JOURNAL" ] && [ ! -L "$AUTHORIZATION_JOURNAL" ] && \
395
+ [ "$(/usr/bin/stat -f '%u:%g:%Lp' "$AUTHORIZATION_JOURNAL")" = "0:0:400" ] && \
396
+ [ "$(/usr/bin/stat -f '%l' "$AUTHORIZATION_JOURNAL")" = "1" ] && \
397
+ [ "$(/usr/bin/wc -l <"$AUTHORIZATION_JOURNAL" | /usr/bin/tr -d ' ')" = "6" ] || {
398
+ echo "Managed bootstrap authorization journal is missing or unsafe." >&2
399
+ exit 1
400
+ }
401
+ require_no_extended_acl "$AUTHORIZATION_ROOT"
402
+ require_no_extended_acl "$AUTHORIZATION_JOURNAL"
403
+ [ "$(read_journal_value schema_version)" = "1" ] || { echo "Managed bootstrap authorization journal schema is invalid." >&2; exit 1; }
404
+ JOURNAL_PHASE="$(read_journal_value phase)"
405
+ JOURNAL_PACKAGE_SHA256="$(read_journal_value package_sha256)"
406
+ JOURNAL_PACKAGE_BOOTSTRAP_ID="$(read_journal_value package_bootstrap_id)"
407
+ JOURNAL_INSTALLER_TEAM_ID="$(read_journal_value installer_team_id)"
408
+ JOURNAL_INSTALLER_CERTIFICATE_SHA256="$(read_journal_value installer_certificate_sha256)"
409
+ case "$JOURNAL_PHASE" in authorized|installer-started) ;; *) echo "Managed bootstrap authorization journal phase is invalid." >&2; exit 1 ;; esac
410
+ [[ "$JOURNAL_PACKAGE_SHA256" =~ ^[0-9a-f]{64}$ ]] && \
411
+ [[ "$JOURNAL_PACKAGE_BOOTSTRAP_ID" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]] && \
412
+ [[ "$JOURNAL_INSTALLER_TEAM_ID" =~ ^[A-Z0-9]{10}$ ]] && \
413
+ [[ "$JOURNAL_INSTALLER_CERTIFICATE_SHA256" =~ ^[0-9a-f]{64}$ ]] || {
414
+ echo "Managed bootstrap authorization journal values are malformed." >&2
415
+ exit 1
416
+ }
417
+ [ "$JOURNAL_PACKAGE_SHA256" = "$ACTUAL_PKG_SHA256" ] && \
418
+ [ "$JOURNAL_PACKAGE_BOOTSTRAP_ID" = "$PACKAGE_BOOTSTRAP_ID" ] && \
419
+ [ "$JOURNAL_INSTALLER_TEAM_ID" = "$EXPECTED_INSTALLER_TEAM_ID" ] && \
420
+ [ "$JOURNAL_INSTALLER_CERTIFICATE_SHA256" = "$ACTUAL_INSTALLER_CERTIFICATE_SHA256" ] || {
421
+ echo "Managed bootstrap authorization journal belongs to a different authenticated package." >&2
422
+ exit 1
423
+ }
424
+ }
425
+
426
+ write_authorization_journal() {
427
+ local temporary_journal
428
+ ! path_exists_or_symlink "$AUTHORIZATION_ROOT" && ! path_exists_or_symlink "$AUTHORIZATION_JOURNAL" || {
429
+ echo "Managed bootstrap refuses to replace existing authorization evidence." >&2
430
+ exit 1
431
+ }
432
+ /bin/mkdir -m 0700 "$AUTHORIZATION_ROOT"
433
+ /usr/sbin/chown root:wheel "$AUTHORIZATION_ROOT"
434
+ /bin/chmod -N "$AUTHORIZATION_ROOT"
435
+ /bin/chmod 0700 "$AUTHORIZATION_ROOT"
436
+ temporary_journal="$(/usr/bin/mktemp "$AUTHORIZATION_ROOT/.journal.XXXXXX")"
437
+ /usr/bin/printf 'schema_version=1\nphase=authorized\npackage_sha256=%s\npackage_bootstrap_id=%s\ninstaller_team_id=%s\ninstaller_certificate_sha256=%s\n' \
438
+ "$ACTUAL_PKG_SHA256" "$PACKAGE_BOOTSTRAP_ID" "$EXPECTED_INSTALLER_TEAM_ID" \
439
+ "$ACTUAL_INSTALLER_CERTIFICATE_SHA256" >"$temporary_journal"
440
+ /usr/sbin/chown root:wheel "$temporary_journal"
441
+ /bin/chmod -N "$temporary_journal"
442
+ /bin/chmod 0400 "$temporary_journal"
443
+ require_no_extended_acl "$temporary_journal"
444
+ /bin/mv "$temporary_journal" "$AUTHORIZATION_JOURNAL"
445
+ /bin/sync
446
+ require_authorization_journal
447
+ }
448
+
449
+ remove_authorization_journal() {
450
+ require_authorization_journal
451
+ if path_exists_or_symlink "$AUTHORIZATION_NEXT"; then
452
+ [ -f "$AUTHORIZATION_NEXT" ] && [ ! -L "$AUTHORIZATION_NEXT" ] && \
453
+ [ "$(/usr/bin/stat -f '%u:%g' "$AUTHORIZATION_NEXT")" = "0:0" ] && \
454
+ [ "$(/usr/bin/stat -f '%l' "$AUTHORIZATION_NEXT")" = "1" ] || {
455
+ echo "Managed bootstrap authorization journal staging is unsafe." >&2
456
+ exit 1
457
+ }
458
+ case "$(/usr/bin/stat -f '%Lp' "$AUTHORIZATION_NEXT")" in 400|600) ;; *) echo "Managed bootstrap authorization journal staging mode is unsafe." >&2; exit 1 ;; esac
459
+ require_no_extended_acl "$AUTHORIZATION_NEXT"
460
+ /bin/rm -f "$AUTHORIZATION_NEXT"
461
+ fi
462
+ /bin/rm -f "$AUTHORIZATION_JOURNAL"
463
+ /bin/rmdir "$AUTHORIZATION_ROOT"
464
+ /bin/sync
465
+ }
466
+
467
+ require_safe_existing_cohort_path() {
468
+ local path="$1"
469
+ local expected_mode="$2"
470
+ local entry listing device mode owner links invalid=0
471
+ [ ! -L "$path" ] && [ "$(/usr/bin/stat -f '%u' "$path")" = "0" ] || {
472
+ echo "Managed bootstrap cohort evidence is linked or not root-owned: $path" >&2
473
+ exit 1
474
+ }
475
+ mode="$(/usr/bin/stat -f '%Lp' "$path")"
476
+ case "$expected_mode:$mode" in
477
+ "$mode:$mode"|700-or-755:700|700-or-755:755) ;;
478
+ *)
479
+ echo "Managed bootstrap cohort evidence has ownership/mode drift: $path" >&2
480
+ exit 1
481
+ ;;
482
+ esac
483
+ require_tree_without_extended_acl "$path"
484
+ if [ -f "$path" ]; then
485
+ [ "$(/usr/bin/stat -f '%l' "$path")" = "1" ] || { echo "Managed bootstrap cohort evidence is hard-linked: $path" >&2; exit 1; }
486
+ return
487
+ fi
488
+ [ -d "$path" ] || { echo "Managed bootstrap cohort evidence is not a regular file or directory: $path" >&2; exit 1; }
489
+ listing="$(/usr/bin/mktemp -t recordings-bootstrap-cohort)"
490
+ device="$(/usr/bin/stat -f '%d' "$path")"
491
+ /usr/bin/find -x "$path" -print0 >"$listing" || { /bin/rm -f "$listing"; exit 1; }
492
+ while IFS= read -r -d '' entry; do
493
+ owner="$(/usr/bin/stat -f '%u' "$entry")" || { invalid=1; break; }
494
+ mode="$(/usr/bin/stat -f '%Lp' "$entry")" || { invalid=1; break; }
495
+ [ ! -L "$entry" ] && [ "$owner" = "0" ] && \
496
+ [ "$(/usr/bin/stat -f '%d' "$entry")" = "$device" ] && \
497
+ [ $((8#$mode & 8#022)) -eq 0 ] || { invalid=1; break; }
498
+ if [ -f "$entry" ]; then
499
+ links="$(/usr/bin/stat -f '%l' "$entry")" || { invalid=1; break; }
500
+ [ "$links" = "1" ] || { invalid=1; break; }
501
+ elif [ ! -d "$entry" ]; then
502
+ invalid=1
503
+ break
504
+ fi
505
+ done <"$listing"
506
+ /bin/rm -f "$listing"
507
+ [ "$invalid" -eq 0 ] || { echo "Managed bootstrap cohort evidence contains unsafe structure: $path" >&2; exit 1; }
508
+ }
509
+
510
+ cohort_expected_mode() {
511
+ case "$1" in
512
+ "/Library/PrivilegedHelperTools/com.hasna.recordings.updater"|\
513
+ "/Library/PrivilegedHelperTools/com.hasna.recordings.artifact-verifier") printf '555\n' ;;
514
+ "/Library/LaunchDaemons/com.hasna.recordings.updater.plist") printf '444\n' ;;
515
+ "/Applications/Recordings.app"|"$CLIENT") printf '755\n' ;;
516
+ "/Library/Application Support/Hasna/Recordings/Trust") printf '700-or-755\n' ;;
517
+ "/Library/Application Support/Hasna/Recordings/Bootstrap"|\
518
+ "/Library/Application Support/Hasna/Recordings/Updates"|\
519
+ "/var/db/com.hasna.recordings.updater") printf '700\n' ;;
520
+ *) return 1 ;;
521
+ esac
522
+ }
523
+
524
+ # Cryptographic release preflight is mandatory before Installer or any exact
525
+ # bootstrap recovery replay. No packaged script or release key is trusted first.
526
+ run_preflight false
527
+
528
+ present_count=0
529
+ for cohort_path in "${COHORT_PATHS[@]}"; do
530
+ if path_exists_or_symlink "$cohort_path"; then
531
+ present_count=$((present_count + 1))
532
+ require_safe_existing_cohort_path "$cohort_path" "$(cohort_expected_mode "$cohort_path")"
533
+ fi
534
+ done
535
+ service_present=false
536
+ if /bin/launchctl print system/com.hasna.recordings.updater >/dev/null 2>&1; then service_present=true; fi
537
+
538
+ if path_exists_or_symlink "$AUTHORIZATION_ROOT" || path_exists_or_symlink "$AUTHORIZATION_JOURNAL"; then
539
+ require_authorization_journal
540
+ else
541
+ if [ "$present_count" -ne 0 ] || [ "$service_present" = true ] || path_exists_or_symlink "$STATE"; then
542
+ if path_exists_or_symlink "$STATE"; then
543
+ echo "Managed bootstrap refuses a committed different cohort or any committed cohort without its exact authorization journal." >&2
544
+ else
545
+ echo "Managed bootstrap refuses an existing unrelated cohort without exact-package authorization." >&2
546
+ fi
547
+ exit 1
548
+ fi
549
+ write_authorization_journal
550
+ fi
551
+
552
+ BOOTSTRAP_MODE=""
553
+ if [ "$service_present" = true ]; then
554
+ [ "$present_count" -eq "${#COHORT_PATHS[@]}" ] || {
555
+ echo "Recordings updater cohort evidence is partial or conflicting while the service is registered; recovery is refused." >&2
556
+ exit 1
557
+ }
558
+ BOOTSTRAP_MODE="recover"
559
+ else
560
+ if path_exists_or_symlink "$STATE"; then
561
+ echo "Recordings updater refuses a committed cohort with no registered service." >&2
562
+ exit 1
563
+ fi
564
+ BOOTSTRAP_MODE="repair"
565
+ if [ "$present_count" -eq "${#COHORT_PATHS[@]}" ]; then
566
+ echo "Managed bootstrap will repair an authorized full cohort without a registered service." >&2
567
+ elif [ "$present_count" -gt 0 ]; then
568
+ echo "Managed bootstrap will repair an authorized partial cohort." >&2
569
+ fi
570
+ fi
571
+
572
+ if [ "$BOOTSTRAP_MODE" = repair ]; then
573
+ validate_target_ancestry
574
+ /usr/bin/env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin HOME=/var/empty TMPDIR="$WORK_DIR" \
575
+ /usr/sbin/installer -pkg "$SNAPSHOT_PKG" -target /
576
+ for cohort_path in "${COHORT_PATHS[@]}"; do
577
+ path_exists_or_symlink "$cohort_path" && [ ! -L "$cohort_path" ] || {
578
+ echo "Installer did not produce a complete immutable updater cohort." >&2
579
+ exit 1
580
+ }
581
+ require_safe_existing_cohort_path "$cohort_path" "$(cohort_expected_mode "$cohort_path")"
582
+ done
583
+ /bin/launchctl print system/com.hasna.recordings.updater >/dev/null 2>&1 || {
584
+ echo "Installer did not start the immutable updater broker." >&2
585
+ exit 1
586
+ }
587
+ fi
588
+
589
+ if path_exists_or_symlink "$STATE"; then
590
+ STATE_PHASE="$(/usr/bin/plutil -extract phase raw -o - "$STATE")" || {
591
+ echo "Installed release state does not expose a valid bootstrap phase." >&2
592
+ exit 1
593
+ }
594
+ if [ "$STATE_PHASE" = committed ]; then
595
+ run_preflight true
596
+ remove_authorization_journal
597
+ exit 0
598
+ fi
599
+ [ "$STATE_PHASE" = seen ] || {
600
+ echo "Installed release state is neither an exact recoverable nor committed bootstrap." >&2
601
+ exit 1
602
+ }
603
+ fi
604
+
605
+ # Reauthenticate the root-private payload immediately before executing its
606
+ # signed client. Never execute the installed app copy during bootstrap or
607
+ # recovery; only the package copy bound by the signed envelope is authoritative.
608
+ validate_target_ancestry
609
+ require_tree_without_extended_acl "$EXPANDED"
610
+ run_preflight false
611
+ /usr/bin/env -i PATH=/usr/bin:/bin:/usr/sbin:/sbin HOME=/var/empty TMPDIR="$WORK_DIR" \
612
+ "$PAYLOAD_CLIENT" bootstrap \
613
+ --artifact "$SNAPSHOT_PKG" \
614
+ --manifest "$SNAPSHOT_MANIFEST" \
615
+ --envelope "$SNAPSHOT_ENVELOPE"
616
+
617
+ [ -f "$STATE" ] && [ ! -L "$STATE" ] && [ "$(/usr/bin/stat -f '%u' "$STATE")" = 0 ] || {
618
+ echo "Broker did not initialize root-owned anti-rollback state." >&2
619
+ exit 1
620
+ }
621
+ require_no_extended_acl "$STATE"
622
+ run_preflight true
623
+ remove_authorization_journal
@@ -0,0 +1,37 @@
1
+ # Extract exactly one SHA-256 fingerprint from pkgutil --check-signature output.
2
+ # macOS has emitted both same-line and indented following-line forms. Accept
3
+ # whitespace/colon separators only and fail closed on malformed or missing data.
4
+ BEGIN {
5
+ collecting = 0
6
+ candidate = ""
7
+ found = 0
8
+ }
9
+
10
+ function consume(value) {
11
+ gsub(/[[:space:]:]/, "", value)
12
+ if (value == "") return
13
+ if (value ~ /[^0-9A-Fa-f]/) exit 1
14
+ candidate = candidate value
15
+ if (length(candidate) > 64) exit 1
16
+ if (length(candidate) == 64) {
17
+ print tolower(candidate)
18
+ found = 1
19
+ exit 0
20
+ }
21
+ }
22
+
23
+ !collecting && /SHA256 Fingerprint:/ {
24
+ collecting = 1
25
+ value = $0
26
+ sub(/^.*SHA256 Fingerprint:[[:space:]]*/, "", value)
27
+ consume(value)
28
+ next
29
+ }
30
+
31
+ collecting {
32
+ consume($0)
33
+ }
34
+
35
+ END {
36
+ if (!found) exit 1
37
+ }