From 972887bbe5eb6a00e5f0e73781d6d73bfdcafb93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc=20Cornell=C3=A0?= <hello@mcornella.com>
Date: Mon, 24 Jan 2022 09:43:28 +0100
Subject: [PATCH] security/89: Partially work around CVE-2021-45444 in VCS_Info
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This patch is a partial, VCS_Info-specific work-around for CVE-2021-45444,
which is mitigated in the shell itself in 5.8.1 and later versions. It is
offered for users who are concerned about an exploit but are unable to update
their binaries to receive the complete fix.

The patch works around the vulnerability by pre-escaping values substituted
into format strings in VCS_Info. Please note that this may break some user
configurations that rely on those values being un-escaped (which is why it was
not included directly in 5.8.1). It may be possible to limit this breakage by
adjusting exactly which ones are pre-escaped, but of course this may leave
them vulnerable again.

If applying the patch to the file system is inconvenient or not possible, the
following script can be used to idempotently patch the relevant function
running in memory (and thus must be re-run when the shell is restarted):


# Impacted versions go from v5.0.3 to v5.8 (v5.8.1 is the first patched version)
autoload -Uz is-at-least
if is-at-least 5.8.1 || ! is-at-least 5.0.3; then
  return
fi

# Quote necessary $hook_com[<field>] items just before they are used
# in the line "VCS_INFO_hook 'post-backend'" of the VCS_INFO_formats
# function, where <field> is:
#
#   base:       the full path of the repository's root directory.
#   base-name:  the name of the repository's root directory.
#   branch:     the name of the currently checked out branch.
#   revision:   an identifier of the currently checked out revision.
#   subdir:     the path of the current directory relative to the
#               repository's root directory.
#   misc:       a string that may contain anything the vcs_info backend wants.
#
# This patch %-quotes these fields previous to their use in vcs_info hooks and
# the zformat call and, eventually, when they get expanded in the prompt.
# It's important to quote these here, and not later after hooks have modified the
# fields, because then we could be quoting % characters from valid prompt sequences,
# like %F{color}, %B, etc.
#
#  32   │ hook_com[subdir]="$(VCS_INFO_reposub ${hook_com[base]})"
#  33   │ hook_com[subdir_orig]="${hook_com[subdir]}"
#  34   │
#  35 + │ for tmp in base base-name branch misc revision subdir; do
#  36 + │     hook_com[$tmp]="${hook_com[$tmp]//\%/%%}"
#  37 + │ done
#  38 + │
#  39   │ VCS_INFO_hook 'post-backend'
#
# This is especially important so that no command substitution is performed
# due to malicious input as a consequence of CVE-2021-45444, which affects
# zsh versions from 5.0.3 to 5.8.
#
autoload -Uz +X regexp-replace VCS_INFO_formats

# We use $tmp here because it's already a local variable in VCS_INFO_formats
typeset PATCH='for tmp (base base-name branch misc revision subdir) hook_com[$tmp]="${hook_com[$tmp]//\%/%%}"'
# Unique string to avoid reapplying the patch if this code gets called twice
typeset PATCH_ID=vcs_info-patch-9b9840f2-91e5-4471-af84-9e9a0dc68c1b
# Only patch the VCS_INFO_formats function if not already patched
if [[ "$functions[VCS_INFO_formats]" != *$PATCH_ID* ]]; then
  regexp-replace 'functions[VCS_INFO_formats]' \
    "VCS_INFO_hook 'post-backend'" \
    ': ${PATCH_ID}; ${PATCH}; ${MATCH}'
fi
unset PATCH PATCH_ID


---
 Functions/VCS_Info/VCS_INFO_formats | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Functions/VCS_Info/VCS_INFO_formats b/Functions/VCS_Info/VCS_INFO_formats
index e0e1dc738..4d88e28b6 100644
--- a/Functions/VCS_Info/VCS_INFO_formats
+++ b/Functions/VCS_Info/VCS_INFO_formats
@@ -32,6 +32,10 @@ hook_com[base-name_orig]="${hook_com[base_name]}"
 hook_com[subdir]="$(VCS_INFO_reposub ${hook_com[base]})"
 hook_com[subdir_orig]="${hook_com[subdir]}"
 
+for tmp in base base-name branch misc revision subdir; do
+    hook_com[$tmp]="${hook_com[$tmp]//\%/%%}"
+done
+
 VCS_INFO_hook 'post-backend'
 
 ## description (for backend authors):
-- 
2.34.1
