c90141ed77ff89cc4d9e957aaee713e576ad2c39

Author
Marc Cornellà <marc@mcornella.com>
Committer
Marc Cornellà <marc@mcornella.com>
Date

Message

fix: escape % characters in git prompts

This patch adds missing % character escaping for custom git prompts
used in a few themes. It also includes escaping for git-prompt.sh.

In combination with CVE-2021-45444, this could allow code execution
when displaying branch information in cloned malicious git repositories.
However, zsh 5.8.1 and newer are largely the default zsh versions, and
on those supported distributions with older zsh versions, the CVE has been
found to be also patched.

For this reason, this doesn't qualify as a security patch, but a
bug fix for proper printing of git branches.

Diff

  1diff --git a/plugins/gitfast/git-prompt.sh b/plugins/gitfast/git-prompt.sh
  2index 76ee4ab1e54965f6bf02afb5b562eb327d66ebac..ae508518255a26de2fc1fb2203d84ac3a3ca7e09 100644
  3--- a/plugins/gitfast/git-prompt.sh
  4+++ b/plugins/gitfast/git-prompt.sh
  5@@ -235,7 +235,7 @@ __git_ps1_show_upstream ()
  6 			if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
  7 				upstream="$upstream \${__git_ps1_upstream_name}"
  8 			else
  9-				upstream="$upstream ${__git_ps1_upstream_name}"
 10+				upstream="$upstream ${__git_ps1_upstream_name//\%/%%}"
 11 				# not needed anymore; keep user's
 12 				# environment clean
 13 				unset __git_ps1_upstream_name
 14@@ -570,6 +570,9 @@ __git_ps1 ()
 15 	if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
 16 		__git_ps1_branch_name=$b
 17 		b="\${__git_ps1_branch_name}"
 18+	else
 19+		# escape % in branch name to avoid prompt expansion issues
 20+		b="${b//\%/%%}"
 21 	fi
 22 
 23 	if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
 24diff --git a/themes/eastwood.zsh-theme b/themes/eastwood.zsh-theme
 25index 31e24fa7ffb2e764848c2443628c9fcf18e0c2c6..0dd2d42d3e585f4285e29630ae8aa38bc52d9fca 100644
 26--- a/themes/eastwood.zsh-theme
 27+++ b/themes/eastwood.zsh-theme
 28@@ -16,7 +16,8 @@ ZSH_THEME_GIT_PROMPT_CLEAN=""
 29 git_custom_status() {
 30   local cb=$(git_current_branch)
 31   if [ -n "$cb" ]; then
 32-    echo "$(parse_git_dirty)$ZSH_THEME_GIT_PROMPT_PREFIX$(git_current_branch)$ZSH_THEME_GIT_PROMPT_SUFFIX"
 33+    cb="${cb//\%/%%}"
 34+    echo "$(parse_git_dirty)$ZSH_THEME_GIT_PROMPT_PREFIX${cb}$ZSH_THEME_GIT_PROMPT_SUFFIX"
 35   fi
 36 }
 37 
 38diff --git a/themes/gallois.zsh-theme b/themes/gallois.zsh-theme
 39index 3fc349072bd4670d81132696584557dec1e16e4d..eb04e66edfd483df06679ec60fb791f2986f5476 100644
 40--- a/themes/gallois.zsh-theme
 41+++ b/themes/gallois.zsh-theme
 42@@ -10,6 +10,7 @@ ZSH_THEME_GIT_PROMPT_CLEAN=""
 43 git_custom_status() {
 44   local branch=$(git_current_branch)
 45   [[ -n "$branch" ]] || return 0
 46+  branch="${branch//\%/%%}"
 47   print "%{${fg_bold[yellow]}%}$(work_in_progress)%{$reset_color%}\
 48 ${ZSH_THEME_GIT_PROMPT_PREFIX}$(parse_git_dirty)${branch}\
 49 ${ZSH_THEME_GIT_PROMPT_SUFFIX}"
 50diff --git a/themes/josh.zsh-theme b/themes/josh.zsh-theme
 51index df59280d7fca2ad7f6184db6f16a0f5bae335a7f..e8ae18dda289defa12e48ea623e686fb18417452 100644
 52--- a/themes/josh.zsh-theme
 53+++ b/themes/josh.zsh-theme
 54@@ -31,7 +31,7 @@ function josh_prompt {
 55     prompt=" $prompt"
 56   done
 57 
 58-  prompt="%{%F{green}%}$PWD$prompt%{%F{red}%}$(ruby_prompt_info)%{$reset_color%} $(git_current_branch)"
 59+  prompt="%{%F{green}%}$PWD$prompt%{%F{red}%}$(ruby_prompt_info)%{$reset_color%} ${branch//\%/%%}"
 60 
 61   echo $prompt
 62 }
 63diff --git a/themes/juanghurtado.zsh-theme b/themes/juanghurtado.zsh-theme
 64index 95a400e61aec8fb7379af9de746f5b32d7a4e1a9..625f46a3adefd92eec38134f9a4a7ccdbbda54de 100644
 65--- a/themes/juanghurtado.zsh-theme
 66+++ b/themes/juanghurtado.zsh-theme
 67@@ -41,4 +41,4 @@ USER_COLOR=$GREEN_BOLD
 68 PROMPT='
 69 %{$USER_COLOR%}%n@%m%{$WHITE%}:%{$YELLOW%}%~%u$(parse_git_dirty)$(git_prompt_ahead)%{$RESET_COLOR%}
 70 %{$BLUE%}>%{$RESET_COLOR%} '
 71-RPROMPT='%{$GREEN_BOLD%}$(git_current_branch)$(git_prompt_short_sha)$(git_prompt_status)%{$RESET_COLOR%}'
 72+RPROMPT='%{$GREEN_BOLD%}${$(git_current_branch)//\%/%%}$(git_prompt_short_sha)$(git_prompt_status)%{$RESET_COLOR%}'
 73diff --git a/themes/lukerandall.zsh-theme b/themes/lukerandall.zsh-theme
 74index cdecd284fcbab86be74221b1cdae4b0fe403bb18..d5a452ebba688cd414ab400be6a96843a93c37de 100644
 75--- a/themes/lukerandall.zsh-theme
 76+++ b/themes/lukerandall.zsh-theme
 77@@ -7,7 +7,7 @@ function my_git_prompt_info() {
 78   ref=$(git symbolic-ref HEAD 2> /dev/null) || return
 79   GIT_STATUS=$(git_prompt_status)
 80   [[ -n $GIT_STATUS ]] && GIT_STATUS=" $GIT_STATUS"
 81-  echo "$ZSH_THEME_GIT_PROMPT_PREFIX${ref#refs/heads/}$GIT_STATUS$ZSH_THEME_GIT_PROMPT_SUFFIX"
 82+  echo "$ZSH_THEME_GIT_PROMPT_PREFIX${${ref#refs/heads/}//\%/%%}$GIT_STATUS$ZSH_THEME_GIT_PROMPT_SUFFIX"
 83 }
 84 
 85 PROMPT='%{$fg_bold[green]%}%n@%m%{$reset_color%} %{$fg_bold[blue]%}%2~%{$reset_color%} $(my_git_prompt_info)%{$reset_color%}%B»%b '
 86diff --git a/themes/mortalscumbag.zsh-theme b/themes/mortalscumbag.zsh-theme
 87index c9994c0f9550645fb508e07832f2eb32f19badd1..80c2d70dbe3c8238fc08ab4b0bfba0db63fdd3e8 100644
 88--- a/themes/mortalscumbag.zsh-theme
 89+++ b/themes/mortalscumbag.zsh-theme
 90@@ -42,7 +42,9 @@ function my_git_prompt() {
 91 }
 92 
 93 function my_current_branch() {
 94-  echo $(git_current_branch || echo "(no branch)")
 95+  local branch
 96+  branch=$(git_current_branch || echo "(no branch)")
 97+  echo "${branch//\%/%%}"
 98 }
 99 
100 function ssh_connection() {
101diff --git a/themes/oldgallois.zsh-theme b/themes/oldgallois.zsh-theme
102index bb97bfb1782cc47f6faaf97cfbb5c0485a907b33..7c77135c057e28279d82478d1c2df54573e51dde 100644
103--- a/themes/oldgallois.zsh-theme
104+++ b/themes/oldgallois.zsh-theme
105@@ -10,6 +10,7 @@ ZSH_THEME_GIT_PROMPT_CLEAN=""
106 git_custom_status() {
107   local branch=$(git_current_branch)
108   [[ -n "$branch" ]] || return 0
109+  branch="${branch//\%/%%}"
110   echo "$(parse_git_dirty)\
111 %{${fg_bold[yellow]}%}$(work_in_progress)%{$reset_color%}\
112 ${ZSH_THEME_GIT_PROMPT_PREFIX}${branch}${ZSH_THEME_GIT_PROMPT_SUFFIX}"
113diff --git a/themes/peepcode.zsh-theme b/themes/peepcode.zsh-theme
114index 044534614f03cd948b31a1ba9deb4b02a606a92f..4010ef1ff68bf0d5f299e8b2c33b0d152ede4785 100644
115--- a/themes/peepcode.zsh-theme
116+++ b/themes/peepcode.zsh-theme
117@@ -31,7 +31,7 @@ git_prompt() {
118   local cb=$(git_current_branch)
119   if [[ -n "$cb" ]]; then
120     local repo_path=$(git_repo_path)
121-    echo " %{$fg_bold[grey]%}$cb %{$fg[white]%}$(git_commit_id)%{$reset_color%}$(git_mode)$(git_dirty)"
122+    echo " %{$fg_bold[grey]%}${cb//\%/%%} %{$fg[white]%}$(git_commit_id)%{$reset_color%}$(git_mode)$(git_dirty)"
123   fi
124 }
125 
126diff --git a/themes/rkj-repos.zsh-theme b/themes/rkj-repos.zsh-theme
127index a9fe1a9af0a7fda6bcfb29d75bfe9e8ab37c9484..6ce45c9696991239b86f3307a1245ab1e154370c 100644
128--- a/themes/rkj-repos.zsh-theme
129+++ b/themes/rkj-repos.zsh-theme
130@@ -23,7 +23,8 @@ function mygit() {
131   if [[ "$(git config --get oh-my-zsh.hide-status)" != "1" ]]; then
132     ref=$(command git symbolic-ref HEAD 2> /dev/null) || \
133     ref=$(command git rev-parse --short HEAD 2> /dev/null) || return
134-    echo "$ZSH_THEME_GIT_PROMPT_PREFIX${ref#refs/heads/}$(git_prompt_short_sha)$(git_prompt_status)%{$fg_bold[blue]%}$ZSH_THEME_GIT_PROMPT_SUFFIX "
135+    ref=${${ref#refs/heads/}//\%/%%}
136+    echo "${ZSH_THEME_GIT_PROMPT_PREFIX}${ref}$(git_prompt_short_sha)$(git_prompt_status)%{$fg_bold[blue]%}${ZSH_THEME_GIT_PROMPT_SUFFIX} "
137   fi
138 }
139 
140diff --git a/themes/sunrise.zsh-theme b/themes/sunrise.zsh-theme
141index 11f6af127a84d1892b6d4d32534b1f419ee38be8..86ae722fdc78d670ab48fc2460e6e312eb202c6e 100644
142--- a/themes/sunrise.zsh-theme
143+++ b/themes/sunrise.zsh-theme
144@@ -62,7 +62,7 @@ custom_git_prompt_status() {
145 # get the name of the branch we are on (copied and modified from git.zsh)
146 function custom_git_prompt() {
147   ref=$(git symbolic-ref HEAD 2> /dev/null) || return
148-  echo "$ZSH_THEME_GIT_PROMPT_PREFIX${ref#refs/heads/}$(parse_git_dirty)$(git_prompt_ahead)$(custom_git_prompt_status)$ZSH_THEME_GIT_PROMPT_SUFFIX"
149+  echo "$ZSH_THEME_GIT_PROMPT_PREFIX${${ref#refs/heads/}//\%/%%}$(parse_git_dirty)$(git_prompt_ahead)$(custom_git_prompt_status)$ZSH_THEME_GIT_PROMPT_SUFFIX"
150 }
151 
152 # %B sets bold text