From 37b9230226db2a3a81df2e92a44ea655076cd0c4 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Thu, 3 Apr 2025 10:26:21 -0400 Subject: [PATCH 01/12] git-gui: _which, only add .exe suffix if not present The _which function finds executables on $PATH, and adds .exe on Windows unless -script was given. However, win32.tcl executes "wscript.exe" and "cscript.exe", both of which fail as _which adds .exe to both. This is already fixed in git-gui released by Git for Windows. Do so here. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 3e5907a460..2e079b6ab1 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -101,6 +101,9 @@ proc _which {what args} { if {[is_Windows] && [lsearch -exact $args -script] >= 0} { set suffix {} + } elseif {[is_Windows] && [string match *$_search_exe [string tolower $what]]} { + # The search string already has the file extension + set suffix {} } else { set suffix $_search_exe } From c5c32781c99bfa9d8b7c51b4a1ad66a9fa1e6bfe Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Wed, 2 Apr 2025 11:23:03 -0400 Subject: [PATCH 02/12] git-gui: use [is_Windows], not bad _shellpath Commit 7d076d56757c (git-gui: handle shell script text filters when loading for blame, 2011-12-09) added open_cmd_pipe, with special handling for Windows detected by seeing that _shellpath does not point to an executable shell. That is bad practice, and is broken by the next commit that assures _shellpath is valid on all platforms. Fix this by using [is_Windows] as done for all Windows specific code. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 2e079b6ab1..3135116169 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -543,7 +543,7 @@ proc is_shellscript {filename} { # scripts specifically otherwise just call the filter command. proc open_cmd_pipe {cmd path} { global env - if {![file executable [shellpath]]} { + if {[is_Windows]} { set exe [auto_execok [lindex $cmd 0]] if {[is_shellscript [lindex $exe 0]]} { set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path] From 10637fc327fe9d3afd19a11ed64bd9e1c7a9c6b5 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Tue, 1 Apr 2025 11:45:06 -0400 Subject: [PATCH 03/12] git-gui: make _shellpath usable on startup Since commit d5257fb3c1de (git-gui: handle textconv filter on Windows and in development, 2010-08-07), git-gui will search for a usable shell if _shellpath is not configured, and on Windows may resort to using auto_execok to find 'sh'. While this was intended for development use, checks are insufficient to assure a proper configuration when deployed where _shellpath is always set, but might not give a usable shell. Let's make this more robust by only searching if _shellpath was not defined, and then using only our restricted search functions. Furthermore, we should convert to a Windows path on Windows. Always check for a valid shell on startup, meaning an absolute path to an executable, aborting if these conditions are not met. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 3135116169..d56610c892 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -307,15 +307,37 @@ if {$_trace >= 0} { # branches). set _last_merged_branch {} -proc shellpath {} { - global _shellpath env - if {[string match @@* $_shellpath]} { - if {[info exists env(SHELL)]} { - return $env(SHELL) - } else { - return /bin/sh - } +# for testing, allow unconfigured _shellpath +if {[string match @@* $_shellpath]} { + if {[info exists env(SHELL)]} { + set _shellpath $env(SHELL) + } else { + set _shellpath /bin/sh } +} + +if {[is_Windows]} { + set _shellpath [exec cygpath -m $_shellpath] +} + +if {![file executable $_shellpath] || \ + !([file pathtype $_shellpath] eq {absolute})} { + set errmsg "The defined shell ('$_shellpath') is not usable, \ + it must be an absolute path to an executable." + puts stderr $errmsg + + catch {wm withdraw .} + tk_messageBox \ + -icon error \ + -type ok \ + -title "git-gui: configuration error" \ + -message $errmsg + exit 1 +} + + +proc shellpath {} { + global _shellpath return $_shellpath } From 02dd866ba9dfe6b6090b351450894489ca80311f Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Sun, 6 Apr 2025 18:20:14 -0400 Subject: [PATCH 04/12] git-gui: use only the configured shell git-gui has a few places where a bare "sh" is passed to exec, meaning that the first instance of "sh" on $PATH will be used rather than the shell configured. This violates expectations that the configured shell is being used. Let's use [shellpath] everywhere. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- lib/sshkey.tcl | 3 ++- lib/tools.tcl | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/sshkey.tcl b/lib/sshkey.tcl index 589ff8f78a..c0c5d1dad8 100644 --- a/lib/sshkey.tcl +++ b/lib/sshkey.tcl @@ -83,7 +83,8 @@ proc make_ssh_key {w} { set sshkey_title [mc "Generating..."] $w.header.gen configure -state disabled - set cmdline [list sh -c {echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}] + set cmdline [list [shellpath] -c \ + {echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}] if {[catch { set sshkey_fd [_open_stdout_stderr $cmdline] } err]} { error_popup [mc "Could not start ssh-keygen:\n\n%s" $err] diff --git a/lib/tools.tcl b/lib/tools.tcl index 413f1a1700..b86f72ed16 100644 --- a/lib/tools.tcl +++ b/lib/tools.tcl @@ -110,14 +110,14 @@ proc tools_exec {fullname} { set cmdline $repo_config(guitool.$fullname.cmd) if {[is_config_true "guitool.$fullname.noconsole"]} { - tools_run_silent [list sh -c $cmdline] \ + tools_run_silent [list [shellpath] -c $cmdline] \ [list tools_complete $fullname {}] } else { regsub {/} $fullname { / } title set w [console::new \ [mc "Tool: %s" $title] \ [mc "Running: %s" $cmdline]] - console::exec $w [list sh -c $cmdline] \ + console::exec $w [list [shellpath] -c $cmdline] \ [list tools_complete $fullname $w] } From 4eb9b1157b6d597ba3f599e2ebbfd4c6d8504073 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sun, 18 May 2025 16:08:06 +0200 Subject: [PATCH 05/12] git-gui: remove special treatment of Windows from open_cmd_pipe Commit 7d076d56757c (git-gui: handle shell script text filters when loading for blame, 2011-12-09) added open_cmd_pipe to run text conversion in support of blame, with special handling for shell scripts on Windows. To determine whether the command is a shell script, 'lindex' is used to pick off the first token from the command. However, cmd is actually a command string taken from .gitconfig literally and is not necessarily a syntactically correct Tcl list. Hence, it cannot be processed by 'lindex' and 'lrange' reliably. Pass the command string to the shell just like on non-Windows platforms to avoid the potentially incorrect treatment. A use of 'auto_execok' is removed by this change. This function is dangerous on Windows, because it searches programs in the current directory. Delegating the path lookup to the shell is safe, because /bin/sh and /bin/bash follow POSIX on all platforms, including the Git for Windows port. A possible regression is that the old code, given filter command of 'foo', could find 'foo.bat' as a script, and not just bare 'foo', or 'foo.exe'. This rewrite requires explicitly giving the suffix if it is not .exe. This part of Git GUI can be exercised using git gui blame -- some.file while some.file has a textconv filter configured and has unstaged modifications. Helped-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index d56610c892..cb0c02e5b8 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -559,22 +559,13 @@ proc is_shellscript {filename} { return [expr {$magic eq "#!"}] } -# Run a command connected via pipes on stdout. +# Run a shell command connected via pipes on stdout. # This is for use with textconv filters and uses sh -c "..." to allow it to -# contain a command with arguments. On windows we must check for shell -# scripts specifically otherwise just call the filter command. +# contain a command with arguments. We presume this +# to be a shellscript that the configured shell (/bin/sh by default) knows +# how to run. proc open_cmd_pipe {cmd path} { - global env - if {[is_Windows]} { - set exe [auto_execok [lindex $cmd 0]] - if {[is_shellscript [lindex $exe 0]]} { - set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path] - } else { - set run [concat $exe [lrange $cmd 1 end] $path] - } - } else { - set run [list [shellpath] -c "$cmd \"\$0\"" $path] - } + set run [list [shellpath] -c "$cmd \"\$0\"" $path] return [open |$run r] } From 2c66188b123795e4083594ee682dcf012540bee2 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Fri, 4 Apr 2025 16:55:59 -0400 Subject: [PATCH 06/12] git-gui: remove unused proc is_shellscript Commit 7d076d56757c (git-gui: handle shell script text filters when loading for blame, 2011-12-09) added is_shellscript to test if a file is executable by the shell, used only when searching for textconv filters. The previous commit rearranged the tests for finding such filters, and removed the only user of is_shellscript. Remove this function. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index cb0c02e5b8..72da2443e5 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -549,16 +549,6 @@ proc _git_cmd {name} { return $v } -# Test a file for a hashbang to identify executable scripts on Windows. -proc is_shellscript {filename} { - if {![file exists $filename]} {return 0} - set f [open $filename r] - fconfigure $f -encoding binary - set magic [read $f 2] - close $f - return [expr {$magic eq "#!"}] -} - # Run a shell command connected via pipes on stdout. # This is for use with textconv filters and uses sh -c "..." to allow it to # contain a command with arguments. We presume this From 411cd493cb9998c2ba1c17dd98c3d3d4b121a1dd Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Wed, 2 Apr 2025 17:37:27 -0400 Subject: [PATCH 07/12] git-gui: avoid auto_execok for git-bash menu item On Windows, git-gui offers to open a git-bash session for the current repository from the menu, but uses [auto_execok start] to get the command to actually run that shell. The code for auto_execok, in /usr/share/tcl8.6/tcl.init, has 'start' in the 'shellBuiltins' list for cmd.exe on Windows: as a result, auto_execok does not actually search for start, meaning this usage is technically ok with auto_execok now. However, leaving this use of auto_execok in place will just induce confusion about why a known unsafe function is being used on Windows. Instead, let's switch to using our known safe _which function that looks only in $PATH, excluding the current working directory. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-gui.sh b/git-gui.sh index 72da2443e5..3bfe4364c7 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2769,7 +2769,7 @@ if {[is_Windows]} { } .mbar.repository add command \ -label [mc "Git Bash"] \ - -command {eval exec [auto_execok start] $cmdLine} + -command {eval exec [list [_which cmd] /c start] $cmdLine} } if {[is_Windows] || ![is_bare]} { From 00c7aa86e905175476e0dff149d570b48c34c8f1 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Thu, 3 Apr 2025 00:37:08 -0400 Subject: [PATCH 08/12] git-gui: avoid auto_execok in do_windows_shortcut git-gui on Windows uses auto_execok to locate git-gui.exe, which performs the same flawed search as does the builtin exec. Use _which instead, performing a safe PATH lookup. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- lib/shortcut.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shortcut.tcl b/lib/shortcut.tcl index 674a41f5e0..263f4899c9 100644 --- a/lib/shortcut.tcl +++ b/lib/shortcut.tcl @@ -12,7 +12,7 @@ proc do_windows_shortcut {} { set fn ${fn}.lnk } # Use git-gui.exe if available (ie: git-for-windows) - set cmdLine [auto_execok git-gui.exe] + set cmdLine [list [_which git-gui]] if {$cmdLine eq {}} { set cmdLine [list [info nameofexecutable] \ [file normalize $::argv0]] From 676c49583f063c6cb92e5b38eb6576929cebf1ff Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Mon, 7 Apr 2025 17:12:56 -0400 Subject: [PATCH 09/12] git-gui: cleanup git-bash menu item git-gui on Git for Windows creates a menu item to start a git-bash session for the current repository. This menu-item works as desired when git-gui is installed in the Git for Windows (g4w) distribution, but not when run from a different location such as normally done in development. The reason is that git-bash's location is known to be '/git-bash' in the Unix pathname space known to MSYS, but this is not known in the Windows pathname space. Instead, git-gui derives a pathname for git-bash assuming it is at a known relative location. If git-gui is run from a different directory than assumed in g4w, the relative location changes, and git-gui resorts to running a generic bash login session in a Windows console. But, the MSYS system underlying Git for Windows includes the 'cygpath' utility to convert between Unix and Windows pathnames. Let's use this so git-bash's Windows pathname is determined directly from /git-bash. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 3bfe4364c7..570c236f57 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -2759,17 +2759,16 @@ if {![is_bare]} { if {[is_Windows]} { # Use /git-bash.exe if available - set normalized [file normalize $::argv0] - regsub "/mingw../libexec/git-core/git-gui$" \ - $normalized "/git-bash.exe" cmdLine - if {$cmdLine != $normalized && [file exists $cmdLine]} { - set cmdLine [list "Git Bash" $cmdLine &] + set _git_bash [exec cygpath -m /git-bash.exe] + if {[file executable $_git_bash]} { + set _bash_cmdline [list "Git Bash" $_git_bash &] } else { - set cmdLine [list "Git Bash" bash --login -l &] + set _bash_cmdline [list "Git Bash" bash --login -l &] } .mbar.repository add command \ -label [mc "Git Bash"] \ - -command {eval exec [list [_which cmd] /c start] $cmdLine} + -command {eval exec [list [_which cmd] /c start] $_bash_cmdline} + unset _git_bash } if {[is_Windows] || ![is_bare]} { From 8fe7861c5185248a5786e87af71e29000cd4f214 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Fri, 11 Apr 2025 10:08:52 -0400 Subject: [PATCH 10/12] git-gui: assure PATH has only absolute elements. Since 8f23432b38d9 (windows: ignore empty `PATH` elements, 2022-11-23), git-gui excises all empty paths from $PATH, but still allows '.' or other relative paths, which can also allow executing code from the repository. Let's remove anything except absolute elements. While here, let's remove duplicated elements, which are very common on Windows: only the first such item can do anything except waste time repeating a search. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 570c236f57..9ccd888930 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -88,10 +88,22 @@ proc _which {what args} { set gitguidir [file dirname [info script]] regsub -all ";" $gitguidir "\\;" gitguidir set env(PATH) "$gitguidir;$env(PATH)" - set _search_path [split $env(PATH) {;}] - # Skip empty `PATH` elements - set _search_path [lsearch -all -inline -not -exact \ - $_search_path ""] + + set _path_seen [dict create] + foreach p [split $env(PATH) {;}] { + # Keep only absolute paths, getting rid of ., empty, etc. + if {[file pathtype $p] ne {absolute}} { + continue + } + # Keep only the first occurence of any duplicates. + set norm_p [file normalize $p] + if {[dict exists $_path_seen $norm_p]} { + continue + } + dict set _path_seen $norm_p 1 + lappend _search_path $norm_p + } + unset _path_seen set _search_exe .exe } else { set _search_path [split $env(PATH) :] From 384b1409e8ba05bb908979a3f6aaa45bf93ac3c9 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Fri, 11 Apr 2025 10:47:04 -0400 Subject: [PATCH 11/12] git-gui: sanitize $PATH on all platforms Since 8f23432b38d9 (windows: ignore empty `PATH` elements, 2022-11-23), git-gui removes empty elements from $PATH, and a prior commit made this remove all non-absolute elements from $PATH. But, this happens only on Windows. Unsafe $PATH elements in $PATH are possible on all platforms. Let's sanitize $PATH on all platforms to have consistent behavior. If a user really wants the current repository on $PATH, they can add its absolute name to $PATH. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 64 +++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9ccd888930..217aeb9ce3 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -77,39 +77,43 @@ proc is_Cygwin {} { ###################################################################### ## -## PATH lookup +## PATH lookup. Sanitize $PATH, assure exec/open use only that + +if {[is_Windows]} { + set _path_sep {;} + set _search_exe .exe +} else { + set _path_sep {:} + set _search_exe {} +} + +if {[is_Windows]} { + set gitguidir [file dirname [info script]] + regsub -all ";" $gitguidir "\\;" gitguidir + set env(PATH) "$gitguidir;$env(PATH)" +} set _search_path {} -proc _which {what args} { - global env _search_exe _search_path - - if {$_search_path eq {}} { - if {[is_Windows]} { - set gitguidir [file dirname [info script]] - regsub -all ";" $gitguidir "\\;" gitguidir - set env(PATH) "$gitguidir;$env(PATH)" - - set _path_seen [dict create] - foreach p [split $env(PATH) {;}] { - # Keep only absolute paths, getting rid of ., empty, etc. - if {[file pathtype $p] ne {absolute}} { - continue - } - # Keep only the first occurence of any duplicates. - set norm_p [file normalize $p] - if {[dict exists $_path_seen $norm_p]} { - continue - } - dict set _path_seen $norm_p 1 - lappend _search_path $norm_p - } - unset _path_seen - set _search_exe .exe - } else { - set _search_path [split $env(PATH) :] - set _search_exe {} - } +set _path_seen [dict create] +foreach p [split $env(PATH) $_path_sep] { + # Keep only absolute paths, getting rid of ., empty, etc. + if {[file pathtype $p] ne {absolute}} { + continue } + # Keep only the first occurence of any duplicates. + set norm_p [file normalize $p] + if {[dict exists $_path_seen $norm_p]} { + continue + } + dict set _path_seen $norm_p 1 + lappend _search_path $norm_p +} +unset _path_seen + +set env(PATH) [join $_search_path $_path_sep] + +proc _which {what args} { + global _search_exe _search_path if {[is_Windows] && [lsearch -exact $args -script] >= 0} { set suffix {} From a1ccd2512072cf52835050f4c97a4fba9f0ec8f9 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Fri, 11 Apr 2025 10:58:20 -0400 Subject: [PATCH 12/12] git-gui: override exec and open only on Windows Since aae9560a355d (Work around Tcl's default `PATH` lookup, 2022-11-23), git-gui overrides exec and open on all platforms. But, this was done in response to Tcl adding elements to $PATH on Windows, while exec, open, and auto_execok honor $PATH as given on all other platforms. Let's do the override only on Windows, restoring others to using their native exec and open. These honor the sanitized $PATH as that is written out to env(PATH) in a previous commit. auto_execok is also safe on these platforms, so can be used for _which. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt Signed-off-by: Taylor Blau --- git-gui.sh | 122 +++++++++++++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 217aeb9ce3..21c3858d2e 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -112,81 +112,91 @@ unset _path_seen set env(PATH) [join $_search_path $_path_sep] -proc _which {what args} { - global _search_exe _search_path +if {[is_Windows]} { + proc _which {what args} { + global _search_exe _search_path - if {[is_Windows] && [lsearch -exact $args -script] >= 0} { - set suffix {} - } elseif {[is_Windows] && [string match *$_search_exe [string tolower $what]]} { - # The search string already has the file extension - set suffix {} - } else { - set suffix $_search_exe - } - - foreach p $_search_path { - set p [file join $p $what$suffix] - if {[file exists $p]} { - return [file normalize $p] + if {[lsearch -exact $args -script] >= 0} { + set suffix {} + } elseif {[string match *$_search_exe [string tolower $what]]} { + # The search string already has the file extension + set suffix {} + } else { + set suffix $_search_exe } - } - return {} -} -proc sanitize_command_line {command_line from_index} { - set i $from_index - while {$i < [llength $command_line]} { - set cmd [lindex $command_line $i] - if {[llength [file split $cmd]] < 2} { - set fullpath [_which $cmd] - if {$fullpath eq ""} { - throw {NOT-FOUND} "$cmd not found in PATH" + foreach p $_search_path { + set p [file join $p $what$suffix] + if {[file exists $p]} { + return [file normalize $p] } - lset command_line $i $fullpath } + return {} + } - # handle piped commands, e.g. `exec A | B` - for {incr i} {$i < [llength $command_line]} {incr i} { - if {[lindex $command_line $i] eq "|"} { + proc sanitize_command_line {command_line from_index} { + set i $from_index + while {$i < [llength $command_line]} { + set cmd [lindex $command_line $i] + if {[llength [file split $cmd]] < 2} { + set fullpath [_which $cmd] + if {$fullpath eq ""} { + throw {NOT-FOUND} "$cmd not found in PATH" + } + lset command_line $i $fullpath + } + + # handle piped commands, e.g. `exec A | B` + for {incr i} {$i < [llength $command_line]} {incr i} { + if {[lindex $command_line $i] eq "|"} { + incr i + break + } + } + } + return $command_line + } + + # Override `exec` to avoid unsafe PATH lookup + + rename exec real_exec + + proc exec {args} { + # skip options + for {set i 0} {$i < [llength $args]} {incr i} { + set arg [lindex $args $i] + if {$arg eq "--"} { incr i break } + if {[string range $arg 0 0] ne "-"} { + break + } } + set args [sanitize_command_line $args $i] + uplevel 1 real_exec $args } - return $command_line -} -# Override `exec` to avoid unsafe PATH lookup + # Override `open` to avoid unsafe PATH lookup -rename exec real_exec + rename open real_open -proc exec {args} { - # skip options - for {set i 0} {$i < [llength $args]} {incr i} { - set arg [lindex $args $i] - if {$arg eq "--"} { - incr i - break - } - if {[string range $arg 0 0] ne "-"} { - break + proc open {args} { + set arg0 [lindex $args 0] + if {[string range $arg0 0 0] eq "|"} { + set command_line [string trim [string range $arg0 1 end]] + lset args 0 "| [sanitize_command_line $command_line 0]" } + uplevel 1 real_open $args } - set args [sanitize_command_line $args $i] - uplevel 1 real_exec $args -} -# Override `open` to avoid unsafe PATH lookup +} else { + # On non-Windows platforms, auto_execok, exec, and open are safe, and will + # use the sanitized search path. But, we need _which for these. -rename open real_open - -proc open {args} { - set arg0 [lindex $args 0] - if {[string range $arg0 0 0] eq "|"} { - set command_line [string trim [string range $arg0 1 end]] - lset args 0 "| [sanitize_command_line $command_line 0]" + proc _which {what args} { + return [lindex [auto_execok $what] 0] } - uplevel 1 real_open $args } ######################################################################