From c1f7010a92a40fb8155482f2d1c1a4749b4f6d97 Mon Sep 17 00:00:00 2001 From: Ozgur Ersoy Date: Thu, 17 Apr 2025 18:48:19 +0200 Subject: [PATCH] fix(actions): enhance macOS notarization workflow by adding detailed status reporting and quarantine checks for improved application security --- .gitea/workflows/macos-build.yml | 167 ++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/macos-build.yml b/.gitea/workflows/macos-build.yml index fa5964ef..6ed700c7 100644 --- a/.gitea/workflows/macos-build.yml +++ b/.gitea/workflows/macos-build.yml @@ -425,9 +425,11 @@ jobs: echo "Verifying stapled ticket is properly attached..." xcrun stapler validate -v "${{ env.APP_PATH }}" + echo "NOTARIZATION_STATUS=success" >> $GITHUB_ENV echo "::set-output name=notarized::true" else echo "⚠️ Stapling completed with status $STAPLE_STATUS (may still be valid)" + echo "NOTARIZATION_STATUS=partial" >> $GITHUB_ENV fi else # Get detailed logs for failed notarization @@ -441,6 +443,7 @@ jobs: echo "$LOGS_OUTPUT" echo "==================================" + echo "NOTARIZATION_STATUS=failed" >> $GITHUB_ENV echo "❌ Notarization failed with status: $FINAL_STATUS" exit 1 fi @@ -453,6 +456,7 @@ jobs: echo " - NOTARY_API_KEY_ID: Your API key ID" echo " - NOTARY_API_KEY_ISSUER_ID: Your API issuer ID" echo " - NOTARY_API_KEY_PATH: Your p8 file content" + echo "NOTARIZATION_STATUS=skipped" >> $GITHUB_ENV fi - name: Create DMG Package @@ -480,8 +484,14 @@ jobs: echo "✅ Created DMG package: $DMG_FILE" echo "Size: $(du -h "$DMG_FILE" | cut -f1)" echo "DMG_PATH=$DMG_FILE" >> $GITHUB_ENV + echo "DMG_STATUS=success" >> $GITHUB_ENV + echo "DMG_SIZE=$(du -h "$DMG_FILE" | cut -f1)" >> $GITHUB_ENV + echo "::set-output name=dmg_created::true" + echo "::set-output name=dmg_path::$DMG_FILE" else echo "❌ Failed to create DMG package" + echo "DMG_STATUS=failed" >> $GITHUB_ENV + echo "::set-output name=dmg_created::false" exit 1 fi @@ -490,12 +500,167 @@ jobs: - name: Upload DMG Package uses: actions/upload-artifact@v3 - if: steps.package.outputs.DMG_PATH != '' + if: env.DMG_PATH != '' with: name: LuckyWorld-macOS-Signed-Notarized path: ${{ env.DMG_PATH }} retention-days: 30 + - name: Check Quarantine Attributes + id: quarantine + if: steps.notarize.outputs.notarized == 'true' + shell: bash + run: | + echo "🔍 Checking quarantine attributes..." + + # Check quarantine attributes on the app + if command -v xattr &> /dev/null; then + QUARANTINE=$(xattr -l "${{ env.APP_PATH }}" | grep -i "quarantine" || echo "None") + + if [ "$QUARANTINE" == "None" ]; then + echo "✅ No quarantine attributes found (good)" + echo "QUARANTINE_STATUS=clean" >> $GITHUB_ENV + else + echo "⚠️ Warning: Quarantine attributes found on the app:" + echo "$QUARANTINE" + echo "QUARANTINE_STATUS=present" >> $GITHUB_ENV + fi + + # Check for provenance attribute (indicates successful notarization) + PROVENANCE=$(xattr -l "${{ env.APP_PATH }}" | grep -i "com.apple.provenance" || echo "None") + + if [ "$PROVENANCE" != "None" ]; then + echo "✅ Provenance attribute found (indicates successful notarization)" + echo "PROVENANCE_STATUS=present" >> $GITHUB_ENV + else + echo "⚠️ Warning: No provenance attribute found - notarization may not be properly attached" + echo "PROVENANCE_STATUS=missing" >> $GITHUB_ENV + fi + else + echo "⚠️ xattr command not available, can't check quarantine status" + echo "QUARANTINE_STATUS=unknown" >> $GITHUB_ENV + echo "PROVENANCE_STATUS=unknown" >> $GITHUB_ENV + fi + + # If DMG exists, check it too + if [ -n "$DMG_PATH" ] && [ -f "$DMG_PATH" ]; then + if command -v xattr &> /dev/null; then + DMG_QUARANTINE=$(xattr -l "$DMG_PATH" | grep -i "quarantine" || echo "None") + + if [ "$DMG_QUARANTINE" == "None" ]; then + echo "✅ No quarantine attributes found on DMG (good)" + echo "DMG_QUARANTINE_STATUS=clean" >> $GITHUB_ENV + else + echo "⚠️ Warning: Quarantine attributes found on the DMG:" + echo "$DMG_QUARANTINE" + echo "DMG_QUARANTINE_STATUS=present" >> $GITHUB_ENV + fi + fi + fi + + - name: Status Report + if: always() + shell: bash + run: | + echo "📋 ========== macOS Build Status Report ==========" + echo "" + + # App Info + if [ -n "${{ env.APP_PATH }}" ]; then + echo "🔍 Application Info:" + echo " Path: ${{ env.APP_PATH }}" + echo " Bundle ID: ${{ env.BUNDLE_ID || 'Unknown' }}" + + if [ -f "${{ env.APP_PATH }}/Contents/Info.plist" ]; then + VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "${{ env.APP_PATH }}/Contents/Info.plist" 2>/dev/null || echo "Unknown") + BUILD=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "${{ env.APP_PATH }}/Contents/Info.plist" 2>/dev/null || echo "Unknown") + echo " Version: $VERSION (Build $BUILD)" + fi + else + echo "❌ No application found" + fi + + echo "" + + # Code Signing Status + echo "🔏 Code Signing Status:" + if [ "${{ steps.sign.outputs.signed }}" == "identity" ]; then + echo " ✅ Successfully signed with Developer ID" + elif [ "${{ steps.sign.outputs.signed }}" == "adhoc" ]; then + echo " ⚠️ Signed ad-hoc (not suitable for distribution)" + else + echo " ❌ Not signed or signing failed" + fi + + echo "" + + # Notarization Status + echo "🔐 Notarization Status:" + if [ "${{ steps.notarize.outputs.notarized }}" == "true" ]; then + echo " ✅ Successfully notarized and stapled" + elif [ "${{ env.NOTARIZATION_STATUS }}" == "partial" ]; then + echo " ⚠️ Notarized but stapling may have issues" + elif [ "${{ env.NOTARIZATION_STATUS }}" == "skipped" ]; then + echo " ⚠️ Notarization was skipped (missing credentials)" + elif [ "${{ env.NOTARIZATION_STATUS }}" == "failed" ]; then + echo " ❌ Notarization failed" + else + echo " ❓ Notarization status unknown" + fi + + # Quarantine Status + if [ -n "${{ env.QUARANTINE_STATUS }}" ]; then + echo "" + echo "🛡️ Quarantine & Security Status:" + if [ "${{ env.QUARANTINE_STATUS }}" == "clean" ]; then + echo " ✅ No quarantine attributes (good)" + elif [ "${{ env.QUARANTINE_STATUS }}" == "present" ]; then + echo " ⚠️ Quarantine attributes present" + else + echo " ❓ Quarantine status unknown" + fi + + if [ "${{ env.PROVENANCE_STATUS }}" == "present" ]; then + echo " ✅ Provenance attribute present (indicates successful notarization)" + elif [ "${{ env.PROVENANCE_STATUS }}" == "missing" ]; then + echo " ⚠️ No provenance attribute (might indicate notarization issues)" + fi + fi + + # DMG Package Status + echo "" + echo "📦 DMG Package Status:" + if [ "${{ env.DMG_STATUS }}" == "success" ]; then + echo " ✅ DMG created successfully" + echo " 📍 Path: ${{ env.DMG_PATH }}" + echo " 📏 Size: ${{ env.DMG_SIZE || 'Unknown' }}" + + if [ "${{ env.DMG_QUARANTINE_STATUS }}" == "clean" ]; then + echo " ✅ DMG has no quarantine attributes (good)" + elif [ "${{ env.DMG_QUARANTINE_STATUS }}" == "present" ]; then + echo " ⚠️ DMG has quarantine attributes" + fi + elif [ "${{ env.DMG_STATUS }}" == "failed" ]; then + echo " ❌ DMG creation failed" + elif [ "${{ steps.package.outputs.dmg_created }}" == "true" ]; then + echo " ✅ DMG created successfully" + echo " 📍 Path: ${{ steps.package.outputs.dmg_path }}" + else + echo " ❓ DMG was not created" + fi + + # Artifact Upload Status + echo "" + echo "🚀 Artifact Upload Status:" + if [ -n "${{ env.DMG_PATH }}" ] && [ -f "${{ env.DMG_PATH }}" ]; then + echo " ✅ DMG artifact should be uploaded" + else + echo " ❌ DMG artifact not available for upload" + fi + + echo "" + echo "==================================================" + - name: Cleanup if: always() shell: bash