From 1819ca55ffb54de2a4d09d9aa1ec62dcc22dab69 Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Sun, 9 Aug 2026 02:33:58 +0700 Subject: [PATCH] ci(macos): harden candidate verification diagnostics --- .github/workflows/macos-release-candidate.yml | 19 ++++++++++-- electron/macosDistributionPolicy.test.mjs | 9 ++++++ scripts/verify-macos-distribution.mjs | 31 +++++++++++++++---- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/.github/workflows/macos-release-candidate.yml b/.github/workflows/macos-release-candidate.yml index d7b7843c..0e56ff16 100644 --- a/.github/workflows/macos-release-candidate.yml +++ b/.github/workflows/macos-release-candidate.yml @@ -158,12 +158,27 @@ jobs: cert_path="$RUNNER_TEMP/recordly-developer-id.p12" cert_pem_path="$RUNNER_TEMP/recordly-developer-id.pem" printf '%s' "$APPLE_SIGNING_CERTIFICATE_P12_BASE64" | base64 --decode > "$cert_path" - openssl pkcs12 \ + if ! openssl pkcs12 \ -in "$cert_path" \ -clcerts \ -nokeys \ -passin env:APPLE_SIGNING_CERTIFICATE_PASSWORD \ - -out "$cert_pem_path" + -out "$cert_pem_path"; then + rm -f "$cert_pem_path" + pkcs12_help="$(openssl pkcs12 -help 2>&1 || true)" + if [[ "$pkcs12_help" != *"-legacy"* ]]; then + echo "PKCS#12 extraction failed and this OpenSSL has no legacy-provider fallback." + exit 1 + fi + echo "Standard PKCS#12 extraction failed; retrying legacy Keychain compatibility." + openssl pkcs12 \ + -legacy \ + -in "$cert_path" \ + -clcerts \ + -nokeys \ + -passin env:APPLE_SIGNING_CERTIFICATE_PASSWORD \ + -out "$cert_pem_path" + fi openssl x509 -in "$cert_pem_path" -noout -checkend 86400 cert_subject="$(openssl x509 -in "$cert_pem_path" -noout -subject -nameopt RFC2253 | sed 's/^subject=//')" diff --git a/electron/macosDistributionPolicy.test.mjs b/electron/macosDistributionPolicy.test.mjs index e287c014..bd744802 100644 --- a/electron/macosDistributionPolicy.test.mjs +++ b/electron/macosDistributionPolicy.test.mjs @@ -83,6 +83,15 @@ describe("macOS distribution entitlement policy", () => { "unexpected root application entitlement: com.apple.security.cs.allow-dyld-environment-variables", ]); }); + + it("rejects disabled but unreviewed entitlement keys", () => { + expect( + collectEntitlementErrors({ + ...validEntitlements, + "com.apple.security.get-task-allow": false, + }), + ).toEqual(["unexpected root application entitlement: com.apple.security.get-task-allow"]); + }); }); describe("macOS distribution architecture policy", () => { diff --git a/scripts/verify-macos-distribution.mjs b/scripts/verify-macos-distribution.mjs index 72cf03d1..0bd85e6e 100644 --- a/scripts/verify-macos-distribution.mjs +++ b/scripts/verify-macos-distribution.mjs @@ -26,6 +26,7 @@ const packageJson = JSON.parse(readFileSync(path.join(projectRoot, "package.json const productName = packageJson.productName ?? packageJson.name ?? "Recordly"; const expectedBundleId = "dev.recordly.app"; const commandTimeoutMs = 5 * 60 * 1000; +const fileClassificationBatchSize = 100; const maxReportDetailLength = 4_000; function parseArguments(argv) { @@ -281,10 +282,20 @@ function verifyEntitlements(appPath, label, tempRoot, check) { function verifyMachOBinaries(appPath, arch, check) { check("packaged app: nested Mach-O signatures and architectures", () => { const machOBinaries = []; - for (const filePath of walkRegularFiles(appPath)) { - const fileType = runProcess("file", ["-b", filePath]).stdout; - if (fileType.includes("Mach-O")) { - machOBinaries.push(filePath); + const regularFiles = walkRegularFiles(appPath); + for (let index = 0; index < regularFiles.length; index += fileClassificationBatchSize) { + const batch = regularFiles.slice(index, index + fileClassificationBatchSize); + const fileTypes = runProcess("file", ["-b", ...batch]).stdout.split(/\r?\n/); + if (fileTypes.length !== batch.length) { + throw new Error( + `file classification returned ${fileTypes.length} rows for ${batch.length} paths`, + ); + } + + for (let batchIndex = 0; batchIndex < batch.length; batchIndex += 1) { + if (fileTypes[batchIndex].includes("Mach-O")) { + machOBinaries.push(batch[batchIndex]); + } } } @@ -408,7 +419,7 @@ export function verifyMacOSDistribution(argv = process.argv.slice(2)) { check("DMG filesystem integrity", () => runProcess("hdiutil", ["verify", dmgPath]).output); const dmgMountPath = path.join(tempRoot, "dmg"); - check("DMG mounts read-only", () => { + check("DMG attaches read-only for inspection", () => { runProcess("mkdir", ["-p", dmgMountPath]); runProcess("hdiutil", [ "attach", @@ -453,7 +464,15 @@ export function verifyMacOSDistribution(argv = process.argv.slice(2)) { console.log(`[macos-distribution] verification report: ${options.reportPath}`); return report; } catch (error) { - writeReport(report, options.reportPath, options.summaryPath); + try { + writeReport(report, options.reportPath, options.summaryPath); + } catch (reportError) { + console.error( + `[macos-distribution] failed to write report: ${ + reportError instanceof Error ? reportError.message : String(reportError) + }`, + ); + } throw error; } finally { if (mountedDmgPath) {