

























@@ -3,6 +3,10 @@ import { EventEmitter } from "node:events";
33import fs from "node:fs";
44import path from "node:path";
55import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
6+import {
7+_resetWindowsInstallRootsForTests,
8+getWindowsInstallRoots,
9+} from "../infra/windows-install-roots.js";
610711const { spawnMock, spawnSyncMock, execFileMock, execFilePromisifyMock } = vi.hoisted(() => {
812const execFilePromisifyMock = vi.fn();
@@ -101,6 +105,10 @@ function expectCmdWrappedInvocation(params: {
101105expect(params.captured[2].windowsVerbatimArguments).toBe(true);
102106}
103107108+function expectedTrustedCmdExe(): string {
109+return path.win32.join(getWindowsInstallRoots().systemRoot, "System32", "cmd.exe");
110+}
111+104112async function expectShimmedWindowsCommandWithoutExitCodeSucceeds(params?: { killed?: boolean }) {
105113const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
106114const child = createMockChild({
@@ -127,6 +135,10 @@ describe("windows command wrapper behavior", () => {
127135});
128136129137beforeEach(() => {
138+// Stub the registry probe so install-root resolution is fully driven by
139+// process.env in tests; on real Windows runners the registry returns the
140+// canonical SystemRoot and would shadow the test's env setup.
141+_resetWindowsInstallRootsForTests({ queryRegistryValue: () => null });
130142spawnMock.mockReset();
131143spawnSyncMock.mockReset();
132144spawnSyncMock.mockReturnValue({ stdout: "Active code page: 936", stderr: "" });
@@ -157,7 +169,7 @@ describe("windows command wrapper behavior", () => {
157169158170it("wraps .cmd commands via cmd.exe in runCommandWithTimeout", async () => {
159171const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
160-const expectedComSpec = process.env.ComSpec ?? "cmd.exe";
172+const expectedComSpec = expectedTrustedCmdExe();
161173162174spawnMock.mockImplementation(
163175(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
@@ -173,9 +185,91 @@ describe("windows command wrapper behavior", () => {
173185}
174186});
175187188+it("ignores ComSpec when selecting the Windows command wrapper", async () => {
189+const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
190+const previousComSpec = process.env.ComSpec;
191+const previousSystemRoot = process.env.SystemRoot;
192+process.env.ComSpec = "C:\\workspace\\evil\\cmd.exe";
193+process.env.SystemRoot = "C:\\Windows";
194+195+spawnMock.mockImplementation(
196+(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
197+);
198+199+try {
200+const result = await runCommandWithTimeout(["pnpm", "--version"], { timeoutMs: 1000 });
201+expect(result.code).toBe(0);
202+const captured = spawnMock.mock.calls[0] as SpawnCall | undefined;
203+expectCmdWrappedInvocation({
204+ captured,
205+expectedComSpec: path.win32.join("C:\\Windows", "System32", "cmd.exe"),
206+});
207+} finally {
208+if (previousComSpec === undefined) {
209+delete process.env.ComSpec;
210+} else {
211+process.env.ComSpec = previousComSpec;
212+}
213+if (previousSystemRoot === undefined) {
214+delete process.env.SystemRoot;
215+} else {
216+process.env.SystemRoot = previousSystemRoot;
217+}
218+platformSpy.mockRestore();
219+}
220+});
221+222+it("rejects unsafe Windows root values when selecting the command wrapper", async () => {
223+const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
224+const previousSystemRoot = process.env.SystemRoot;
225+const previousWindir = process.env.WINDIR;
226+227+spawnMock.mockImplementation(
228+(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
229+);
230+231+try {
232+for (const unsafeRoot of [
233+"\\\\evil\\share",
234+"C:\\Windows;C:\\evil",
235+"\\Windows",
236+"relative\\path",
237+]) {
238+_resetWindowsInstallRootsForTests({ queryRegistryValue: () => null });
239+// Set every install-root env source to the unsafe value so the
240+// resolver rejects each one and falls through to the safe default.
241+// Deleting WINDIR here is unreliable on real Windows runners, so
242+// overwrite it with the same rejected payload.
243+process.env.SystemRoot = unsafeRoot;
244+process.env.WINDIR = unsafeRoot;
245+spawnMock.mockClear();
246+247+const result = await runCommandWithTimeout(["pnpm", "--version"], { timeoutMs: 1000 });
248+expect(result.code).toBe(0);
249+const captured = spawnMock.mock.calls[0] as SpawnCall | undefined;
250+expectCmdWrappedInvocation({
251+ captured,
252+expectedComSpec: path.win32.join("C:\\Windows", "System32", "cmd.exe"),
253+});
254+}
255+} finally {
256+if (previousSystemRoot === undefined) {
257+delete process.env.SystemRoot;
258+} else {
259+process.env.SystemRoot = previousSystemRoot;
260+}
261+if (previousWindir === undefined) {
262+delete process.env.WINDIR;
263+} else {
264+process.env.WINDIR = previousWindir;
265+}
266+platformSpy.mockRestore();
267+}
268+});
269+176270it("wraps corepack.cmd via cmd.exe in runCommandWithTimeout", async () => {
177271const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
178-const expectedComSpec = process.env.ComSpec ?? "cmd.exe";
272+const expectedComSpec = expectedTrustedCmdExe();
179273180274spawnMock.mockImplementation(
181275(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
@@ -243,7 +337,7 @@ describe("windows command wrapper behavior", () => {
243337it("falls back to npm.cmd when npm-cli.js is unavailable", async () => {
244338const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
245339const existsSpy = vi.spyOn(fs, "existsSync").mockReturnValue(false);
246-const expectedComSpec = process.env.ComSpec ?? "cmd.exe";
340+const expectedComSpec = expectedTrustedCmdExe();
247341248342spawnMock.mockImplementation(
249343(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
@@ -297,7 +391,7 @@ describe("windows command wrapper behavior", () => {
297391298392it("uses cmd.exe wrapper with windowsVerbatimArguments in runExec for .cmd shims", async () => {
299393const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
300-const expectedComSpec = process.env.ComSpec ?? "cmd.exe";
394+const expectedComSpec = expectedTrustedCmdExe();
301395302396execFileMock.mockImplementation(
303397(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。