Merge branch 'rh/prompt-pcmode-avoid-eval-on-refname'
authorJunio C Hamano <gitster@pobox.com>
Fri, 2 May 2014 20:10:53 +0000 (13:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 2 May 2014 20:10:53 +0000 (13:10 -0700)
* rh/prompt-pcmode-avoid-eval-on-refname:
git-prompt.sh: don't put unsanitized branch names in $PS1

contrib/completion/git-prompt.sh
t/t9903-bash-prompt.sh
index 54489080f8ae25a4483cd4bae076942f47f53031..96b8087385ee77dfb7e39fbc91d82af1c2e73714 100644 (file)
@@ -207,7 +207,18 @@ __git_ps1_show_upstream ()
                        p=" u+${count#* }-${count%      *}" ;;
                esac
                if [[ -n "$count" && -n "$name" ]]; then
-                       p="$p $(git rev-parse --abbrev-ref "$upstream" 2>/dev/null)"
+                       __git_ps1_upstream_name=$(git rev-parse \
+                               --abbrev-ref "$upstream" 2>/dev/null)
+                       if [ $pcmode = yes ]; then
+                               # see the comments around the
+                               # __git_ps1_branch_name variable below
+                               p="$p \${__git_ps1_upstream_name}"
+                       else
+                               p="$p ${__git_ps1_upstream_name}"
+                               # not needed anymore; keep user's
+                               # environment clean
+                               unset __git_ps1_upstream_name
+                       fi
                fi
        fi
 
@@ -445,8 +456,27 @@ __git_ps1 ()
                __git_ps1_colorize_gitstring
        fi
 
+       b=${b##refs/heads/}
+       if [ $pcmode = yes ]; then
+               # In pcmode (and only pcmode) the contents of
+               # $gitstring are subject to expansion by the shell.
+               # Avoid putting the raw ref name in the prompt to
+               # protect the user from arbitrary code execution via
+               # specially crafted ref names (e.g., a ref named
+               # '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute
+               # 'sudo rm -rf /' when the prompt is drawn).  Instead,
+               # put the ref name in a new global variable (in the
+               # __git_ps1_* namespace to avoid colliding with the
+               # user's environment) and reference that variable from
+               # PS1.
+               __git_ps1_branch_name=$b
+               # note that the $ is escaped -- the variable will be
+               # expanded later (when it's time to draw the prompt)
+               b="\${__git_ps1_branch_name}"
+       fi
+
        local f="$w$i$s$u"
-       local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
+       local gitstring="$c$b${f:+$z$f}$r$p"
 
        if [ $pcmode = yes ]; then
                if [ "${__git_printf_supports_v-}" != yes ]; then
index 59f875e830df432ed200eeca96e70100a1fb2d2b..6efd0d9c78fda8b28e79fad7153b610a0b40173f 100755 (executable)
@@ -452,53 +452,53 @@ test_expect_success 'prompt - format string starting with dash' '
 '
 
 test_expect_success 'prompt - pc mode' '
-       printf "BEFORE: (master):AFTER" >expected &&
+       printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
        printf "" >expected_output &&
        (
                __git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
                test_cmp expected_output "$actual" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - branch name' '
-       printf "BEFORE: (${c_green}master${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
        (
                GIT_PS1_SHOWCOLORHINTS=y &&
                __git_ps1 "BEFORE:" ":AFTER" >"$actual"
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - detached head' '
-       printf "BEFORE: (${c_red}(%s...)${c_clear}):AFTER" $(git log -1 --format="%h" b1^) >expected &&
+       printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
        (
                GIT_PS1_SHOWCOLORHINTS=y &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
-       printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
        echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
                GIT_PS1_SHOWCOLORHINTS=y &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
-       printf "BEFORE: (${c_green}master${c_clear} ${c_green}+${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
        echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        git add -u &&
@@ -506,13 +506,13 @@ test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirt
                GIT_PS1_SHOWDIRTYSTATE=y &&
                GIT_PS1_SHOWCOLORHINTS=y &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
-       printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
        echo "dirty index" >file &&
        test_when_finished "git reset --hard" &&
        git add -u &&
@@ -521,25 +521,25 @@ test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirt
                GIT_PS1_SHOWCOLORHINTS=y &&
                GIT_PS1_SHOWDIRTYSTATE=y &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
-       printf "BEFORE: (${c_green}master${c_clear} ${c_green}#${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
                GIT_PS1_SHOWCOLORHINTS=y &&
                cd otherrepo &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - inside .git directory' '
-       printf "BEFORE: (${c_green}GIT_DIR!${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
        echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        (
@@ -547,13 +547,13 @@ test_expect_success 'prompt - bash color pc mode - inside .git directory' '
                GIT_PS1_SHOWCOLORHINTS=y &&
                cd .git &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - stash status indicator' '
-       printf "BEFORE: (${c_green}master${c_clear} ${c_lblue}\$${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
        echo 2 >file &&
        git stash &&
        test_when_finished "git stash drop" &&
@@ -561,29 +561,29 @@ test_expect_success 'prompt - bash color pc mode - stash status indicator' '
                GIT_PS1_SHOWSTASHSTATE=y &&
                GIT_PS1_SHOWCOLORHINTS=y &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
-       printf "BEFORE: (${c_green}master${c_clear} ${c_red}%%${c_clear}):AFTER" >expected &&
+       printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
                GIT_PS1_SHOWCOLORHINTS=y &&
                __git_ps1 "BEFORE:" ":AFTER" &&
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - zsh color pc mode' '
-       printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
+       printf "BEFORE: (%%F{green}\${__git_ps1_branch_name}%%f):AFTER\\nmaster" >expected &&
        (
                ZSH_VERSION=5.0.0 &&
                GIT_PS1_SHOWCOLORHINTS=y &&
                __git_ps1 "BEFORE:" ":AFTER" >"$actual"
-               printf "%s" "$PS1" >"$actual"
+               printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '