Skip to content

Commit 9f0d1c2

Browse files
j6tttaylorr
authored andcommitted
gitk: sanitize 'exec' arguments: simple cases
Tcl 'exec' assigns special meaning to its argument when they begin with redirection, pipe or background operator. There are a number of invocations of 'exec' which construct arguments that are taken from the Git repository or a user input. However, when file names or ref names are taken from the repository, it is possible to find names with have these special forms. They must not be interpreted by 'exec' lest it redirects input or output, or attempts to build a pipeline using a command name controlled by the repository. Introduce a helper function that identifies such arguments and prepends "./" to force such a name to be regarded as a relative file name. Convert those 'exec' calls where the arguments can simply be packed into a list. Signed-off-by: Johannes Sixt <[email protected]> Signed-off-by: Taylor Blau <[email protected]>
1 parent 6eb797f commit 9f0d1c2

File tree

1 file changed

+48
-18
lines changed

1 file changed

+48
-18
lines changed

gitk

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,35 @@ exec wish "$0" -- "$@"
1010
package require Tk
1111

1212

13-
# Wrap open to sanitize arguments
13+
# Wrap exec/open to sanitize arguments
14+
15+
# unsafe arguments begin with redirections or the pipe or background operators
16+
proc is_arg_unsafe {arg} {
17+
regexp {^([<|>&]|2>)} $arg
18+
}
19+
20+
proc make_arg_safe {arg} {
21+
if {[is_arg_unsafe $arg]} {
22+
set arg [file join . $arg]
23+
}
24+
return $arg
25+
}
26+
27+
proc make_arglist_safe {arglist} {
28+
set res {}
29+
foreach arg $arglist {
30+
lappend res [make_arg_safe $arg]
31+
}
32+
return $res
33+
}
34+
35+
# executes one command
36+
# no redirections or pipelines are possible
37+
# cmd is a list that specifies the command and its arguments
38+
# calls `exec` and returns its value
39+
proc safe_exec {cmd} {
40+
eval exec [make_arglist_safe $cmd]
41+
}
1442

