From c6166d4b56d6c6e4ad33980464edc8697672168a Mon Sep 17 00:00:00 2001 From: Avocado Date: Wed, 14 Oct 2026 09:39:27 +1100 Subject: [PATCH] fix(javascript): retain bridge conditions on IPC joins (#2747) * fix(javascript): retain bridge conditions on IPC joins * fix(javascript): retain IPC condition ownership * style(javascript): format bridge path regression * fix(process): report no remaining time after a provider deadline fires Node timers can fire while performance.now() is still a fraction of a millisecond short of the monotonic deadline. ProviderStartupDeadline then reported interruption "timeout" and an aborted signal while remainingMs() returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0 (Hopper and Ghidra startup) could run past an elapsed deadline, and the real-timer test "aborts a bounded wait when its real monotonic deadline expires" failed intermittently in CI (expected 1 to be 0). Once the deadline's own timer has classified a timeout, report zero remaining time. A deterministic regression test fires the timer with the monotonic clock 0.4 ms short of the deadline. Co-Authored-By: Claude Opus 5.5 * fix(javascript): preserve Electron join condition coverage --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 Co-authored-by: Codex --- .../ElectronBoundaryGraphCallPaths.ts | 3 +- .../cli/electronWrappedCallPaths.test.ts | 44 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/application/javascript/ElectronBoundaryGraphCallPaths.ts b/src/application/javascript/ElectronBoundaryGraphCallPaths.ts index b815e82bf..d1f1886db 100644 --- a/src/application/javascript/ElectronBoundaryGraphCallPaths.ts +++ b/src/application/javascript/ElectronBoundaryGraphCallPaths.ts @@ -165,7 +165,8 @@ export const addElectronCallPaths = ( pairing_basis: "unique-exact-literal-channel", resolution: "candidate", handler_path: to.file.path, - conditions: to.path.conditions, + bridge_conditions: from.path.conditions, + handler_conditions: to.path.conditions, }, limitations: [ "The unique static channel match does not establish runtime registration, sender acceptance, or dispatch.", diff --git a/tests/boundary/cli/electronWrappedCallPaths.test.ts b/tests/boundary/cli/electronWrappedCallPaths.test.ts index 4dd0d02a8..186abcbbe 100644 --- a/tests/boundary/cli/electronWrappedCallPaths.test.ts +++ b/tests/boundary/cli/electronWrappedCallPaths.test.ts @@ -55,7 +55,8 @@ cliTest( expect( byId.get(edge.target_node_id)?.observations[0]?.properties.channel, ).toBe(edge.properties.channel); - expect(edge.properties.conditions).toEqual([ + expect(edge.properties.bridge_conditions).toEqual([]); + expect(edge.properties.handler_conditions).toEqual([ expect.objectContaining({ expression: "allowed(event)" }), ]); } @@ -495,3 +496,44 @@ cliTest( ).toEqual(["empty:key"]); }, ); + +cliTest( + "retains bridge and handler conditions on Electron IPC join edges", + async ({ cli }) => { + const root = await createTestTempDirectory("rea-electron-join-conditions-"); + await writeFixtureFiles(root, { + "preload.js": `const e=require("electron"); + e.contextBridge.exposeInMainWorld("desktop", { + choose: flag + ? () => e.ipcRenderer.send("test:copy") + : () => e.ipcRenderer.send("test:cut") + });`, + "main.js": `const e=require("electron"); + if(!flag)e.ipcMain.on("test:copy",event=>allowed(event)&&event.sender.copy()); + if(flag)e.ipcMain.on("test:cut",event=>allowed(event)&&event.sender.cut());`, + }); + const { graph } = await analyzeJavaScriptDirectory("CLI", cli, root); + const joins = graph.edges.filter( + (edge) => + edge.evidence.extractor.operation === "join-electron-wrapper-paths", + ); + expect(joins).toHaveLength(2); + for (const [channel, bridgeCondition] of [ + ["test:copy", "flag"], + ["test:cut", "!(flag)"], + ]) { + const edge = joins.find( + (candidate) => candidate.properties.channel === channel, + ); + expect(edge?.properties.bridge_conditions).toEqual([ + expect.objectContaining({ expression: bridgeCondition }), + ]); + expect(edge?.properties.handler_conditions).toEqual([ + expect.objectContaining({ + expression: channel === "test:copy" ? "!flag" : "flag", + }), + expect.objectContaining({ expression: "allowed(event)" }), + ]); + } + }, +);