CVE-2014-9938 (Shell, published 2017-03-20T00:59Z)
Description: value: contrib/completion/git-prompt.sh in Git before 1.9.3 does not sanitize branch names in the PS1 variable, allowing a malicious repository to cause code execution.
Severity: MEDIUM (CVSS v3 base score: 8.8)
Weakness: CWE-116 \u2014 Improper Encoding or Escaping of Output
The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.
Repository: https://github.com/git/git
Files changed:
contrib/completion/git-prompt.sh
t/t9903-bash-prompt.sh
Commit message: git-prompt.sh: don't put unsanitized branch names in $PS1

Both bash and zsh subject the value of PS1 to parameter expansion,
command substitution, and arithmetic expansion.  Rather than include
the raw, unescaped branch name in PS1 when running in two- or
three-argument mode, construct PS1 to reference a variable that holds
the branch name.  Because the shells do not recursively expand, this
avoids arbitrary code execution by specially-crafted branch names such
as '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)'.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>


Vulnerable code:
			p="$p $(git rev-parse --abbrev-ref "$upstream" 2>/dev/null)"
	local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
	printf "BEFORE: (master):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}master${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_red}(%s...)${c_clear}):AFTER" $(git log -1 --format="%h" b1^) >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}master${c_clear} ${c_green}+${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}master${c_clear} ${c_green}#${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}GIT_DIR!${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}master${c_clear} ${c_lblue}\$${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (${c_green}master${c_clear} ${c_red}%%${c_clear}):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"
	printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
		printf "%s" "$PS1" >"$actual"

Fixed code:
			__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
	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 gitstring="$c$b${f:+$z$f}$r$p"
	printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nGIT_DIR!" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
	printf "BEFORE: (%%F{green}\${__git_ps1_branch_name}%%f):AFTER\\nmaster" >expected &&
		printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"