From 7acd8514de695b7595372b57169ef02661da32cd Mon Sep 17 00:00:00 2001 From: Zane Duffield Date: Wed, 11 Feb 2026 09:37:40 +1100 Subject: [PATCH 1/2] Remove all uses of eval Beyond being risky, many of these uses of eval were actually vulnerable to shell injection, if the inputs are untrusted. Switching all cases to nameref variables improves both security and readability, while only raising the minimum bash version from 4 to 4.3. --- .shellcheckrc | 2 -- cmdarg.sh | 40 ++++++++++++++++++---------------------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/.shellcheckrc b/.shellcheckrc index 7bc8420..135fd0d 100644 --- a/.shellcheckrc +++ b/.shellcheckrc @@ -1,5 +1,3 @@ -# Many array expressions are constructed and eval-ed -disable=SC2016 # False positive on "CMDARG_ERROR_BEHAVIOR=return" disable=SC2209 # Checking exit code with $? is more of a preference when the script doesn't try to support the errexit shell option diff --git a/cmdarg.sh b/cmdarg.sh index 5ec40f6..32dbd4c 100644 --- a/cmdarg.sh +++ b/cmdarg.sh @@ -1,7 +1,8 @@ #!/bin/bash -if (( BASH_VERSINFO[0] < 4 )); then - echo "cmdarg is incompatible with bash versions < 4, please upgrade bash" >&2 +# Require Bash >= 4.3 for associative arrays and nameref support +if (( BASH_VERSINFO[0] < 4 )) || (( BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 3 )); then + echo "cmdarg is incompatible with bash versions < 4.3, please upgrade bash" >&2 exit 1 fi @@ -227,10 +228,8 @@ function cmdarg_set_opt cmdarg_validate "$key" "$arg" || ${CMDARG_ERROR_BEHAVIOR} 1 ;; "$CMDARG_TYPE_ARRAY") - local arrname="${key}" - local str='${#'"$arrname"'[@]}' - local prevlen=$(eval "echo $str") - eval "${arrname}[$((prevlen + 1))]=\"$arg\"" + local -n arr="$key" + arr+=("$arg") cmdarg_validate "$key" "$arg" || ${CMDARG_ERROR_BEHAVIOR} 1 ;; "$CMDARG_TYPE_HASH") @@ -240,7 +239,10 @@ function cmdarg_set_opt echo "Malformed hash argument: $arg" >&2 ${CMDARG_ERROR_BEHAVIOR} 1 fi - eval "${key}[\$k]=\$v" + + local -n map="$key" + # shellcheck disable=SC2034 + map["$k"]="$v" cmdarg_validate "$key" "$v" "$k" || ${CMDARG_ERROR_BEHAVIOR} 1 ;; *) @@ -265,14 +267,12 @@ function cmdarg_check_empty echo "${cmdarg_cfg[$longopt]}" ;; "$CMDARG_TYPE_ARRAY") - local arrname="${longopt}" - local lval='${!'"${arrname}"'[@]}' - eval "echo $lval" + local -n ref="$longopt" + echo "${!ref[@]}" ;; "$CMDARG_TYPE_HASH") - local arrname="${longopt}" - local lval='${!'"${arrname}"'[@]}' - eval "echo $lval" + local -n ref="$longopt" + echo "${!ref[@]}" ;; *) echo "${cmdarg_cfg[$longopt]}" @@ -389,8 +389,6 @@ function cmdarg_dump { local key local repr - local arrname - local keys local idx local ref local value @@ -399,14 +397,11 @@ function cmdarg_dump do repr="${key}:${CMDARG_TYPES[$key]}" if [[ ${CMDARG_TYPES[$key]} == "$CMDARG_TYPE_ARRAY" ]] || [[ ${CMDARG_TYPES[$key]} == "$CMDARG_TYPE_HASH" ]] ; then - arrname="${key}" echo "${repr} => " - keys='${!'"$arrname"'[@]}' - for idx in $(eval "echo $keys") + local -n ref="$key" + for idx in "${!ref[@]}" do - ref='${'"$arrname"'[$idx]}' - value=$(eval "echo $ref") - echo " ${idx} => $value" + echo " ${idx} => ${ref[$idx]}" done else echo "${repr} => ${cmdarg_cfg[$key]}" @@ -428,7 +423,8 @@ function cmdarg_purge arrays="$arrays CMDARG_FLAGS CMDARG_TYPES cmdarg_argv cmdarg_helpers" for arr in $arrays do - eval "$arr=()" + local -n ref="$arr" + ref=() done cmdarg_helpers['describe']=cmdarg_describe_default cmdarg_helpers['usage']=cmdarg_usage From f10bde0ae5dd5e63edbfe84be164b70660c01ad5 Mon Sep 17 00:00:00 2001 From: Zane Duffield Date: Wed, 11 Feb 2026 09:50:52 +1100 Subject: [PATCH 2/2] Update comment about validator function --- cmdarg.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmdarg.sh b/cmdarg.sh index 32dbd4c..1762476 100644 --- a/cmdarg.sh +++ b/cmdarg.sh @@ -26,11 +26,9 @@ function cmdarg # description : The text description for this option to be used in cmdarg_usage # # default value : The default value, if any, for the argument - # validator : This is passed through eval(), with $OPTARG equal to the current + # validator : This is a bash function, invoked with one argument equal to the current # value of the argument in question, and must return non-zero if - # the argument value is invalid. Can be straight bash, but it really - # should be the name of a function. This may be enforced in future versions - # of the library. + # the argument value is invalid. local shortopt=${1:0:1} local key="$2" if [[ "$shortopt" == "h" ]]; then