Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/koalaman/shellcheck into …
Browse files Browse the repository at this point in the history
…mrshu/pushd-popd-like-cd

Signed-off-by: mr.Shu <[email protected]>
  • Loading branch information
mrshu committed Jun 12, 2017
2 parents 9e35aa7 + bf9b841 commit 79872f9
Show file tree
Hide file tree
Showing 7 changed files with 400 additions and 165 deletions.
34 changes: 34 additions & 0 deletions .prepare_deploy
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/bash
# This script packages up Travis compiled binaries
set -ex
shopt -s nullglob
cd deploy

cp ../LICENSE LICENSE.txt
sed -e $'s/$/\r/' > README.txt << END
This is a precompiled ShellCheck binary.
http://www.shellcheck.net/
ShellCheck is a static analysis tool for shell scripts.
It's licensed under the GNU General Public License v3.0.
Information and source code is available on the website.
This binary was compiled on $(date -u).
====== Latest commits ======
$(git log -n 3)
END

for file in ./*.exe
do
zip "${file%.*}.zip" README.txt LICENSE.txt "$file"
done

for file in ./*
do
sha512sum "$file" > "$file.sha512sum"
done

28 changes: 26 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,35 @@ before_install:
export TAG=$([ "$TRAVIS_BRANCH" == "master" ] && echo "latest" || ([ -n "$TRAVIS_TAG" ] && echo "$TRAVIS_TAG") || echo "$TRAVIS_BRANCH")
script:
- mkdir deploy
# Windows .exe
- docker pull koalaman/winghc
- docker run -v "$PWD:/appdata" koalaman/winghc cuib
- cp "dist/build/ShellCheck/shellcheck.exe" "deploy/shellcheck-$TAG.exe"
# Linux Docker
- docker build -t builder -f Dockerfile_builder .
- docker run --rm -it -v $(pwd):/mnt builder
- docker build -t $DOCKER_REPO:$TAG .
- docker run --rm -it -v "$(pwd):/mnt" builder
- docker build -t "$DOCKER_REPO:$TAG" .

after_success:
- ./.prepare_deploy
- docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
- |-
([ "$TRAVIS_BRANCH" == "master" ] || [ -n "$TRAVIS_TAG" ]) && docker push "$DOCKER_REPO:$TAG"
after_failure:
- pwd
- df -h
- find . -name '*.log' -type f -exec grep "" /dev/null {} +
- find .

deploy:
provider: gcs
skip_cleanup: true
access_key_id: GOOG7MDN7WEH6IIGBDCA
secret_access_key:
secure: Bcx2cT0/E2ikj7sdamVq52xlLZF9dz9ojGPtoKfPyQhkkZa+McVI4xgUSuyyoSxyKj77sofx2y8m6PJYYumT4g5hREV1tfeUkl0J2DQFMbGDYEt7kxVkXCxojNvhHwTzLFv0ezstrxWWxQm81BfQQ4U9lggRXtndAP4czZnOeHPINPSiue1QNwRAEw05r5UoIUJXy/5xyUrjIxn381pAs+gJqP2COeN9kTKYH53nS/AAws29RprfZFnPlo7xxWmcjRcdS5KPdGXI/c6tQp5zl2iTh510VC1PN2w1Wvnn/oNWhiNdqPyVDsojIX5+sS3nejzJA+KFMxXSBlyXIY3wPpS/MdscU79X6Q5f9ivsFfsm7gNBmxHUPNn0HAvU4ROT/CCE9j6jSbs5PC7QBo3CK4++jxAwE/pd9HUc2rs3k0ofx3rgveJ7txpy5yPKfwIIBi98kVKlC4w7dLvNTOfjW1Imt2yH87XTfsE0UIG9st1WII6s4l/WgBx2GuwKdt6+3QUYiAlCFckkxWi+fAvpHZUEL43Qxub5fN+ZV7Zib1n7opchH4QKGBb6/y0WaDCmtCfu0lppoe/TH6saOTjDFj67NJSElK6ZDxGZ3uw4R+ret2gm6WRKT2Oeub8J33VzSa7VkmFpMPrAAfPa9N1Z4ewBLoTmvxSg2A0dDrCdJio=
bucket: shellcheck
local-dir: deploy
on:
repo: koalaman/shellcheck
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ There are a variety of ways to use ShellCheck!
#### On the web
Paste a shell script on http://www.shellcheck.net for instant feedback.

[ShellCheck.net](http://www.shellcheck.net) is always synchronized to the latest git commit, and is the simplest way to give ShellCheck a go. Tell your friends!
[ShellCheck.net](http://www.shellcheck.net) is always synchronized to the latest git commit, and is the easiest way to give ShellCheck a go. Tell your friends!


#### From your terminal
Expand Down Expand Up @@ -126,13 +126,9 @@ or use OneClickInstall - https://software.opensuse.org/package/ShellCheck
From Docker Hub:

docker pull koalaman/shellcheck
docker run -v "$PWD:/mnt" koalaman/shellcheck myscript

Using the Docker image can be done like so:

docker run -v $(pwd):/scripts koalaman/shellcheck /scripts/myscript.sh

Here the local directory ( $(pwd) ) is mounted into the containers directory "/scripts". The script "myscript.sh" is checked.

For Windows, you can download [precompiled Windows executables](https://storage.googleapis.com/shellcheck/shellcheck-latest.zip).
## Compiling from source

This section describes how to build ShellCheck from a source directory. ShellCheck is written in Haskell and requires 2GB of RAM to compile.
Expand All @@ -154,6 +150,10 @@ Verify that `cabal` is installed and update its dependency list with

$ cabal install

Or if you intend to run the tests:

$ cabal install --enable-tests

This will compile ShellCheck and install it to your `~/.cabal/bin` directory.

Add this directory to your `PATH` (for bash, add this to your `~/.bashrc`):
Expand Down Expand Up @@ -349,7 +349,9 @@ https://github.com/koalaman/shellcheck/issues

## Contributing

Please submit patches to code or documentation as GitHub pull requests!
Please submit patches to code or documentation as GitHub pull requests! Check
out the [DevGuide](https://github.com/koalaman/shellcheck/wiki/DevGuide) on the
ShellCheck Wiki.

Contributions must be licensed under the GNU GPLv3.
The contributor retains the copyright.
Expand Down
87 changes: 11 additions & 76 deletions ShellCheck/Analytics.hs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ nodeChecks = [
,checkWrongArithmeticAssignment
,checkConditionalAndOrs
,checkFunctionDeclarations
,checkCatastrophicRm
,checkStderrPipe
,checkOverridingPath
,checkArrayAsString
Expand Down Expand Up @@ -661,15 +660,12 @@ prop_checkUnquotedDollarAt6 = verifyNot checkUnquotedDollarAt "a=$@"
prop_checkUnquotedDollarAt7 = verify checkUnquotedDollarAt "for f in ${var[@]}; do true; done"
prop_checkUnquotedDollarAt8 = verifyNot checkUnquotedDollarAt "echo \"${args[@]:+${args[@]}}\""
prop_checkUnquotedDollarAt9 = verifyNot checkUnquotedDollarAt "echo ${args[@]:+\"${args[@]}\"}"
prop_checkUnquotedDollarAt10 = verifyNot checkUnquotedDollarAt "echo ${@+\"$@\"}"
checkUnquotedDollarAt p word@(T_NormalWord _ parts) | not $ isStrictlyQuoteFree (parentMap p) word =
forM_ (take 1 $ filter isArrayExpansion parts) $ \x ->
unless (isAlternative x) $
unless (isQuotedAlternativeReference x) $
err (getId x) 2068
"Double quote array expansions to avoid re-splitting elements."
where
-- Fixme: should detect whether the alternative is quoted
isAlternative b@(T_DollarBraced _ t) = ":+" `isInfixOf` bracedString b
isAlternative _ = False
checkUnquotedDollarAt _ _ = return ()

prop_checkConcatenatedDollarAt1 = verify checkConcatenatedDollarAt "echo \"foo$@\""
Expand Down Expand Up @@ -1375,10 +1371,11 @@ prop_checkInexplicablyUnquoted4 = verify checkInexplicablyUnquoted "echo \"VALUE
prop_checkInexplicablyUnquoted5 = verifyNot checkInexplicablyUnquoted "\"$dir\"/\"$file\""
prop_checkInexplicablyUnquoted6 = verifyNot checkInexplicablyUnquoted "\"$dir\"some_stuff\"$file\""
prop_checkInexplicablyUnquoted7 = verifyNot checkInexplicablyUnquoted "${dir/\"foo\"/\"bar\"}"
prop_checkInexplicablyUnquoted8 = verifyNot checkInexplicablyUnquoted " 'foo'\\\n 'bar'"
checkInexplicablyUnquoted _ (T_NormalWord id tokens) = mapM_ check (tails tokens)
where
check (T_SingleQuoted _ _:T_Literal id str:_)
| all isAlphaNum str =
| not (null str) && all isAlphaNum str =
info id 2026 "This word is outside of quotes. Did you intend to 'nest '\"'single quotes'\"' instead'? "

check (T_DoubleQuoted _ a:trapped:T_DoubleQuoted _ b:_) =
Expand Down Expand Up @@ -1527,6 +1524,7 @@ prop_subshellAssignmentCheck15 = verifyNotTree subshellAssignmentCheck "#!/bin/k
prop_subshellAssignmentCheck16 = verifyNotTree subshellAssignmentCheck "(set -e); echo $@"
prop_subshellAssignmentCheck17 = verifyNotTree subshellAssignmentCheck "foo=${ { bar=$(baz); } 2>&1; }; echo $foo $bar"
prop_subshellAssignmentCheck18 = verifyTree subshellAssignmentCheck "( exec {n}>&2; ); echo $n"
prop_subshellAssignmentCheck19 = verifyNotTree subshellAssignmentCheck "#!/bin/bash\nshopt -s lastpipe; echo a | read -r b; echo \"$b\""
subshellAssignmentCheck params t =
let flow = variableFlow params
check = findSubshelled flow [("oops",[])] Map.empty
Expand Down Expand Up @@ -1611,6 +1609,7 @@ prop_checkSpacefulness31= verifyNotTree checkSpacefulness "echo \"`echo \\\"$1\\
prop_checkSpacefulness32= verifyNotTree checkSpacefulness "var=$1; [ -v var ]"
prop_checkSpacefulness33= verifyTree checkSpacefulness "for file; do echo $file; done"
prop_checkSpacefulness34= verifyTree checkSpacefulness "declare foo$n=$1"
prop_checkSpacefulness35= verifyNotTree checkSpacefulness "echo ${1+\"$1\"}"

checkSpacefulness params t =
doVariableFlowAnalysis readF writeF (Map.fromList defaults) (variableFlow params)
Expand Down Expand Up @@ -2110,7 +2109,7 @@ checkLoopKeywordScope params t |
where
name = getCommandName t
path = let p = getPath (parentMap params) t in filter relevant p
subshellType t = case leadType (shellType params) (parentMap params) t of
subshellType t = case leadType params t of
NoneScope -> Nothing
SubshellScope str -> return str
relevant t = isLoop t || isFunction t || isJust (subshellType t)
Expand Down Expand Up @@ -2139,72 +2138,6 @@ checkFunctionDeclarations params
checkFunctionDeclarations _ _ = return ()


prop_checkCatastrophicRm1 = verify checkCatastrophicRm "rm -r $1/$2"
prop_checkCatastrophicRm2 = verify checkCatastrophicRm "rm -r /home/$foo"
prop_checkCatastrophicRm3 = verifyNot checkCatastrophicRm "rm -r /home/${USER:?}/*"
prop_checkCatastrophicRm4 = verify checkCatastrophicRm "rm -fr /home/$(whoami)/*"
prop_checkCatastrophicRm5 = verifyNot checkCatastrophicRm "rm -r /home/${USER:-thing}/*"
prop_checkCatastrophicRm6 = verify checkCatastrophicRm "rm --recursive /etc/*$config*"
prop_checkCatastrophicRm8 = verify checkCatastrophicRm "rm -rf /home"
prop_checkCatastrophicRm9 = verifyNot checkCatastrophicRm "rm -rf -- /home"
prop_checkCatastrophicRm10= verifyNot checkCatastrophicRm "rm -r \"${DIR}\"/{.gitignore,.gitattributes,ci}"
prop_checkCatastrophicRm11= verify checkCatastrophicRm "rm -r /{bin,sbin}/$exec"
prop_checkCatastrophicRm12= verify checkCatastrophicRm "rm -r /{{usr,},{bin,sbin}}/$exec"
prop_checkCatastrophicRm13= verifyNot checkCatastrophicRm "rm -r /{{a,b},{c,d}}/$exec"
prop_checkCatastrophicRmA = verify checkCatastrophicRm "rm -rf /usr /lib/nvidia-current/xorg/xorg"
prop_checkCatastrophicRmB = verify checkCatastrophicRm "rm -rf \"$STEAMROOT/\"*"
checkCatastrophicRm params t@(T_SimpleCommand id _ tokens) | t `isCommand` "rm" =
when (any isRecursiveFlag simpleArgs) $
mapM_ (mapM_ checkWord . braceExpand) tokens
where
simpleArgs = oversimplify t

checkWord token =
case getLiteralString token of
Just str ->
when (notElem "--" simpleArgs && (fixPath str `elem` importantPaths)) $
warn (getId token) 2114 "Warning: deletes a system directory. Use 'rm --' to disable this message."
Nothing ->
checkWord' token

checkWord' token = fromMaybe (return ()) $ do
filename <- getPotentialPath token
let path = fixPath filename
return . when (path `elem` importantPaths) $
warn (getId token) 2115 $ "Use \"${var:?}\" to ensure this never expands to " ++ path ++ " ."

fixPath filename =
let normalized = skipRepeating '/' . skipRepeating '*' $ filename in
if normalized == "/" then normalized else stripTrailing '/' normalized

getPotentialPath = getLiteralStringExt f
where
f (T_Glob _ str) = return str
f (T_DollarBraced _ word) =
let var = onlyLiteralString word in
if any (`isInfixOf` var) [":?", ":-", ":="]
then Nothing
else return ""
f _ = return ""

isRecursiveFlag "--recursive" = True
isRecursiveFlag ('-':'-':_) = False
isRecursiveFlag ('-':str) = 'r' `elem` str || 'R' `elem` str
isRecursiveFlag _ = False

stripTrailing c = reverse . dropWhile (== c) . reverse
skipRepeating c (a:b:rest) | a == b && b == c = skipRepeating c (b:rest)
skipRepeating c (a:r) = a:skipRepeating c r
skipRepeating _ [] = []

paths = [
"", "/bin", "/etc", "/home", "/mnt", "/usr", "/usr/share", "/usr/local",
"/var", "/lib"
]
importantPaths = filter (not . null) $
["", "/", "/*", "/*/*"] >>= (\x -> map (++x) paths)
checkCatastrophicRm _ _ = return ()


