From 0472dca30c2ce727a4f3b55966ee9badc58af70a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=9A=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D1=82=D1=8C=D0=BA=D0=BE?= Date: Tue, 19 May 2026 06:23:58 +0300 Subject: [PATCH] fix(ide/process-utils): spawn powershell.exe with -NoProfile -NonInteractive Switch to execFile with explicit argv to match the -NoProfile pattern used by every other powershell call site in the repo. --- packages/core/src/ide/process-utils.test.ts | 42 +++++++++++++++++++-- packages/core/src/ide/process-utils.ts | 13 +++++-- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/packages/core/src/ide/process-utils.test.ts b/packages/core/src/ide/process-utils.test.ts index db9f4982868..1eae3605258 100644 --- a/packages/core/src/ide/process-utils.test.ts +++ b/packages/core/src/ide/process-utils.test.ts @@ -71,9 +71,15 @@ describe('getIdeProcessInfo', () => { command: 'C:\\Program Files\\VSCode\\Code.exe', }); expect(mockedExec).toHaveBeenCalledWith( - expect.stringContaining( - 'Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine', - ), + 'powershell.exe', + expect.arrayContaining([ + '-NoProfile', + '-NonInteractive', + '-Command', + expect.stringContaining( + 'Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine', + ), + ]), expect.anything(), ); }); @@ -142,11 +148,39 @@ describe('getIdeProcessInfo', () => { const result = await getIdeProcessInfo(); expect(result).toEqual({ pid: 900, command: 'powershell.exe' }); expect(mockedExec).toHaveBeenCalledWith( - expect.stringContaining('Get-CimInstance Win32_Process'), + 'powershell.exe', + expect.arrayContaining([ + '-NoProfile', + '-NonInteractive', + '-Command', + expect.stringContaining('Get-CimInstance Win32_Process'), + ]), expect.anything(), ); }); + it('should invoke powershell.exe with -NoProfile -NonInteractive flags', async () => { + (os.platform as Mock).mockReturnValue('win32'); + const processes = [ + { + ProcessId: 1000, + ParentProcessId: 0, + Name: 'node.exe', + CommandLine: 'node.exe', + }, + ]; + mockedExec.mockResolvedValueOnce({ stdout: JSON.stringify(processes) }); + + await getIdeProcessInfo(); + + expect(mockedExec).toHaveBeenCalledTimes(1); + const [program, args] = mockedExec.mock.calls[0]; + expect(program).toBe('powershell.exe'); + expect(args).toContain('-NoProfile'); + expect(args).toContain('-NonInteractive'); + expect(args).toContain('-Command'); + }); + it('should handle short process chains', async () => { (os.platform as Mock).mockReturnValue('win32'); // process (1000) -> root (0) diff --git a/packages/core/src/ide/process-utils.ts b/packages/core/src/ide/process-utils.ts index 6708d53ed7e..06367b15b6d 100644 --- a/packages/core/src/ide/process-utils.ts +++ b/packages/core/src/ide/process-utils.ts @@ -4,12 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { exec } from 'node:child_process'; +import { exec, execFile } from 'node:child_process'; import { promisify } from 'node:util'; import os from 'node:os'; import path from 'node:path'; const execAsync = promisify(exec); +const execFileAsync = promisify(execFile); const MAX_TRAVERSAL_DEPTH = 32; @@ -37,9 +38,13 @@ async function getProcessTableWindows(): Promise> { const powershellCommand = 'Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine | ConvertTo-Json -Compress'; // Increase maxBuffer to handle large process lists (default is 1MB) - const { stdout } = await execAsync(`powershell "${powershellCommand}"`, { - maxBuffer: 10 * 1024 * 1024, - }); + const { stdout } = await execFileAsync( + 'powershell.exe', + ['-NoProfile', '-NonInteractive', '-Command', powershellCommand], + { + maxBuffer: 10 * 1024 * 1024, + }, + ); if (!stdout.trim()) { return processMap;