Skip to content

fix(javascript): read utilityProcess.fork options from the argv slot - #6280

Merged
kaoru0822-kitauji merged 1 commit into
mainfrom
fix/6208-utility-fork-options
Oct 22, 2026
Merged

kaoru0822-kitauji merged 1 commit into
mainfrom
fix/6208-utility-fork-options

Conversation

@kaoru0822-kitauji

Copy link
Copy Markdown
Collaborator

What broke: utilityProcess.fork("./worker.js", { serviceName: "gpu" }) reported service_name: null with exact confidence and complete coverage, because the extractor only read the third argument.
Fix: The options-selection rule (target[, argv][, options]) that child_process already used now lives in the shared expression-shape owner. Utility processes use it too, so second-slot options are read, and argv shapes that can't be proven stay unknown.
Risk: Low. The change is limited to the static utility-process extractor and a behavior-preserving refactor of child_process option selection. fork(path, argsVar, opts) where argsVar can't be proven to be an array now reports unknown (partial) instead of a name that may be wrong.

Summary

Fixes #6208

Electron's utilityProcess.fork treats a non-array object in the args slot as the options and then ignores the third argument. REA's static extractor always read argument three. As a result, the second-slot form lost its declared service name while still reporting complete coverage. The extractor also had related errors: it read a name from an ignored third argument, and it missed unknown options supplied through spreads or identifiers.

Problem and expected behavior

  • Trigger: utilityProcess.fork("./worker.js", { serviceName: "worker-gpu" }).
  • Violated invariant: an observed declared value must be preserved. If the value can't be interpreted, it must remain unknown with partial coverage (unknowns contract). Before this fix it was reported as absent with complete coverage.
  • New behavior: the options come from the slot Electron uses, and a value that can't be proven stays unknown.

Change and scope

  • optionsAfterOptionalArgv (in javascriptSemanticExpressionShapes.ts) is now the single owner of the (target[, argv][, options]) options-selection rule:
    • The third slot is read only when the second argument is a proven argv array or a nullish placeholder (null, void 0, an unshadowed undefined).
    • Otherwise the second argument is the options.
    • Spreads are returned as-is, so callers keep them unknown.
    • The result is unwrapped, so TS/parenthesized wrappers are transparent.
  • javascriptSemanticChildProcesses.ts now calls this helper instead of its inline copy. Behavior is unchanged; exec keeps its argv-free signature.
  • The static analysis finding context gets the shared createSemanticExpressionShapeResolver(bindings), so the Electron inspector reuses the same bounded argv and nullish proofs. The resolver is lazy and only walks the program when a named binding has to be proven.
  • inspectUtilityProcess now selects options through the helper. Nullish options and serviceName: null/undefined declare no name, matching Electron's options.serviceName != null check.
  • Not included:
    • Resolving identifier option objects (const opts = {...}; fork(p, opts)). It stays unknown, as it did before when used as the third argument.
    • Adding a separate per-observation "unknown" status field for service_name. Unknown is still reported through file-level partial coverage and the existing limitation, the same as the existing computed-key case.
  • Scope: an ordinary fix within existing static-analysis boundaries; no scope decision needed.

Contract and boundary impact

  • Semantic owner and earliest changed stage: optionsAfterOptionalArgv in src/domain/javascript/javascriptSemanticExpressionShapes.ts, consumed during the static Electron finding traversal.
  • Contribution scope fit and linked maintainer decision: not applicable (bug fix within the existing extractor).
  • CLI and MCP/tool-catalog contract: no schema change. service_name, label and coverage values change only for the affected call shapes.
  • Provider, bridge, target-format, or platform compatibility: none.
  • Evidence, artifact, provenance, or reconstruction contract: affected forms gain the correct name, or move from false "complete" to "partial". fork(p, opaqueArgs, {serviceName}) moves from exact name to unknown/partial, because the runtime may use opaqueArgs as the options.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:
    • tests/boundary/filesystem/javascript-applications/electronOptionKeys.test.ts now has a table of 19 utility-process argument shapes run through the shared application reconstruction workflow:
      • second-slot options, [], undefined, null, void 0, and a const argv array
      • wrapped options, and an ignored third argument
      • omitted options, options without a name, nullish options, a nullish name
      • identifier options, spread arguments, spread options, an unproven argv call
      • computed keys in both slots, and a shadowed undefined
    • The earlier single computed-key test is folded into this table. Its observations[0] selection depended on observation ordering, so the table selects the observe-utility-process-fork observation by operation.
    • I didn't run the new table on the base revision. Based on the baseline CLI matrix below, the second-slot, ignored-third, wrapped, identifier, spread, computed-key and unproven-argv rows would fail there.
    • javascriptStaticAnalysis.mapEndpoints.test.ts supplies the new context field.
  • Base reproduction (executed, CLI rea analyze-javascript-application --format json on tiny authored Electron source directories; Electron was not launched). Projection: [service_name, confidence, coverage] of the observe-utility-process-fork observation:
