惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
M
MIT News - Artificial intelligence
S
SegmentFault 最新的问题
博客园 - 叶小钗
量子位
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - Franky
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
人人都是产品经理
人人都是产品经理
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - 【当耐特】
Google DeepMind News
Google DeepMind News
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
博客园_首页
U
Unit 42
月光博客
月光博客
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(qa-lab): resolve Windows taskkill path · openclaw/ope...
vincentkoc · 2026-06-21 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -1025,11 +1025,12 @@ describe("buildQaRuntimeEnv", () => {

10251025

runTaskkill,

10261026

);

10271027
1028-

expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {

1028+

const taskkillPath = path.win32.join("C:\\Windows", "System32", "taskkill.exe");

1029+

expect(runTaskkill).toHaveBeenNthCalledWith(1, taskkillPath, ["/PID", "12345", "/T"], {

10291030

stdio: "ignore",

10301031

windowsHide: true,

10311032

});

1032-

expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {

1033+

expect(runTaskkill).toHaveBeenNthCalledWith(2, taskkillPath, ["/PID", "12345", "/T", "/F"], {

10331034

stdio: "ignore",

10341035

windowsHide: true,

10351036

});

Original file line numberDiff line numberDiff line change

@@ -50,6 +50,7 @@ import { stageQaMockAuthProfiles } from "./providers/shared/mock-auth.js";

5050

import { seedQaAgentWorkspace } from "./qa-agent-workspace.js";

5151

import { buildQaGatewayConfig, type QaThinkingLevel } from "./qa-gateway-config.js";

5252

import type { QaTransportAdapter } from "./qa-transport.js";

53+

import { resolveQaWindowsSystem32ExePath } from "./windows-system-tools.js";

5354
5455

export type { QaCliBackendAuthMode } from "./providers/env.js";

5556

const QA_GATEWAY_CHILD_STARTUP_MAX_ATTEMPTS = 5;

@@ -398,19 +399,20 @@ function signalQaGatewayWindowsProcessTree(

398399

signal: NodeJS.Signals,

399400

runTaskkill: QaGatewayTaskkillRunner = spawnSync,

400401

) {

402+

const taskkillPath = resolveQaWindowsSystem32ExePath("taskkill.exe");

401403

const args = ["/PID", String(pid), "/T"];

402404

if (signal === "SIGKILL") {

403405

args.push("/F");

404406

}

405-

const result = runTaskkill("taskkill", args, {

407+

const result = runTaskkill(taskkillPath, args, {

406408

stdio: "ignore",

407409

windowsHide: true,

408410

});

409411

if (!result.error && result.status === 0) {

410412

return true;

411413

}

412414

if (signal !== "SIGKILL") {

413-

const forceResult = runTaskkill("taskkill", [...args, "/F"], {

415+

const forceResult = runTaskkill(taskkillPath, [...args, "/F"], {

414416

stdio: "ignore",

415417

windowsHide: true,

416418

});

Original file line numberDiff line numberDiff line change

@@ -24,6 +24,7 @@ import {

2424

QA_CHANNEL_REQUIRED_PLUGIN_IDS,

2525

} from "./qa-channel-transport.js";

2626

import { buildQaGatewayConfig } from "./qa-gateway-config.js";

27+

import { resolveQaWindowsSystem32ExePath } from "./windows-system-tools.js";

2728
2829

type ModelRow = {

2930

key: string;

@@ -120,10 +121,14 @@ function killProcessTree(pid: number | undefined, signal: NodeJS.Signals) {

120121

}

121122

try {

122123

if (process.platform === "win32") {

123-

const killer = spawn("taskkill", ["/pid", String(pid), "/t", "/f"], {

124-

stdio: "ignore",

125-

windowsHide: true,

126-

});

124+

const killer = spawn(

125+

resolveQaWindowsSystem32ExePath("taskkill.exe"),

126+

["/pid", String(pid), "/t", "/f"],

127+

{

128+

stdio: "ignore",

129+

windowsHide: true,

130+

},

131+

);

127132

killer.once("error", () => {

128133

try {

129134

process.kill(pid, signal);

Original file line numberDiff line numberDiff line change

@@ -213,10 +213,14 @@ describe("qa suite runtime agent process helpers", () => {

213213
214214

await waitForSpawnCount(1);

215215

await timeoutAssertion;

216-

expect(spawnSyncMock).toHaveBeenCalledWith("taskkill", ["/PID", "12345", "/T", "/F"], {

217-

stdio: "ignore",

218-

windowsHide: true,

219-

});

216+

expect(spawnSyncMock).toHaveBeenCalledWith(

217+

path.win32.join("C:\\Windows", "System32", "taskkill.exe"),

218+

["/PID", "12345", "/T", "/F"],

219+

{

220+

stdio: "ignore",

221+

windowsHide: true,

222+

},

223+

);

220224

expect(child.kill).not.toHaveBeenCalled();

221225

} finally {

222226

if (platformDescriptor) {

Original file line numberDiff line numberDiff line change

@@ -19,6 +19,7 @@ import { liveTurnTimeoutMs } from "./suite-runtime-agent-common.js";

1919

import { waitForGatewayHealthy, waitForTransportReady } from "./suite-runtime-gateway.js";

2020

import type { QaDreamingStatus, QaSuiteRuntimeEnv } from "./suite-runtime-types.js";

2121

import { resolveQaGatewayTimeoutWithGraceMs } from "./timer-timeouts.js";

22+

import { resolveQaWindowsSystem32ExePath } from "./windows-system-tools.js";

2223
2324

type QaMemorySearchResult = {

2425

results?: Array<{ snippet?: string; text?: string; path?: string }>;

@@ -209,10 +210,14 @@ function signalQaCliProcessTree(

209210

) {

210211

if (process.platform === "win32") {

211212

if (typeof child.pid === "number") {

212-

const result = spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], {

213-

stdio: "ignore",

214-

windowsHide: true,

215-

});

213+

const result = spawnSync(

214+

resolveQaWindowsSystem32ExePath("taskkill.exe"),

215+

["/PID", String(child.pid), "/T", "/F"],

216+

{

217+

stdio: "ignore",

218+

windowsHide: true,

219+

},

220+

);

216221

if (!result.error && result.status === 0) {

217222

return;

218223

}

Original file line numberDiff line numberDiff line change

@@ -443,11 +443,12 @@ describe("qa test file scenario runner", () => {

443443

runTaskkill,

444444

),

445445

).toBe(true);

446-

expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/pid", "12345", "/T"], {

446+

const taskkillPath = path.win32.join("C:\\Windows", "System32", "taskkill.exe");

447+

expect(runTaskkill).toHaveBeenNthCalledWith(1, taskkillPath, ["/pid", "12345", "/T"], {

447448

stdio: "ignore",

448449

windowsHide: true,

449450

});

450-

expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/pid", "12345", "/T", "/F"], {

451+

expect(runTaskkill).toHaveBeenNthCalledWith(2, taskkillPath, ["/pid", "12345", "/T", "/F"], {

451452

stdio: "ignore",

452453

windowsHide: true,

453454

});

Original file line numberDiff line numberDiff line change

@@ -21,6 +21,7 @@ import type { QaProviderMode } from "./providers/index.js";

2121

import type { QaSeedScenarioWithSource } from "./scenario-catalog.js";

2222

import type { QaScorecardEvidenceMode } from "./scorecard-taxonomy.js";

2323

import { shellQuote } from "./shell-quote.js";

24+

import { resolveQaWindowsSystem32ExePath } from "./windows-system-tools.js";

2425
2526

export type QaTestFileScenario = QaSeedScenarioWithSource & {

2627

execution: Extract<

@@ -196,19 +197,20 @@ function killQaScenarioWindowsProcessTree(

196197

if (pid === undefined) {

197198

return false;

198199

}

200+

const taskkillPath = resolveQaWindowsSystem32ExePath("taskkill.exe");

199201

const args = ["/pid", String(pid), "/T"];

200202

if (signal === "SIGKILL") {

201203

args.push("/F");

202204

}

203-

const result = runTaskkill("taskkill", args, {

205+

const result = runTaskkill(taskkillPath, args, {

204206

stdio: "ignore",

205207

windowsHide: true,

206208

});

207209

if (!result.error && result.status === 0) {

208210

return true;

209211

}

210212

if (signal !== "SIGKILL") {

211-

const forceResult = runTaskkill("taskkill", [...args, "/F"], {

213+

const forceResult = runTaskkill(taskkillPath, [...args, "/F"], {

212214

stdio: "ignore",

213215

windowsHide: true,

214216

});

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,30 @@

1+

// Qa Lab tests cover Windows system tool path resolution.

2+

import { describe, expect, it } from "vitest";

3+

import {

4+

resolveQaWindowsSystem32ExePath,

5+

resolveQaWindowsSystemRoot,

6+

} from "./windows-system-tools.js";

7+
8+

describe("qa-lab windows system tools", () => {

9+

it("resolves System32 executables from a trusted SystemRoot", () => {

10+

expect(resolveQaWindowsSystemRoot({ SystemRoot: "D:\\Windows\\" })).toBe("D:\\Windows");

11+

expect(resolveQaWindowsSystem32ExePath("taskkill.exe", { SystemRoot: "D:\\Windows\\" })).toBe(

12+

"D:\\Windows\\System32\\taskkill.exe",

13+

);

14+

});

15+
16+

it("falls back to the default Windows root when env roots are unsafe", () => {

17+

expect(resolveQaWindowsSystem32ExePath("taskkill.exe", { SystemRoot: "C:\\tmp;C:\\bad" })).toBe(

18+

"C:\\Windows\\System32\\taskkill.exe",

19+

);

20+

});

21+
22+

it("rejects non-basename System32 executable names", () => {

23+

expect(() => resolveQaWindowsSystem32ExePath("..\\taskkill.exe")).toThrow(

24+

"Invalid Windows System32 executable name",

25+

);

26+

expect(() => resolveQaWindowsSystem32ExePath("taskkill")).toThrow(

27+

"Invalid Windows System32 executable name",

28+

);

29+

});

30+

});

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,62 @@

1+

// Qa Lab resolves Windows system tools without trusting PATH.

2+

import path from "node:path";

3+
4+

const DEFAULT_WINDOWS_SYSTEM_ROOT = "C:\\Windows";

5+
6+

function getEnvValueCaseInsensitive(

7+

env: Record<string, string | undefined>,

8+

expectedKey: string,

9+

): string | undefined {

10+

const direct = env[expectedKey];

11+

if (direct !== undefined) {

12+

return direct;

13+

}

14+

const expected = expectedKey.toUpperCase();

15+

const actualKey = Object.keys(env).find((key) => key.toUpperCase() === expected);

16+

return actualKey ? env[actualKey] : undefined;

17+

}

18+
19+

function normalizeWindowsSystemRoot(raw: string | undefined): string | null {

20+

const trimmed = raw?.trim();

21+

if (

22+

!trimmed ||

23+

trimmed.includes("\0") ||

24+

trimmed.includes("\r") ||

25+

trimmed.includes("\n") ||

26+

trimmed.includes(";")

27+

) {

28+

return null;

29+

}

30+

const normalized = path.win32.normalize(trimmed);

31+

if (!path.win32.isAbsolute(normalized) || normalized.startsWith("\\\\")) {

32+

return null;

33+

}

34+

const parsed = path.win32.parse(normalized);

35+

if (!/^[A-Za-z]:\\$/u.test(parsed.root) || normalized.length <= parsed.root.length) {

36+

return null;

37+

}

38+

return normalized.replace(/[\\/]+$/u, "");

39+

}

40+
41+

export function resolveQaWindowsSystemRoot(

42+

env: Record<string, string | undefined> = process.env,

43+

): string {

44+

return (

45+

normalizeWindowsSystemRoot(getEnvValueCaseInsensitive(env, "SystemRoot")) ??

46+

normalizeWindowsSystemRoot(getEnvValueCaseInsensitive(env, "WINDIR")) ??

47+

DEFAULT_WINDOWS_SYSTEM_ROOT

48+

);

49+

}

50+
51+

export function resolveQaWindowsSystem32ExePath(

52+

executableName: string,

53+

env: Record<string, string | undefined> = process.env,

54+

): string {

55+

if (

56+

path.win32.basename(executableName) !== executableName ||

57+

!/^[A-Za-z0-9_.-]+\.exe$/u.test(executableName)

58+

) {

59+

throw new Error(`Invalid Windows System32 executable name: ${executableName}`);

60+

}

61+

return path.win32.join(resolveQaWindowsSystemRoot(env), "System32", executableName);

62+

}