From cf8066c07c063aca986d441583a4ee291dab74ae Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Tue, 3 Aug 2021 12:54:03 -0700 Subject: [PATCH] SC2295 Warn about unquoted variables in PE patterns (fixes #2290) --- CHANGELOG.md | 1 + src/ShellCheck/Analytics.hs | 33 ++++++++++++++++++++++++++++++--- src/ShellCheck/AnalyzerLib.hs | 2 ++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe8d321..2c99322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - 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 +- SC2295: Warn about "${x#$y}" treating $y as a pattern when not quoted ### Fixed - SC2102 about repetitions in ranges no longer triggers on [[ -v arr[xx] ]] diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index d036c40..a415ca2 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -197,6 +197,7 @@ nodeChecks = [ ,checkSecondArgIsComparison ,checkComparisonWithLeadingX ,checkCommandWithTrailingSymbol + ,checkUnquotedParameterExpansionPattern ] optionalChecks = map fst optionalTreeChecks @@ -388,7 +389,7 @@ replaceToken id params r = repInsertionPoint = InsertBefore } -surroundWidth id params s = fixWith [replaceStart id params 0 s, replaceEnd id params 0 s] +surroundWith id params s = fixWith [replaceStart id params 0 s, replaceEnd id params 0 s] fixWith fixes = newFix { fixReplacements = fixes } prop_checkEchoWc3 = verify checkEchoWc "n=$(echo $foo | wc -c)" @@ -1977,7 +1978,7 @@ quotesMayConflictWithSC2281 params t = (getId t) == (getId me) && (parentId == getId cmd) _ -> False -addDoubleQuotesAround params token = (surroundWidth (getId token) params "\"") +addDoubleQuotesAround params token = (surroundWith (getId token) params "\"") checkSpacefulness' :: (SpaceStatus -> Token -> String -> Writer [TokenComment] ()) -> Parameters -> Token -> [TokenComment] @@ -3274,7 +3275,7 @@ checkArrayAssignmentIndices params root = T_Literal id str <- parts let (before, after) = break ('=' ==) str guard $ all isDigit before && not (null after) - return $ warnWithFix id 2191 "The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it." (surroundWidth id params "\"") + return $ warnWithFix id 2191 "The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it." (surroundWith id params "\"") in if null literalEquals && isAssociative then warn (getId t) 2190 "Elements in associative arrays need index, e.g. array=( [index]=value ) ." @@ -4405,5 +4406,31 @@ checkRequireDoubleBracket params = _ -> False +prop_checkUnquotedParameterExpansionPattern1 = verify checkUnquotedParameterExpansionPattern "echo \"${var#$x}\"" +prop_checkUnquotedParameterExpansionPattern2 = verify checkUnquotedParameterExpansionPattern "echo \"${var%%$(x)}\"" +prop_checkUnquotedParameterExpansionPattern3 = verifyNot checkUnquotedParameterExpansionPattern "echo \"${var[#$x]}\"" +prop_checkUnquotedParameterExpansionPattern4 = verifyNot checkUnquotedParameterExpansionPattern "echo \"${var%\"$x\"}\"" + +checkUnquotedParameterExpansionPattern params x = + case x of + T_DollarBraced _ True word@(T_NormalWord _ (T_Literal _ s : rest@(_:_))) -> do + let modifier = getBracedModifier $ concat $ oversimplify word + when ("%" `isPrefixOf` modifier || "#" `isPrefixOf` modifier) $ + mapM_ check rest + _ -> return () + where + check t = + case t of + T_DollarBraced {} -> inform t + T_DollarExpansion {} -> inform t + T_Backticked {} -> inform t + _ -> return () + + inform t = + infoWithFix (getId t) 2295 + "Expansions inside ${..} need to be quoted separately, otherwise they match as patterns." $ + surroundWith (getId t) params "\"" + + return [] runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |]) diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index beb4e5c..439b48f 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -167,6 +167,8 @@ errWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m ( errWithFix = addCommentWithFix ErrorC warnWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m () warnWithFix = addCommentWithFix WarningC +infoWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m () +infoWithFix = addCommentWithFix InfoC styleWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m () styleWithFix = addCommentWithFix StyleC