[GH-ISSUE #530] Windows: V8 Inspector excludes every file-backed Node target (Node mangles file://C:/ to file://C:_ which fails URL parsing) #276

Closed
opened 2026-10-15 18:23:19 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @whft on GitHub (Oct 5, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/530

Summary

list_javascript_runtime_targets excludes every file-backed Node target on Windows with unsupported_location, so observe_javascript_runtime is effectively unreachable for plain Node scripts on Windows x64.

Reproduction

Launch any Node script with the inspector enabled:

node --inspect=127.0.0.1:9230 C:\tools\rea-node-probe.js

REA sees the target but refuses to list it:

{
  "runtime": { "product": "node.js/v24.19.0", "protocol_version": "1.1" },
  "targets": [],
  "excluded": { "unsupported_location": 1, "unconnectable": 0 }
}

Attaching directly to the endpoint shows the target is perfectly healthy:

$ curl http://127.0.0.1:9230/json/list
[ { "description": "node.js instance",
    "title": "C:_tools_rea-node-probe.js",
    "url":  "file://C:_tools_rea-node-probe.js",
    "webSocketDebuggerUrl": "ws://127.0.0.1:9230/29a7..." } ]

Root cause

Node's Inspector sanitizes the drive-letter colon into an underscore in both title and url on Windows. A drive-rooted path C:\tools\x.js becomes C:_tools_x.js, so the URL is emitted as file://C:_tools_x.js — two slashes, no third slash, and no colon.

authorizedElectronFile() in src/browser/ElectronFileScope.ts resolves such a location like this:

let url: URL;
try {
  url = new URL(value);
} catch {
  return undefined;          // <-- taken here
}

and that URL does not even parse:

$ node -e 'try { new URL("file://C:_tools_x.js") } catch (e) { console.log(e.message) }'
Invalid URL

Compare the well-formed POSIX/Windows form, which parses fine and is accepted:

file:///C:/tools/rea-node-probe.js
   protocol: file: | hostname: "" | pathname: /C:/tools/rea-node-probe.js

So the failure is not the root check — authorizedElectronFile() deliberately imposes no directory-root restriction (it only requires a valid file: URL, no remote host, no encoded separators, and an existing regular file). The mangled URL simply never reaches that logic.

Why this blocks the whole feature on Windows

JavaScriptRuntimeScope admits only three location kinds:

  • node: builtins — never the url of a script target
  • file: URLs — always mangled on Windows, therefore always rejected
  • http: / https: URLs — not something a Node script target reports

A Node script target's url is always file:, so on Windows no Node script target can ever be admitted. Only an Electron page works, because Electron's CDP endpoint emits a well-formed file:///… URL.

This is Windows-only: on Linux and macOS the path has no drive letter, so file:///home/user/x.js parses and is accepted.

Suggested fix

Normalize the sanitized form before parsing, rather than treating it as unparseable. Something like:

// Node's Inspector sanitizes the Windows drive-letter colon to "_", which
// yields "file://C_<path>" instead of "file:///C:/<path>". Restore the
// canonical form before parsing so Windows script targets stay admissible.
const normalizeWindowsInspectorFileUrl = (value: string): string =>
  /^file:\/\/([A-Za-z])_(.*)$/u.test(value)
    ? `file:///${value[7]}:/${value.slice(8)}`
    : value;

Alternatively, resolve from realpath on the target title, or fall back to accepting file: URLs whose new URL() throws but whose title maps to an existing regular file. Whatever the route, the important part is that the admission check should not silently drop every Windows target — right now the only signal is unsupported_location, which reads like a policy decision rather than a parse failure.

Suggested reporting improvement

unsupported_location currently conflates at least three distinct causes:

  • the URL is unparseable (this bug)
  • the file: path does not exist / is not a regular file
  • a non-file:, non-node:, non-http(s): scheme

Splitting "malformed" from "not authorized" would have made this diagnosable without reading the source.

Environment

REA 3.2.1, Windows 11 x64, Node v24.19.0, Ghidra 12.1.4, REA_V8_INSPECTOR_OBSERVE_ENABLED=true, inspector_endpoint supplied explicitly.

Originally created by @whft on GitHub (Oct 5, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/530 ## Summary `list_javascript_runtime_targets` excludes **every** file-backed Node target on Windows with `unsupported_location`, so `observe_javascript_runtime` is effectively unreachable for plain Node scripts on Windows x64. ## Reproduction Launch any Node script with the inspector enabled: ``` node --inspect=127.0.0.1:9230 C:\tools\rea-node-probe.js ``` REA sees the target but refuses to list it: ```json { "runtime": { "product": "node.js/v24.19.0", "protocol_version": "1.1" }, "targets": [], "excluded": { "unsupported_location": 1, "unconnectable": 0 } } ``` Attaching directly to the endpoint shows the target is perfectly healthy: ``` $ curl http://127.0.0.1:9230/json/list [ { "description": "node.js instance", "title": "C:_tools_rea-node-probe.js", "url": "file://C:_tools_rea-node-probe.js", "webSocketDebuggerUrl": "ws://127.0.0.1:9230/29a7..." } ] ``` ## Root cause Node's Inspector **sanitizes the drive-letter colon into an underscore** in both `title` and `url` on Windows. A drive-rooted path `C:\tools\x.js` becomes `C:_tools_x.js`, so the URL is emitted as `file://C:_tools_x.js` — two slashes, no third slash, and no colon. `authorizedElectronFile()` in `src/browser/ElectronFileScope.ts` resolves such a location like this: ```ts let url: URL; try { url = new URL(value); } catch { return undefined; // <-- taken here } ``` and that URL does not even parse: ``` $ node -e 'try { new URL("file://C:_tools_x.js") } catch (e) { console.log(e.message) }' Invalid URL ``` Compare the well-formed POSIX/Windows form, which parses fine and is accepted: ``` file:///C:/tools/rea-node-probe.js protocol: file: | hostname: "" | pathname: /C:/tools/rea-node-probe.js ``` So the failure is not the root check — `authorizedElectronFile()` deliberately imposes **no** directory-root restriction (it only requires a valid `file:` URL, no remote host, no encoded separators, and an existing regular file). The mangled URL simply never reaches that logic. ## Why this blocks the whole feature on Windows `JavaScriptRuntimeScope` admits only three location kinds: - `node:` builtins — never the `url` of a script target - `file:` URLs — always mangled on Windows, therefore always rejected - `http:` / `https:` URLs — not something a Node script target reports A Node script target's `url` is *always* `file:`, so on Windows **no** Node script target can ever be admitted. Only an Electron page works, because Electron's CDP endpoint emits a well-formed `file:///…` URL. This is Windows-only: on Linux and macOS the path has no drive letter, so `file:///home/user/x.js` parses and is accepted. ## Suggested fix Normalize the sanitized form before parsing, rather than treating it as unparseable. Something like: ```ts // Node's Inspector sanitizes the Windows drive-letter colon to "_", which // yields "file://C_<path>" instead of "file:///C:/<path>". Restore the // canonical form before parsing so Windows script targets stay admissible. const normalizeWindowsInspectorFileUrl = (value: string): string => /^file:\/\/([A-Za-z])_(.*)$/u.test(value) ? `file:///${value[7]}:/${value.slice(8)}` : value; ``` Alternatively, resolve from `realpath` on the target `title`, or fall back to accepting `file:` URLs whose `new URL()` throws but whose `title` maps to an existing regular file. Whatever the route, the important part is that the admission check should not silently drop every Windows target — right now the only signal is `unsupported_location`, which reads like a policy decision rather than a parse failure. ## Suggested reporting improvement `unsupported_location` currently conflates at least three distinct causes: - the URL is unparseable (this bug) - the `file:` path does not exist / is not a regular file - a non-`file:`, non-`node:`, non-`http(s):` scheme Splitting "malformed" from "not authorized" would have made this diagnosable without reading the source. ## Environment REA 3.2.1, Windows 11 x64, Node v24.19.0, Ghidra 12.1.4, `REA_V8_INSPECTOR_OBSERVE_ENABLED=true`, `inspector_endpoint` supplied explicitly.
bsakaguchi 2026-10-15 18:23:19 +00:00
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#276
No description provided.