native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #78 from onevcat/fix/install-target-safety

Harden install targets to prevent unsafe app deletion

authored by

Wei Wang and committed by
GitHub
f9991cae da7ee096

+77 -9
+77 -9
Makefile
··· 43 43 bash -o pipefail -c 'xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug build -skipMacroValidation -clonedSourcePackagesDirPath $(SPM_CACHE_DIR) 2>&1 | mise exec -- xcsift -qw --format toon' 44 44 45 45 run-app: build-app # Build then launch (Debug) with log streaming 46 - @settings="$$(xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug -showBuildSettings -json 2>/dev/null)"; \ 47 - build_dir="$$(echo "$$settings" | jq -r '.[0].buildSettings.BUILT_PRODUCTS_DIR')"; \ 48 - product="$$(echo "$$settings" | jq -r '.[0].buildSettings.FULL_PRODUCT_NAME')"; \ 46 + @set -euo pipefail; \ 47 + settings="$$(xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug -showBuildSettings -json 2>/dev/null)"; \ 48 + build_dir="$$(echo "$$settings" | jq -er '.[0].buildSettings.BUILT_PRODUCTS_DIR')"; \ 49 + product="$$(echo "$$settings" | jq -er '.[0].buildSettings.FULL_PRODUCT_NAME')"; \ 49 50 exec_name="$$(echo "$$settings" | jq -r '.[0].buildSettings.EXECUTABLE_NAME')"; \ 51 + if [ -z "$$build_dir" ] || [ -z "$$product" ] || [ "$$build_dir" = "null" ] || [ "$$product" = "null" ] || [ -z "$$exec_name" ] || [ "$$exec_name" = "null" ]; then \ 52 + echo "error: failed to resolve app path from build settings"; \ 53 + exit 1; \ 54 + fi; \ 50 55 "$$build_dir/$$product/Contents/MacOS/$$exec_name" 51 56 52 57 install-dev-build: build-app # install dev build to /Applications 53 - @settings="$$(xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug -showBuildSettings -json 2>/dev/null)"; \ 54 - build_dir="$$(echo "$$settings" | jq -r '.[0].buildSettings.BUILT_PRODUCTS_DIR')"; \ 55 - product="$$(echo "$$settings" | jq -r '.[0].buildSettings.FULL_PRODUCT_NAME')"; \ 58 + @set -euo pipefail; \ 59 + settings="$$(xcodebuild -project supacode.xcodeproj -scheme supacode -configuration Debug -showBuildSettings -json 2>/dev/null)"; \ 60 + build_dir="$$(echo "$$settings" | jq -er '.[0].buildSettings.BUILT_PRODUCTS_DIR')"; \ 61 + product="$$(echo "$$settings" | jq -er '.[0].buildSettings.FULL_PRODUCT_NAME')"; \ 62 + if [ -z "$$build_dir" ] || [ -z "$$product" ] || [ "$$build_dir" = "null" ] || [ "$$product" = "null" ]; then \ 63 + echo "error: failed to resolve app path from build settings"; \ 64 + exit 1; \ 65 + fi; \ 66 + if [ "$$product" != "$$(basename "$$product")" ]; then \ 67 + echo "error: invalid product name (contains path separators): $$product"; \ 68 + exit 1; \ 69 + fi; \ 70 + if [[ "$$product" != *.app ]]; then \ 71 + echo "error: unexpected product name: $$product"; \ 72 + exit 1; \ 73 + fi; \ 56 74 src="$$build_dir/$$product"; \ 57 75 dst="/Applications/$$product"; \ 76 + dst_parent="$$(cd "$$(dirname "$$dst")" && pwd -P)"; \ 77 + if [ "$$dst_parent" != "/Applications" ]; then \ 78 + echo "error: refusing to install outside /Applications: $$dst"; \ 79 + exit 1; \ 80 + fi; \ 81 + if [ "$$src" = "/" ] || [ "$$dst" = "/Applications" ] || [ "$$dst" = "/Applications/" ]; then \ 82 + echo "error: unsafe install path (src=$$src, dst=$$dst)"; \ 83 + exit 1; \ 84 + fi; \ 85 + case "$$dst" in \ 86 + /Applications/*.app) ;; \ 87 + *) \ 88 + echo "error: refusing to install outside /Applications/*.app: $$dst"; \ 89 + exit 1; \ 90 + ;; \ 91 + esac; \ 58 92 if [ ! -d "$$src" ]; then \ 59 93 echo "app not found: $$src"; \ 60 94 exit 1; \ 61 95 fi; \ 96 + if [ ! -d "$$src/Contents" ]; then \ 97 + echo "error: source is not an app bundle: $$src"; \ 98 + exit 1; \ 99 + fi; \ 62 100 echo "copying $$src -> $$dst"; \ 63 - rm -rf "$$dst"; \ 101 + if [ -e "$$dst" ]; then \ 102 + if ! command -v trash >/dev/null 2>&1; then \ 103 + echo "error: trash command not found; refusing to remove $$dst"; \ 104 + exit 1; \ 105 + fi; \ 106 + echo "moving existing app to Trash: $$dst"; \ 107 + trash "$$dst"; \ 108 + fi; \ 64 109 ditto "$$src" "$$dst"; \ 65 110 echo "installed $$dst" 66 111 67 112 install-release: build-ghostty-xcframework # Build Release, sign locally, install to /Applications 68 - @SIGNING_IDENTITY="$$(security find-identity -v -p codesigning 2>/dev/null | awk -F'"' '/Developer ID Application/ {print $$2; exit}')"; \ 113 + @set -euo pipefail; \ 114 + SIGNING_IDENTITY="$$(security find-identity -v -p codesigning 2>/dev/null | awk -F'"' '/Developer ID Application/ {print $$2; exit}')"; \ 69 115 if [ -z "$$SIGNING_IDENTITY" ]; then \ 70 116 echo "error: no Developer ID Application identity found"; \ 71 117 exit 1; \ ··· 114 160 codesign -f -s "$$IDENTITY_SHA" -o runtime --timestamp --preserve-metadata=entitlements,requirements,flags -v "$$APP_PATH"; \ 115 161 codesign -vvv --deep --strict "$$APP_PATH"; \ 116 162 PRODUCT="$$(basename "$$APP_PATH")"; \ 163 + if [ -z "$$PRODUCT" ] || [ "$$PRODUCT" = "." ] || [[ "$$PRODUCT" != *.app ]]; then \ 164 + echo "error: unexpected release product name: $$PRODUCT"; \ 165 + exit 1; \ 166 + fi; \ 117 167 DST="/Applications/$$PRODUCT"; \ 168 + if [ "$$DST" = "/Applications" ] || [ "$$DST" = "/Applications/" ]; then \ 169 + echo "error: unsafe install destination: $$DST"; \ 170 + exit 1; \ 171 + fi; \ 172 + case "$$DST" in \ 173 + /Applications/*.app) ;; \ 174 + *) \ 175 + echo "error: refusing to install outside /Applications/*.app: $$DST"; \ 176 + exit 1; \ 177 + ;; \ 178 + esac; \ 118 179 echo "copying $$APP_PATH -> $$DST"; \ 119 - rm -rf "$$DST"; \ 180 + if [ -e "$$DST" ]; then \ 181 + if ! command -v trash >/dev/null 2>&1; then \ 182 + echo "error: trash command not found; refusing to remove $$DST"; \ 183 + exit 1; \ 184 + fi; \ 185 + echo "moving existing app to Trash: $$DST"; \ 186 + trash "$$DST"; \ 187 + fi; \ 120 188 ditto "$$APP_PATH" "$$DST"; \ 121 189 echo "installed $$DST (Release build, locally signed)" 122 190