[GH-ISSUE #7413] [Bug] analyze-javascript-application loses the case predicate on a recovered Electron bridge path #4418

Closed
opened 2026-10-31 00:49:38 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 27, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7413

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

The application-analysis CLI recovers an api.ping bridge-member path from an exposure inside the sole case "win32" branch of switch (process.platform), but its trace edges and terminal observation contain empty conditions. The graph reports complete coverage. The equivalent if input retains platform === "win32" and reports partial coverage. Both commands exit 0 and retain one bridge API and one paired renderer transmission. The recovered paths still say resolution: "candidate"; the failure is missing source predicate evidence, not a claim of observed runtime execution.

Reproduction

  1. Use the prepared baseline checkout, with its locked dependencies and built runtime, as a directory named rea beside an empty directory named repro. Start in repro with Node 24.22.1 on PATH. No Electron installation is required.
  2. Run this program. It creates the inert fixtures inline, isolates HOME, invokes the public CLI using relative paths, and reads the complete exported Evidence. It does not execute the fixture JavaScript.
python3 - <<'PYREPRO'
import json
import os
from pathlib import Path
import subprocess

Path("home").mkdir()
Path("app").mkdir()
Path("app/package.json").write_text('{"name":"switch-condition-probe","version":"1.0.0","main":"main.js"}\n')
Path("app/main.js").write_text('const { BrowserWindow, ipcMain } = require("electron");\nipcMain.handle("ping", () => "pong");\nnew BrowserWindow({ webPreferences: { preload: "preload.js" } });\n')
sources = {
    "switch": 'const { contextBridge, ipcRenderer } = require("electron");\nswitch (process.platform) {\n  case "win32":\n    contextBridge.exposeInMainWorld("api", { ping: () => ipcRenderer.invoke("ping") });\n}\n',
    "if": 'const { contextBridge, ipcRenderer } = require("electron");\nconst platform = process.platform;\nif (platform === "win32") {\n  contextBridge.exposeInMainWorld("api", { ping: () => ipcRenderer.invoke("ping") });\n}\n',
}
environment = dict(os.environ, HOME=str(Path("home").resolve()))
for name, source in sources.items():
    Path("app/preload.js").write_text(source)
    result = subprocess.run(
        ["node", "../rea/scripts/rea.mjs", "analyze-javascript-application", "./app",
         "--detail", "summary", "--evidence_output", name + ".json", "--format", "json"],
        env=environment, text=True, capture_output=True, timeout=60,
    )
    Path(name + "-stdout.json").write_text(result.stdout)
    Path(name + "-stderr.txt").write_text(result.stderr)
    assert result.returncode == 0, (result.stdout, result.stderr)
    data = json.loads(Path(name + ".json").read_text())["records"][0]["normalized_result"]
    print(name, "exit", result.returncode, "bridge_apis", data["summary"]["context_bridge_apis"],
          "paired", data["summary"]["ipc"]["paired_renderer_transmissions"],
          "coverage", data["graph"]["coverage"]["status"])
    for edge in data["graph"]["edges"]:
        if edge["evidence"]["extractor"]["operation"] == "trace-electron-wrapper-path":
            print(edge["relation"], json.dumps([c["expression"] for c in edge["properties"]["conditions"]]))
PYREPRO
  1. Compare the output below: the switch omits the predicate on both trace edges, while the if retains it. Across independent CLI runs, the switch failed in all three attempts and the if retained its predicate in both control attempts. The program above accounts for one switch attempt and one control attempt.

Expected behavior

Under “What is reconstructed,” docs/javascript-artifact-reconstruction.md:539 states: “Recovered Electron paths retain source ranges, module identities, literal channels and branch conditions as static candidates.” The recovered api.ping path should retain the case-selection predicate and its source location, equivalent to process.platform === "win32" for this single-case input. If the syntax cannot be interpreted, its guard uncertainty should remain explicit. An empty condition array with complete graph coverage loses the documented branch evidence.

The documentation does not explicitly enumerate switch cases, but it gives no switch exception to the recovered-path condition contract. The equivalent ordinary-source if control demonstrates predicate retention for this same bridge path. Neither result establishes whether a runtime guard passes.

Impact and scope

Consumers of this static graph cannot recover the Windows case constraint from the path's condition metadata. The tested switch fixture also reports complete application coverage while its equivalent if reports partial coverage. Raw source and IPC facts remain available, and the pairing summary remains intact. These observations establish a bounded evidence-correctness defect; no target was executed and no runtime harm was observed.

Only a single case without a default, fallthrough, break, loop, or preceding early return was exercised. Other switch shapes, MCP execution, real-application frequency, and other platforms remain untested. No supported option restoring the predicate from unchanged input was demonstrated; the if version is a control that changes the artifact.

REA version

6.8.0 (source commit a905a71e31)

Runtime and platform

Node 24.22.1; macOS 27.0.1 arm64

Target and artifact context

Synthetic extracted application directory containing a package record, Electron main source, and preload source. All inputs are local inert JavaScript; no external provider or live Electron application was used.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed output from the reproduction program:

switch exit 0 bridge_apis 1 paired 1 coverage complete
calls []
maps_to []
if exit 0 bridge_apis 1 paired 1 coverage partial
calls ["platform === \"win32\""]
maps_to ["platform === \"win32\""]

The first switch replay also showed an api.ping terminal observation with conditions: [], resolution: "candidate", and terminal electron.ipcRenderer.invoke; the if terminal retained the same predicate as its edges. Both inputs parsed two JavaScript files with zero parse failures. The switch preload observation had parse_status: "complete"; the if preload had parse_status: "partial". The empty main callback does not produce a join-electron-wrapper-paths edge in this fixture, so the paired-transmission summary does not demonstrate a join-edge result.

Source-only (not executed as an instrumented trace): src/domain/javascript/electronWrappedCallPaths.ts:1228 starts the direct-call ancestor scan; its guard dispatch at line 1235 handles enclosing if/ternary/logical forms and block prefixes without a switch-case branch. src/application/javascript/ElectronBoundaryGraphCallPaths.ts:64 and src/application/javascript/ElectronBoundaryGraphCallPaths.ts:133 copy the produced conditions into path observations and trace edges.

Typed outcome or recovery expected

Analyze the unchanged single-case input and retain its case-selection constraint on the recovered bridge path and trace edges, with source provenance and static-candidate authority. Preserve the equivalent if control's predicate and the observed bridge/IPC summary facts. If switch interpretation remains unsupported, expose that uncertainty rather than describing the missing predicate as a completely reconstructed path.

Analysis or suspected cause

Source-only (not executed as an instrumented trace): direct exposure seeds begin with an empty condition list at src/domain/javascript/electronWrappedCallPaths.ts:1225; the ancestor scan reaches the exposure call without adding a SwitchCase predicate. The function-body walker handles conditional forms at src/domain/javascript/electronWrappedCallPaths.ts:1167 and logical forms at line 1183, then falls back to generic child traversal at line 1205, also without adding switch predicates. This explains the observed differential without requiring a join-projection failure. Correct handling of defaults, multiple cases and fallthrough was not investigated by this reproduction.

Additional context

Related closed reports: #2668 loses already-produced bridge conditions only during join projection; #3196 loses predicates from preceding conditional returns. This fixture loses a case-selection predicate at the recovered path itself, contains no preceding return, and still reproduces at the stated baseline after both fixes. No open issue with the same behavior and acceptance criteria was found.

Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7413 ### 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 The application-analysis CLI recovers an `api.ping` bridge-member path from an exposure inside the sole `case "win32"` branch of `switch (process.platform)`, but its trace edges and terminal observation contain empty `conditions`. The graph reports complete coverage. The equivalent `if` input retains `platform === "win32"` and reports partial coverage. Both commands exit 0 and retain one bridge API and one paired renderer transmission. The recovered paths still say `resolution: "candidate"`; the failure is missing source predicate evidence, not a claim of observed runtime execution. ### Reproduction 1. Use the prepared baseline checkout, with its locked dependencies and built runtime, as a directory named `rea` beside an empty directory named `repro`. Start in `repro` with Node 24.22.1 on PATH. No Electron installation is required. 2. Run this program. It creates the inert fixtures inline, isolates HOME, invokes the public CLI using relative paths, and reads the complete exported Evidence. It does not execute the fixture JavaScript. ```sh python3 - <<'PYREPRO' import json import os from pathlib import Path import subprocess Path("home").mkdir() Path("app").mkdir() Path("app/package.json").write_text('{"name":"switch-condition-probe","version":"1.0.0","main":"main.js"}\n') Path("app/main.js").write_text('const { BrowserWindow, ipcMain } = require("electron");\nipcMain.handle("ping", () => "pong");\nnew BrowserWindow({ webPreferences: { preload: "preload.js" } });\n') sources = { "switch": 'const { contextBridge, ipcRenderer } = require("electron");\nswitch (process.platform) {\n case "win32":\n contextBridge.exposeInMainWorld("api", { ping: () => ipcRenderer.invoke("ping") });\n}\n', "if": 'const { contextBridge, ipcRenderer } = require("electron");\nconst platform = process.platform;\nif (platform === "win32") {\n contextBridge.exposeInMainWorld("api", { ping: () => ipcRenderer.invoke("ping") });\n}\n', } environment = dict(os.environ, HOME=str(Path("home").resolve())) for name, source in sources.items(): Path("app/preload.js").write_text(source) result = subprocess.run( ["node", "../rea/scripts/rea.mjs", "analyze-javascript-application", "./app", "--detail", "summary", "--evidence_output", name + ".json", "--format", "json"], env=environment, text=True, capture_output=True, timeout=60, ) Path(name + "-stdout.json").write_text(result.stdout) Path(name + "-stderr.txt").write_text(result.stderr) assert result.returncode == 0, (result.stdout, result.stderr) data = json.loads(Path(name + ".json").read_text())["records"][0]["normalized_result"] print(name, "exit", result.returncode, "bridge_apis", data["summary"]["context_bridge_apis"], "paired", data["summary"]["ipc"]["paired_renderer_transmissions"], "coverage", data["graph"]["coverage"]["status"]) for edge in data["graph"]["edges"]: if edge["evidence"]["extractor"]["operation"] == "trace-electron-wrapper-path": print(edge["relation"], json.dumps([c["expression"] for c in edge["properties"]["conditions"]])) PYREPRO ``` 3. Compare the output below: the switch omits the predicate on both trace edges, while the `if` retains it. Across independent CLI runs, the switch failed in all three attempts and the `if` retained its predicate in both control attempts. The program above accounts for one switch attempt and one control attempt. ### Expected behavior Under “What is reconstructed,” `docs/javascript-artifact-reconstruction.md:539` states: “Recovered Electron paths retain source ranges, module identities, literal channels and branch conditions as static candidates.” The recovered `api.ping` path should retain the case-selection predicate and its source location, equivalent to `process.platform === "win32"` for this single-case input. If the syntax cannot be interpreted, its guard uncertainty should remain explicit. An empty condition array with complete graph coverage loses the documented branch evidence. The documentation does not explicitly enumerate switch cases, but it gives no switch exception to the recovered-path condition contract. The equivalent ordinary-source `if` control demonstrates predicate retention for this same bridge path. Neither result establishes whether a runtime guard passes. ### Impact and scope Consumers of this static graph cannot recover the Windows case constraint from the path's condition metadata. The tested switch fixture also reports complete application coverage while its equivalent `if` reports partial coverage. Raw source and IPC facts remain available, and the pairing summary remains intact. These observations establish a bounded evidence-correctness defect; no target was executed and no runtime harm was observed. Only a single case without a default, fallthrough, break, loop, or preceding early return was exercised. Other switch shapes, MCP execution, real-application frequency, and other platforms remain untested. No supported option restoring the predicate from unchanged input was demonstrated; the `if` version is a control that changes the artifact. ### REA version 6.8.0 (source commit a905a71e31f71457cb21fd056b234f872126974e) ### Runtime and platform Node 24.22.1; macOS 27.0.1 arm64 ### Target and artifact context Synthetic extracted application directory containing a package record, Electron main source, and preload source. All inputs are local inert JavaScript; no external provider or live Electron application was used. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed output from the reproduction program: ```text switch exit 0 bridge_apis 1 paired 1 coverage complete calls [] maps_to [] if exit 0 bridge_apis 1 paired 1 coverage partial calls ["platform === \"win32\""] maps_to ["platform === \"win32\""] ``` The first switch replay also showed an `api.ping` terminal observation with `conditions: []`, `resolution: "candidate"`, and terminal `electron.ipcRenderer.invoke`; the `if` terminal retained the same predicate as its edges. Both inputs parsed two JavaScript files with zero parse failures. The switch preload observation had `parse_status: "complete"`; the `if` preload had `parse_status: "partial"`. The empty main callback does not produce a `join-electron-wrapper-paths` edge in this fixture, so the paired-transmission summary does not demonstrate a join-edge result. Source-only (not executed as an instrumented trace): `src/domain/javascript/electronWrappedCallPaths.ts:1228` starts the direct-call ancestor scan; its guard dispatch at line 1235 handles enclosing if/ternary/logical forms and block prefixes without a switch-case branch. `src/application/javascript/ElectronBoundaryGraphCallPaths.ts:64` and `src/application/javascript/ElectronBoundaryGraphCallPaths.ts:133` copy the produced conditions into path observations and trace edges. ### Typed outcome or recovery expected Analyze the unchanged single-case input and retain its case-selection constraint on the recovered bridge path and trace edges, with source provenance and static-candidate authority. Preserve the equivalent `if` control's predicate and the observed bridge/IPC summary facts. If switch interpretation remains unsupported, expose that uncertainty rather than describing the missing predicate as a completely reconstructed path. ### Analysis or suspected cause Source-only (not executed as an instrumented trace): direct exposure seeds begin with an empty condition list at `src/domain/javascript/electronWrappedCallPaths.ts:1225`; the ancestor scan reaches the exposure call without adding a SwitchCase predicate. The function-body walker handles conditional forms at `src/domain/javascript/electronWrappedCallPaths.ts:1167` and logical forms at line 1183, then falls back to generic child traversal at line 1205, also without adding switch predicates. This explains the observed differential without requiring a join-projection failure. Correct handling of defaults, multiple cases and fallthrough was not investigated by this reproduction. ### Additional context Related closed reports: [#2668](https://github.com/morluto/rea/issues/2668) loses already-produced bridge conditions only during join projection; [#3196](https://github.com/morluto/rea/issues/3196) loses predicates from preceding conditional returns. This fixture loses a case-selection predicate at the recovered path itself, contains no preceding return, and still reproduces at the stated baseline after both fixes. No open issue with the same behavior and acceptance criteria was found.
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#4418
No description provided.