fixture call args main (c537c52) this PR
{ serviceName: "worker-gpu" } null, exact, complete "worker-gpu", exact, complete
[], { serviceName: "worker-gpu" } "worker-gpu", exact, complete unchanged
undefined, {…} / null, {…} "worker-gpu", complete unchanged
omitted null, complete unchanged
{ serviceName: "a" }, { serviceName: "b" } "b", complete "a", complete
({ serviceName: "worker-gpu" }) null, complete "worker-gpu", complete
opts (identifier) null, complete null, partial
...rest null, complete null, partial
{ [k]: "worker-gpu" } null, complete null, partial
getArgs(), { serviceName: "worker-gpu" } "worker-gpu", complete null, partial
shadowed undefined, {…} "worker-gpu", complete null, partial
[], { serviceName: null } null, partial null, complete
  • User-visible CLI/MCP output: shown in the table above (CLI). MCP uses the same application workflow; the MCP adapter itself was not rerun.
  • Remaining proof gaps:
    • A top-level const args = [...] in a plain .js script without ES module syntax is classified as runtime context unknown. The shared shape resolver deliberately does not prove such bindings, so fork(p, args, {serviceName}) there now reports unknown/partial. Previously it reported the name, without checking what args actually is. Function-local or ES-module const argv arrays and inline [] resolve as before. Relaxing that resolver policy would also change child_process analysis, so it is out of scope here.
    • Identifier option objects are not resolved; they remain unknown.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

Local Node v26.5.0. The pinned 24.18.0 runtime was not available on this host.

  • CLI repro matrix above on the base and fixed builds (npm run build:cached, then rea analyze-javascript-application <fixture> --format json): results as tabulated.
  • npm run test:focused -- tests/boundary/filesystem/javascript-applications/electronOptionKeys.test.ts src/domain/javascript/electronStaticAnalysisOptions.test.ts src/domain/javascript/childProcessOptionsRegression.test.ts src/domain/javascript/javascriptSemanticChildProcesses.test.ts src/domain/javascript/javascriptStaticAnalysis.mapEndpoints.test.ts src/domain/javascript/javascriptStaticAnalysisCalleeBoundary.test.ts src/domain/javascript/javascriptSemanticExpressionShapes.test.ts tests/boundary/filesystem/javascript-applications/electronApplicationAnalysis.test.ts: 8 files, 302 tests passed.
  • npm run test:focused -- on the other utilityProcess-related suites (javascriptApplicationTypeScript, the javascriptStaticPath* tests, electronStaticAnalysisIdentity, javascriptStaticAnalysisSharedHelperWrites, javascriptStaticAnalysisDelegateEligibility, javascriptSemanticAnalysis.runtime_boundaries, tests/boundary/cli/javascriptSelfImportRelationships, tests/acceptance/applications/javascriptLiteralPercentPaths, plus electronOptionKeys): 13 files, 328 tests passed.
  • After rebasing onto c9b4cae: the 8-file focused set above was rerun (302 passed), and npm run check:fast and npm run format:check were rerun (both passed). The CLI matrix and the 13-file set ran on the pre-rebase base c537c52.
  • npm run check:fast: passed (typecheck and lint).
  • npm run format:check: passed.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Output schema is unchanged; values change only for the call shapes above.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable (static source analysis only; macOS arm64 local run).
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: source is parsed as inert data; nothing is executed.

Author readiness

Human owner: @kaoru0822-kitauji

  • 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.

🤖 Generated with Claude Code

Electron's utilityProcess.fork(modulePath[, args][, options]) treats a
non-array object in the second argument as the options and ignores the
third. The static extractor only read the third argument, so
`fork("./worker.js", { serviceName })` lost the declared name while
reporting exact, complete coverage, and an object in the second slot let
an ignored third argument supply the name.

Move the optional-argv options selection that child_process already used
into the shared expression-shape owner and reuse it for utility
processes. Unproven argv shapes, spreads and identifier options remain
unknown with partial coverage; nullish options or a nullish serviceName
declare no name, matching Electron's runtime checks.

Fixes #6208

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kaoru0822-kitauji kaoru0822-kitauji added bug Something isn't working track:bugs Small-bug loop track PRs labels Oct 22, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

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

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-22T07:28:49.703503Z
We couldn’t complete this review. Comment @codex review to try again.
38b2cc8 PR opened
ℹ️ 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.

@greptile-apps

greptile-apps Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable defects were found.

Summary

The PR fixes how utilityProcess.fork reads options and shares that selection rule with child-process analysis.

  • Utility-process findings now read serviceName from Electron's options slot.

Acknowledged limits:

  • kaoru0822-kitauji explicitly deferred resolving identifier option objects; they remain unknown.
  • kaoru0822-kitauji explicitly kept top-level argv bindings in plain scripts unknown because relaxing that proof would also affect child-process analysis.

Reviews (1) · Last reviewed commit: "fix(javascript): read utilityProcess.for..." · Reviewed by Greptile

@kaoru0822-kitauji
kaoru0822-kitauji merged commit 73b7564 into main Oct 22, 2026
42 checks passed
@kaoru0822-kitauji
kaoru0822-kitauji deleted the fix/6208-utility-fork-options branch October 22, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working track:bugs Small-bug loop track PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Second-argument utilityProcess.fork options lose the service name

1 participant