Replace built-in shell array quoting with printf %q

This commit is contained in:
Tom "Ravi" Hale
2018-09-12 17:57:46 +07:00
parent 21d155776a
commit 80ec1ad2ad
4 changed files with 81 additions and 36 deletions

View File

@@ -18,18 +18,45 @@ _alias() {
fi
}
# Quote the contents of "$@"
function token_quote {
# Older versions of {ba,z}sh don't support the built-in quoting, so fall back to printf %q
local quoted=()
for token; do
quoted+=( "$(printf '%q' "$token")" )
done
printf '%s\n' "${quoted[*]}"
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
# See https://github.com/scmbreeze/scm_breeze/issues/260
#
# if [[ $shell = bash ]]; then
# # ${parameter@operator} where parameter is ${@} and operator is 'Q'
# # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
# eval "${@@Q}"
# else # zsh
# # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
# eval "${(q-)@}"
# fi
}
# Quote "$@" before `eval` to prevent arbitrary code execution.
# Eg, the following will run `date`:
# evil() { eval "$@"; }; evil "echo" "foo;date"
function _safe_eval() {
if [[ $shell = bash ]]; then
# ${parameter@operator} where parameter is ${@} and operator is 'Q'
# https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
eval "${@@Q}"
else # zsh
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
eval "${(q-)@}"
fi
eval $(token_quote "$@")
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
# See https://github.com/scmbreeze/scm_breeze/issues/260
#
# if [[ $shell = bash ]]; then
# # ${parameter@operator} where parameter is ${@} and operator is 'Q'
# # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
# eval "${@@Q}"
# else # zsh
# # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
# eval "${(q-)@}"
# fi
}
find_binary(){

View File

@@ -51,38 +51,70 @@ setupTestRepo() {
test_scmb_expand_args() {
local e1="one" e2="two" e3="three" e4="four" e5="five" e6="six" e7='$dollar' e8='two words'
local error="Args not expanded correctly"
assertEquals "$error" "'one' 'three' 'six'" \
assertEquals "$error" 'one three six' \
"$(eval a=$(scmb_expand_args 1 3 6); token_quote "${a[@]}")"
assertEquals "$error" "'one' 'two' 'three' 'five'" \
assertEquals "$error" 'one two three five' \
"$(eval a=$(scmb_expand_args 1-3 5); token_quote "${a[@]}")"
assertEquals "$error" "'\$dollar' 'two' 'three' 'four' 'one'" \
assertEquals "$error" '\$dollar two three four one' \
"$(eval a=$(scmb_expand_args 7 2-4 1); token_quote "${a[@]}")"
# Test that any args with spaces remain quoted
assertEquals "$error" "'-m' 'Test Commit Message' 'one'" \
assertEquals "$error" '-m Test\ Commit\ Message one' \
"$(eval a=$(scmb_expand_args -m "Test Commit Message" 1); token_quote "${a[@]}")"
assertEquals "$error" "'-ma' 'Test Commit Message' 'Unquoted'"\
assertEquals "$error" '-ma Test\ Commit\ Message Unquoted'\
"$(eval a=$(scmb_expand_args -ma "Test Commit Message" "Unquoted"); token_quote "${a[@]}")"
assertEquals "$error" "'\$dollar' 'one' 'two words'" \
assertEquals "$error" '\$dollar one two\ words' \
"$(eval a=$(scmb_expand_args 7 1-1 8); token_quote "${a[@]}")"
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
# See token_quote() source and https://github.com/scmbreeze/scm_breeze/issues/260
# local e1="one" e2="two" e3="three" e4="four" e5="five" e6="six" e7='$dollar' e8='two words'
# local error="Args not expanded correctly"
# assertEquals "$error" "'one' 'three' 'six'" \
# "$(eval a=$(scmb_expand_args 1 3 6); token_quote "${a[@]}")"
# assertEquals "$error" "'one' 'two' 'three' 'five'" \
# "$(eval a=$(scmb_expand_args 1-3 5); token_quote "${a[@]}")"
# assertEquals "$error" "'\$dollar' 'two' 'three' 'four' 'one'" \
# "$(eval a=$(scmb_expand_args 7 2-4 1); token_quote "${a[@]}")"
#
# # Test that any args with spaces remain quoted
# assertEquals "$error" "'-m' 'Test Commit Message' 'one'" \
# "$(eval a=$(scmb_expand_args -m "Test Commit Message" 1); token_quote "${a[@]}")"
# assertEquals "$error" "'-ma' 'Test Commit Message' 'Unquoted'"\
# "$(eval a=$(scmb_expand_args -ma "Test Commit Message" "Unquoted"); token_quote "${a[@]}")"
# assertEquals "$error" "'\$dollar' 'one' 'two words'" \
# "$(eval a=$(scmb_expand_args 7 1-1 8); token_quote "${a[@]}")"
}
test_exec_scmb_expand_args() {
local e1="one" e2="a b c" e3='$dollar' e4="single'quote" e5='double"quote' e6='a(){:;};a&'
assertEquals "literals with spaces not preserved" "'foo' 'bar baz'" \
assertEquals "literals with spaces not preserved" 'foo bar\ baz' \
"$(eval a="$(scmb_expand_args foo 'bar baz')"; token_quote "${a[@]}")"
assertEquals "variables with spaces not preserved" "'one' 'a b c'" \
assertEquals "variables with spaces not preserved" 'one a\ b\ c' \
"$(eval a="$(scmb_expand_args 1-2)"; token_quote "${a[@]}")"
# Expecting text: '$dollar' "single'quote" 'double"quote'
# Generate quoted expected string with: token_quote "$(cat)" then copy/paste, ^D
assertEquals "special characters are preserved" \
"'\$dollar' 'single'\\''quote' 'double\"quote' 'a(){:;};a&'" \
'\$dollar single\'\''quote double\"quote a\(\)\{:\;\}\;a\&' \
"$(eval a="$(scmb_expand_args 3-6)"; token_quote "${a[@]}")"
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
# See token_quote() source and https://github.com/scmbreeze/scm_breeze/issues/260
# local e1="one" e2="a b c" e3='$dollar' e4="single'quote" e5='double"quote' e6='a(){:;};a&'
# assertEquals "literals with spaces not preserved" "'foo' 'bar baz'" \
# "$(eval a="$(scmb_expand_args foo 'bar baz')"; token_quote "${a[@]}")"
# assertEquals "variables with spaces not preserved" "'one' 'a b c'" \
# "$(eval a="$(scmb_expand_args 1-2)"; token_quote "${a[@]}")"
# # Expecting text: '$dollar' "single'quote" 'double"quote'
# # Generate quoted expected string with: token_quote "$(cat)" then copy/paste, ^D
# assertEquals "special characters are preserved" \
# "'\$dollar' 'single'\\''quote' 'double\"quote' 'a(){:;};a&'" \
# "$(eval a="$(scmb_expand_args 3-6)"; token_quote "${a[@]}")"
}
test_command_wrapping_escapes_special_characters() {
assertEquals 'should escape | the pipe' "$(exec_scmb_expand_args echo "should escape | the pipe")"
assertEquals 'should escape ; the semicolon' "$(exec_scmb_expand_args echo "should escape ; the semicolon")"
assertEquals 'should escape | the pipe' "$(exec_scmb_expand_args echo should escape '|' the pipe)"
assertEquals 'should escape ; the semicolon' "$(exec_scmb_expand_args echo should escape ';' the semicolon)"
}
test_git_status_shortcuts() {

View File

@@ -16,9 +16,9 @@ source "$scmbDir/lib/scm_breeze.sh"
#-----------------------------------------------------------------------------
test__safe_eval() {
assertEquals "runs eval with simple words" "'one' 'two' 'three'" "$(_safe_eval token_quote one two three)"
assertEquals "quotes spaces" "'a' 'b c' 'd'" "$(_safe_eval token_quote a b\ c d)"
assertEquals "quotes special chars" "'a b' '\$dollar' '\\slash' 'c d'" "$(_safe_eval token_quote a\ b '$dollar' '\slash' c\ d)"
assertEquals "runs eval with simple words" 'one two three' "$(_safe_eval token_quote one two three)"
assertEquals "quotes spaces" 'a b\ c d' "$(_safe_eval token_quote a b\ c d)"
assertEquals "quotes special chars" 'a\ b \$dollar \\slash c\ d' "$(_safe_eval token_quote a\ b '$dollar' '\slash' c\ d)"
}

View File

@@ -32,20 +32,6 @@ verboseGitCommands() {
unset -f git
}
# Quote the contents of "$@" in single quotes
# Avoid printf '%q' as 'a b' becomes a\ b in both {ba,z}sh
# See also quote_args and double_quote
function token_quote {
if [[ $shell = bash ]]; then
# Single quotes are always added
echo "${@@Q}"
else # zsh
# Single quotes only added when needed
#shellcheck disable=2154 # zsh
echo "${(qq)@}"
fi
}
# Asserts
#-----------------------------------------------------------------------------