Files
OmniRoute/tests/unit/windows-hide-child-process-spawns-8131.test.ts
Austin Liu 5660bdefbd fix(windows): add windowsHide to all child process spawns (#8131) (#8167)
* fix(windows): add windowsHide to all child process spawns (#8131)

On Windows, child processes spawned without windowsHide: true cause
transient conhost.exe/cmd console windows to flash open. Audited all
spawn/exec/execFile/execSync/execFileSync call sites and added
windowsHide: true where missing.

Files patched:
- src/mitm/manager.ts (MITM server spawn)
- src/mitm/systemCommands.ts (sudo/system command spawn)
- src/mitm/inspector/systemProxyConfig.ts (execFile wrapper)
- src/shared/services/cliRuntime.ts (CLI spawn + npm execFileSync)
- src/lib/plugins/loader.ts (plugin host spawn)
- src/lib/providerModels/cursorAgent.ts (cursor binary spawn)
- src/lib/cloudflaredTunnel.ts (cloudflared spawn)

Unix-only call sites (shell: /bin/bash, which) are unaffected.
electron/main.js already had windowsHide: true.

* fix(windows): cover remaining spawn sites missed by #8131 windowsHide sweep

Extends the #8131 windowsHide audit to the three call sites the original
sweep missed: ServiceSupervisor.start() and processManager.startProcess()
(both spawn() embedded-service child processes), and
installers/utils.ts::buildNpmExecOptions() (the execFile() options runNpm()
uses to install services). All three now always set windowsHide: true so
no transient conhost.exe/cmd console window flashes open on Windows.

The two spawn() options objects are factored into small, pure, exported
builder functions (buildServiceSpawnOptions, buildCliproxyapiSpawnOptions)
so the regression test can assert on the constructed options directly,
since both call sites use a bare named `import { spawn } from
"node:child_process"` that ESM live-binding semantics make unmockable
without --experimental-test-module-mocks (not currently enabled repo-wide).

Bumps config/quality/file-size-baseline.json for cloudflaredTunnel.ts
934->935 (the PR's own +1 windowsHide line at the existing spawn options
object).

Co-Authored-By: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: Austin Liu <austinliu@Austins-MacBook-Air-3.local>
Co-authored-by: Probe Test <probe@example.com>
Co-authored-by: Dingding-leo <Dingding-leo@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-22 18:08:15 -03:00

41 lines
2.0 KiB
TypeScript

/**
* Regression test for #8131 — on Windows, child processes spawned without
* `windowsHide: true` cause a transient conhost.exe/cmd console window to
* flash open. PR #8167 audited most spawn/exec call sites but missed the
* two `child_process.spawn()` call sites below plus the `execFile()` wrapper
* used by `runNpm()` (covered separately in
* tests/unit/services/installers/runNpm-shell-5379.test.ts).
*
* Both `ServiceSupervisor.start()` and `processManager.startProcess()` use a
* bare named `import { spawn } from "node:child_process"`, which Node's ESM
* live-binding semantics make impossible to intercept with
* `mock.method()`/`mock.module()` without the (project-wide, not currently
* enabled) `--experimental-test-module-mocks` flag. Rather than widen the
* test-runner flags, the options object each call site passes to `spawn()`
* is factored into a small, pure, exported builder function — asserted on
* directly here instead of mocking `node:child_process`.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { buildServiceSpawnOptions } from "../../src/lib/services/ServiceSupervisor.ts";
import { buildCliproxyapiSpawnOptions } from "../../src/lib/versionManager/processManager.ts";
test("ServiceSupervisor: buildServiceSpawnOptions sets windowsHide: true (#8131)", () => {
const opts = buildServiceSpawnOptions({ FOO: "bar" }, "/tmp/cwd");
assert.equal(opts.windowsHide, true);
// Sanity: the rest of the previously-inline options object still round-trips.
assert.equal(opts.env?.FOO, "bar");
assert.equal(opts.cwd, "/tmp/cwd");
assert.equal(opts.detached, false);
assert.deepEqual(opts.stdio, ["ignore", "pipe", "pipe"]);
});
test("processManager: buildCliproxyapiSpawnOptions sets windowsHide: true (#8131)", () => {
const opts = buildCliproxyapiSpawnOptions();
assert.equal(opts.windowsHide, true);
assert.equal(opts.detached, false);
assert.deepEqual(opts.stdio, ["ignore", "pipe", "pipe"]);
assert.ok(opts.env, "env should be populated from process.env");
});