From 11de3fb12ba1f51f19e7022481b624edb7b6bc72 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 1 May 2026 19:38:05 +0300 Subject: [PATCH] =?UTF-8?q?cmd/drover:=20ReElevate=20=E2=80=94=20surface?= =?UTF-8?q?=20UTF16=20+=20Getwd=20errors,=20escape=20quotes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code review found 5 silently-ignored errors in ReElevate (UTF16 conversions and os.Getwd) plus unescaped argument quoting that breaks args containing literal `"`. Each error is now wrapped with a clear message; quotes are backslash-escaped per the MSVC argv convention. Co-Authored-By: Claude Opus 4.7 (1M context) --- cmd/drover/uac_windows.go | 33 ++++++++++++++++++++++++++------- cmd/drover/uac_windows_test.go | 6 +++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/cmd/drover/uac_windows.go b/cmd/drover/uac_windows.go index 093c20d..8515cdd 100644 --- a/cmd/drover/uac_windows.go +++ b/cmd/drover/uac_windows.go @@ -3,7 +3,9 @@ package main import ( + "fmt" "os" + "strings" "syscall" "unsafe" @@ -59,15 +61,23 @@ func ReElevate(args []string) error { if err != nil { return err } - verb, _ := syscall.UTF16PtrFromString("runas") - exePtr, _ := syscall.UTF16PtrFromString(exe) + verb, err := syscall.UTF16PtrFromString("runas") + if err != nil { + return fmt.Errorf("encode verb: %w", err) + } + exePtr, err := syscall.UTF16PtrFromString(exe) + if err != nil { + return fmt.Errorf("encode exe: %w", err) + } var paramsPtr *uint16 if len(args) > 0 { - // Quote each arg in case of spaces. + // Quote each arg in case of spaces, and escape internal quotes. quoted := make([]string, len(args)) for i, a := range args { - quoted[i] = `"` + a + `"` + // Escape any internal quotes with backslash (MSVC argv convention). + escaped := strings.ReplaceAll(a, "\"", "\\\"") + quoted[i] = `"` + escaped + `"` } joined := "" for i, q := range quoted { @@ -76,11 +86,20 @@ func ReElevate(args []string) error { } joined += q } - paramsPtr, _ = syscall.UTF16PtrFromString(joined) + paramsPtr, err = syscall.UTF16PtrFromString(joined) + if err != nil { + return fmt.Errorf("encode params: %w", err) + } } - cwd, _ := os.Getwd() - cwdPtr, _ := syscall.UTF16PtrFromString(cwd) + cwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("get cwd: %w", err) + } + cwdPtr, err := syscall.UTF16PtrFromString(cwd) + if err != nil { + return fmt.Errorf("encode cwd: %w", err) + } // SW_NORMAL = 1 return windows.ShellExecute(0, verb, exePtr, paramsPtr, cwdPtr, 1) diff --git a/cmd/drover/uac_windows_test.go b/cmd/drover/uac_windows_test.go index 0d1de09..1f72680 100644 --- a/cmd/drover/uac_windows_test.go +++ b/cmd/drover/uac_windows_test.go @@ -16,12 +16,12 @@ func TestCmdNeedsAdmin_NoAdminFlags(t *testing.T) { args []string needsAdm bool }{ - {[]string{}, true}, // bare drover.exe → GUI mode → needs admin - {[]string{"check"}, false}, // diagnostic only, no driver + {[]string{}, true}, // bare drover.exe → GUI mode → needs admin + {[]string{"check"}, false}, // diagnostic only, no driver {[]string{"check", "--host", "x"}, false}, {[]string{"--version"}, false}, {[]string{"version"}, false}, - {[]string{"update"}, false}, // self-update doesn't need driver + {[]string{"update"}, false}, // self-update doesn't need driver } for _, c := range cases { got := CmdNeedsAdmin(c.args)