feat: add support for auto-merging ElectronAsarIntegrity values
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
"@types/debug": "^4.1.5",
|
"@types/debug": "^4.1.5",
|
||||||
"@types/fs-extra": "^9.0.4",
|
"@types/fs-extra": "^9.0.4",
|
||||||
"@types/node": "^14.14.7",
|
"@types/node": "^14.14.7",
|
||||||
|
"@types/plist": "^3.0.2",
|
||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"lint-staged": "^10.5.1",
|
"lint-staged": "^10.5.1",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.1.2",
|
||||||
@@ -38,10 +39,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@malept/cross-spawn-promise": "^1.1.0",
|
"@malept/cross-spawn-promise": "^1.1.0",
|
||||||
"asar": "^3.0.3",
|
"asar": "^3.1.0",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"dir-compare": "^2.4.0",
|
"dir-compare": "^2.4.0",
|
||||||
"fs-extra": "^9.0.1"
|
"fs-extra": "^9.0.1",
|
||||||
|
"plist": "^3.0.4"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import * as asar from 'asar';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { d } from './debug';
|
import { d } from './debug';
|
||||||
@@ -19,3 +21,13 @@ export const detectAsarMode = async (appPath: string) => {
|
|||||||
d('determined has asar');
|
d('determined has asar');
|
||||||
return AsarMode.HAS_ASAR;
|
return AsarMode.HAS_ASAR;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const generateAsarIntegrity = (asarPath: string) => {
|
||||||
|
return {
|
||||||
|
algorithm: 'SHA256' as const,
|
||||||
|
hash: crypto
|
||||||
|
.createHash('SHA256')
|
||||||
|
.update(asar.getRawHeader(asarPath).headerString)
|
||||||
|
.digest('hex'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const MACHO_PREFIX = 'Mach-O ';
|
|||||||
export enum AppFileType {
|
export enum AppFileType {
|
||||||
MACHO,
|
MACHO,
|
||||||
PLAIN,
|
PLAIN,
|
||||||
|
INFO_PLIST,
|
||||||
SNAPSHOT,
|
SNAPSHOT,
|
||||||
APP_CODE,
|
APP_CODE,
|
||||||
}
|
}
|
||||||
@@ -50,6 +51,8 @@ export const getAllAppFiles = async (appPath: string): Promise<AppFile[]> => {
|
|||||||
fileType = AppFileType.MACHO;
|
fileType = AppFileType.MACHO;
|
||||||
} else if (p.endsWith('.bin')) {
|
} else if (p.endsWith('.bin')) {
|
||||||
fileType = AppFileType.SNAPSHOT;
|
fileType = AppFileType.SNAPSHOT;
|
||||||
|
} else if (path.basename(p) === 'Info.plist') {
|
||||||
|
fileType = AppFileType.INFO_PLIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
files.push({
|
files.push({
|
||||||
|
|||||||
53
src/index.ts
53
src/index.ts
@@ -1,11 +1,14 @@
|
|||||||
import { spawn } from '@malept/cross-spawn-promise';
|
import { spawn } from '@malept/cross-spawn-promise';
|
||||||
import * as asar from 'asar';
|
import * as asar from 'asar';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as plist from 'plist';
|
||||||
import * as dircompare from 'dir-compare';
|
import * as dircompare from 'dir-compare';
|
||||||
|
|
||||||
import { AppFile, AppFileType, getAllAppFiles } from './file-utils';
|
import { AppFile, AppFileType, getAllAppFiles } from './file-utils';
|
||||||
import { AsarMode, detectAsarMode } from './asar-utils';
|
import { AsarMode, detectAsarMode, generateAsarIntegrity } from './asar-utils';
|
||||||
import { sha } from './sha';
|
import { sha } from './sha';
|
||||||
import { d } from './debug';
|
import { d } from './debug';
|
||||||
|
|
||||||
@@ -172,6 +175,9 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generatedIntegrity: Record<string, { algorithm: 'SHA256'; hash: string }> = {};
|
||||||
|
let didSplitAsar = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If we have an ASAR we just need to check if the two "app.asar" files have the same hash,
|
* 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
|
* if they are, same as above, we can leave one there and call it a day. If they're different
|
||||||
@@ -188,11 +194,10 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (x64AsarSha !== arm64AsarSha) {
|
if (x64AsarSha !== arm64AsarSha) {
|
||||||
|
didSplitAsar = true;
|
||||||
d('x64 and arm64 asars are different');
|
d('x64 and arm64 asars are different');
|
||||||
await fs.move(
|
const x64AsarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64.asar');
|
||||||
path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'),
|
await fs.move(path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'), x64AsarPath);
|
||||||
path.resolve(tmpApp, 'Contents', 'Resources', 'app-x64.asar'),
|
|
||||||
);
|
|
||||||
const x64Unpacked = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar.unpacked');
|
const x64Unpacked = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar.unpacked');
|
||||||
if (await fs.pathExists(x64Unpacked)) {
|
if (await fs.pathExists(x64Unpacked)) {
|
||||||
await fs.move(
|
await fs.move(
|
||||||
@@ -201,9 +206,10 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const arm64AsarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64.asar');
|
||||||
await fs.copy(
|
await fs.copy(
|
||||||
path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'),
|
path.resolve(opts.arm64AppPath, 'Contents', 'Resources', 'app.asar'),
|
||||||
path.resolve(tmpApp, 'Contents', 'Resources', 'app-arm64.asar'),
|
arm64AsarPath,
|
||||||
);
|
);
|
||||||
const arm64Unpacked = path.resolve(
|
const arm64Unpacked = path.resolve(
|
||||||
opts.arm64AppPath,
|
opts.arm64AppPath,
|
||||||
@@ -234,15 +240,42 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
|
|||||||
);
|
);
|
||||||
pj.main = 'index.js';
|
pj.main = 'index.js';
|
||||||
await fs.writeJson(path.resolve(entryAsar, 'package.json'), pj);
|
await fs.writeJson(path.resolve(entryAsar, 'package.json'), pj);
|
||||||
await asar.createPackage(
|
const asarPath = path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar');
|
||||||
entryAsar,
|
await asar.createPackage(entryAsar, asarPath);
|
||||||
path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'),
|
|
||||||
);
|
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(asarPath);
|
||||||
|
generatedIntegrity['Resources/app-x64.asar'] = generateAsarIntegrity(x64AsarPath);
|
||||||
|
generatedIntegrity['Resources/app-arm64.asar'] = generateAsarIntegrity(arm64AsarPath);
|
||||||
} else {
|
} else {
|
||||||
d('x64 and arm64 asars are the same');
|
d('x64 and arm64 asars are the same');
|
||||||
|
generatedIntegrity['Resources/app.asar'] = generateAsarIntegrity(
|
||||||
|
path.resolve(tmpApp, 'Contents', 'Resources', 'app.asar'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const plistFiles = x64Files.filter((f) => f.type === AppFileType.INFO_PLIST);
|
||||||
|
for (const plistFile of plistFiles) {
|
||||||
|
const x64PlistPath = path.resolve(opts.x64AppPath, plistFile.relativePath);
|
||||||
|
const arm64PlistPath = path.resolve(opts.arm64AppPath, plistFile.relativePath);
|
||||||
|
|
||||||
|
const { ElectronAsarIntegrity: x64Integrity, ...x64Plist } = plist.parse(
|
||||||
|
await fs.readFile(x64PlistPath, 'utf8'),
|
||||||
|
) as any;
|
||||||
|
const { ElectronAsarIntegrity: arm64Integrity, ...arm64Plist } = plist.parse(
|
||||||
|
await fs.readFile(arm64PlistPath, 'utf8'),
|
||||||
|
) as any;
|
||||||
|
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 = { ...x64Plist, ElectronAsarIntegrity: generatedIntegrity };
|
||||||
|
|
||||||
|
await fs.writeFile(path.resolve(tmpApp, plistFile.relativePath), plist.build(mergedPlist));
|
||||||
|
}
|
||||||
|
|
||||||
for (const snapshotsFile of arm64Files.filter((f) => f.type === AppFileType.SNAPSHOT)) {
|
for (const snapshotsFile of arm64Files.filter((f) => f.type === AppFileType.SNAPSHOT)) {
|
||||||
d('copying snapshot file', snapshotsFile.relativePath, 'to target application');
|
d('copying snapshot file', snapshotsFile.relativePath, 'to target application');
|
||||||
await fs.copy(
|
await fs.copy(
|
||||||
|
|||||||
39
yarn.lock
39
yarn.lock
@@ -314,6 +314,14 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||||
|
|
||||||
|
"@types/plist@^3.0.2":
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01"
|
||||||
|
integrity sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
xmlbuilder ">=11.0.1"
|
||||||
|
|
||||||
"@types/retry@^0.12.0":
|
"@types/retry@^0.12.0":
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||||
@@ -497,10 +505,10 @@ asap@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||||
|
|
||||||
asar@^3.0.3:
|
asar@^3.1.0:
|
||||||
version "3.0.3"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b"
|
resolved "https://registry.yarnpkg.com/asar/-/asar-3.1.0.tgz#70b0509449fe3daccc63beb4d3c7d2e24d3c6473"
|
||||||
integrity sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw==
|
integrity sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
chromium-pickle-js "^0.2.0"
|
chromium-pickle-js "^0.2.0"
|
||||||
commander "^5.0.0"
|
commander "^5.0.0"
|
||||||
@@ -559,6 +567,11 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base64-js@^1.5.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
bcrypt-pbkdf@^1.0.0:
|
bcrypt-pbkdf@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
||||||
@@ -3711,6 +3724,14 @@ please-upgrade-node@^3.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver-compare "^1.0.0"
|
semver-compare "^1.0.0"
|
||||||
|
|
||||||
|
plist@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe"
|
||||||
|
integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.5.1"
|
||||||
|
xmlbuilder "^9.0.7"
|
||||||
|
|
||||||
prepend-http@^1.0.1:
|
prepend-http@^1.0.1:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||||
@@ -5037,6 +5058,16 @@ xdg-basedir@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||||
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
||||||
|
|
||||||
|
xmlbuilder@>=11.0.1:
|
||||||
|
version "15.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
|
||||||
|
integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==
|
||||||
|
|
||||||
|
xmlbuilder@^9.0.7:
|
||||||
|
version "9.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
|
||||||
|
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
|
||||||
|
|
||||||
xtend@~4.0.1:
|
xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
|||||||
Reference in New Issue
Block a user