diff --git a/src/electron/ElectronFuseFailures.ts b/src/electron/ElectronFuseFailures.ts index e7658c3a03..8c1bedca37 100644 --- a/src/electron/ElectronFuseFailures.ts +++ b/src/electron/ElectronFuseFailures.ts @@ -25,6 +25,7 @@ import { } from "../filesystem/RegularFile.js"; import { ElectronFusePathFailure } from "./ElectronFusePath.js"; import { + ELECTRON_FUSE_LIMITS, ElectronFuseFormatFailure, ElectronFuseResourceFailure, } from "./ElectronFuseReader.js"; @@ -91,9 +92,21 @@ export const electronFuseFailure = ( cause.boundary === "file-size" ? "file-size" : "capacity", cause.message, cause.boundary === "file-size" - ? { maximum_input_bytes: 1024 * 1024 * 1024 } - : { maximum_sentinels_per_slice: 32 }, - { cause }, + ? { maximum_input_bytes: ELECTRON_FUSE_LIMITS.maximumFileBytes } + : { + maximum_sentinels_per_slice: + ELECTRON_FUSE_LIMITS.maximumSentinelsPerSlice, + }, + { + cause, + // Both bounds are fixed reader limits with no caller override, so + // neither waiting nor a larger reply allowance admits the same file. + retryable: false, + remediationAction: + cause.boundary === "file-size" + ? "Select an Electron executable within maximum_input_bytes; this fixed reader bound has no override, and retrying the unchanged file will fail again." + : "Select an Electron executable whose slices each contain at most maximum_sentinels_per_slice fuse sentinels; this fixed reader bound has no override, and retrying the unchanged file will fail again.", + }, ); if (cause instanceof ElectronFuseFormatFailure) return cause.kind === "unsupported" diff --git a/src/electron/ElectronFuseProvider.test.ts b/src/electron/ElectronFuseProvider.test.ts index dd6227caaf..c02376c7cc 100644 --- a/src/electron/ElectronFuseProvider.test.ts +++ b/src/electron/ElectronFuseProvider.test.ts @@ -573,10 +573,16 @@ it("enforces the 32 sentinel boundary with typed and public limit details", asyn expect(result.ok).toBe(false); if (!result.ok) { expect(result.error._tag).toBe("AnalysisResourceConstraintError"); - expect(projectAnalysisError(result.error)).toMatchObject({ + const projected = projectAnalysisError(result.error); + expect(projected).toMatchObject({ code: "resource_constraint", + retryable: false, details: { reported_limits: { maximum_sentinels_per_slice: 32 } }, }); + expect(projected.remediation.action).toContain( + "maximum_sentinels_per_slice", + ); + expect(projected.remediation.action).not.toMatch(/wait/iu); } } } @@ -591,10 +597,14 @@ it("rejects a sparse artifact over 1 GiB before scanning", async () => { expect(result.ok).toBe(false); if (!result.ok) { expect(result.error._tag).toBe("AnalysisResourceConstraintError"); - expect(projectAnalysisError(result.error)).toMatchObject({ + const projected = projectAnalysisError(result.error); + expect(projected).toMatchObject({ code: "resource_constraint", + retryable: false, details: { reported_limits: { maximum_input_bytes: 1024 * 1024 * 1024 } }, }); + expect(projected.remediation.action).toContain("maximum_input_bytes"); + expect(projected.remediation.action).not.toMatch(/allowance/iu); } });