update electron to v43
All checks were successful
Android Build / publish (push) Successful in 55s
Linux Build / publish (push) Successful in 1m6s

This commit is contained in:
olcxja 2026-07-09 22:38:33 +02:00
commit fb6c8b6ee9
5385 changed files with 513060 additions and 123058 deletions

View file

@ -1,8 +1,9 @@
/// <reference types="node" />
export declare enum AsarMode {
NO_ASAR = 0,
HAS_ASAR = 1
}
export declare type MergeASARsOptions = {
export type MergeASARsOptions = {
x64AsarPath: string;
arm64AsarPath: string;
outputAsarPath: string;
@ -14,3 +15,4 @@ export declare const generateAsarIntegrity: (asarPath: string) => {
hash: string;
};
export declare const mergeASARs: ({ x64AsarPath, arm64AsarPath, outputAsarPath, singleArchFiles, }: MergeASARsOptions) => Promise<void>;
export declare const isUniversalMachO: (fileContent: Buffer) => boolean;

View file

@ -1,10 +1,10 @@
import * as asar from 'asar';
import asar from '@electron/asar';
import { execFileSync } from 'child_process';
import * as crypto from 'crypto';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as minimatch from 'minimatch';
import * as os from 'os';
import crypto from 'crypto';
import fs from 'fs-extra';
import path from 'path';
import { minimatch } from 'minimatch';
import os from 'os';
import { d } from './debug';
const LIPO = 'lipo';
export var AsarMode;
@ -15,11 +15,13 @@ export var 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,
0xfeedface, 0xcefaedfe,
// 64-bit Mach-O
0xfeedfacf,
0xcffaedfe,
0xfeedfacf, 0xcffaedfe,
]);
const MACHO_UNIVERSAL_MAGIC = new Set([
// universal
0xcafebabe, 0xbebafeca,
]);
export const detectAsarMode = async (appPath) => {
d('checking asar mode of', appPath);
@ -54,8 +56,8 @@ function checkSingleArch(archive, file, allowList) {
}
export const mergeASARs = async ({ x64AsarPath, arm64AsarPath, outputAsarPath, singleArchFiles, }) => {
d(`merging ${x64AsarPath} and ${arm64AsarPath}`);
const x64Files = new Set(asar.listPackage(x64AsarPath).map(toRelativePath));
const arm64Files = new Set(asar.listPackage(arm64AsarPath).map(toRelativePath));
const x64Files = new Set(asar.listPackage(x64AsarPath, { isPack: false }).map(toRelativePath));
const arm64Files = new Set(asar.listPackage(arm64AsarPath, { isPack: false }).map(toRelativePath));
//
// Build set of unpacked directories and files
//
@ -103,9 +105,14 @@ export const mergeASARs = async ({ x64AsarPath, arm64AsarPath, outputAsarPath, s
}
const x64Content = asar.extractFile(x64AsarPath, file);
const arm64Content = asar.extractFile(arm64AsarPath, file);
// Skip file if the same content
if (x64Content.compare(arm64Content) === 0) {
continue;
}
// Skip universal Mach-O files.
if (isUniversalMachO(x64Content)) {
continue;
}
if (!MACHO_MAGIC.has(x64Content.readUInt32LE(0))) {
throw new Error(`Can't reconcile two non-macho files ${file}`);
}
@ -141,8 +148,15 @@ export const mergeASARs = async ({ x64AsarPath, arm64AsarPath, outputAsarPath, s
}
d(`creating archive at ${outputAsarPath}`);
const resolvedUnpack = Array.from(unpackedFiles).map((file) => path.join(x64Dir, file));
let unpack;
if (resolvedUnpack.length > 1) {
unpack = `{${resolvedUnpack.join(',')}}`;
}
else if (resolvedUnpack.length === 1) {
unpack = resolvedUnpack[0];
}
await asar.createPackageWithOptions(x64Dir, outputAsarPath, {
unpack: `{${resolvedUnpack.join(',')}}`,
unpack,
});
d('done merging');
}
@ -150,4 +164,7 @@ export const mergeASARs = async ({ x64AsarPath, arm64AsarPath, outputAsarPath, s
await Promise.all([fs.remove(x64Dir), fs.remove(arm64Dir)]);
}
};
export const isUniversalMachO = (fileContent) => {
return MACHO_UNIVERSAL_MAGIC.has(fileContent.readUInt32LE(0));
};
//# sourceMappingURL=asar-utils.js.map

File diff suppressed because one or more lines are too long

View file

@ -1,2 +1,2 @@
import * as debug from 'debug';
import debug from 'debug';
export declare const d: debug.Debugger;

View file

@ -1,3 +1,3 @@
import * as debug from 'debug';
import debug from 'debug';
export const d = debug('electron-universal');
//# sourceMappingURL=debug.js.map

View file

@ -1 +1 @@
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC"}
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC"}

View file

@ -1,3 +1,4 @@
/// <reference types="node" />
export declare enum AppFileType {
MACHO = 0,
PLAIN = 1,
@ -5,7 +6,7 @@ export declare enum AppFileType {
SNAPSHOT = 3,
APP_CODE = 4
}
export declare type AppFile = {
export type AppFile = {
relativePath: string;
type: AppFileType;
};
@ -14,3 +15,4 @@ export declare type AppFile = {
* @param appPath Path to the application
*/
export declare const getAllAppFiles: (appPath: string) => Promise<AppFile[]>;
export declare const readMachOHeader: (path: string) => Promise<Buffer>;

View file

@ -1,6 +1,26 @@
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
import { spawn, ExitCodeError } from '@malept/cross-spawn-promise';
import * as fs from 'fs-extra';
import * as path from 'path';
import { promises as stream } from 'node:stream';
const MACHO_PREFIX = 'Mach-O ';
export var AppFileType;
(function (AppFileType) {
@ -39,7 +59,7 @@ export const getAllAppFiles = async (appPath) => {
throw e;
}
}
if (p.includes('app.asar')) {
if (p.endsWith('.asar')) {
fileType = AppFileType.APP_CODE;
}
else if (fileOutput.startsWith(MACHO_PREFIX)) {
@ -65,4 +85,29 @@ export const getAllAppFiles = async (appPath) => {
await traverse(appPath);
return files;
};
export const readMachOHeader = async (path) => {
const chunks = [];
// no need to read the entire file, we only need the first 4 bytes of the file to determine the header
await stream.pipeline(fs.createReadStream(path, { start: 0, end: 3 }), function (source) {
return __asyncGenerator(this, arguments, function* () {
var _a, e_1, _b, _c;
try {
for (var _d = true, source_1 = __asyncValues(source), source_1_1; source_1_1 = yield __await(source_1.next()), _a = source_1_1.done, !_a; _d = true) {
_c = source_1_1.value;
_d = false;
const chunk = _c;
chunks.push(chunk);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = source_1.return)) yield __await(_b.call(source_1));
}
finally { if (e_1) throw e_1.error; }
}
});
});
return Buffer.concat(chunks);
};
//# sourceMappingURL=file-utils.js.map

View file

@ -1 +1 @@
{"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../src/file-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,YAAY,GAAG,SAAS,CAAC;AAE/B,MAAM,CAAN,IAAY,WAMX;AAND,WAAY,WAAW;IACrB,+CAAK,CAAA;IACL,+CAAK,CAAA;IACL,yDAAU,CAAA;IACV,qDAAQ,CAAA;IACR,qDAAQ,CAAA;AACV,CAAC,EANW,WAAW,KAAX,WAAW,QAMtB;AAOD;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,OAAe,EAAsB,EAAE;IAC1E,MAAM,KAAK,GAAc,EAAE,CAAC;IAE5B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAS,EAAE,EAAE;QACnC,CAAC,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO;QAC3B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEf,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,cAAc,EAAE;YAAE,OAAO;QAClC,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;YACjB,IAAI,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC;YAEjC,IAAI,UAAU,GAAG,EAAE,CAAC;YACpB,IAAI;gBACF,UAAU,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;aAC9D;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,YAAY,aAAa,EAAE;oBAC9B,6CAA6C;iBAC9C;qBAAM;oBACL,MAAM,CAAC,CAAC;iBACT;aACF;YACD,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;gBAC1B,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;aACjC;iBAAM,IAAI,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE;gBAC9C,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC;aAC9B;iBAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;gBAC7B,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;aACjC;iBAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE;gBAC5C,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC;aACnC;YAED,KAAK,CAAC,IAAI,CAAC;gBACT,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;gBACvC,IAAI,EAAE,QAAQ;aACf,CAAC,CAAC;SACJ;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;YACtB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;gBACvC,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;aACxC;SACF;IACH,CAAC,CAAC;IACF,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;IAExB,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
{"version":3,"file":"file-utils.js","sourceRoot":"","sources":["../../src/file-utils.ts"],"names":[],"mappings":"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"}

View file

@ -1,30 +1,60 @@
declare type MakeUniversalOpts = {
/**
* Options to pass into the {@link makeUniversalApp} function.
*
* Requires absolute paths for input x64 and arm64 apps and an absolute path to the
* output universal app.
*/
export type MakeUniversalOpts = {
/**
* Absolute file system path to the x64 version of your application. E.g. /Foo/bar/MyApp_x64.app
* Absolute file system path to the x64 version of your application (e.g. `/Foo/bar/MyApp_x64.app`).
*/
x64AppPath: string;
/**
* Absolute file system path to the arm64 version of your application. E.g. /Foo/bar/MyApp_arm64.app
* Absolute file system path to the arm64 version of your application (e.g. `/Foo/bar/MyApp_arm64.app`).
*/
arm64AppPath: string;
/**
* Absolute file system path you want the universal app to be written to. E.g. /Foo/var/MyApp_universal.app
* Absolute file system path you want the universal app to be written to (e.g. `/Foo/var/MyApp_universal.app`).
*
* If this file exists it will be overwritten ONLY if "force" is set to true
* If this file exists on disk already, it will be overwritten ONLY if {@link MakeUniversalOpts.force} is set to `true`.
*/
outAppPath: string;
/**
* Forcefully overwrite any existing files that are in the way of generating the universal application
* Forcefully overwrite any existing files that are in the way of generating the universal application.
*
* @defaultValue `false`
*/
force: boolean;
force?: boolean;
/**
* Merge x64 and arm64 ASARs into one.
*
* @defaultValue `false`
*/
mergeASARs?: boolean;
/**
* Minimatch pattern of paths that are allowed to be present in one of the ASAR files, but not in the other.
* If {@link MakeUniversalOpts.mergeASARs} is enabled, this property provides a
* {@link https://github.com/isaacs/minimatch?tab=readme-ov-file#features | minimatch}
* pattern of paths that are allowed to be present in one of the ASAR files, but not in the other.
*
*/
singleArchFiles?: string;
/**
* A {@link https://github.com/isaacs/minimatch?tab=readme-ov-file#features | minimatch}
* pattern of binaries that are expected to be the same x64 binary in both
*
* Use this if your application contains binaries that have already been merged into a universal file
* using the `lipo` tool.
*
* @see Apple's {@link https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary | Building a universal macOS binary} documentation
*
*/
x64ArchFiles?: string;
/**
* A {@link https://github.com/isaacs/minimatch?tab=readme-ov-file#features | minimatch} pattern of `Info.plist`
* paths that should not receive an injected `ElectronAsarIntegrity` value.
*
* Use this if your application contains another bundle that's already signed.
*/
infoPlistsToIgnore?: string;
};
export declare const makeUniversalApp: (opts: MakeUniversalOpts) => Promise<void>;
export {};

View file

@ -9,17 +9,19 @@ var __rest = (this && this.__rest) || function (s, e) {
}
return t;
};
import * as asar from '@electron/asar';
import { spawn } from '@malept/cross-spawn-promise';
import * as asar from 'asar';
import * as dircompare from 'dir-compare';
import * as fs from 'fs-extra';
import { minimatch } from 'minimatch';
import * as os from 'os';
import * as path from 'path';
import * as plist from 'plist';
import * as dircompare from 'dir-compare';
import { AppFileType, getAllAppFiles } from './file-utils';
import { AsarMode, detectAsarMode, generateAsarIntegrity, mergeASARs } from './asar-utils';
import { AsarMode, detectAsarMode, isUniversalMachO, mergeASARs } from './asar-utils';
import { AppFileType, getAllAppFiles, readMachOHeader } from './file-utils';
import { sha } from './sha';
import { d } from './debug';
import { computeIntegrityData } from './integrity';
const dupedFiles = (files) => files.filter((f) => f.type !== AppFileType.SNAPSHOT && f.type !== AppFileType.APP_CODE);
export const makeUniversalApp = async (opts) => {
d('making a universal app with options', opts);
@ -86,9 +88,27 @@ export const makeUniversalApp = async (opts) => {
throw new Error(`Expected all non-binary files to have identical SHAs when creating a universal build but "${file.relativePath}" did not`);
}
}
const knownMergedMachOFiles = new Set();
for (const machOFile of x64Files.filter((f) => f.type === AppFileType.MACHO)) {
const first = await fs.realpath(path.resolve(tmpApp, machOFile.relativePath));
const second = await fs.realpath(path.resolve(opts.arm64AppPath, machOFile.relativePath));
if (isUniversalMachO(await readMachOHeader(first)) &&
isUniversalMachO(await readMachOHeader(second))) {
d(machOFile.relativePath, `is already universal across builds, skipping lipo`);
knownMergedMachOFiles.add(machOFile.relativePath);
continue;
}
const x64Sha = await sha(path.resolve(opts.x64AppPath, machOFile.relativePath));
const arm64Sha = await sha(path.resolve(opts.arm64AppPath, machOFile.relativePath));
if (x64Sha === arm64Sha) {
if (opts.x64ArchFiles === undefined ||
!minimatch(machOFile.relativePath, opts.x64ArchFiles, { matchBase: true })) {
throw new Error(`Detected file "${machOFile.relativePath}" that's the same in both x64 and arm64 builds and not covered by the ` +
`x64ArchFiles rule: "${opts.x64ArchFiles}"`);
}
d('SHA for Mach-O file', machOFile.relativePath, `matches across builds ${x64Sha}===${arm64Sha}, skipping lipo`);
continue;
}
d('joining two MachO files with lipo', {
first,
second,
@ -100,6 +120,7 @@ export const makeUniversalApp = async (opts) => {
'-output',
await fs.realpath(path.resolve(tmpApp, machOFile.relativePath)),
]);
knownMergedMachOFiles.add(machOFile.relativePath);
}
/**
* If we don't have an ASAR we need to check if the two "app" folders are identical, if
@ -110,7 +131,12 @@ export const makeUniversalApp = async (opts) => {
if (x64AsarMode === AsarMode.NO_ASAR) {
d('checking if the x64 and arm64 app folders are identical');
const comparison = await dircompare.compare(path.resolve(tmpApp, 'Contents', 'Resources', 'app'), path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app'), { compareSize: true, compareContent: true });
if (!comparison.same) {
const differences = comparison.diffSet.filter((difference) => difference.state !== 'equal');
d(`Found ${differences.length} difference(s) between the x64 and arm64 folders`);
const nonMergedDifferences = differences.filter((difference) => !difference.name1 ||
!knownMergedMachOFiles.has(path.join('Contents', 'Resources', 'app', difference.relativePath, difference.name1)));
d(`After discluding MachO files merged with lipo ${nonMergedDifferences.length} remain.`);
if (nonMergedDifferences.length > 0) {
d('x64 and arm64 app folders are different, creating dynamic entry ASAR');
await fs.move(path.resolve(tmpApp, 'Contents', 'Resources', 'app'), path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64'));
await fs.copy(path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app'), path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64'));
@ -126,8 +152,6 @@ export const makeUniversalApp = async (opts) => {
d('x64 and arm64 app folders are the same');
}
}
const generatedIntegrity = {};
let didSplitAsar = false;
/**
* If we have an ASAR we just need to check if the two "app.asar" files have the same hash,
* if they are, same as above, we can leave one there and call it a day. If they're different
@ -145,14 +169,12 @@ export const makeUniversalApp = async (opts) => {
outputAsarPath: output,
singleArchFiles: opts.singleArchFiles,
});
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(output);
}
else if (x64AsarMode === AsarMode.HAS_ASAR) {
d('checking if the x64 and arm64 asars are identical');
const x64AsarSha = await sha(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'));
const arm64AsarSha = await sha(path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'));
if (x64AsarSha !== arm64AsarSha) {
didSplitAsar = true;
d('x64 and arm64 asars are different');
const x64AsarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64.asar');
await fs.move(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'), x64AsarPath);
@ -174,15 +196,12 @@ export const makeUniversalApp = async (opts) => {
await fs.writeJson(path.resolve(entryAsar, 'package.json'), pj);
const asarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar');
await asar.createPackage(entryAsar, asarPath);
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(asarPath);
generatedIntegrity['Resources/app-x64.asar'] = generateAsarIntegrity(x64AsarPath);
generatedIntegrity['Resources/app-arm64.asar'] = generateAsarIntegrity(arm64AsarPath);
}
else {
d('x64 and arm64 asars are the same');
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'));
}
}
const generatedIntegrity = await computeIntegrityData(path.join(tmpApp, 'Contents'));
const plistFiles = x64Files.filter((f) => f.type === AppFileType.INFO_PLIST);
for (const plistFile of plistFiles) {
const x64PlistPath = path.resolve(opts.x64AppPath, plistFile.relativePath);
@ -192,7 +211,10 @@ export const makeUniversalApp = async (opts) => {
if (JSON.stringify(x64Plist) !== JSON.stringify(arm64Plist)) {
throw new Error(`Expected all Info.plist files to be identical when ignoring integrity when creating a universal build but "${plistFile.relativePath}" was not`);
}
const mergedPlist = Object.assign(Object.assign({}, x64Plist), { ElectronAsarIntegrity: generatedIntegrity });
const injectAsarIntegrity = !opts.infoPlistsToIgnore ||
minimatch(plistFile.relativePath, opts.infoPlistsToIgnore, { matchBase: true });
const mergedPlist = injectAsarIntegrity
? Object.assign(Object.assign({}, x64Plist), { ElectronAsarIntegrity: generatedIntegrity }) : Object.assign({}, x64Plist);
await fs.writeFile(path.resolve(tmpApp, plistFile.relativePath), plist.build(mergedPlist));
}
for (const snapshotsFile of arm64Files.filter((f) => f.type === AppFileType.SNAPSHOT)) {

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
export interface HeaderHash {
algorithm: 'SHA256';
hash: string;
}
export interface AsarIntegrity {
[key: string]: HeaderHash;
}
export declare function computeIntegrityData(contentsPath: string): Promise<AsarIntegrity>;

View file

@ -0,0 +1,23 @@
import * as fs from 'fs-extra';
import path from 'path';
import { AppFileType, getAllAppFiles } from './file-utils';
import { generateAsarIntegrity } from './asar-utils';
export async function computeIntegrityData(contentsPath) {
const root = await fs.realpath(contentsPath);
const resourcesRelativePath = 'Resources';
const resourcesPath = path.resolve(root, resourcesRelativePath);
const resources = await getAllAppFiles(resourcesPath);
const resourceAsars = resources
.filter((file) => file.type === AppFileType.APP_CODE)
.reduce((prev, file) => (Object.assign(Object.assign({}, prev), { [path.join(resourcesRelativePath, file.relativePath)]: path.join(resourcesPath, file.relativePath) })), {});
// sort to produce constant result
const allAsars = Object.entries(resourceAsars).sort(([name1], [name2]) => name1.localeCompare(name2));
const hashes = await Promise.all(allAsars.map(async ([, from]) => generateAsarIntegrity(from)));
const asarIntegrity = {};
for (let i = 0; i < allAsars.length; i++) {
const [asar] = allAsars[i];
asarIntegrity[asar] = hashes[i];
}
return asarIntegrity;
}
//# sourceMappingURL=integrity.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../src/integrity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAerD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,YAAoB;IAC7D,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAE7C,MAAM,qBAAqB,GAAG,WAAW,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;IAEhE,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,aAAa,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,SAAS;SAC5B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC,QAAQ,CAAC;SACpD,MAAM,CACL,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,iCACX,IAAI,KACP,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAC9D,aAAa,EACb,IAAI,CAAC,YAAY,CAClB,IACD,EACF,EAAE,CACH,CAAC;IAEJ,kCAAkC;IAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CACvE,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAC3B,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChG,MAAM,aAAa,GAAkB,EAAE,CAAC;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACxC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC3B,aAAa,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;KACjC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC"}

View file

@ -1,16 +1,12 @@
import * as fs from 'fs-extra';
import * as crypto from 'crypto';
import { pipeline } from 'stream/promises';
import { d } from './debug';
export const sha = async (filePath) => {
d('hashing', filePath);
const hash = crypto.createHash('sha256');
hash.setEncoding('hex');
const fileStream = fs.createReadStream(filePath);
fileStream.pipe(hash);
await new Promise((resolve, reject) => {
fileStream.on('end', () => resolve());
fileStream.on('error', (err) => reject(err));
});
await pipeline(fs.createReadStream(filePath), hash);
return hash.read();
};
//# sourceMappingURL=sha.js.map

View file

@ -1 +1 @@
{"version":3,"file":"sha.js","sourceRoot":"","sources":["../../src/sha.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,SAAS,CAAC;AAE5B,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,QAAgB,EAAE,EAAE;IAC5C,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACjD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACpC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QACtC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACrB,CAAC,CAAC"}
{"version":3,"file":"sha.js","sourceRoot":"","sources":["../../src/sha.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,EAAE,CAAC,EAAE,MAAM,SAAS,CAAC;AAE5B,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,EAAE,QAAgB,EAAE,EAAE;IAC5C,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,QAAQ,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IACpD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACrB,CAAC,CAAC"}