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 #-----------------------------------------------------------------------------