const TEAM_ID_PATTERN = /^[A-Z0-9]{10}$/; const MACH_O_MAGIC_NUMBERS = new Set([ 0xfeedface, 0xfeedfacf, 0xcefaedfe, 0xcffaedfe, 0xcafebabe, 0xcafebabf, 0xbebafeca, 0xbfbafeca, ]); export const REQUIRED_MACOS_ENTITLEMENTS = Object.freeze([ "com.apple.security.cs.allow-jit", "com.apple.security.cs.allow-unsigned-executable-memory", "com.apple.security.cs.disable-library-validation", "com.apple.security.device.audio-input", "com.apple.security.device.camera", ]); const ALLOWED_MACOS_ENTITLEMENTS = new Set(REQUIRED_MACOS_ENTITLEMENTS); function readCodeSignValue(details, key) { const prefix = `${key}=`; return details .split(/\r?\n/) .find((line) => line.startsWith(prefix)) ?.slice(prefix.length) .trim(); } export function assertValidAppleTeamId(teamId) { if (!TEAM_ID_PATTERN.test(teamId)) { throw new Error("APPLE_TEAM_ID must be exactly 10 uppercase letters or digits"); } } export function hasMachOMagic(header) { if (!(header instanceof Uint8Array) || header.byteLength < 4) { return false; } const view = new DataView(header.buffer, header.byteOffset, header.byteLength); return MACH_O_MAGIC_NUMBERS.has(view.getUint32(0, false)); } export function collectCodeSigningMetadataErrors(details, expectedTeamId) { const errors = []; const authorities = details .split(/\r?\n/) .filter((line) => line.startsWith("Authority=")) .map((line) => line.slice("Authority=".length).trim()); const identifier = readCodeSignValue(details, "Identifier"); const teamIdentifier = readCodeSignValue(details, "TeamIdentifier"); const timestamp = readCodeSignValue(details, "Timestamp"); const codeDirectory = details.split(/\r?\n/).find((line) => line.startsWith("CodeDirectory ")); if (identifier !== "dev.recordly.app") { errors.push(`unexpected bundle identifier: ${identifier ?? "missing"}`); } if (!authorities[0]?.startsWith("Developer ID Application:")) { errors.push("the leaf signing authority is not Developer ID Application"); } if (teamIdentifier !== expectedTeamId) { errors.push(`unexpected TeamIdentifier: ${teamIdentifier ?? "missing"}`); } if (!timestamp || timestamp.toLowerCase() === "none") { errors.push("secure signing timestamp is missing"); } if (!codeDirectory?.includes("runtime")) { errors.push("hardened runtime flag is missing"); } return errors; } export function collectEntitlementErrors(entitlements) { const errors = []; for (const entitlement of REQUIRED_MACOS_ENTITLEMENTS) { if (entitlements[entitlement] !== true) { errors.push(`required entitlement is missing or disabled: ${entitlement}`); } } for (const entitlement of Object.keys(entitlements).sort()) { if (ALLOWED_MACOS_ENTITLEMENTS.has(entitlement)) { continue; } if ( entitlement === "com.apple.security.get-task-allow" && entitlements[entitlement] === true ) { errors.push("distribution build must not enable com.apple.security.get-task-allow"); continue; } errors.push(`unexpected root application entitlement: ${entitlement}`); } return errors; } export function expectedMachOArchitecture(filePath, buildArch) { const normalizedPath = filePath.replaceAll("\\", "/"); if (normalizedPath.includes("/darwin-arm64/")) { return "arm64"; } if (normalizedPath.includes("/darwin-x64/")) { return "x86_64"; } return buildArch === "arm64" ? "arm64" : "x86_64"; } export function parseLipoArchitectures(output) { const trimmed = output.trim(); const architectureList = trimmed.match(/are:\s+(.+)$/i)?.[1]; if (architectureList) { return architectureList.trim().split(/\s+/); } const singleArchitecture = trimmed.match(/architecture:\s+([^\s]+)$/i)?.[1]; return singleArchitecture ? [singleArchitecture] : trimmed.split(/\s+/).filter(Boolean); } export function collectArchitectureErrors(filePath, lipoOutput, buildArch) { const expectedArchitecture = expectedMachOArchitecture(filePath, buildArch); const architectures = parseLipoArchitectures(lipoOutput); if (architectures.includes(expectedArchitecture)) { return []; } return [ `${filePath} does not contain ${expectedArchitecture} (found: ${architectures.join(", ") || "none"})`, ]; }