1543
proc safe_open_file {filename flags} {
1644
# a file name starting with "|" would attempt to run a process
@@ -22,6 +50,8 @@ proc safe_open_file {filename flags} {
2250
open $filename $flags
2351
}
2452

53+
# End exec/open wrappers
54+
2555
proc hasworktree {} {
2656
return [expr {[exec git rev-parse --is-bare-repository] == "false" &&
2757
[exec git rev-parse --is-inside-git-dir] == "false"}]
@@ -387,7 +417,7 @@ proc start_rev_list {view} {
387417
set args $viewargs($view)
388418
if {$viewargscmd($view) ne {}} {
389419
if {[catch {
390-
set str [exec sh -c $viewargscmd($view)]
420+
set str [safe_exec [list sh -c $viewargscmd($view)]]
391421
} err]} {
392422
error_popup "[mc "Error executing --argscmd command:"] $err"
393423
return 0
@@ -459,9 +489,9 @@ proc stop_instance {inst} {
459489
set pid [pid $fd]
460490

461491
if {$::tcl_platform(platform) eq {windows}} {
462-
exec taskkill /pid $pid
492+
safe_exec [list taskkill /pid $pid]
463493
} else {
464-
exec kill $pid
494+
safe_exec [list kill $pid]
465495
}
466496
}
467497
catch {close $fd}
@@ -1540,8 +1570,8 @@ proc getcommitlines {fd inst view updating} {
15401570
# and if we already know about it, using the rewritten
15411571
# parent as a substitute parent for $id's children.
15421572
if {![catch {
1543-
set rwid [exec git rev-list --first-parent --max-count=1 \
1544-
$id -- $vfilelimit($view)]
1573+
set rwid [safe_exec [list git rev-list --first-parent --max-count=1 \
1574+
$id -- $vfilelimit($view)]]
15451575
}]} {
15461576
if {$rwid ne {} && [info exists varcid($view,$rwid)]} {
15471577
# use $rwid in place of $id
@@ -1845,7 +1875,7 @@ proc readrefs {} {
18451875
set selectheadid {}
18461876
if {$selecthead ne {}} {
18471877
catch {
1848-
set selectheadid [exec git rev-parse --verify $selecthead]
1878+
set selectheadid [safe_exec [list git rev-parse --verify $selecthead]]
18491879
}
18501880
}
18511881
}
@@ -3603,7 +3633,7 @@ proc gitknewtmpdir {} {
36033633
set tmpdir $gitdir
36043634
}
36053635
set gitktmpformat [file join $tmpdir ".gitk-tmp.XXXXXX"]
3606-
if {[catch {set gitktmpdir [exec mktemp -d $gitktmpformat]}]} {
3636+
if {[catch {set gitktmpdir [safe_exec [list mktemp -d $gitktmpformat]]}]} {
36073637
set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]]
36083638
}
36093639
if {[catch {file mkdir $gitktmpdir} err]} {
@@ -8774,7 +8804,7 @@ proc gotocommit {} {
87748804
set id [lindex $matches 0]
87758805
}
87768806
} else {
8777-
if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
8807+
if {[catch {set id [safe_exec [list git rev-parse --verify $sha1string]]}]} {
87788808
error_popup [mc "Revision %s is not known" $sha1string]
87798809
return
87808810
}
@@ -9394,9 +9424,9 @@ proc domktag {} {
93949424
}
93959425
if {[catch {
93969426
if {$msg != {}} {
9397-
exec git tag -a -m $msg $tag $id
9427+
safe_exec [list git tag -a -m $msg $tag $id]
93989428
} else {
9399-
exec git tag $tag $id
9429+
safe_exec [list git tag $tag $id]
94009430
}
94019431
} err]} {
94029432
error_popup "[mc "Error creating tag:"] $err" $mktagtop
@@ -9723,7 +9753,7 @@ proc cherrypick {} {
97239753
update
97249754
# Unfortunately git-cherry-pick writes stuff to stderr even when
97259755
# no error occurs, and exec takes that as an indication of error...
9726-
if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
9756+
if {[catch {safe_exec [list sh -c "git cherry-pick -r $rowmenuid 2>&1"]} err]} {
97279757
notbusy cherrypick
97289758
if {[regexp -line \
97299759
{Entry '(.*)' (would be overwritten by merge|not uptodate)} \
@@ -9785,7 +9815,7 @@ proc revert {} {
97859815
nowbusy revert [mc "Reverting"]
97869816
update
97879817

9788-
if [catch {exec git revert --no-edit $rowmenuid} err] {
9818+
if [catch {safe_exec [list git revert --no-edit $rowmenuid]} err] {
97899819
notbusy revert
97909820
if [regexp {files would be overwritten by merge:(\n(( |\t)+[^\n]+\n)+)}\
97919821
$err match files] {
@@ -10018,7 +10048,7 @@ proc rmbranch {} {
1001810048
}
1001910049
nowbusy rmbranch
1002010050
update
10021-
if {[catch {exec git branch -D $head} err]} {
10051+
if {[catch {safe_exec [list git branch -D $head]} err]} {
1002210052
notbusy rmbranch
1002310053
error_popup $err
1002410054
return
@@ -11336,7 +11366,7 @@ proc add_tag_ctext {tag} {
1133611366

1133711367
if {![info exists cached_tagcontent($tag)]} {
1133811368
catch {
11339-
set cached_tagcontent($tag) [exec git cat-file -p $tag]
11369+
set cached_tagcontent($tag) [safe_exec [list git cat-file -p $tag]]
1134011370
}
1134111371
}
1134211372
$ctext insert end "[mc "Tag"]: $tag\n" bold
@@ -12222,7 +12252,7 @@ proc gitattr {path attr default} {
1222212252
set r $path_attr_cache($attr,$path)
1222312253
} else {
1222412254
set r "unspecified"
12225-
if {![catch {set line [exec git check-attr $attr -- $path]}]} {
12255+
if {![catch {set line [safe_exec [list git check-attr $attr -- $path]]}]} {
1222612256
regexp "(.*): $attr: (.*)" $line m f r
1222712257
}
1222812258
set path_attr_cache($attr,$path) $r
@@ -12302,11 +12332,11 @@ if {[catch {package require Tk 8.4} err]} {
1230212332

1230312333
# on OSX bring the current Wish process window to front
1230412334
if {[tk windowingsystem] eq "aqua"} {
12305-
exec osascript -e [format {
12335+
safe_exec [list osascript -e [format {
1230612336
tell application "System Events"
1230712337
set frontmost of processes whose unix id is %d to true
1230812338
end tell
12309-
} [pid] ]
12339+
} [pid] ]]
1231012340
}
1231112341

1231212342
# Unset GIT_TRACE var if set

0 commit comments

Comments
 (0)