-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ASAR files in extraResources
are not included in integrity calculations
#8660
Comments
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days. |
Still an issue AFAICT |
I migrated electron-builder to |
That unfortunately does not seem to have resolved the issue
the
The issue looks to be that https://github.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/platformPackager.ts#L318 happens after |
What are these?
Can you provide a sample repo for this that I could debug deeper with? AFAICT currently, there is no issue here per the reasoning I gave above. |
Indeed, but one can have multiple asars. The only rule with asar integrity is that the electron main process code lives in I've dug a bit deeper and there's another slightly different issue for macOS Universal builds due to the Info.plist being mangled by The non-universal issue with additional asars added via resources can be fixed via a patch akin to Subject: [PATCH] Remove stale handler for `extend-info`
---
Index: packages/app-builder-lib/src/asar/integrity.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/packages/app-builder-lib/src/asar/integrity.ts b/packages/app-builder-lib/src/asar/integrity.ts
--- a/packages/app-builder-lib/src/asar/integrity.ts (revision 443ee8debd4e4d73d9f63a31fb93ce1dbed2538c)
+++ b/packages/app-builder-lib/src/asar/integrity.ts (date 1737562196873)
@@ -1,13 +1,17 @@
import BluebirdPromise from "bluebird-lst"
+import { FilterStats, statOrNull, walk } from "builder-util"
import { createHash } from "crypto"
import { createReadStream } from "fs"
import { readdir } from "fs/promises"
import * as path from "path"
+import { FileMatcher } from "../fileMatcher"
import { readAsarHeader, NodeIntegrity } from "./asar"
export interface AsarIntegrityOptions {
readonly resourcesPath: string
+ readonly resourcesFinalPath: string
readonly resourcesRelativePath: string
+ readonly extraResourceMatchers: Array<FileMatcher> | null
}
export interface HeaderHash {
@@ -19,12 +23,48 @@
[key: string]: HeaderHash
}
-export async function computeData({ resourcesPath, resourcesRelativePath }: AsarIntegrityOptions): Promise<AsarIntegrity> {
+const asarFilter = (file: string) => {
+ return file.endsWith(".asar")
+}
+
+export async function computeData({ resourcesPath, resourcesRelativePath, resourcesFinalPath, extraResourceMatchers }: AsarIntegrityOptions): Promise<AsarIntegrity> {
// sort to produce constant result
- const names = (await readdir(resourcesPath)).filter(it => it.endsWith(".asar")).sort()
+ const names = (await readdir(resourcesPath)).filter(asarFilter).sort()
const checksums = await BluebirdPromise.map(names, it => hashHeader(path.join(resourcesPath, it)))
const result: AsarIntegrity = {}
+
+ if (extraResourceMatchers != null && extraResourceMatchers.length > 0) {
+ const matches = (
+ await BluebirdPromise.map(extraResourceMatchers, async (matcher: FileMatcher): Promise<[realPath: string, targetPath: string][]> => {
+ const fromStat = await statOrNull(matcher.from)
+ if (fromStat == null) {
+ return []
+ }
+
+ if (fromStat.isFile()) {
+ if (asarFilter(matcher.from)) {
+ return [[matcher.from, matcher.to] as const]
+ }
+ return []
+ }
+
+ if (matcher.isEmpty() || matcher.containsOnlyIgnore()) {
+ matcher.prependPattern("**/*")
+ }
+ const matcherFilter = matcher.createFilter()
+ const results = await walk(matcher.from, (file: string, stats: FilterStats) => matcherFilter(file, stats) && asarFilter(file))
+ return results.map(it => [it, matcher.to] as const)
+ })
+ ).flat(1)
+
+ await BluebirdPromise.each(matches, async ([realPath, targetPath]) => {
+ const prefix = path.relative(resourcesFinalPath, targetPath)
+ const key = path.join(resourcesRelativePath, prefix, path.basename(realPath))
+ result[key] = await hashHeader(realPath)
+ })
+ }
+
for (let i = 0; i < names.length; i++) {
result[path.join(resourcesRelativePath, names[i])] = checksums[i]
}
Index: packages/app-builder-lib/src/platformPackager.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/packages/app-builder-lib/src/platformPackager.ts b/packages/app-builder-lib/src/platformPackager.ts
--- a/packages/app-builder-lib/src/platformPackager.ts (revision 443ee8debd4e4d73d9f63a31fb93ce1dbed2538c)
+++ b/packages/app-builder-lib/src/platformPackager.ts (date 1737562196907)
@@ -301,11 +301,12 @@
if (framework.beforeCopyExtraFiles != null) {
const resourcesRelativePath = this.platform === Platform.MAC ? "Resources" : isElectronBased(framework) ? "resources" : ""
+ const resourcesFinalPath = this.getResourcesDir(appOutDir)
await framework.beforeCopyExtraFiles({
packager: this,
appOutDir,
- asarIntegrity: asarOptions == null || disableAsarIntegrity ? null : await computeData({ resourcesPath, resourcesRelativePath }),
+ asarIntegrity: asarOptions == null || disableAsarIntegrity ? null : await computeData({ resourcesPath, resourcesFinalPath, resourcesRelativePath, extraResourceMatchers }),
platformName,
})
} With this patch, the Windows INTEGRITY ELECTRONASAR value is Alternatively, would be cleaner if one could generate the asar integrity after copying resources, but that seemed like a more invasive change.
I can but definitely not right now, spent too much time digging into this already.
|
This is brilliant documentation, thank you so much. I'll take a look at this later tonight or at least this week. |
I managed to come up with a user-land workeraround for macOS Universal packages, will tackle Windows tomorrow
fortunately most of the guts of app-builder-lib responsible were exported |
Managed a workaround for Windows & macOS in element-hq/element-desktop#1979 - I call the following in async function injectAsarIntegrity(context: AfterPackContext) {
const packager = context.packager;
if (packager.platform === Platform.LINUX) return; // no ASAR integrity support on Linux
if (packager.platform === Platform.MAC && context.arch !== Arch.universal) return; // We only need to generate asar on universal Mac builds
const framework = packager.info.framework;
const resourcesPath = packager.getResourcesDir(context.appOutDir);
const resourcesRelativePath =
packager.platform === Platform.MAC ? "Resources" : isElectronBased(framework) ? "resources" : "";
const asarIntegrity = await computeData({
resourcesPath,
resourcesRelativePath,
});
if (packager.platform === Platform.WINDOWS) {
const executablePath = path.join(context.appOutDir, `${packager.appInfo.productFilename}.exe`);
const buffer = await readFile(executablePath);
const executable = NtExecutable.from(buffer);
const resource = NtExecutableResource.from(executable);
const integrityList = Array.from(Object.entries(asarIntegrity)).map(
([file, { algorithm: alg, hash: value }]) => ({
file: path.win32.normalize(file),
alg,
value,
}),
);
// We edit the resource that electron-builder already wrote to ensure it includes all asar files
const electronAsarResource = resource.entries.find((entry) => entry.id === "ELECTRONASAR")!;
electronAsarResource.bin = Buffer.from(JSON.stringify(integrityList));
resource.outputResource(executable);
await writeFile(executablePath, Buffer.from(executable.generate()));
} else if (packager.platform === Platform.MAC) {
const plistPath = path.join(resourcesPath, "..", "Info.plist");
const data = plist.parse(await readFile(plistPath, "utf8")) as unknown as Writable<plist.PlistObject>;
data["ElectronAsarIntegrity"] = asarIntegrity as unknown as Writable<plist.PlistValue>;
await writeFile(plistPath, plist.build(data));
}
} |
This is fantastic and definitely wasn't a use case (multiple asar's) I was aware of during implementation. I'll try and work on a PR (with you CC'd for credit) this weekend unless you'd like to open one before then. |
I'd prefer to defer to you on doing it in the most appropriate way for the project, my patch in #8660 (comment) feels awful and if you can refactor to do asar integrity after copying extraResources it'd be a lot cleaner but it wasn't an avenue I was comfortable in without fear of breaking everything else |
Quick update, implementation is done, just trying to set up some unit tests for it now |
@t3chguy released in |
@mmaietta amazing work, did hit one issue though unfortunately: #8812 - seems to be running happily otherwise! (though am still needing to re-calculate asar integrity for macOS universal builds in afterPack due to electron/universal#116 ) |
electron-builder does a seemingly fine job supporting
EnableEmbeddedAsarIntegrityValidation
unless yourextraResources
contains additionalasar
files. The files are not considered in the ASAR integrity resolution at https://github.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/platformPackager.ts#L305-L310 as theextraResources
are not copied until https://github.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/src/platformPackager.ts#L318Empirically the
computeData
function is never called for mac universal packages so the fix there would need to be different.The text was updated successfully, but these errors were encountered: