[GH-ISSUE #8118] [Bug] A literal IPC send is reported unpaired beside an ipcMain handler on an unresolved channel #4680

Closed
opened 2026-10-31 00:52:22 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @kaoru0822-kitauji on GitHub (Oct 30, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8118

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

analyze-javascript-application reports a literal renderer send as unpaired (summary.ipc.unpaired_literal_renderer_transmissions) when the main process registers a compatible handler on an unresolved channel through ipcMain. That handler may receive the send at runtime. The same computed handler registered on win.webContents.ipc already makes the send ambiguous.

Reproduction

Reproduced on every run on main 317bf3fe2 through the CLI:

  1. main.js:

    const { BrowserWindow, ipcMain } = require("electron");
    const path = require("node:path");
    new BrowserWindow({ webPreferences: { preload: path.join(__dirname, "preload.js") } });
    ipcMain.on(process.env.CHANNEL, () => {});
    
  2. preload.js: require("electron").ipcRenderer.send("app:log", 1);

  3. rea analyze-javascript-application <dir> --format json

  4. Observe ambiguous_renderer_transmissions: 0 and unpaired_literal_renderer_transmissions: 1.

If the handler line is win.webContents.ipc.on(process.env.CHANNEL, …), the result is ambiguous: 1, unpaired: 0. Two more shapes give the same wrong result:

  • A wrapper function register(ch, h) { ipcMain.on(ch, h) } called once with a literal and once with process.env.CHANNEL: the send is reported paired.
  • The same wrapper exported with module.exports = register: the send is reported unpaired.

Expected behavior

An unresolved handler channel is unknown applicability, and AGENTS.md says that must not be treated as absence. A literal send of a compatible mode beside such a handler should be ambiguous, not unpaired or paired. "Dynamic channels are never paired" (docs/javascript-artifact-reconstruction.md) still holds. A wrapper whose every invocation is a traced direct call with a literal channel is already represented by those per-invocation findings, so it should not change pairing.

Impact and scope

Pairing summaries and caller-to-handler joins overclaim that a renderer message has no main handler, or has exactly one, whenever an app registers handlers on computed channels. Real-app impact is untested; the 54 Electron docs fiddles and electron-api-demos do not use this pattern.

REA version

source main 317bf3fe2 (6.8.0)

Runtime and platform

Node 26.5.0; macOS 26 arm64

Target and artifact context

Directory Electron application, CommonJS

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

summary.ipc from the command above: {"main_handlers":1,"dynamic_channel_operations":1,"paired_renderer_transmissions":0,"ambiguous_renderer_transmissions":0,"unpaired_literal_renderer_transmissions":1}.

Analysis or suspected cause

Source-derived: createElectronIpcContext (src/application/javascript/ElectronBoundaryAnalysis.ts) treats an unresolved-channel handler as a competitor only when it has a scoped receiver. The wrapper pass (electronWrappedCallPaths.ts) seeds only calls whose first argument is a string literal, so a wrapper's computed or external call sites are never traced.

Originally created by @kaoru0822-kitauji on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8118 Originally assigned to: @kaoru0822-kitauji on GitHub. ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `analyze-javascript-application` reports a literal renderer send as unpaired (`summary.ipc.unpaired_literal_renderer_transmissions`) when the main process registers a compatible handler on an unresolved channel through `ipcMain`. That handler may receive the send at runtime. The same computed handler registered on `win.webContents.ipc` already makes the send `ambiguous`. ### Reproduction Reproduced on every run on main 317bf3fe2 through the CLI: 1. `main.js`: ```js const { BrowserWindow, ipcMain } = require("electron"); const path = require("node:path"); new BrowserWindow({ webPreferences: { preload: path.join(__dirname, "preload.js") } }); ipcMain.on(process.env.CHANNEL, () => {}); ``` 2. `preload.js`: `require("electron").ipcRenderer.send("app:log", 1);` 3. `rea analyze-javascript-application <dir> --format json` 4. Observe `ambiguous_renderer_transmissions: 0` and `unpaired_literal_renderer_transmissions: 1`. If the handler line is `win.webContents.ipc.on(process.env.CHANNEL, …)`, the result is `ambiguous: 1, unpaired: 0`. Two more shapes give the same wrong result: - A wrapper `function register(ch, h) { ipcMain.on(ch, h) }` called once with a literal and once with `process.env.CHANNEL`: the send is reported `paired`. - The same wrapper exported with `module.exports = register`: the send is reported `unpaired`. ### Expected behavior An unresolved handler channel is unknown applicability, and AGENTS.md says that must not be treated as absence. A literal send of a compatible mode beside such a handler should be `ambiguous`, not `unpaired` or `paired`. "Dynamic channels are never paired" (`docs/javascript-artifact-reconstruction.md`) still holds. A wrapper whose every invocation is a traced direct call with a literal channel is already represented by those per-invocation findings, so it should not change pairing. ### Impact and scope Pairing summaries and caller-to-handler joins overclaim that a renderer message has no main handler, or has exactly one, whenever an app registers handlers on computed channels. Real-app impact is untested; the 54 Electron docs fiddles and electron-api-demos do not use this pattern. ### REA version source main 317bf3fe2 (6.8.0) ### Runtime and platform Node 26.5.0; macOS 26 arm64 ### Target and artifact context Directory Electron application, CommonJS ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence `summary.ipc` from the command above: `{"main_handlers":1,"dynamic_channel_operations":1,"paired_renderer_transmissions":0,"ambiguous_renderer_transmissions":0,"unpaired_literal_renderer_transmissions":1}`. ### Analysis or suspected cause Source-derived: `createElectronIpcContext` (`src/application/javascript/ElectronBoundaryAnalysis.ts`) treats an unresolved-channel handler as a competitor only when it has a scoped `receiver`. The wrapper pass (`electronWrappedCallPaths.ts`) seeds only calls whose first argument is a string literal, so a wrapper's computed or external call sites are never traced.
bsakaguchi 2026-10-31 00:52:22 +00:00
  • closed this issue
  • added the
    bug
    triage
    labels
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4680
No description provided.