Skip to content

fix(gwmi): pass -NoProfile -NonInteractive to powershell - #193

Open
Shmuel3 wants to merge 1 commit into
soyuka:mainfrom
Shmuel3:fix/gwmi-noprofile
Open

fix(gwmi): pass -NoProfile -NonInteractive to powershell#193
Shmuel3 wants to merge 1 commit into
soyuka:mainfrom
Shmuel3:fix/gwmi-noprofile

Conversation

@Shmuel3

@Shmuel3 Shmuel3 commented Jul 30, 2026

Copy link
Copy Markdown
Q A
Branch? main
Bug fix? yes
New feature? no
Deprecations? no
Tickets Fix #192
License MIT
Doc PR n/a

Problem

lib/gwmi.js spawns PowerShell through Node's shell option, which Node turns into powershell.exe -c <pipeline>. Neither -NoProfile nor -NonInteractive is passed, so every stats poll loads and executes the user's PowerShell profile: arbitrary user startup code on every poll, the profile's startup cost added to every call, a failed poll whenever the profile writes to stderr (lib/bin.js treats non-empty stderr as fatal), and a permanently hung poll plus a leaked powershell whenever the profile blocks.

Full write-up, including a real incident where the leak exhausted Windows socket buffer space, is in #192. Same class of failure as the 4.0.1 fix ("fix spawned wmic processes not exiting … leading to infinite build up").

Worth noting from #192: because spawn('wmic', function (err) { ... }) in lib/stats.js throws ERR_INVALID_ARG_TYPE synchronously on every platform, the catch always fires and gwmi is always the Windows backend on 4.x — so this path affects every Windows user, not only machines missing wmic. I've left that alone to keep this PR to one concern.

Fix

Spawn powershell.exe directly with explicit flags, passing the pipeline as a single -Command argument.

  • The query, property list and format-table output are unchanged, so the parser below the spawn is untouched.
  • Dropping windowsVerbatimArguments lets Node quote the -Command argument. It contains spaces and |, but no double quotes or backslashes, so it is wrapped in "…" and PowerShell receives exactly one command string. pids are already parseInt-validated in stats.js before reaching here, so nothing user-controlled can introduce a quote.
  • -ExecutionPolicy Bypass is deliberately not added: execution policy applies to script files, not -Command.

Tests

Added a unit test asserting the spawn target and that -NoProfile / -NonInteractive are passed, mirroring the existing gwmi/wmic mock style. It is declared test.serial so it cannot race the existing test over the shared mockery and history state.

What I verified, and what I couldn't

  • npm test passes (17 tests) on macOS.
  • I don't have a Windows machine, so I could not exercise the new command line end to end — the test-windows job covers that path, and per the note above integration.js on Windows goes through gwmi.
  • npm run lint reports one error, lib/stats.js:64:14: Extra semicolon. It is pre-existing on main (from 36383b8), not introduced here, and in a file this PR deliberately doesn't touch. Happy to fold the one-character fix in if you'd rather the lint job go green.

The gwmi fallback spawned powershell through the `shell` option, which
produces `powershell.exe -c <pipeline>`. Without -NoProfile, every stats
poll loads and executes the user's PowerShell profile. That runs
arbitrary user startup code on each poll, adds the profile's startup
cost to every call, and fails the poll outright when a profile writes to
stderr (bin.js treats any stderr output as a fatal error). Profiles that
block also leave the spawned powershell hanging forever, so polling apps
accumulate orphaned processes.

Spawn powershell.exe directly with -NoProfile -NonInteractive and pass
the pipeline as a single -Command argument. The query, property list and
format-table output are unchanged, so the parser is untouched.
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.

gwmi backend runs the user's PowerShell profile on every poll

1 participant