Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport Windows security fix #85

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 133 additions & 74 deletions git-gui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,139 @@ if {[catch {package require Tcl 8.5} err]

catch {rename send {}} ; # What an evil concept...

######################################################################
##
## Enabling platform-specific code paths

proc is_MacOSX {} {
if {[tk windowingsystem] eq {aqua}} {
return 1
}
return 0
}

proc is_Windows {} {
if {$::tcl_platform(platform) eq {windows}} {
return 1
}
return 0
}

set _iscygwin {}
proc is_Cygwin {} {
global _iscygwin
if {$_iscygwin eq {}} {
if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
set _iscygwin 1
} else {
set _iscygwin 0
}
}
return $_iscygwin
}

######################################################################
##
## PATH lookup

set _search_path {}
proc _which {what args} {
global env _search_exe _search_path

if {$_search_path eq {}} {
if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
set _search_path [split [exec cygpath \
--windows \
--path \
--absolute \
$env(PATH)] {;}]
set _search_exe .exe
} elseif {[is_Windows]} {
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 _search_exe .exe
} else {
set _search_path [split $env(PATH) :]
set _search_exe {}
}
}

if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
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]
}
}
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 {[file pathtype $cmd] ne "absolute"} {
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
}

# Override `open` to avoid unsafe PATH lookup

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]"
}
uplevel 1 real_open $args
}

######################################################################
##
## locate our library
Expand Down Expand Up @@ -163,8 +296,6 @@ set _isbare {}
set _gitexec {}
set _githtmldir {}
set _reponame {}
set _iscygwin {}
set _search_path {}
set _shellpath {@@SHELL_PATH@@}

set _trace [lsearch -exact $argv --trace]
Expand Down Expand Up @@ -252,40 +383,6 @@ proc reponame {} {
return $::_reponame
}

proc is_MacOSX {} {
if {[tk windowingsystem] eq {aqua}} {
return 1
}
return 0
}

proc is_Windows {} {
if {$::tcl_platform(platform) eq {windows}} {
return 1
}
return 0
}

proc is_Cygwin {} {
global _iscygwin
if {$_iscygwin eq {}} {
if {$::tcl_platform(platform) eq {windows}} {
if {[catch {set p [exec cygpath --windir]} err]} {
set _iscygwin 0
} else {
set _iscygwin 1
# Handle MSys2 which is only cygwin when MSYSTEM is MSYS.
if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} {
set _iscygwin 0
}
}
} else {
set _iscygwin 0
}
}
return $_iscygwin
}

proc is_enabled {option} {
global enabled_options
if {[catch {set on $enabled_options($option)}]} {return 0}
Expand Down Expand Up @@ -448,44 +545,6 @@ proc _git_cmd {name} {
return $v
}

proc _which {what args} {
global env _search_exe _search_path

if {$_search_path eq {}} {
if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
set _search_path [split [exec cygpath \
--windows \
--path \
--absolute \
$env(PATH)] {;}]
set _search_exe .exe
} elseif {[is_Windows]} {
set gitguidir [file dirname [info script]]
regsub -all ";" $gitguidir "\\;" gitguidir
set env(PATH) "$gitguidir;$env(PATH)"
set _search_path [split $env(PATH) {;}]
set _search_exe .exe
} else {
set _search_path [split $env(PATH) :]
set _search_exe {}
}
}

if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
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]
}
}
return {}
}

# Test a file for a hashbang to identify executable scripts on Windows.
proc is_shellscript {filename} {
if {![file exists $filename]} {return 0}
Expand Down