gitk: sanitize 'exec' arguments: redirections

As in the previous commits, introduce a function that sanitizes arguments intended for the process and in addition allows to pass redirections verbatim, which are interpreted by Tcl's 'exec'. Redirections can include the background operator '&'. Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Taylor Blau <me@ttaylorr.com>

Johannes Sixt committed Mar 29, 2025 at 17:01 UTC 6b631ee8ed76c13a28e482588fd1264d591a46de
1 file changed +17 -8
gitk
+17 -8
@@ -40,6 +40,15 @@ proc safe_exec {cmd} {
40 eval exec [make_arglist_safe $cmd]
41 }
42
43 +# executes one command with redirections
44 +# no pipelines are possible
45 +# cmd is a list that specifies the command and its arguments
46 +# redir is a list that specifies redirections (output, background)
47 +# calls `exec` and returns its value
48 +proc safe_exec_redirect {cmd redir} {
49 + eval exec [make_arglist_safe $cmd] $redir
50 +}
51 +
52 proc safe_open_file {filename flags} {
53 # a file name starting with "|" would attempt to run a process
54 # but such a file name must be treated as a relative path
@@ -2135,7 +2144,7 @@ proc makewindow {} {
2144 {mc "Reread re&ferences" command rereadrefs}
2145 {mc "&List references" command showrefs -accelerator F2}
2146 {xx "" separator}
2138 - {mc "Start git &gui" command {exec git gui &}}
2147 + {mc "Start git &gui" command {safe_exec_redirect [list git gui] [list &]}}
2148 {xx "" separator}
2149 {mc "&Quit" command doquit -accelerator Meta1-Q}
2150 }}
@@ -3655,7 +3664,7 @@ proc gitknewtmpdir {} {
3664 proc save_file_from_commit {filename output what} {
3665 global nullfile
3666
3658 - if {[catch {exec git show $filename -- > $output} err]} {
3667 + if {[catch {safe_exec_redirect [list git show $filename --] [list > $output]} err]} {
3668 if {[string match "fatal: bad revision *" $err]} {
3669 return $nullfile
3670 }
@@ -3884,7 +3893,7 @@ proc external_blame {parent_idx {line {}}} {
3893 # being given an absolute path...
3894 set f [make_relative $f]
3895 lappend cmdline $base_commit $f
3887 - if {[catch {eval exec $cmdline &} err]} {
3896 + if {[catch {safe_exec_redirect $cmdline [list &]} err]} {
3897 error_popup "[mc "git gui blame: command failed:"] $err"
3898 }
3899 }
@@ -7193,8 +7202,8 @@ proc browseweb {url} {
7202 global web_browser
7203
7204 if {$web_browser eq {}} return
7196 - # Use eval here in case $web_browser is a command plus some arguments
7197 - if {[catch {eval exec $web_browser [list $url] &} err]} {
7205 + # Use concat here in case $web_browser is a command plus some arguments
7206 + if {[catch {safe_exec_redirect [concat $web_browser [list $url]] [list &]} err]} {
7207 error_popup "[mc "Error starting web browser:"] $err"
7208 }
7209 }
@@ -9207,8 +9216,8 @@ proc diffcommits {a b} {
9216 set fna [file join $tmpdir "commit-[string range $a 0 7]"]
9217 set fnb [file join $tmpdir "commit-[string range $b 0 7]"]
9218 if {[catch {
9210 - exec git diff-tree -p --pretty $a >$fna
9211 - exec git diff-tree -p --pretty $b >$fnb
9219 + safe_exec_redirect [list git diff-tree -p --pretty $a] [list >$fna]
9220 + safe_exec_redirect [list git diff-tree -p --pretty $b] [list >$fnb]
9221 } err]} {
9222 error_popup [mc "Error writing commit to file: %s" $err]
9223 return
@@ -9730,7 +9739,7 @@ proc exec_citool {tool_args {baseid {}}} {
9739 }
9740 }
9741
9733 - eval exec git citool $tool_args &
9742 + safe_exec_redirect [concat git citool $tool_args] [list &]
9743
9744 array unset env GIT_AUTHOR_*
9745 array set env $save_env