From 385ddfc0bc7adb133d8b0e01224b0fba9d96d116 Mon Sep 17 00:00:00 2001 From: Ludovic Vielle Date: Tue, 21 Jul 2026 16:20:09 +0200 Subject: [PATCH] Fail Windows elevate on UAC cancel Start-Process failures left $p null, then exit $null made PowerShell return 0, so install/uninstall looked successful. Make launch errors terminating and exit nonzero before reading ExitCode. Signed-off-by: Ludovic Vielle --- pkg/deviceagent/elevate/elevate_windows.go | 44 ++++++++-------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/pkg/deviceagent/elevate/elevate_windows.go b/pkg/deviceagent/elevate/elevate_windows.go index 3aff83f6a..cd21087a7 100644 --- a/pkg/deviceagent/elevate/elevate_windows.go +++ b/pkg/deviceagent/elevate/elevate_windows.go @@ -28,6 +28,7 @@ import ( "strings" "go.probo.inc/probo/pkg/deviceagent/checks" + "golang.org/x/sys/windows" ) func runElevatedInstall(opts InstallOptions, enrollmentToken string) error { @@ -42,31 +43,7 @@ func runElevatedInstall(opts InstallOptions, enrollmentToken string) error { args = append(args, "--dir", opts.ConfigDir) } - argList := make([]string, len(args)) - for i, arg := range args { - argList[i] = "'" + escapePowerShellSingleQuoted(arg) + "'" - } - - script := fmt.Sprintf( - `$p = Start-Process -FilePath %s -ArgumentList @(%s) -Verb RunAs -Wait -PassThru; if ($p.ExitCode -ne 0) { exit $p.ExitCode }`, - "'"+escapePowerShellSingleQuoted(opts.ExePath)+"'", - strings.Join(argList, ","), - ) - - candidates := checks.CommandCandidates("powershell.exe") - if len(candidates) == 0 { - return fmt.Errorf("command %q not available at expected absolute path", "powershell.exe") - } - - out, err := exec.Command( - candidates[0], - "-NoProfile", - "-NonInteractive", - "-Command", - script, - ).CombinedOutput() - - return commandError(out, err) + return runPowerShellCommand(elevatedStartProcess(opts.ExePath, args)) } func runElevatedUninstall(opts UninstallOptions) error { @@ -75,17 +52,26 @@ func runElevatedUninstall(opts UninstallOptions) error { args = append(args, "--dir", opts.ConfigDir) } + return runPowerShellCommand(elevatedStartProcess(opts.ExePath, args)) +} + +// elevatedStartProcess builds a PowerShell script that launches exePath elevated +// via UAC. Launch failures (including UAC cancel) are terminating and exit +// nonzero before $p.ExitCode is inspected. +func elevatedStartProcess(exePath string, args []string) string { argList := make([]string, len(args)) for i, arg := range args { - argList[i] = "'" + escapePowerShellSingleQuoted(arg) + "'" + argList[i] = "'" + escapePowerShellSingleQuoted(windows.EscapeArg(arg)) + "'" } - script := fmt.Sprintf( - `$p = Start-Process -FilePath %s -ArgumentList @(%s) -Verb RunAs -Wait -PassThru; if ($p.ExitCode -ne 0) { exit $p.ExitCode }`, - "'"+escapePowerShellSingleQuoted(opts.ExePath)+"'", + return fmt.Sprintf( + `$ErrorActionPreference = 'Stop'; $p = Start-Process -FilePath %s -ArgumentList @(%s) -Verb RunAs -Wait -PassThru; if ($null -eq $p) { exit 1 }; if ($p.ExitCode -ne 0) { exit $p.ExitCode }`, + "'"+escapePowerShellSingleQuoted(exePath)+"'", strings.Join(argList, ","), ) +} +func runPowerShellCommand(script string) error { candidates := checks.CommandCandidates("powershell.exe") if len(candidates) == 0 { return fmt.Errorf("command %q not available at expected absolute path", "powershell.exe")