From fb7d227c30d705ddafc982fac390bcded0867bee Mon Sep 17 00:00:00 2001 From: "Tom \"Ravi\" Hale" Date: Fri, 24 Aug 2018 19:30:16 +0700 Subject: [PATCH] Add _safe_eval: quote $@ elements before eval --- lib/scm_breeze.sh | 14 ++++++++++++++ test/lib/scm_breeze_test.sh | 26 ++++++++++++++++++++++++++ test/support/test_helper.sh | 16 ++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100755 test/lib/scm_breeze_test.sh diff --git a/lib/scm_breeze.sh b/lib/scm_breeze.sh index 2c692bb..b177db9 100644 --- a/lib/scm_breeze.sh +++ b/lib/scm_breeze.sh @@ -18,6 +18,20 @@ _alias() { 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 +} + find_binary(){ if [ $shell = "zsh" ]; then builtin type -p "$1" | sed "s/$1 is //" | head -1 diff --git a/test/lib/scm_breeze_test.sh b/test/lib/scm_breeze_test.sh new file mode 100755 index 0000000..f74beef --- /dev/null +++ b/test/lib/scm_breeze_test.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +export scmbDir="$( cd -P "$( dirname "$0" )" && pwd )/../.." + +# Zsh compatibility +if [ -n "${ZSH_VERSION:-}" ]; then shell="zsh"; SHUNIT_PARENT=$0; setopt shwordsplit; fi + +# Load test helpers +source "$scmbDir/test/support/test_helper.sh" + +# Load functions to test +source "$scmbDir/lib/scm_breeze.sh" + +#----------------------------------------------------------------------------- +# Unit tests +#----------------------------------------------------------------------------- + +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)" +} + + +# load and run shUnit2 +source "$scmbDir/test/support/shunit2" diff --git a/test/support/test_helper.sh b/test/support/test_helper.sh index a021b03..4649373 100644 --- a/test/support/test_helper.sh +++ b/test/support/test_helper.sh @@ -26,11 +26,27 @@ tab_completions(){ echo "${COMPREPLY[@]}"; } silentGitCommands() { git() { /usr/bin/env git "$@" > /dev/null 2>&1; } } + # Cancel silent git commands 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 #-----------------------------------------------------------------------------