[GH-ISSUE #3474] [Bug] capture_electron_scenario active hook does not block or record shell.trashItem #1299

Closed
opened 2026-10-16 21:03:54 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @keeponlight on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3474

Before submitting

  • I searched the open and closed issues for a related report. (Related: #3452 noted shell.trashItem as an untested conjecture while reporting app.moveToApplicationsFolder.)
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Process capture or runtime observation

Observed behavior

In scripts/electron-active-hook-boundaries.cjs, patchShell instruments electron.shell:

const patchShell = (shell) => {
  if (shell === null || shell === undefined) return;
  for (const method of ["openExternal", "openPath"]) {
    const original = shell[method];
    if (typeof original !== "function") continue;
    shell[method] = function blockedShellCall(...args) {
      recordRuntime("shell-attempt", method, "blocked", {
        argument_shape_values: args,
        error: true,
      });
      return Promise.reject(new Error("External shell effects are blocked"));
    };
  }
};

While shell.openExternal and shell.openPath are intercepted, shell.trashItem is omitted. When an inspected application calls shell.trashItem(fullPath) during an active scenario, the call is neither blocked nor intercepted. The target moves host files to the OS Trash without generating any shell-attempt event on the capture timeline.

Reproduction

Executed harness verification on revision cbc0e97 (Node v24.14.1, macOS Darwin 24.6.0 arm64):

node -e '
const { createElectronActiveBoundaryPatches } = require("./scripts/electron-active-hook-boundaries.cjs");
const recorded = [];
const patches = createElectronActiveBoundaryPatches({
  identity: (x) => "shell",
  patchEmitter: () => {},
  processType: "browser",
  recordIpc: () => {},
  recordRuntime: (kind, event, phase, meta) => recorded.push({ kind, event, phase, meta }),
  shape: (x) => typeof x,
});
let trashed = false;
const shell = {
  openExternal: () => Promise.resolve(),
  openPath: () => Promise.resolve(),
  trashItem: () => { trashed = true; return Promise.resolve(); }
};
patches.patchShell(shell);
shell.openExternal("https://example.invalid").catch(() => {});
shell.trashItem("/path/to/host/file");
console.log("trashed:", trashed);
console.log("events:", recorded.map(e => `${e.kind}:${e.event}:${e.phase}`));
'

Output:

trashed: true
events: [ 'shell-attempt:openExternal:blocked' ]

shell.trashItem ran through to the underlying implementation (trashed: true), was not blocked, and recorded no shell-attempt event.

Expected behavior

docs/electron-observation.md specifies:

"The active hook blocks and records external shell/navigation, permission, download, popup, updater, and OS-integration effects."

shell.trashItem is a native OS-integration and shell effect that directly alters host filesystem state by moving files to Trash. It should be included in patchShell, reject with new Error("External shell effects are blocked"), and record a shell-attempt event with phase blocked.

Impact and scope

An inspected Electron application can alter the host filesystem by trashing files during active scenario capture without the operation being contained or evidenced in the timeline. While the documentation notes that active process ownership does not provide an OS sandbox, leaving shell.trashItem uninstrumented contradicts the declared contract that external shell effects are blocked and recorded.

REA version

Source cbc0e97 (main)

Runtime and platform

Node 24.14.1; macOS Darwin 24.6.0 arm64

Originally created by @keeponlight on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3474 ### Before submitting - [x] I searched the open and closed issues for a related report. (Related: #3452 noted shell.trashItem as an untested conjecture while reporting app.moveToApplicationsFolder.) - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Process capture or runtime observation ### Observed behavior In `scripts/electron-active-hook-boundaries.cjs`, `patchShell` instruments `electron.shell`: ```javascript const patchShell = (shell) => { if (shell === null || shell === undefined) return; for (const method of ["openExternal", "openPath"]) { const original = shell[method]; if (typeof original !== "function") continue; shell[method] = function blockedShellCall(...args) { recordRuntime("shell-attempt", method, "blocked", { argument_shape_values: args, error: true, }); return Promise.reject(new Error("External shell effects are blocked")); }; } }; ``` While `shell.openExternal` and `shell.openPath` are intercepted, `shell.trashItem` is omitted. When an inspected application calls `shell.trashItem(fullPath)` during an active scenario, the call is neither blocked nor intercepted. The target moves host files to the OS Trash without generating any `shell-attempt` event on the capture timeline. ### Reproduction Executed harness verification on revision `cbc0e97` (Node v24.14.1, macOS Darwin 24.6.0 arm64): ```bash node -e ' const { createElectronActiveBoundaryPatches } = require("./scripts/electron-active-hook-boundaries.cjs"); const recorded = []; const patches = createElectronActiveBoundaryPatches({ identity: (x) => "shell", patchEmitter: () => {}, processType: "browser", recordIpc: () => {}, recordRuntime: (kind, event, phase, meta) => recorded.push({ kind, event, phase, meta }), shape: (x) => typeof x, }); let trashed = false; const shell = { openExternal: () => Promise.resolve(), openPath: () => Promise.resolve(), trashItem: () => { trashed = true; return Promise.resolve(); } }; patches.patchShell(shell); shell.openExternal("https://example.invalid").catch(() => {}); shell.trashItem("/path/to/host/file"); console.log("trashed:", trashed); console.log("events:", recorded.map(e => `${e.kind}:${e.event}:${e.phase}`)); ' ``` Output: ``` trashed: true events: [ 'shell-attempt:openExternal:blocked' ] ``` `shell.trashItem` ran through to the underlying implementation (`trashed: true`), was not blocked, and recorded no `shell-attempt` event. ### Expected behavior `docs/electron-observation.md` specifies: > "The active hook blocks and records external shell/navigation, permission, download, popup, updater, and OS-integration effects." `shell.trashItem` is a native OS-integration and shell effect that directly alters host filesystem state by moving files to Trash. It should be included in `patchShell`, reject with `new Error("External shell effects are blocked")`, and record a `shell-attempt` event with phase `blocked`. ### Impact and scope An inspected Electron application can alter the host filesystem by trashing files during active scenario capture without the operation being contained or evidenced in the timeline. While the documentation notes that active process ownership does not provide an OS sandbox, leaving `shell.trashItem` uninstrumented contradicts the declared contract that external shell effects are blocked and recorded. ### REA version Source `cbc0e97` (main) ### Runtime and platform Node 24.14.1; macOS Darwin 24.6.0 arm64
Author
Owner

@matbrgz commented on GitHub (Oct 16, 2026):

Cross-referencing with sibling issue #3452 (capture_electron_scenario does not block app.moveToApplicationsFolder). Both issues stem from the same defect boundary in scripts/electron-active-hook-boundaries.cjs, where host-mutating Electron runtime calls (app.moveToApplicationsFolder and shell.trashItem) are neither intercepted/blocked nor recorded during active scenario capture.

Both reports have been indexed under Cluster F (Electron Active Hook Host Filesystem Mutation Bypass) in the consolidation tracker: #4330.

<!-- gh-comment-id:6206896302 --> @matbrgz commented on GitHub (Oct 16, 2026): Cross-referencing with sibling issue #3452 (`capture_electron_scenario does not block app.moveToApplicationsFolder`). Both issues stem from the same defect boundary in `scripts/electron-active-hook-boundaries.cjs`, where host-mutating Electron runtime calls (`app.moveToApplicationsFolder` and `shell.trashItem`) are neither intercepted/blocked nor recorded during active scenario capture. Both reports have been indexed under **Cluster F (Electron Active Hook Host Filesystem Mutation Bypass)** in the consolidation tracker: #4330.
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#1299
No description provided.