Skip to content

fix(javascript): omit child-process spawns whose command or options Node rejects - #10528

Open
Plastic-time wants to merge 4 commits into
morluto:mainfrom
Plastic-time:rrfz/omit-spawns-node-rejects
Open

Plastic-time wants to merge 4 commits into
morluto:mainfrom
Plastic-time:rrfz/omit-spawns-node-rejects

Conversation

@Plastic-time

@Plastic-time Plastic-time commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

JavaScript static analysis reported a child-process spawn for node:child_process calls that Node refuses before any child exists: a command it rejects, or an option it validates while normalizing the spawn. These calls no longer produce a childProcessSpawns entry.

Fixes #10522

Problem and expected behavior

Each of these produced one spawn fact on main, and each throws synchronously in Node 24.22.1:

spawn("");                                         // ERR_INVALID_ARG_VALUE
fork(null);                                        // ERR_INVALID_ARG_TYPE
spawn("no\0de");                                   // ERR_INVALID_ARG_VALUE
spawn("node", [], { timeout: -1 });                // ERR_OUT_OF_RANGE
fork("worker.js", [], { serialization: "JSON" });  // ERR_INVALID_ARG_VALUE
execFile("node", [], { shell: 0 }, () => {});      // ERR_INVALID_ARG_TYPE
exec("node", { maxBuffer: -1 }, () => {});         // ERR_OUT_OF_RANGE

Measured rules (all four methods unless noted):

Argument Rejected Accepted
command missing, null, undefined, number, boolean, array, function, NUL byte; object or new value except in fork; "" except in fork any other string, including " "; fork(""); fork(new URL(...)) and other URL or byte values
timeout anything but a non-negative integer, null or undefined 0, 1e3, 2 ** 53
detached, windowsHide, windowsVerbatimArguments (spawn, fork) not a boolean, null or undefined booleans
serialization (spawn, fork) anything but "json", "advanced" or undefined, including null "json", "advanced"
shell (spawn, execFile) not a boolean, string, null or undefined "", "cmd.exe", booleans
maxBuffer (execFile, exec) negative, NaN, string, BigInt 0, 1.5, Infinity

execFile does not forward the boolean flags or serialization to its spawn, and exec replaces a non-string shell, so those methods keep their spawn for the same values.

Change and scope

  • rejectsCommand judges the first argument. A spread or computed command stays. fork validates its module as a path, so a URL, a URL-like object or a Uint8Array starts a child. For fork, only arrays, functions and regular expressions are rejected among object values.
  • rejectsValidatedOption reads each member through the existing staticObjectProperty owner, and classifies its value only as far as syntax proves it. Unknown values stay.
  • Both run beside the existing stdio, cwd/argv0, options-shape and callback filters in collectJavaScriptSemanticChildProcesses.
  • Not included: killSignal, because Node's signal list differs by platform, and uid/gid, because Windows refuses any value with ENOTSUP.

Contribution scope: ordinary fix within existing boundaries; maintainer scope decision not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: collectJavaScriptSemanticChildProcesses in javascriptSemanticChildProcesses.ts.

  • Contribution scope fit and linked maintainer decision: not applicable.

  • CLI and MCP/tool-catalog contract: none.

  • Provider, bridge, target-format, or platform compatibility: none.

  • Evidence, artifact, provenance, or reconstruction contract: fewer false spawn facts; undecided calls are unchanged.

  • Target selection, authority, upstream behavior, and cleanup details: not applicable.

  • Generated metadata, package, or installation impact: none.

  • Read existing files or captured data.

Evidence and regression coverage

  • Tests added: src/domain/javascript/javascriptSemanticAnalysis.child_process_command.test.ts, one row per rejected and accepted form above, across the four methods.
  • Base reproduction: the new file on main source: 46 failed / 29 passed. With this change: 75 passed.
  • Every row was first measured in Node 24.22.1 by calling the API with process.execPath -e 0 and recording the synchronous throw or the returned ChildProcess (killed at once).

Validation performed

Windows 11 Home 10.0.26200 x64, Node 24.22.1, base 9e24b36.

  • npx vitest run src/domain/javascript/javascriptSemanticAnalysis.child_process_command.test.ts on main source: 46 failed / 29 passed; with this change: 75 passed.
  • npx vitest run src/domain/javascript src/application/javascript: 217 files, 6457 passed / 7 skipped.
  • Review follow-up (a3a7aa3): signed numeric and BigInt option values such as -1n and -1e400 are now priced; -0 stays an accepted timeout.
  • npm run check:fast: passed (typecheck, lint)

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: Node 24.22.1 on Windows 11 for the measurements. The argument checks are in Node's platform-independent normalizeSpawnArguments; Linux was not measured.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: analysis only; nothing is executed.

Author readiness

Human owner: @Plastic-time

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

spawn, execFile, exec and fork validate the command before anything else.
A missing, nullish or non-string command, a command with a NUL byte, and an
empty command (except for fork) throw synchronously, so no child exists.
Node validates timeout, the spawn and fork boolean flags, serialization,
shell and maxBuffer while normalizing a spawn. A literal it refuses throws
before any child exists. Each rule applies only to the methods that forward
that member.
fork validates its module as a path, so a URL, a URL-like object, a class
with static href/protocol and a Uint8Array start a child. Share the
object-valued syntax check with the option reader.
@greptile-apps

greptile-apps Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; the previous finding is fixed and no new actionable issues were found.

Summary

The PR omits child-process facts for commands and options that Node rejects before starting a child.

  • Child-process facts skip commands Node rejects before a child starts.
  • Child-process facts skip forwarded options Node rejects before a child starts.

Reviews (2) · Last reviewed commit: "fix(javascript): price signed numeric an..." · Reviewed by Greptile

@Plastic-time
Plastic-time marked this pull request as ready for review November 14, 2026 11:42
@chatgpt-codex-connector

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-14T11:42:41.072697Z
We couldn’t complete this review. Comment @codex review to try again.
d11b317 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

A negative BigInt timeout and a negative non-finite maxBuffer are proven values
Node rejects, so the spawn is omitted. -0 stays an accepted timeout.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] JavaScript analysis reports child-process spawns for commands and options Node rejects

1 participant