
























11import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2+import {
3+DEFAULT_WINDOWS_SYSTEM_ROOT,
4+_resetWindowsInstallRootsForTests,
5+} from "../infra/windows-install-roots.js";
26import type { WindowsAclEntry, WindowsAclSummary } from "./windows-acl.js";
3748const MOCK_USERNAME = "MockUser";
59const mockUserInfo = () => ({ username: MOCK_USERNAME });
610const emptyUserInfo = () => ({ username: "" });
11+const DEFAULT_ICACLS = `${DEFAULT_WINDOWS_SYSTEM_ROOT}\\System32\\icacls.exe`;
12+const DEFAULT_WHOAMI = `${DEFAULT_WINDOWS_SYSTEM_ROOT}\\System32\\whoami.exe`;
713814let createIcaclsResetCommand: typeof import("./windows-acl.js").createIcaclsResetCommand;
915let formatIcaclsResetCommand: typeof import("./windows-acl.js").formatIcaclsResetCommand;
@@ -27,6 +33,7 @@ beforeAll(async () => {
27332834beforeEach(() => {
2935vi.unstubAllEnvs();
36+_resetWindowsInstallRootsForTests();
3037});
31383239function aclEntry(params: {
@@ -420,11 +427,32 @@ Successfully processed 1 files`;
420427421428const result = await inspectWindowsAcl("C:\\test\\file.txt", {
422429exec: mockExec,
430+env: {},
423431});
424432expectInspectSuccess(result, 2);
425433// /sid is passed so that account names are printed as SIDs, making the
426434// audit locale-independent (fixes #35834).
427-expect(mockExec).toHaveBeenCalledWith("icacls.exe", ["C:\\test\\file.txt", "/sid"]);
435+expect(mockExec).toHaveBeenCalledWith(DEFAULT_ICACLS, ["C:\\test\\file.txt", "/sid"]);
436+});
437+438+it("uses the discovered process SystemRoot when env options are omitted", async () => {
439+_resetWindowsInstallRootsForTests({ queryRegistryValue: () => null });
440+vi.stubEnv("SystemRoot", "D:\\Windows");
441+442+const mockExec = vi.fn().mockResolvedValue({
443+stdout: "C:\\test\\file.txt *S-1-5-18:(F)",
444+stderr: "",
445+});
446+447+const result = await inspectWindowsAcl("C:\\test\\file.txt", {
448+exec: mockExec,
449+});
450+451+expectInspectSuccess(result, 1);
452+expect(mockExec).toHaveBeenCalledWith("D:\\Windows\\System32\\icacls.exe", [
453+"C:\\test\\file.txt",
454+"/sid",
455+]);
428456});
429457430458it("classifies *S-1-5-18 (SID form of SYSTEM from /sid) as trusted", async () => {
@@ -469,8 +497,8 @@ Successfully processed 1 files`;
469497expectInspectSuccess(result, 2);
470498expect(result.trusted).toHaveLength(2);
471499expect(result.untrustedGroup).toHaveLength(0);
472-expect(mockExec).toHaveBeenNthCalledWith(1, "icacls.exe", ["C:\\test\\file.txt", "/sid"]);
473-expect(mockExec).toHaveBeenNthCalledWith(2, "whoami.exe", ["/user", "/fo", "csv", "/nh"]);
500+expect(mockExec).toHaveBeenNthCalledWith(1, DEFAULT_ICACLS, ["C:\\test\\file.txt", "/sid"]);
501+expect(mockExec).toHaveBeenNthCalledWith(2, DEFAULT_WHOAMI, ["/user", "/fo", "csv", "/nh"]);
474502});
475503476504it("returns error state on exec failure", async () => {
@@ -533,21 +561,82 @@ Successfully processed 1 files`;
533561534562const result = await inspectWindowsAcl("C:\\test\\file.txt", {
535563exec: mockExec,
536-env: { SystemRoot: "C:\\Windows" },
564+env: { SystemRoot: "D:\\Windows" },
537565});
538566539567expectInspectSuccess(result, 1);
540-expect(mockExec).toHaveBeenNthCalledWith(1, "C:\\Windows\\System32\\icacls.exe", [
568+expect(mockExec).toHaveBeenNthCalledWith(1, "D:\\Windows\\System32\\icacls.exe", [
541569"C:\\test\\file.txt",
542570"/sid",
543571]);
544-expect(mockExec).toHaveBeenNthCalledWith(2, "C:\\Windows\\System32\\whoami.exe", [
572+expect(mockExec).toHaveBeenNthCalledWith(2, "D:\\Windows\\System32\\whoami.exe", [
545573"/user",
546574"/fo",
547575"csv",
548576"/nh",
549577]);
550578});
579+580+it.each([
581+["systemroot", "D:\\Windows"],
582+["windir", "E:\\Windows"],
583+])("resolves explicit env key %s case-insensitively", async (key, root) => {
584+const mockExec = vi.fn().mockResolvedValueOnce({
585+stdout: "C:\\test\\file.txt *S-1-5-18:(F)",
586+stderr: "",
587+});
588+589+const result = await inspectWindowsAcl("C:\\test\\file.txt", {
590+exec: mockExec,
591+env: { [key]: root },
592+});
593+594+expectInspectSuccess(result, 1);
595+expect(mockExec).toHaveBeenCalledWith(`${root}\\System32\\icacls.exe`, [
596+"C:\\test\\file.txt",
597+"/sid",
598+]);
599+});
600+601+it("does not resolve Windows system commands through a relative SystemRoot", async () => {
602+const mockExec = vi
603+.fn()
604+.mockResolvedValueOnce({
605+stdout: "C:\\test\\file.txt *S-1-5-21-111-222-333-1001:(F)",
606+stderr: "",
607+})
608+.mockResolvedValueOnce({
609+stdout: '"mock-host\\\\MockUser","S-1-5-21-111-222-333-1001"\r\n',
610+stderr: "",
611+});
612+613+const result = await inspectWindowsAcl("C:\\test\\file.txt", {
614+exec: mockExec,
615+env: { SystemRoot: ".\\fake-root" },
616+});
617+618+expectInspectSuccess(result, 1);
619+expect(mockExec).toHaveBeenNthCalledWith(1, DEFAULT_ICACLS, ["C:\\test\\file.txt", "/sid"]);
620+expect(mockExec).toHaveBeenNthCalledWith(2, DEFAULT_WHOAMI, ["/user", "/fo", "csv", "/nh"]);
621+});
622+623+it("uses a valid WINDIR when SystemRoot is invalid", async () => {
624+const mockExec = vi.fn().mockResolvedValueOnce({
625+stdout: "C:\\test\\file.txt *S-1-5-18:(F)",
626+stderr: "",
627+});
628+629+const result = await inspectWindowsAcl("C:\\test\\file.txt", {
630+exec: mockExec,
631+env: { SystemRoot: ".\\fake-root", WINDIR: "E:\\Windows" },
632+});
633+634+expectInspectSuccess(result, 1);
635+expect(mockExec).toHaveBeenCalledWith("E:\\Windows\\System32\\icacls.exe", [
636+"C:\\test\\file.txt",
637+"/sid",
638+]);
639+});
551640});
552641553642describe("formatWindowsAclSummary", () => {
@@ -619,7 +708,15 @@ Successfully processed 1 files`;
619708 env,
620709});
621710expect(result).toBe(
622-'icacls "C:\\test\\file.txt" /inheritance:r /grant:r "WORKGROUP\\TestUser:F" /grant:r "*S-1-5-18:F"',
711+[
712+DEFAULT_ICACLS,
713+'"C:\\test\\file.txt"',
714+"/inheritance:r",
715+"/grant:r",
716+'"WORKGROUP\\TestUser:F"',
717+"/grant:r",
718+'"*S-1-5-18:F"',
719+].join(" "),
623720);
624721});
625722@@ -632,6 +729,15 @@ Successfully processed 1 files`;
632729expect(result).toContain("(OI)(CI)F");
633730});
634731732+it("uses a validated SystemRoot in the display command", () => {
733+const result = formatIcaclsResetCommand("C:\\test\\file.txt", {
734+isDir: false,
735+env: { SystemRoot: "D:\\Windows", USERNAME: "TestUser" },
736+});
737+738+expect(result.startsWith("D:\\Windows\\System32\\icacls.exe ")).toBe(true);
739+});
740+635741it("uses system username when env is empty (falls back to os.userInfo)", () => {
636742// When env is empty, resolveWindowsUserPrincipal falls back to os.userInfo().username
637743const result = formatIcaclsResetCommand("C:\\test\\file.txt", {
@@ -653,11 +759,20 @@ Successfully processed 1 files`;
653759 env,
654760});
655761expect(result).not.toBeNull();
656-expect(result?.command).toBe("icacls");
762+expect(result?.command).toBe(DEFAULT_ICACLS);
657763expect(result?.args).toContain("C:\\test\\file.txt");
658764expect(result?.args).toContain("/inheritance:r");
659765});
660766767+it("uses a validated SystemRoot for the structured command executable", () => {
768+const result = createIcaclsResetCommand("C:\\test\\file.txt", {
769+isDir: false,
770+env: { SystemRoot: "D:\\Windows", USERNAME: "TestUser" },
771+});
772+773+expect(result?.command).toBe("D:\\Windows\\System32\\icacls.exe");
774+});
775+661776it("returns command with system username when env is empty (falls back to os.userInfo)", () => {
662777// When env is empty, resolveWindowsUserPrincipal falls back to os.userInfo().username
663778const result = createIcaclsResetCommand("C:\\test\\file.txt", {
@@ -667,7 +782,7 @@ Successfully processed 1 files`;
667782});
668783// Should return a valid command using the system username
669784expect(result).not.toBeNull();
670-expect(result?.command).toBe("icacls");
785+expect(result?.command).toBe(DEFAULT_ICACLS);
671786expect(result?.args).toContain(`${MOCK_USERNAME}:F`);
672787});
673788此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。