[GH-ISSUE #2561] fix(javascript): literal file URL conversion is recorded as a module path #879

Closed
opened 2026-10-15 18:29:14 +00:00 by bsakaguchi · 1 comment
Owner

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

Summary

Static analysis records an Electron preload as a file URL when the source explicitly converts that literal URL to a filesystem path with Node's fileURLToPath. The window observation and preload-role record both retain the URL and classify it as a module specifier. The tested expression denotes /app/preload file.js, but analysis reports file:///app/preload%20file.js.

Steps to reproduce

On the source baseline under Runtime or environment, place this probe beside the JavaScript static analyzer as src/domain/javascript/fileUrlProjection.test.ts, run npm run test:focused -- src/domain/javascript/fileUrlProjection.test.ts, then remove the probe. The source fixture is parsed as data; it does not launch Electron or read an application file.

import { expect, it } from 'vitest';
import { fileURLToPath } from 'node:url';
import { analyzeJavaScriptStaticSource } from './javascriptStaticAnalysis.js';

it('preserves literal file URL conversion semantics', () => {
  const source = [
    'import { fileURLToPath } from "node:url";',
    'import { BrowserWindow } from "electron";',
    'new BrowserWindow({ webPreferences: {',
    '  preload: fileURLToPath("file:///app/preload%20file.js")',
    '} });',
  ].join('\n');
  const analysis = analyzeJavaScriptStaticSource(source);
  const window = analysis.electron.browser_windows[0];
  const role = analysis.role_paths.find(entry => entry.role === 'preload');
  console.log(JSON.stringify({ parse: analysis.parse_status, window, role }));
  const expected = fileURLToPath('file:///app/preload%20file.js', { windows: false });
  expect(window?.preload_path).toBe(expected);
  expect(role?.path).toBe(expected);
  expect(role?.resolution_context).toBe('filesystem-expression');
});

The verifier executed the same imported-source expression through the complete static-source analysis function and compared the result with Node's nonexecuting URL decoder. Source-relative conversion and an unresolved identifier argument were checked separately as controls.

Expected behavior

A known literal file URL passed to Node's converter should be recorded as its decoded filesystem path with filesystem-expression context. If the analyzer cannot safely establish a path, it should retain that uncertainty rather than emit the original URL as a resolved module path. Source-relative conversion should keep its existing result.

Actual behavior

Parsing completed and recovered one BrowserWindow and one preload role. Both paths were file:///app/preload%20file.js, and both contexts were module-specifier. The expected decoded-path assertion failed. Node's control returned /app/preload file.js. The source-relative control returned ./preload.js, and a dynamic identifier argument remained unresolved. No application-level asset lookup or runtime Electron behavior was exercised.

Evidence

  • Expected source: AGENTS.md:29 distinguishes filesystem paths from file URLs; docs/javascript-artifact-reconstruction.md:212-228 describes literal Electron preload recovery. src/domain/javascript/javascriptStaticAnalysisHelpers.ts:179-205 recognizes inert conversion expressions and :237-266 implements decoding for the source-relative form.
  • Failure source: The executed source-analysis fixture emitted the contrary window and role records. src/domain/javascript/javascriptStaticAnalysisHelpers.ts:227-233 returns the literal URL unchanged, and :306-325 classifies it as a module specifier. The window and role producers consume these results at src/domain/javascript/electronStaticAnalysisBrowser.ts:107-117 and src/domain/javascript/javascriptStaticAnalysisCalls.ts:324-336.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The converter and constructor are explicitly imported. No shadowing, reassignment, parser failure, computed option name or dynamic argument is needed to reach the result. This concerns source expressions, independently of CLI JSON path normalization. The closed HTML script URL report concerns document-reference resolution and does not restore this expression's conversion semantics.

Restoration check

Repeat the fixture. The URL must no longer appear as a resolved module path in either window or role output. If a path is recovered, it must be /app/preload file.js with filesystem-expression context; otherwise the output must retain unresolved status accurately. Keep fileURLToPath(new URL("./preload.js", import.meta.url)) at ./preload.js, and keep a dynamic identifier unresolved. Include the simple file:///app/preload.js form as a control and preserve the source declaration as evidence without running it.

Runtime or environment

Source baseline: 8b181716ac. Node.js v26.10.0, npm 11.19.1 and locked dependencies. The focused Vitest v5.0.3 run completed with one passing observation/control test and one expected failing decoded-path test.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A valid inert conversion expression produces the wrong preload path and resolution context in both static window and role records. Observed impact is bounded to returned static evidence; downstream lookup and runtime consequences were not executed.
  • Workaround status: unverified
  • Workaround basis: Node's decoder produced the correct comparison path, but no alternative REA workflow was demonstrated that restores the returned records without changing the target source.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2561 ## Summary Static analysis records an Electron preload as a file URL when the source explicitly converts that literal URL to a filesystem path with Node's `fileURLToPath`. The window observation and preload-role record both retain the URL and classify it as a module specifier. The tested expression denotes `/app/preload file.js`, but analysis reports `file:///app/preload%20file.js`. ## Steps to reproduce On the source baseline under Runtime or environment, place this probe beside the JavaScript static analyzer as `src/domain/javascript/fileUrlProjection.test.ts`, run `npm run test:focused -- src/domain/javascript/fileUrlProjection.test.ts`, then remove the probe. The source fixture is parsed as data; it does not launch Electron or read an application file. ```ts import { expect, it } from 'vitest'; import { fileURLToPath } from 'node:url'; import { analyzeJavaScriptStaticSource } from './javascriptStaticAnalysis.js'; it('preserves literal file URL conversion semantics', () => { const source = [ 'import { fileURLToPath } from "node:url";', 'import { BrowserWindow } from "electron";', 'new BrowserWindow({ webPreferences: {', ' preload: fileURLToPath("file:///app/preload%20file.js")', '} });', ].join('\n'); const analysis = analyzeJavaScriptStaticSource(source); const window = analysis.electron.browser_windows[0]; const role = analysis.role_paths.find(entry => entry.role === 'preload'); console.log(JSON.stringify({ parse: analysis.parse_status, window, role })); const expected = fileURLToPath('file:///app/preload%20file.js', { windows: false }); expect(window?.preload_path).toBe(expected); expect(role?.path).toBe(expected); expect(role?.resolution_context).toBe('filesystem-expression'); }); ``` The verifier executed the same imported-source expression through the complete static-source analysis function and compared the result with Node's nonexecuting URL decoder. Source-relative conversion and an unresolved identifier argument were checked separately as controls. ## Expected behavior A known literal file URL passed to Node's converter should be recorded as its decoded filesystem path with filesystem-expression context. If the analyzer cannot safely establish a path, it should retain that uncertainty rather than emit the original URL as a resolved module path. Source-relative conversion should keep its existing result. ## Actual behavior Parsing completed and recovered one BrowserWindow and one preload role. Both paths were `file:///app/preload%20file.js`, and both contexts were `module-specifier`. The expected decoded-path assertion failed. Node's control returned `/app/preload file.js`. The source-relative control returned `./preload.js`, and a dynamic identifier argument remained unresolved. No application-level asset lookup or runtime Electron behavior was exercised. ## Evidence - Expected source: AGENTS.md:29 distinguishes filesystem paths from file URLs; docs/javascript-artifact-reconstruction.md:212-228 describes literal Electron preload recovery. src/domain/javascript/javascriptStaticAnalysisHelpers.ts:179-205 recognizes inert conversion expressions and :237-266 implements decoding for the source-relative form. - Failure source: The executed source-analysis fixture emitted the contrary window and role records. src/domain/javascript/javascriptStaticAnalysisHelpers.ts:227-233 returns the literal URL unchanged, and :306-325 classifies it as a module specifier. The window and role producers consume these results at src/domain/javascript/electronStaticAnalysisBrowser.ts:107-117 and src/domain/javascript/javascriptStaticAnalysisCalls.ts:324-336. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The converter and constructor are explicitly imported. No shadowing, reassignment, parser failure, computed option name or dynamic argument is needed to reach the result. This concerns source expressions, independently of CLI JSON path normalization. The closed [HTML script URL report](https://github.com/morluto/rea/issues/1236) concerns document-reference resolution and does not restore this expression's conversion semantics. ## Restoration check Repeat the fixture. The URL must no longer appear as a resolved module path in either window or role output. If a path is recovered, it must be `/app/preload file.js` with filesystem-expression context; otherwise the output must retain unresolved status accurately. Keep `fileURLToPath(new URL("./preload.js", import.meta.url))` at `./preload.js`, and keep a dynamic identifier unresolved. Include the simple `file:///app/preload.js` form as a control and preserve the source declaration as evidence without running it. ## Runtime or environment Source baseline: 8b181716aca2cb467be499cba226abfb5ae19141. Node.js v26.10.0, npm 11.19.1 and locked dependencies. The focused Vitest v5.0.3 run completed with one passing observation/control test and one expected failing decoded-path test. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A valid inert conversion expression produces the wrong preload path and resolution context in both static window and role records. Observed impact is bounded to returned static evidence; downstream lookup and runtime consequences were not executed. - Workaround status: unverified - Workaround basis: Node's decoder produced the correct comparison path, but no alternative REA workflow was demonstrated that restores the returned records without changing the target source.
bsakaguchi 2026-10-15 18:29:14 +00:00
Author
Owner

@jpdefo commented on GitHub (Oct 14, 2026):

I reproduced this on current main 5830088520 using native Windows and Node 24.18.0. The inert-source regression receives file:///app/preload%20file.js with module-specifier context instead of /app/preload file.js with filesystem-expression context. The source-relative and dynamic-argument controls pass.

I will work on a focused correction at the existing static file-URL conversion boundary, preserving source-relative handling and unknown results for invalid or ambiguous inputs. I checked the current open PRs and found no implementation covering this literal conversion.

<!-- gh-comment-id:6160772576 --> @jpdefo commented on GitHub (Oct 14, 2026): I reproduced this on current main 58300885207133422af235e0f2b10658cf958b03 using native Windows and Node 24.18.0. The inert-source regression receives file:///app/preload%20file.js with module-specifier context instead of /app/preload file.js with filesystem-expression context. The source-relative and dynamic-argument controls pass. I will work on a focused correction at the existing static file-URL conversion boundary, preserving source-relative handling and unknown results for invalid or ambiguous inputs. I checked the current open PRs and found no implementation covering this literal conversion.
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#879
No description provided.