From f10161df3f688dcabf70ca6f3dfdfeede6ecded0 Mon Sep 17 00:00:00 2001 From: Ulrich Zogo Date: Sun, 16 Aug 2026 13:00:57 -0400 Subject: [PATCH] fix(windows): preserve scheduled-task elevation launcher arguments --- src/lib/windows-elevation.ts | 6 ++-- tests/windows-elevation-spawn.test.ts | 44 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/lib/windows-elevation.ts b/src/lib/windows-elevation.ts index f27e102cd2..4317a9262f 100644 --- a/src/lib/windows-elevation.ts +++ b/src/lib/windows-elevation.ts @@ -648,12 +648,12 @@ export function runWindowsElevatedScheduledTaskRegistration( "-EncodedCommand", encodedCommand, ]))}`, - " -Verb RunAs -WindowStyle Hidden -PassThru -Wait", + " -Verb RunAs -WindowStyle Hidden -PassThru -Wait;", `if ($null -eq $p) { exit ${OCX_ELEVATED_UAC_CANCELLED} }`, - "$null = $p.Handle", + "$null = $p.Handle;", `if ($null -eq $p.ExitCode) { exit ${OCX_ELEVATED_PROTOCOL_FAILED} }`, "exit $p.ExitCode", - ].join("; "); + ].join(""); return startPowerShellCommand(script).completion.then(result => result.exitCode); } diff --git a/tests/windows-elevation-spawn.test.ts b/tests/windows-elevation-spawn.test.ts index 1eff2dddb0..3847454b8a 100644 --- a/tests/windows-elevation-spawn.test.ts +++ b/tests/windows-elevation-spawn.test.ts @@ -120,6 +120,50 @@ describe("runWindowsElevated spawn contract", () => { await expect(runWindowsElevated("schtasks.exe", ["/create"])).resolves.toBe(1); }); + test("keeps scheduled-task elevation arguments in one Start-Process statement", async () => { + let commandScript = ""; + setWindowsElevationSpawnForTests((( + _cmd: string, + args: ReadonlyArray, + ) => { + commandScript = String(args[args.length - 1] ?? ""); + const child = new EventEmitter() as EventEmitter & { + stdout: EventEmitter & { setEncoding?: (enc: string) => void }; + stderr: EventEmitter & { setEncoding?: (enc: string) => void }; + kill: ReturnType; + }; + child.stdout = new EventEmitter(); + child.stderr = new EventEmitter(); + child.stdout.setEncoding = () => undefined; + child.stderr.setEncoding = () => undefined; + child.kill = mock(() => true); + queueMicrotask(() => child.emit("close", 0, null)); + return child as never; + }) as never); + + await expect(runWindowsElevatedScheduledTaskRegistration( + "opencodex-proxy", + "", + )).resolves.toBe(0); + + const startProcessIndex = commandScript.indexOf("Start-Process"); + const filePathIndex = commandScript.indexOf(" -FilePath "); + const argumentListIndex = commandScript.indexOf(" -ArgumentList "); + const verbIndex = commandScript.indexOf(" -Verb RunAs "); + const waitIndex = commandScript.indexOf(" -Wait"); + const firstTerminator = commandScript.indexOf(";"); + + expect(startProcessIndex).toBeGreaterThanOrEqual(0); + expect(filePathIndex).toBeGreaterThan(startProcessIndex); + expect(argumentListIndex).toBeGreaterThan(filePathIndex); + expect(verbIndex).toBeGreaterThan(argumentListIndex); + expect(waitIndex).toBeGreaterThan(verbIndex); + expect(firstTerminator).toBeGreaterThan(waitIndex); + expect(commandScript.match(/Start-Process/g)).toHaveLength(1); + expect(commandScript).not.toMatch(/powershell\.exe';\s+-ArgumentList/i); + expect(commandScript).not.toMatch(/-ArgumentList\s+'[^']*';\s+-Verb RunAs/); + }); + test("scheduled-task registration embeds immutable XML bytes instead of a file path", async () => { let commandScript = ""; setWindowsElevationSpawnForTests(((