mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 18:39:55 +02:00
Compare commits
4 Commits
feat/relea
...
v0.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 50e9a6c1cc | |||
| 224a8e0e7a | |||
| efacfab213 | |||
|
|
0ec7618ce8 |
9
.github/workflows/apple-release.yml
vendored
9
.github/workflows/apple-release.yml
vendored
@@ -134,6 +134,15 @@ jobs:
|
|||||||
- name: Build, sign & notarize DMG
|
- name: Build, sign & notarize DMG
|
||||||
run: make build-apple-dmg
|
run: make build-apple-dmg
|
||||||
|
|
||||||
|
- name: Upload notarization diagnostics
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: vnidrop-${{ steps.version.outputs.app }}-notarization-diagnostics
|
||||||
|
path: apple/dist/*.notary-log.json
|
||||||
|
if-no-files-found: ignore
|
||||||
|
retention-days: 14
|
||||||
|
|
||||||
- name: Generate appcast
|
- name: Generate appcast
|
||||||
env:
|
env:
|
||||||
RELEASE_REPO: ${{ github.repository }}
|
RELEASE_REPO: ${{ github.repository }}
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -71,7 +71,9 @@ check-version: ## Validate the canonical version and its platform mappings.
|
|||||||
cd $(ROOT) && $(GRADLE) verifyVersion $(GRADLE_FLAGS)
|
cd $(ROOT) && $(GRADLE) verifyVersion $(GRADLE_FLAGS)
|
||||||
|
|
||||||
check-release: ## Validate coordinated release scripts and workflow YAML.
|
check-release: ## Validate coordinated release scripts and workflow YAML.
|
||||||
cd $(ROOT) && bash -n packaging/android/build-release.sh packaging/android/verify-apk-signature.sh packaging/android/tests/test_verify_apk_signature.sh packaging/release/assemble-release.sh packaging/release/test-assemble-release.sh packaging/release/test-release-config.sh
|
cd $(ROOT) && bash -n apple/scripts/notarize.sh apple/scripts/sign-exported-app.sh apple/scripts/tests/test-notarize.sh apple/scripts/tests/test-sign-exported-app.sh packaging/android/build-release.sh packaging/android/verify-apk-signature.sh packaging/android/tests/test_verify_apk_signature.sh packaging/release/assemble-release.sh packaging/release/test-assemble-release.sh packaging/release/test-release-config.sh
|
||||||
|
cd $(ROOT) && apple/scripts/tests/test-notarize.sh
|
||||||
|
cd $(ROOT) && apple/scripts/tests/test-sign-exported-app.sh
|
||||||
cd $(ROOT) && packaging/android/tests/test_verify_apk_signature.sh
|
cd $(ROOT) && packaging/android/tests/test_verify_apk_signature.sh
|
||||||
cd $(ROOT) && packaging/release/test-assemble-release.sh
|
cd $(ROOT) && packaging/release/test-assemble-release.sh
|
||||||
cd $(ROOT) && packaging/release/test-release-config.sh
|
cd $(ROOT) && packaging/release/test-release-config.sh
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ ACTUAL_BUILD="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' \
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo "==> Enforcing hardened-runtime signature"
|
||||||
|
"$SCRIPT_DIR/sign-exported-app.sh" \
|
||||||
|
"$APP" \
|
||||||
|
"$DEVELOPER_ID_APP" \
|
||||||
|
"$APPLE_DIR/VniDrop/Resources/VniDropDirect.entitlements"
|
||||||
|
|
||||||
# --- Build the DMG -----------------------------------------------------------
|
# --- Build the DMG -----------------------------------------------------------
|
||||||
DMG="$DIST_DIR/$APP_NAME-$VERSION.dmg"
|
DMG="$DIST_DIR/$APP_NAME-$VERSION.dmg"
|
||||||
rm -f "$DMG"
|
rm -f "$DMG"
|
||||||
@@ -137,7 +143,8 @@ codesign --force --sign "$DEVELOPER_ID_APP" --timestamp "$DMG"
|
|||||||
# --- Notarize + staple -------------------------------------------------------
|
# --- Notarize + staple -------------------------------------------------------
|
||||||
if [ -n "${NOTARY_PROFILE:-}" ]; then
|
if [ -n "${NOTARY_PROFILE:-}" ]; then
|
||||||
echo "==> Notarizing (profile: $NOTARY_PROFILE)"
|
echo "==> Notarizing (profile: $NOTARY_PROFILE)"
|
||||||
xcrun notarytool submit "$DMG" --keychain-profile "$NOTARY_PROFILE" --wait
|
NOTARY_LOG="$DIST_DIR/$APP_NAME-$VERSION.notary-log.json"
|
||||||
|
"$SCRIPT_DIR/notarize.sh" "$DMG" "$NOTARY_PROFILE" "$NOTARY_LOG"
|
||||||
echo "==> Stapling"
|
echo "==> Stapling"
|
||||||
xcrun stapler staple "$DMG"
|
xcrun stapler staple "$DMG"
|
||||||
xcrun stapler validate "$DMG"
|
xcrun stapler validate "$DMG"
|
||||||
|
|||||||
67
apple/scripts/notarize.sh
Executable file
67
apple/scripts/notarize.sh
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 3 ]]; then
|
||||||
|
printf 'Usage: %s <artifact> <keychain-profile> <log-output>\n' "$0" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
artifact=$1
|
||||||
|
keychain_profile=$2
|
||||||
|
log_output=$3
|
||||||
|
|
||||||
|
[[ -s $artifact ]] || {
|
||||||
|
printf 'error: notarization artifact is missing or empty: %s\n' "$artifact" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
[[ -n $keychain_profile ]] || {
|
||||||
|
printf 'error: notarization keychain profile is empty\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
[[ -n $log_output ]] || {
|
||||||
|
printf 'error: notarization log output path is empty\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -f "$log_output"
|
||||||
|
set +e
|
||||||
|
response="$(
|
||||||
|
xcrun notarytool submit "$artifact" \
|
||||||
|
--keychain-profile "$keychain_profile" \
|
||||||
|
--wait \
|
||||||
|
--output-format json
|
||||||
|
)"
|
||||||
|
submit_exit=$?
|
||||||
|
set -e
|
||||||
|
printf '%s\n' "$response"
|
||||||
|
|
||||||
|
submission_id="$(
|
||||||
|
printf '%s\n' "$response" |
|
||||||
|
jq -r '.id // empty' 2>/dev/null ||
|
||||||
|
true
|
||||||
|
)"
|
||||||
|
status="$(
|
||||||
|
printf '%s\n' "$response" |
|
||||||
|
jq -r '.status // empty' 2>/dev/null ||
|
||||||
|
true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ $submit_exit -eq 0 && $status == Accepted && -n $submission_id ]]; then
|
||||||
|
printf 'Notarization accepted (submission %s)\n' "$submission_id"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'error: notarization was not accepted (status: %s, submission: %s)\n' \
|
||||||
|
"${status:-unknown}" "${submission_id:-unknown}" >&2
|
||||||
|
if [[ -n $submission_id ]]; then
|
||||||
|
mkdir -p "$(dirname "$log_output")"
|
||||||
|
if xcrun notarytool log "$submission_id" "$log_output" \
|
||||||
|
--keychain-profile "$keychain_profile"; then
|
||||||
|
printf '%s\n' 'Apple notarization log:' >&2
|
||||||
|
cat "$log_output" >&2
|
||||||
|
else
|
||||||
|
printf 'error: could not retrieve the Apple notarization log\n' >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
42
apple/scripts/sign-exported-app.sh
Executable file
42
apple/scripts/sign-exported-app.sh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 3 ]]; then
|
||||||
|
printf 'Usage: %s <app-bundle> <signing-identity> <entitlements>\n' "$0" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
app=$1
|
||||||
|
signing_identity=$2
|
||||||
|
entitlements=$3
|
||||||
|
|
||||||
|
[[ -d $app ]] || {
|
||||||
|
printf 'error: exported app bundle does not exist: %s\n' "$app" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
[[ -n $signing_identity ]] || {
|
||||||
|
printf 'error: signing identity is empty\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
[[ -f $entitlements ]] || {
|
||||||
|
printf 'error: entitlements file does not exist: %s\n' "$entitlements" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
codesign \
|
||||||
|
--force \
|
||||||
|
--sign "$signing_identity" \
|
||||||
|
--options runtime \
|
||||||
|
--timestamp \
|
||||||
|
--entitlements "$entitlements" \
|
||||||
|
"$app"
|
||||||
|
codesign --verify --deep --strict --verbose=2 "$app"
|
||||||
|
|
||||||
|
signature_details="$(codesign --display --verbose=4 "$app" 2>&1)"
|
||||||
|
printf '%s\n' "$signature_details"
|
||||||
|
printf '%s\n' "$signature_details" |
|
||||||
|
grep -Eq 'flags=.*\(runtime([^)]*)?\)' || {
|
||||||
|
printf 'error: exported app signature does not enable the hardened runtime\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
87
apple/scripts/tests/test-notarize.sh
Executable file
87
apple/scripts/tests/test-notarize.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
notarize="$script_dir/../notarize.sh"
|
||||||
|
scratch="$(mktemp -d "${TMPDIR:-/tmp}/vnidrop-notarize-test.XXXXXX")"
|
||||||
|
trap 'rm -rf "$scratch"' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$scratch/bin"
|
||||||
|
artifact="$scratch/VniDrop.dmg"
|
||||||
|
calls="$scratch/calls.txt"
|
||||||
|
log_output="$scratch/notary/notary-log.json"
|
||||||
|
printf 'dmg\n' > "$artifact"
|
||||||
|
|
||||||
|
cat > "$scratch/bin/xcrun" <<'SCRIPT'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
printf '%s\n' "$*" >> "$FAKE_NOTARY_CALLS"
|
||||||
|
if [[ $1 == notarytool && $2 == submit ]]; then
|
||||||
|
case "${FAKE_NOTARY_MODE:-accepted}" in
|
||||||
|
accepted)
|
||||||
|
printf '%s\n' \
|
||||||
|
'{"id":"11111111-1111-1111-1111-111111111111","status":"Accepted"}'
|
||||||
|
;;
|
||||||
|
invalid)
|
||||||
|
printf '%s\n' \
|
||||||
|
'{"id":"22222222-2222-2222-2222-222222222222","status":"Invalid"}'
|
||||||
|
;;
|
||||||
|
transport-error)
|
||||||
|
printf '%s\n' 'notary service unavailable' >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
elif [[ $1 == notarytool && $2 == log ]]; then
|
||||||
|
mkdir -p "$(dirname "$4")"
|
||||||
|
printf '%s\n' \
|
||||||
|
'{"status":"Invalid","issues":[{"message":"The signature is invalid."}]}' \
|
||||||
|
> "$4"
|
||||||
|
else
|
||||||
|
printf 'unexpected xcrun invocation: %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
SCRIPT
|
||||||
|
chmod +x "$scratch/bin/xcrun"
|
||||||
|
|
||||||
|
PATH="$scratch/bin:$PATH" \
|
||||||
|
FAKE_NOTARY_CALLS="$calls" \
|
||||||
|
FAKE_NOTARY_MODE=accepted \
|
||||||
|
"$notarize" "$artifact" test-profile "$log_output" >/dev/null
|
||||||
|
[[ ! -e $log_output ]]
|
||||||
|
[[ $(grep -c '^notarytool submit ' "$calls") -eq 1 ]]
|
||||||
|
if grep -q '^notarytool log ' "$calls"; then
|
||||||
|
printf 'Accepted submissions must not request a rejection log\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
: > "$calls"
|
||||||
|
if PATH="$scratch/bin:$PATH" \
|
||||||
|
FAKE_NOTARY_CALLS="$calls" \
|
||||||
|
FAKE_NOTARY_MODE=invalid \
|
||||||
|
"$notarize" "$artifact" test-profile "$log_output" >/dev/null 2>&1; then
|
||||||
|
printf 'Invalid notarization must fail\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -F '"The signature is invalid."' "$log_output" >/dev/null
|
||||||
|
grep -F \
|
||||||
|
'notarytool log 22222222-2222-2222-2222-222222222222' \
|
||||||
|
"$calls" >/dev/null
|
||||||
|
|
||||||
|
: > "$calls"
|
||||||
|
rm -f "$log_output"
|
||||||
|
if PATH="$scratch/bin:$PATH" \
|
||||||
|
FAKE_NOTARY_CALLS="$calls" \
|
||||||
|
FAKE_NOTARY_MODE=transport-error \
|
||||||
|
"$notarize" "$artifact" test-profile "$log_output" >/dev/null 2>&1; then
|
||||||
|
printf 'Notary transport errors must fail\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
[[ ! -e $log_output ]]
|
||||||
|
if grep -q '^notarytool log ' "$calls"; then
|
||||||
|
printf 'A submission without an ID cannot request a rejection log\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Notarization helper tests passed.\n'
|
||||||
78
apple/scripts/tests/test-sign-exported-app.sh
Executable file
78
apple/scripts/tests/test-sign-exported-app.sh
Executable file
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
sign_exported_app="$script_dir/../sign-exported-app.sh"
|
||||||
|
scratch="$(mktemp -d "${TMPDIR:-/tmp}/vnidrop-codesign-test.XXXXXX")"
|
||||||
|
trap 'rm -rf "$scratch"' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$scratch/bin" "$scratch/VniDrop.app/Contents/MacOS"
|
||||||
|
app="$scratch/VniDrop.app"
|
||||||
|
entitlements="$scratch/VniDropDirect.entitlements"
|
||||||
|
calls="$scratch/calls.txt"
|
||||||
|
printf '<plist><dict/></plist>\n' > "$entitlements"
|
||||||
|
printf 'binary\n' > "$app/Contents/MacOS/VniDrop"
|
||||||
|
|
||||||
|
cat > "$scratch/bin/codesign" <<'SCRIPT'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
printf '%s\n' "$*" >> "$FAKE_CODESIGN_CALLS"
|
||||||
|
case " $* " in
|
||||||
|
*" --display "*)
|
||||||
|
if [[ ${FAKE_CODESIGN_MODE:-runtime} == missing-runtime ]]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
'CodeDirectory v=20500 size=123 flags=0x0(none) hashes=1+0 location=embedded' \
|
||||||
|
>&2
|
||||||
|
else
|
||||||
|
printf '%s\n' \
|
||||||
|
'CodeDirectory v=20500 size=123 flags=0x10000(runtime) hashes=1+0 location=embedded' \
|
||||||
|
>&2
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*" --verify "*)
|
||||||
|
if [[ ${FAKE_CODESIGN_MODE:-runtime} == verify-error ]]; then
|
||||||
|
printf '%s\n' 'invalid signature' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
SCRIPT
|
||||||
|
chmod +x "$scratch/bin/codesign"
|
||||||
|
|
||||||
|
PATH="$scratch/bin:$PATH" \
|
||||||
|
FAKE_CODESIGN_CALLS="$calls" \
|
||||||
|
"$sign_exported_app" \
|
||||||
|
"$app" \
|
||||||
|
'Developer ID Application: Example (ABCDEFGHIJ)' \
|
||||||
|
"$entitlements" >/dev/null
|
||||||
|
grep -F -- \
|
||||||
|
'--force --sign Developer ID Application: Example (ABCDEFGHIJ) --options runtime --timestamp --entitlements' \
|
||||||
|
"$calls" >/dev/null
|
||||||
|
grep -F -- '--verify --deep --strict --verbose=2' "$calls" >/dev/null
|
||||||
|
grep -F -- '--display --verbose=4' "$calls" >/dev/null
|
||||||
|
|
||||||
|
if PATH="$scratch/bin:$PATH" \
|
||||||
|
FAKE_CODESIGN_CALLS="$calls" \
|
||||||
|
FAKE_CODESIGN_MODE=missing-runtime \
|
||||||
|
"$sign_exported_app" \
|
||||||
|
"$app" \
|
||||||
|
'Developer ID Application: Example (ABCDEFGHIJ)' \
|
||||||
|
"$entitlements" >/dev/null 2>&1; then
|
||||||
|
printf 'A signature without the hardened runtime must fail\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if PATH="$scratch/bin:$PATH" \
|
||||||
|
FAKE_CODESIGN_CALLS="$calls" \
|
||||||
|
FAKE_CODESIGN_MODE=verify-error \
|
||||||
|
"$sign_exported_app" \
|
||||||
|
"$app" \
|
||||||
|
'Developer ID Application: Example (ABCDEFGHIJ)' \
|
||||||
|
"$entitlements" >/dev/null 2>&1; then
|
||||||
|
printf 'Signature verification errors must fail\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Exported app signing tests passed.\n'
|
||||||
@@ -25,4 +25,17 @@ grep -F 'run: make build-apple-dmg' \
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signing_line="$(
|
||||||
|
awk '/sign-exported-app\.sh/ {print NR; exit}' \
|
||||||
|
"$repo_root/apple/scripts/build-dmg.sh"
|
||||||
|
)"
|
||||||
|
dmg_line="$(
|
||||||
|
awk '/echo "==> Building DMG"/ {print NR; exit}' \
|
||||||
|
"$repo_root/apple/scripts/build-dmg.sh"
|
||||||
|
)"
|
||||||
|
[[ -n $signing_line && -n $dmg_line && $signing_line -lt $dmg_line ]] || {
|
||||||
|
printf 'The exported app must enforce hardened-runtime signing before DMG creation\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
printf 'Release configuration tests passed.\n'
|
printf 'Release configuration tests passed.\n'
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
PRODUCT_VERSION=0.2.1
|
PRODUCT_VERSION=0.2.2
|
||||||
RELEASE_CHANNEL=beta
|
RELEASE_CHANNEL=beta
|
||||||
WINDOWS_VERSION_EPOCH=1
|
WINDOWS_VERSION_EPOCH=1
|
||||||
|
|||||||
Reference in New Issue
Block a user