"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.mergeASARs = exports.generateAsarIntegrity = exports.detectAsarMode = exports.AsarMode = void 0; const asar_1 = __importDefault(require("@electron/asar")); const child_process_1 = require("child_process"); const crypto_1 = __importDefault(require("crypto")); const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const minimatch_1 = require("minimatch"); const os_1 = __importDefault(require("os")); const debug_1 = require("./debug"); const LIPO = 'lipo'; var AsarMode; (function (AsarMode) { AsarMode[AsarMode["NO_ASAR"] = 0] = "NO_ASAR"; AsarMode[AsarMode["HAS_ASAR"] = 1] = "HAS_ASAR"; })(AsarMode || (exports.AsarMode = AsarMode = {})); // See: https://github.com/apple-opensource-mirror/llvmCore/blob/0c60489d96c87140db9a6a14c6e82b15f5e5d252/include/llvm/Object/MachOFormat.h#L108-L112 const MACHO_MAGIC = new Set([ // 32-bit Mach-O 0xfeedface, 0xcefaedfe, // 64-bit Mach-O 0xfeedfacf, 0xcffaedfe, ]); const MACHO_UNIVERSAL_MAGIC = new Set([ // universal 0xcafebabe, 0xbebafeca, ]); const detectAsarMode = async (appPath) => { (0, debug_1.d)('checking asar mode of', appPath); const asarPath = path_1.default.resolve(appPath, 'Contents', 'Resources', 'app.asar'); if (!(await fs_extra_1.default.pathExists(asarPath))) { (0, debug_1.d)('determined no asar'); return AsarMode.NO_ASAR; } (0, debug_1.d)('determined has asar'); return AsarMode.HAS_ASAR; }; exports.detectAsarMode = detectAsarMode; const generateAsarIntegrity = (asarPath) => { return { algorithm: 'SHA256', hash: crypto_1.default .createHash('SHA256') .update(asar_1.default.getRawHeader(asarPath).headerString) .digest('hex'), }; }; exports.generateAsarIntegrity = generateAsarIntegrity; function toRelativePath(file) { return file.replace(/^\//, ''); } function isDirectory(a, file) { return Boolean('files' in asar_1.default.statFile(a, file)); } function checkSingleArch(archive, file, allowList) { if (allowList === undefined || !(0, minimatch_1.minimatch)(file, allowList, { matchBase: true })) { throw new Error(`Detected unique file "${file}" in "${archive}" not covered by ` + `allowList rule: "${allowList}"`); } } const mergeASARs = async ({ x64AsarPath, arm64AsarPath, outputAsarPath, singleArchFiles, }) => { (0, debug_1.d)(`merging ${x64AsarPath} and ${arm64AsarPath}`); const x64Files = new Set(asar_1.default.listPackage(x64AsarPath).map(toRelativePath)); const arm64Files = new Set(asar_1.default.listPackage(arm64AsarPath).map(toRelativePath)); // // Build set of unpacked directories and files // const unpackedFiles = new Set(); function buildUnpacked(a, fileList) { for (const file of fileList) { const stat = asar_1.default.statFile(a, file); if (!('unpacked' in stat) || !stat.unpacked) { continue; } if ('files' in stat) { continue; } unpackedFiles.add(file); } } buildUnpacked(x64AsarPath, x64Files); buildUnpacked(arm64AsarPath, arm64Files); // // Build list of files/directories unique to each asar // for (const file of x64Files) { if (!arm64Files.has(file)) { checkSingleArch(x64AsarPath, file, singleArchFiles); } } const arm64Unique = []; for (const file of arm64Files) { if (!x64Files.has(file)) { checkSingleArch(arm64AsarPath, file, singleArchFiles); arm64Unique.push(file); } } // // Find common bindings with different content // const commonBindings = []; for (const file of x64Files) { if (!arm64Files.has(file)) { continue; } // Skip directories if (isDirectory(x64AsarPath, file)) { continue; } const x64Content = asar_1.default.extractFile(x64AsarPath, file); const arm64Content = asar_1.default.extractFile(arm64AsarPath, file); if (x64Content.compare(arm64Content) === 0) { continue; } if (MACHO_UNIVERSAL_MAGIC.has(x64Content.readUInt32LE(0)) && MACHO_UNIVERSAL_MAGIC.has(arm64Content.readUInt32LE(0))) { continue; } if (!MACHO_MAGIC.has(x64Content.readUInt32LE(0))) { throw new Error(`Can't reconcile two non-macho files ${file}`); } commonBindings.push(file); } // // Extract both // const x64Dir = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'x64-')); const arm64Dir = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'arm64-')); try { (0, debug_1.d)(`extracting ${x64AsarPath} to ${x64Dir}`); asar_1.default.extractAll(x64AsarPath, x64Dir); (0, debug_1.d)(`extracting ${arm64AsarPath} to ${arm64Dir}`); asar_1.default.extractAll(arm64AsarPath, arm64Dir); for (const file of arm64Unique) { const source = path_1.default.resolve(arm64Dir, file); const destination = path_1.default.resolve(x64Dir, file); if (isDirectory(arm64AsarPath, file)) { (0, debug_1.d)(`creating unique directory: ${file}`); await fs_extra_1.default.mkdirp(destination); continue; } (0, debug_1.d)(`xopying unique file: ${file}`); await fs_extra_1.default.mkdirp(path_1.default.dirname(destination)); await fs_extra_1.default.copy(source, destination); } for (const binding of commonBindings) { const source = await fs_extra_1.default.realpath(path_1.default.resolve(arm64Dir, binding)); const destination = await fs_extra_1.default.realpath(path_1.default.resolve(x64Dir, binding)); (0, debug_1.d)(`merging binding: ${binding}`); (0, child_process_1.execFileSync)(LIPO, [source, destination, '-create', '-output', destination]); } (0, debug_1.d)(`creating archive at ${outputAsarPath}`); const resolvedUnpack = Array.from(unpackedFiles).map((file) => path_1.default.join(x64Dir, file)); let unpack; if (resolvedUnpack.length > 1) { unpack = `{${resolvedUnpack.join(',')}}`; } else if (resolvedUnpack.length === 1) { unpack = resolvedUnpack[0]; } await asar_1.default.createPackageWithOptions(x64Dir, outputAsarPath, { unpack, }); (0, debug_1.d)('done merging'); } finally { await Promise.all([fs_extra_1.default.remove(x64Dir), fs_extra_1.default.remove(arm64Dir)]); } }; exports.mergeASARs = mergeASARs; //# sourceMappingURL=asar-utils.js.map