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