hadolint 1.17.2 → 1.17.3
raw patch · 4 files changed
+59/−14 lines, 4 filesdep ~ShellCheck
Dependency ranges changed: ShellCheck
Files
- hadolint.cabal +4/−4
- src/Hadolint/Rules.hs +7/−3
- src/Hadolint/Shell.hs +7/−4
- test/Spec.hs +41/−3
hadolint.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 0f8b34c24db80480b579a95b0f4a3ff72ce38bb93a16e92f0578975efa60cbcc+-- hash: 1f42c246369026b5b251c024286a6e3e3a9b7d1af38cefdeffe03b0cadad4473 name: hadolint-version: 1.17.2+version: 1.17.3 synopsis: Dockerfile Linter JavaScript API description: A smarter Dockerfile linter that helps you build best practice Docker images. category: Development@@ -45,7 +45,7 @@ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -optP-Wno-nonportable-include-path build-depends: HsYAML- , ShellCheck >=0.6.0+ , ShellCheck >=0.7.0 , aeson , base >=4.8 && <5 , bytestring@@ -92,7 +92,7 @@ build-depends: HUnit >=1.2 , HsYAML- , ShellCheck >=0.6.0+ , ShellCheck >=0.7.0 , aeson , base >=4.8 && <5 , bytestring >=0.10
src/Hadolint/Rules.hs view
@@ -332,7 +332,7 @@ detectDoubleUsage state args = let newArgs = extractCommands args newState = Set.union state newArgs- in withState newState (Set.size newState < 2)+ in withState newState (Set.null newArgs || Set.size newState < 2) extractCommands args = Set.fromList [w | w <- Shell.findCommandNames args, w == "curl" || w == "wget"] @@ -595,7 +595,7 @@ -- in running this rule relevantInstall cmd = ["install"] `isInfixOf` Shell.getArgs cmd &&- not (["-r"] `isInfixOf` Shell.getArgs cmd || ["."] `isInfixOf` Shell.getArgs cmd)+ not (["--requirement"] `isInfixOf` Shell.getArgs cmd || ["-r"] `isInfixOf` Shell.getArgs cmd || ["."] `isInfixOf` Shell.getArgs cmd) hasBuildConstraint = Shell.hasFlag "constraint" packages cmd = stripInstallPrefix $@@ -787,7 +787,9 @@ where code = "DL4006" severity = WarningC- message = "Set the SHELL option -o pipefail before RUN with a pipe in it"+ message = "Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using \+ \/bin/sh in an alpine image or if your shell is symlinked to busybox then consider \+ \explicitly setting your SHELL to /bin/ash, or disable this check" check _ _ From {} = (False, True) -- Reset the state each time we find a new FROM check _ _ (Shell args) | argumentsRule isPowerShell args = (True, True)@@ -844,6 +846,8 @@ | cmd <- Shell.presentCommands shell , Shell.cmdHasArgs "gem" ["install", "i"] cmd , not (Shell.cmdHasArgs "gem" ["-v"] cmd)+ , not (Shell.cmdHasArgs "gem" ["--version"] cmd)+ , not (Shell.cmdHasPrefixArg "gem" "--version=" cmd) , arg <- Shell.getArgsNoFlags cmd , arg /= "install" , arg /= "i"
src/Hadolint/Shell.hs view
@@ -81,13 +81,11 @@ script = "#!" ++ extractShell sh ++ "\n" ++ printVars ++ Text.unpack txt exclusions = [ 2187 -- exclude the warning about the ash shell not being supported- , 1090 -- requires a directive (shell comment) that can't be expressed in a Dockerfile + , 1090 -- requires a directive (shell comment) that can't be expressed in a Dockerfile ] -- | Shellcheck complains when the shebang has more than one argument, so we only take the first extractShell s =- case listToMaybe . Text.words $ s of- Nothing -> ""- Just shell -> Text.unpack shell+ maybe "" Text.unpack (listToMaybe . Text.words $ s) -- | Inject all the collected env vars as exported variables so they can be used printVars = Text.unpack . Text.unlines . Set.toList $ Set.map (\v -> "export " <> v <> "=1") env @@ -151,6 +149,11 @@ cmdHasArgs expectedName expectedArgs (Command n args _) | expectedName /= n = False | otherwise = not $ null [arg | CmdPart arg _ <- args, arg `elem` expectedArgs]++cmdHasPrefixArg :: Text.Text -> Text.Text -> Command -> Bool+cmdHasPrefixArg expectedName expectedArg (Command n args _)+ | expectedName /= n = False+ | otherwise = not $ null [arg | CmdPart arg _ <- args, expectedArg `Text.isPrefixOf` arg] extractAllArgs :: Token -> [CmdPart] extractAllArgs (T_SimpleCommand _ _ (_:allArgs)) = map mkPart allArgs
test/Spec.hs view
@@ -178,6 +178,12 @@ it "does not warn on -v" $ do ruleCatchesNot gemVersionPinned "RUN gem install bundler -v '2.0.1'" onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler -v '2.0.1'"+ it "does not warn on --version without =" $ do+ ruleCatchesNot gemVersionPinned "RUN gem install bundler --version '2.0.1'"+ onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler --version '2.0.1'"+ it "does not warn on --version with =" $ do+ ruleCatchesNot gemVersionPinned "RUN gem install bundler --version='2.0.1'"+ onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler --version='2.0.1'" it "does not warn on extra flags" $ do ruleCatchesNot gemVersionPinned "RUN gem install bundler:2.0.1 -- --use-system-libraries=true" onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler:2.0.1 -- --use-system-libraries=true"@@ -383,6 +389,9 @@ it "pip install requirements" $ do ruleCatchesNot pipVersionPinned "RUN pip install -r requirements.txt" onBuildRuleCatchesNot pipVersionPinned "RUN pip install -r requirements.txt"+ it "pip install requirements with long flag" $ do+ ruleCatchesNot pipVersionPinned "RUN pip install --requirement requirements.txt"+ onBuildRuleCatchesNot pipVersionPinned "RUN pip install --requirement requirements.txt" it "pip install use setup.py" $ do ruleCatchesNot pipVersionPinned "RUN pip install ." onBuildRuleCatchesNot pipVersionPinned "RUN pip install ."@@ -1109,6 +1118,33 @@ , "RUN curl localhost" ] in ruleCatches wgetOrCurl $ Text.unlines dockerFile+ it "only warns on the relevant RUN instruction" $+ let dockerFile =+ [ "FROM node as foo"+ , "RUN wget my.xyz"+ , "RUN curl my.xyz"+ , "RUN echo hello"+ ]+ in assertChecks wgetOrCurl+ (Text.unlines dockerFile)+ (\checks -> assertBool+ "Expecting warnings only in 1 RUN instruction"+ (length checks == 1)+ )+ it "only warns on many relevant RUN instructions" $+ let dockerFile =+ [ "FROM node as foo"+ , "RUN wget my.xyz"+ , "RUN curl my.xyz"+ , "RUN echo hello"+ , "RUN wget foo.com"+ ]+ in assertChecks wgetOrCurl+ (Text.unlines dockerFile)+ (\checks -> assertBool+ "Expecting warnings only in 2 RUN instructions"+ (length checks == 2)+ ) -- describe "Regression Tests" $ it "Comments with backslashes at the end are just comments" $@@ -1132,7 +1168,6 @@ Left err -> assertFailure $ show err Right dockerFile -> makeAssertions $ analyze [rule] dockerFile - assertOnBuildChecks :: HasCallStack => Rule -> Text.Text -> ([RuleCheck] -> IO a) -> IO a assertOnBuildChecks rule s makeAssertions = case parseText (s <> "\n") of@@ -1143,6 +1178,9 @@ wrapInOnBuild (InstructionPos (Run args) so li) = InstructionPos (OnBuild (Run args)) so li wrapInOnBuild i = i +selectChecksWithLines :: [RuleCheck] -> [RuleCheck]+selectChecksWithLines checks = [c | c <- checks, linenumber c <= 0]+ -- Assert a failed check exists for rule ruleCatches :: HasCallStack => Rule -> Text.Text -> Assertion ruleCatches rule s = assertChecks rule s f@@ -1150,7 +1188,7 @@ f checks = do when (null checks) $ assertFailure "I was expecting to catch at least one error"- assertBool "Incorrect line number for result" $ null [c | c <- checks, linenumber c <= 0]+ assertBool "Incorrect line number for result" $ null $ selectChecksWithLines checks onBuildRuleCatches :: HasCallStack => Rule -> Text.Text -> Assertion onBuildRuleCatches rule s = assertOnBuildChecks rule s f@@ -1158,7 +1196,7 @@ f checks = do when (length checks /= 1) $ assertFailure (Text.unpack . Text.unlines . formatChecks $ checks)- assertBool "Incorrect line number for result" $ null [c | c <- checks, linenumber c <= 0]+ assertBool "Incorrect line number for result" $ null $ selectChecksWithLines checks ruleCatchesNot :: HasCallStack => Rule -> Text.Text -> Assertion ruleCatchesNot rule s = assertChecks rule s f