Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ShellCheck/ASTLib.hs
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ getCommandNameAndToken direct t = fromMaybe (Nothing, t) $ do
"busybox" -> firstArg
"builtin" -> firstArg
"command" -> firstArg
"run" -> firstArg -- Used by bats
"run" -> listToMaybe $ dropWhile isFlag args -- Used by bats
"exec" -> do
opts <- getBsdOpts "cla:" args
(_, (t, _)) <- find (null . fst) opts
Expand Down
32 changes: 31 additions & 1 deletion src/ShellCheck/Analytics.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,16 @@ prop_checkSingleQuotedVariables22 = verifyNot checkSingleQuotedVariables "jq '$_
prop_checkSingleQuotedVariables23 = verifyNot checkSingleQuotedVariables "command jq '$__loc__'"
prop_checkSingleQuotedVariables24 = verifyNot checkSingleQuotedVariables "exec jq '$__loc__'"
prop_checkSingleQuotedVariables25 = verifyNot checkSingleQuotedVariables "exec -c -a foo jq '$__loc__'"
prop_checkSingleQuotedVariables26 = verifyNot checkSingleQuotedVariables "gojq '$__loc__'"
prop_checkSingleQuotedVariables27 = verifyNot checkSingleQuotedVariables "dash -c 'echo $1'"
prop_checkSingleQuotedVariables28 = verifyNot checkSingleQuotedVariables "script -qec 'var=txt; echo \"$var\"' /dev/null"
prop_checkSingleQuotedVariables29 = verifyNot checkSingleQuotedVariables "printf 'eval \"$(pyenv init - %s)\"' \"$shell\""
prop_checkSingleQuotedVariables30 = verifyNot checkSingleQuotedVariables "PS0='`history -a`'$PS0"
prop_checkSingleQuotedVariables31 = verifyNot checkSingleQuotedVariables "echo '\\.$'\"$tld\"'\\.$'"
prop_checkSingleQuotedVariables32 = verifyNot checkSingleQuotedVariables "echo '$foo'\"$bar\""
prop_checkSingleQuotedVariables33 = verify checkSingleQuotedVariables "echo '$foo'"
prop_checkSingleQuotedVariables34 = verifyNot checkSingleQuotedVariables "find . -exec dash -c 'echo \"$1\"' shell {} \\;"
prop_checkSingleQuotedVariables35 = verifyNot checkSingleQuotedVariables "run --separate-stderr bash -c 'echo \"$1\"'"


checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
Expand All @@ -1110,6 +1120,7 @@ checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
,"bash"
,"ksh"
,"zsh"
,"dash"
,"ssh"
,"eval"
,"xprop"
Expand All @@ -1122,17 +1133,21 @@ checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
,"oc"
,"dpkg-query"
,"jq" -- could also check that user provides --arg
,"gojq" -- golang implementation of jq
,"rename"
,"rg"
,"unset"
,"printf"
,"script"
,"git filter-branch"
,"mumps -run %XCMD"
,"mumps -run LOOP%XCMD"
]
|| "awk" `isSuffixOf` commandName
|| "perl" `isPrefixOf` commandName
|| isConcatenatedWithExpansion

commonlyQuoted = ["PS1", "PS2", "PS3", "PS4", "PROMPT_COMMAND"]
commonlyQuoted = ["PS0", "PS1", "PS2", "PS3", "PS4", "PROMPT_COMMAND"] -- PS0 is bash 4.4+
isOkAssignment t =
case t of
T_Assignment _ _ name _ _ -> name `elem` commonlyQuoted
Expand All @@ -1142,6 +1157,21 @@ checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
re = mkRegex "\\$[{(0-9a-zA-Z_]|`[^`]+`"
sedContra = mkRegex "\\$[{dpsaic]($|[^a-zA-Z])"

-- When a single-quoted string is part of a word that also contains
-- double-quoted segments with expansions, the user clearly understands
-- quoting and is intentionally keeping parts in single quotes.
isConcatenatedWithExpansion =
case NE.tail $ getPath parents t of
(T_NormalWord _ parts):_ -> any hasExpansion parts
_ -> False
hasExpansion (T_DoubleQuoted _ parts) = any isExpansionToken parts
hasExpansion _ = False
isExpansionToken T_DollarBraced {} = True
isExpansionToken T_DollarExpansion {} = True
isExpansionToken T_DollarArithmetic {} = True
isExpansionToken T_Backticked {} = True
isExpansionToken _ = False

getFindCommand (T_SimpleCommand _ _ words) =
let list = map getLiteralString words
cmd = dropWhile (\x -> x `notElem` map Just ["-exec", "-execdir", "-ok", "-okdir"]) list
Expand Down