From b8a5dac1a2f07bec19a7d5b7490efa1ce0d222df Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 21 Jun 2026 12:40:57 +0200 Subject: [PATCH] fix(qa-lab): resolve Windows PowerShell path --- extensions/qa-lab/src/process-tree-cpu.ts | 3 +- .../src/process-tree-cpu.windows.test.ts | 52 +++++++++++++++++++ .../qa-lab/src/windows-system-tools.test.ts | 4 ++ extensions/qa-lab/src/windows-system-tools.ts | 12 +++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 extensions/qa-lab/src/process-tree-cpu.windows.test.ts diff --git a/extensions/qa-lab/src/process-tree-cpu.ts b/extensions/qa-lab/src/process-tree-cpu.ts index 317f4facabf4..9066cead3938 100644 --- a/extensions/qa-lab/src/process-tree-cpu.ts +++ b/extensions/qa-lab/src/process-tree-cpu.ts @@ -1,6 +1,7 @@ // Qa Lab plugin module implements process tree cpu behavior. import { spawnSync } from "node:child_process"; import { parseStrictFiniteNumber, parseStrictInteger } from "openclaw/plugin-sdk/number-runtime"; +import { resolveQaWindowsPowerShellExePath } from "./windows-system-tools.js"; type ProcessTreeSnapshot = { childrenByParent: Map; @@ -175,7 +176,7 @@ function collectProcessTreeMetric( function readWindowsProcessTreeSnapshot(): ProcessTreeSnapshot | null { const result = spawnSync( - "powershell.exe", + resolveQaWindowsPowerShellExePath(), [ "-NoProfile", "-ExecutionPolicy", diff --git a/extensions/qa-lab/src/process-tree-cpu.windows.test.ts b/extensions/qa-lab/src/process-tree-cpu.windows.test.ts new file mode 100644 index 000000000000..ea758d0d922c --- /dev/null +++ b/extensions/qa-lab/src/process-tree-cpu.windows.test.ts @@ -0,0 +1,52 @@ +// Qa Lab tests cover Windows process tree sampling command selection. +import path from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +const spawnSyncMock = vi.hoisted(() => vi.fn()); + +vi.mock("node:child_process", async () => { + const actual = await vi.importActual("node:child_process"); + return { + ...actual, + spawnSync: spawnSyncMock, + }; +}); + +import { readProcessTreeCpuMs } from "./process-tree-cpu.js"; + +afterEach(() => { + vi.restoreAllMocks(); + vi.unstubAllEnvs(); + spawnSyncMock.mockReset(); +}); + +describe("readProcessTreeCpuMs on Windows", () => { + it("uses the trusted Windows PowerShell path", () => { + vi.spyOn(process, "platform", "get").mockReturnValue("win32"); + vi.stubEnv("SystemRoot", "D:\\Windows"); + spawnSyncMock.mockReturnValue({ + status: 0, + stdout: JSON.stringify([ + { + ProcessId: 100, + ParentProcessId: 50, + KernelModeTime: "10000", + UserModeTime: "20000", + WorkingSetSize: "1000", + }, + { + ProcessId: 101, + ParentProcessId: 100, + KernelModeTime: "30000", + UserModeTime: "40000", + WorkingSetSize: "2000", + }, + ]), + }); + + expect(readProcessTreeCpuMs(100)).toBe(10); + expect(spawnSyncMock.mock.calls[0]?.[0]).toBe( + path.win32.join("D:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe"), + ); + }); +}); diff --git a/extensions/qa-lab/src/windows-system-tools.test.ts b/extensions/qa-lab/src/windows-system-tools.test.ts index a5e7357f0d5a..632cb7429929 100644 --- a/extensions/qa-lab/src/windows-system-tools.test.ts +++ b/extensions/qa-lab/src/windows-system-tools.test.ts @@ -1,6 +1,7 @@ // Qa Lab tests cover Windows system tool path resolution. import { describe, expect, it } from "vitest"; import { + resolveQaWindowsPowerShellExePath, resolveQaWindowsSystem32ExePath, resolveQaWindowsSystemRoot, } from "./windows-system-tools.js"; @@ -11,6 +12,9 @@ describe("qa-lab windows system tools", () => { expect(resolveQaWindowsSystem32ExePath("taskkill.exe", { SystemRoot: "D:\\Windows\\" })).toBe( "D:\\Windows\\System32\\taskkill.exe", ); + expect(resolveQaWindowsPowerShellExePath({ SystemRoot: "D:\\Windows\\" })).toBe( + "D:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + ); }); it("falls back to the default Windows root when env roots are unsafe", () => { diff --git a/extensions/qa-lab/src/windows-system-tools.ts b/extensions/qa-lab/src/windows-system-tools.ts index 8b232e9ba5ad..c4f87ffb78c8 100644 --- a/extensions/qa-lab/src/windows-system-tools.ts +++ b/extensions/qa-lab/src/windows-system-tools.ts @@ -60,3 +60,15 @@ export function resolveQaWindowsSystem32ExePath( } return path.win32.join(resolveQaWindowsSystemRoot(env), "System32", executableName); } + +export function resolveQaWindowsPowerShellExePath( + env: Record = process.env, +): string { + return path.win32.join( + resolveQaWindowsSystemRoot(env), + "System32", + "WindowsPowerShell", + "v1.0", + "powershell.exe", + ); +}