diff --git a/CHANGELOG.md b/CHANGELOG.md index f21857b..fe8d321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - SC2289: Warn when command name contains tabs or linefeeds - SC2291: Warn about repeated unquoted spaces between words in echo - SC2292: Suggest [[ over [ in Bash/Ksh scripts (optional) +- SC2293/SC2294: Warn when calling `eval` with arrays ### Fixed - SC2102 about repetitions in ranges no longer triggers on [[ -v arr[xx] ]] diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 2e19a6e..beb4e5c 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -872,6 +872,8 @@ getBracedReference s = fromMaybe s $ prop_getBracedModifier1 = getBracedModifier "foo:bar:baz" == ":bar:baz" prop_getBracedModifier2 = getBracedModifier "!var:-foo" == ":-foo" prop_getBracedModifier3 = getBracedModifier "foo[bar]" == "[bar]" +prop_getBracedModifier4 = getBracedModifier "foo[@]@Q" == "[@]@Q" +prop_getBracedModifier5 = getBracedModifier "@@Q" == "@Q" getBracedModifier s = headOrDefault "" $ do let var = getBracedReference s a <- dropModifier s diff --git a/src/ShellCheck/Checks/Commands.hs b/src/ShellCheck/Checks/Commands.hs index 1a08a7f..366438d 100644 --- a/src/ShellCheck/Checks/Commands.hs +++ b/src/ShellCheck/Checks/Commands.hs @@ -96,6 +96,7 @@ commandChecks = [ ,checkChmodDashr ,checkXargsDashi ,checkUnquotedEchoSpaces + ,checkEvalArray ] ++ map checkArgComparison declaringCommands ++ map checkMaskedReturns declaringCommands @@ -1258,5 +1259,25 @@ checkUnquotedEchoSpaces = CommandCheck (Basename "echo") check && not (any (\x -> b < x && x < c) redirs) +prop_checkEvalArray1 = verify checkEvalArray "eval $@" +prop_checkEvalArray2 = verify checkEvalArray "eval \"${args[@]}\"" +prop_checkEvalArray3 = verify checkEvalArray "eval \"${args[@]@Q}\"" +prop_checkEvalArray4 = verifyNot checkEvalArray "eval \"${args[*]@Q}\"" +prop_checkEvalArray5 = verifyNot checkEvalArray "eval \"$*\"" +checkEvalArray = CommandCheck (Exactly "eval") (mapM_ check . concatMap getWordParts . arguments) + where + check t = + when (isArrayExpansion t) $ + if isEscaped t + then style (getId t) 2293 "When eval'ing @Q-quoted words, use * rather than @ as the index." + else warn (getId t) 2294 "eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string)." + + isEscaped q = + case q of + -- Match ${arr[@]@Q} and ${@@Q} and such + T_DollarBraced _ _ l -> 'Q' `elem` getBracedModifier (concat $ oversimplify l) + _ -> False + + return [] runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])