prop_checkStderrPipe1 = verify checkStderrPipe "#!/bin/ksh\nfoo |& bar"
prop_checkStderrPipe2 = verifyNot checkStderrPipe "#!/bin/bash\nfoo |& bar"
Expand Down Expand Up @@ -2238,7 +2171,7 @@ checkUnpassedInFunctions params root =
functions = execWriter $ doAnalysis (tell . maybeToList . findFunction) root

findFunction t@(T_Function id _ _ name body) =
let flow = getVariableFlow (shellType params) (parentMap params) body
let flow = getVariableFlow params body
in
if any (isPositionalReference t) flow && not (any isPositionalAssignment flow)
then return t
Expand Down Expand Up @@ -2541,7 +2474,9 @@ prop_checkUncheckedCd6 = verifyNotTree checkUncheckedCd "cd .."
prop_checkUncheckedCd7 = verifyNotTree checkUncheckedCd "#!/bin/bash -e\ncd foo\nrm bar"
prop_checkUncheckedCd8 = verifyNotTree checkUncheckedCd "set -o errexit; cd foo; rm bar"
checkUncheckedCd params root =
if hasSetE root then [] else execWriter $ doAnalysis checkElement root
if hasSetE params
then []
else execWriter $ doAnalysis checkElement root
where
checkElement t@T_SimpleCommand {} =
when(t `isUnqualifiedCommand` "cd"
Expand Down
Loading

0 comments on commit 79872f9

Please sign in to comment.