hadolint 1.18.2 → 1.19.0
raw patch · 10 files changed
+337/−45 lines, 10 filesdep ~megaparsec
Dependency ranges changed: megaparsec
Files
- README.md +8/−0
- hadolint.cabal +5/−5
- src/Hadolint/Formatter/Checkstyle.hs +5/−4
- src/Hadolint/Formatter/Codacy.hs +5/−4
- src/Hadolint/Formatter/Codeclimate.hs +5/−4
- src/Hadolint/Formatter/Format.hs +5/−4
- src/Hadolint/Formatter/Json.hs +5/−4
- src/Hadolint/Formatter/TTY.hs +5/−4
- src/Hadolint/Rules.hs +160/−4
- test/Spec.hs +134/−12
README.md view
@@ -203,6 +203,14 @@ | [DL3031](https://github.com/hadolint/hadolint/wiki/DL3031) | Do not use `yum update` | | [DL3032](https://github.com/hadolint/hadolint/wiki/DL3032) | `yum clean all` missing after yum command. | | [DL3033](https://github.com/hadolint/hadolint/wiki/DL3033) | Specify version with `yum install -y <package>-<version>` |+| [DL3034](https://github.com/hadolint/hadolint/wiki/DL3034) | Non-interactive switch missing from `zypper` command: `zypper install -y` |+| [DL3035](https://github.com/hadolint/hadolint/wiki/DL3035) | Do not use `zypper update`. |+| [DL3036](https://github.com/hadolint/hadolint/wiki/DL3036) | `zypper clean` missing after zypper use. |+| [DL3037](https://github.com/hadolint/hadolint/wiki/DL3037) | Specify version with `zypper install -y <package>[=]<version>`. |+| [DL3038](https://github.com/hadolint/hadolint/wiki/DL3038) | Use the `-y` switch to avoid manual input `dnf install -y <package>` |+| [DL3039](https://github.com/hadolint/hadolint/wiki/DL3039) | Do not use `dnf update` |+| [DL3040](https://github.com/hadolint/hadolint/wiki/DL3040) | `dnf clean all` missing after dnf command. |+| [DL3041](https://github.com/hadolint/hadolint/wiki/DL3041) | Specify version with `dnf install -y <package>-<version>` | | [DL4000](https://github.com/hadolint/hadolint/wiki/DL4000) | MAINTAINER is deprecated. | | [DL4001](https://github.com/hadolint/hadolint/wiki/DL4001) | Either use Wget or Curl but not both. | | [DL4003](https://github.com/hadolint/hadolint/wiki/DL4003) | Multiple `CMD` instructions found. |
hadolint.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 418683858197c94a7d90196b639708548b3c5966bc20506c43b2383a836a281b+-- hash: afee9524ce1aad80f4f3a651eef3a68c327005e1f97e965b5a82d3cd36afa09e name: hadolint-version: 1.18.2+version: 1.19.0 synopsis: Dockerfile Linter JavaScript API description: A smarter Dockerfile linter that helps you build best practice Docker images. category: Development@@ -61,7 +61,7 @@ , directory >=1.3.0 , filepath , language-docker >=9.1.2 && <10- , megaparsec >=7.0+ , megaparsec >=9.0.0 , mtl , parallel , split >=0.2@@ -82,7 +82,7 @@ , gitrev >=1.3.1 , hadolint , language-docker >=9.1.2 && <10- , megaparsec >=7.0+ , megaparsec >=9.0.0 , optparse-applicative >=0.14.0 , text if flag(static) && !(os(osx))@@ -108,7 +108,7 @@ , hadolint , hspec , language-docker >=9.1.2 && <10- , megaparsec >=7.0+ , megaparsec >=9.0.0 , split >=0.2 , text default-language: Haskell2010
src/Hadolint/Formatter/Checkstyle.hs view
@@ -17,9 +17,10 @@ import Hadolint.Formatter.Format import Hadolint.Rules (Metadata (..), RuleCheck (..)) import ShellCheck.Interface-import Text.Megaparsec (Stream)+import Text.Megaparsec (TraversableStream) import Text.Megaparsec.Error import Text.Megaparsec.Pos (sourceColumn, sourceLine, sourceName, unPos)+import Text.Megaparsec.Stream (VisualStream) data CheckStyle = CheckStyle { file :: String,@@ -30,7 +31,7 @@ source :: String } -errorToCheckStyle :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> CheckStyle+errorToCheckStyle :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => ParseErrorBundle s e -> CheckStyle errorToCheckStyle err = CheckStyle { file = sourceName pos,@@ -83,7 +84,7 @@ else "&#" ++ show (ord c) ++ ";" isOk x = any (\check -> check x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])] -formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Builder.Builder+formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Builder.Builder formatResult (Result errors checks) = "<?xml version='1.0' encoding='UTF-8'?><checkstyle version='4.3'>" <> xmlBody <> "</checkstyle>" where@@ -94,5 +95,5 @@ checkstyleChecks = fmap ruleToCheckStyle checks sameFileName CheckStyle {file = f1} CheckStyle {file = f2} = f1 == f2 -printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()+printResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> IO () printResult result = B.putStr (Builder.toLazyByteString (formatResult result))
src/Hadolint/Formatter/Codacy.hs view
@@ -14,9 +14,10 @@ import qualified Data.Text as Text import Hadolint.Formatter.Format (Result (..), errorPosition) import Hadolint.Rules (Metadata (..), RuleCheck (..))-import Text.Megaparsec (Stream)+import Text.Megaparsec (TraversableStream) import Text.Megaparsec.Error import Text.Megaparsec.Pos (sourceLine, sourceName, unPos)+import Text.Megaparsec.Stream (VisualStream) data Issue = Issue { filename :: String,@@ -29,7 +30,7 @@ toJSON Issue {..} = object ["filename" .= filename, "patternId" .= patternId, "message" .= msg, "line" .= line] -errorToIssue :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> Issue+errorToIssue :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => ParseErrorBundle s e -> Issue errorToIssue err = Issue { filename = sourceName pos,@@ -50,14 +51,14 @@ line = linenumber } -formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Seq Issue+formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Seq Issue formatResult (Result errors checks) = allIssues where allIssues = errorMessages <> checkMessages errorMessages = fmap errorToIssue errors checkMessages = fmap checkToIssue checks -printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()+printResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> IO () printResult result = mapM_ output (formatResult result) where output value = B.putStrLn (encode value)
src/Hadolint/Formatter/Codeclimate.hs view
@@ -17,9 +17,10 @@ import Hadolint.Formatter.Format (Result (..), errorPosition) import Hadolint.Rules (Metadata (..), RuleCheck (..)) import ShellCheck.Interface-import Text.Megaparsec (Stream)+import Text.Megaparsec (TraversableStream) import Text.Megaparsec.Error import Text.Megaparsec.Pos (sourceColumn, sourceLine, sourceName, unPos)+import Text.Megaparsec.Stream (VisualStream) data Issue = Issue { checkName :: String,@@ -60,7 +61,7 @@ "severity" .= impact ] -errorToIssue :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> Issue+errorToIssue :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => ParseErrorBundle s e -> Issue errorToIssue err = Issue { checkName = "DL1000",@@ -90,14 +91,14 @@ InfoC -> "info" StyleC -> "minor" -formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Seq Issue+formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Seq Issue formatResult (Result errors checks) = allIssues where allIssues = errorMessages <> checkMessages errorMessages = fmap errorToIssue errors checkMessages = fmap checkToIssue checks -printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()+printResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> IO () printResult result = mapM_ output (formatResult result) where output value = do
src/Hadolint/Formatter/Format.hs view
@@ -19,9 +19,10 @@ import qualified Data.Sequence as Seq import Hadolint.Rules import ShellCheck.Interface-import Text.Megaparsec (Stream (..), pstateSourcePos)+import Text.Megaparsec (TraversableStream (..), pstateSourcePos) import Text.Megaparsec.Error import Text.Megaparsec.Pos (SourcePos, sourcePosPretty)+import Text.Megaparsec.Stream (VisualStream) data Result s e = Result { errors :: !(Seq (ParseErrorBundle s e)),@@ -62,14 +63,14 @@ else c ) -errorMessageLine :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> String+errorMessageLine :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => ParseErrorBundle s e -> String errorMessageLine err@(ParseErrorBundle e _) = errorPositionPretty err ++ " " ++ parseErrorTextPretty (NE.head e) -errorPositionPretty :: Stream s => ParseErrorBundle s e -> String+errorPositionPretty :: TraversableStream s => ParseErrorBundle s e -> String errorPositionPretty err = sourcePosPretty (errorPosition err) -errorPosition :: Stream s => ParseErrorBundle s e -> Text.Megaparsec.Pos.SourcePos+errorPosition :: TraversableStream s => ParseErrorBundle s e -> Text.Megaparsec.Pos.SourcePos errorPosition (ParseErrorBundle e s) = let (_, posState) = reachOffset (errorOffset (NE.head e)) s in pstateSourcePos posState
src/Hadolint/Formatter/Json.hs view
@@ -13,15 +13,16 @@ import Hadolint.Formatter.Format (Result (..), errorPosition, severityText) import Hadolint.Rules (Metadata (..), RuleCheck (..)) import ShellCheck.Interface-import Text.Megaparsec (Stream)+import Text.Megaparsec (TraversableStream) import Text.Megaparsec.Error import Text.Megaparsec.Pos (sourceColumn, sourceLine, sourceName, unPos)+import Text.Megaparsec.Stream (VisualStream) data JsonFormat s e = JsonCheck RuleCheck | JsonParseError (ParseErrorBundle s e) -instance (Stream s, ShowErrorComponent e) => ToJSON (JsonFormat s e) where+instance (VisualStream s, TraversableStream s, ShowErrorComponent e) => ToJSON (JsonFormat s e) where toJSON (JsonCheck RuleCheck {..}) = object [ "file" .= filename,@@ -43,12 +44,12 @@ where pos = errorPosition err -formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Value+formatResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> Value formatResult (Result errors checks) = toJSON allMessages where allMessages = errorMessages <> checkMessages errorMessages = fmap JsonParseError errors checkMessages = fmap JsonCheck checks -printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()+printResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> IO () printResult result = B.putStrLn (encode (formatResult result))
src/Hadolint/Formatter/TTY.hs view
@@ -13,13 +13,14 @@ import Hadolint.Formatter.Format import Hadolint.Rules import Language.Docker.Syntax-import Text.Megaparsec (Stream (..))+import Text.Megaparsec (TraversableStream) import Text.Megaparsec.Error+import Text.Megaparsec.Stream (VisualStream) -formatErrors :: (Stream s, ShowErrorComponent e, Functor f) => f (ParseErrorBundle s e) -> f String+formatErrors :: (VisualStream s, TraversableStream s, ShowErrorComponent e, Functor f) => f (ParseErrorBundle s e) -> f String formatErrors = fmap formatError -formatError :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> String+formatError :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => ParseErrorBundle s e -> String formatError err = stripNewlines (errorMessageLine err) formatChecks :: Functor f => f RuleCheck -> f Text.Text@@ -31,7 +32,7 @@ formatPos :: Filename -> Linenumber -> Text.Text formatPos source line = source <> ":" <> Text.pack (show line) <> " " -printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()+printResult :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => Result s e -> IO () printResult Result {errors, checks} = printErrors >> printChecks where printErrors = mapM_ putStrLn (formatErrors errors)
src/Hadolint/Rules.hs view
@@ -210,7 +210,15 @@ yumYes, noYumUpdate, yumCleanup,- yumVersionPinned+ yumVersionPinned,+ zypperYes,+ noZypperUpdate,+ zypperCleanup,+ zypperVersionPinned,+ dnfYes,+ noDnfUpdate,+ dnfCleanup,+ dnfVersionPinned ] optionalRules :: RulesConfig -> [Rule]@@ -284,10 +292,14 @@ severity = ErrorC message = "Use absolute WORKDIR" check (Workdir loc)- | "$" `Text.isPrefixOf` loc = True- | "/" `Text.isPrefixOf` loc = True+ | "$" `Text.isPrefixOf` Text.dropAround dropQuotes loc = True+ | "/" `Text.isPrefixOf` Text.dropAround dropQuotes loc = True | otherwise = False check _ = True+ dropQuotes chr+ | chr == '\"' = True+ | chr == '\'' = True+ | otherwise = False hasNoMaintainer :: Rule hasNoMaintainer = instructionRule code severity message check@@ -635,10 +647,12 @@ "upgrade-strategy" ] cmd- versionFixed package = hasVersionSymbol package || isVersionedGit package+ versionFixed package = hasVersionSymbol package || isVersionedGit package || isLocalPackage package isVersionedGit package = "git+http" `Text.isInfixOf` package && "@" `Text.isInfixOf` package versionSymbols = ["==", ">=", "<=", ">", "<", "!=", "~=", "==="] hasVersionSymbol package = or [s `Text.isInfixOf` package | s <- versionSymbols]+ localPackageFileExtensions = [".whl", ".tar.gz"]+ isLocalPackage package = or [s `Text.isSuffixOf` package | s <- localPackageFileExtensions] stripInstallPrefix :: [Text.Text] -> [Text.Text] stripInstallPrefix cmd = dropWhile (== "install") (dropWhile (/= "install") cmd)@@ -950,6 +964,148 @@ yumPackages :: Shell.ParsedShell -> [Text.Text] yumPackages args = [ arg | cmd <- Shell.presentCommands args, Shell.cmdHasArgs "yum" ["install"] cmd, arg <- Shell.getArgsNoFlags cmd, arg /= "install"+ ]++zypperYes :: Rule+zypperYes = instructionRule code severity message check+ where+ code = "DL3034"+ severity = WarningC+ message = "Non-interactive switch missing from `zypper` command: `zypper install -y`"+ check (Run (RunArgs args _)) = argumentsRule (Shell.noCommands forgotZypperYesOption) args+ check _ = True+ forgotZypperYesOption cmd = isZypperInstall cmd && not (hasYesOption cmd)+ isZypperInstall =+ Shell.cmdHasArgs+ "zypper"+ [ "install",+ "in",+ "remove",+ "rm",+ "source-install",+ "si",+ "patch"+ ]+ hasYesOption = Shell.hasAnyFlag ["no-confirm", "y"]++noZypperUpdate :: Rule+noZypperUpdate = instructionRule code severity message check+ where+ code = "DL3035"+ severity = WarningC+ message = "Do not use `zypper update`."+ check (Run (RunArgs args _)) =+ argumentsRule+ ( Shell.noCommands+ ( Shell.cmdHasArgs+ "zypper"+ [ "update",+ "up",+ "dist-upgrade",+ "dup"+ ]+ )+ )+ args+ check _ = True++zypperCleanup :: Rule+zypperCleanup = instructionRule code severity message check+ where+ code = "DL3036"+ severity = WarningC+ message = "`zypper clean` missing after zypper use."+ check (Run (RunArgs args _)) =+ argumentsRule (Shell.noCommands zypperInstall) args+ || ( argumentsRule (Shell.anyCommands zypperInstall) args+ && argumentsRule (Shell.anyCommands zypperClean) args+ )+ check _ = True+ zypperInstall = Shell.cmdHasArgs "zypper" ["install", "in"]+ zypperClean = Shell.cmdHasArgs "zypper" ["clean", "cc"]++zypperVersionPinned :: Rule+zypperVersionPinned = instructionRule code severity message check+ where+ code = "DL3037"+ severity = WarningC+ message = "Specify version with `zypper install -y <package>=<version>`."+ check (Run (RunArgs args _)) = argumentsRule (all versionFixed . zypperPackages) args+ check _ = True+ versionFixed package =+ "=" `Text.isInfixOf` package+ || ">=" `Text.isInfixOf` package+ || ">" `Text.isInfixOf` package+ || "<=" `Text.isInfixOf` package+ || "<" `Text.isInfixOf` package+ || ".rpm" `Text.isSuffixOf` package++zypperPackages :: Shell.ParsedShell -> [Text.Text]+zypperPackages args =+ [ arg | cmd <- Shell.presentCommands args, Shell.cmdHasArgs "zypper" ["install", "in"] cmd, arg <- Shell.getArgsNoFlags cmd, arg /= "install", arg /= "in"+ ]++dnfYes :: Rule+dnfYes = instructionRule code severity message check+ where+ code = "DL3038"+ severity = WarningC+ message = "Use the -y switch to avoid manual input `dnf install -y <package`"+ check (Run (RunArgs args _)) = argumentsRule (Shell.noCommands forgotDnfYesOption) args+ check _ = True+ forgotDnfYesOption cmd = isDnfInstall cmd && not (hasYesOption cmd)+ isDnfInstall = Shell.cmdHasArgs "dnf" ["install", "groupinstall", "localinstall"]+ hasYesOption = Shell.hasAnyFlag ["y", "assumeyes"]++noDnfUpdate :: Rule+noDnfUpdate = instructionRule code severity message check+ where+ code = "DL3039"+ severity = ErrorC+ message = "Do not use dnf update."+ check (Run (RunArgs args _)) =+ argumentsRule+ ( Shell.noCommands+ ( Shell.cmdHasArgs+ "dnf"+ [ "upgrade",+ "upgrade-minimal"+ ]+ )+ )+ args+ check _ = True++dnfCleanup :: Rule+dnfCleanup = instructionRule code severity message check+ where+ code = "DL3040"+ severity = WarningC+ message = "`dnf clean all` missing after dnf command."+ check (Run (RunArgs args _)) =+ argumentsRule (Shell.noCommands dnfInstall) args+ || ( argumentsRule (Shell.anyCommands dnfInstall) args+ && argumentsRule (Shell.anyCommands dnfClean) args+ )+ check _ = True+ dnfInstall = Shell.cmdHasArgs "dnf" ["install"]+ dnfClean = Shell.cmdHasArgs "dnf" ["clean", "all"]++dnfVersionPinned :: Rule+dnfVersionPinned = instructionRule code severity message check+ where+ code = "DL3041"+ severity = WarningC+ message = "Specify version with `dnf install -y <package>-<version>`."+ check (Run (RunArgs args _)) = argumentsRule (all versionFixed . dnfPackages) args+ check _ = True+ versionFixed package =+ "-" `Text.isInfixOf` package+ || ".rpm" `Text.isSuffixOf` package++dnfPackages :: Shell.ParsedShell -> [Text.Text]+dnfPackages args =+ [ arg | cmd <- Shell.presentCommands args, Shell.cmdHasArgs "dnf" ["install"] cmd, arg <- Shell.getArgsNoFlags cmd, arg /= "install" ] gems :: Shell.ParsedShell -> [Text.Text]
test/Spec.hs view
@@ -190,19 +190,127 @@ onBuildRuleCatchesNot gemVersionPinned "RUN gem install bundler:2.0.1 -- --use-system-libraries=true" -- describe "yum rules" $ do- it "yum update" $ do- ruleCatches noYumUpdate "RUN yum update"- onBuildRuleCatches noYumUpdate "RUN yum update"- it "yum version pinning" $ do- ruleCatches yumVersionPinned "RUN yum install -y tomcat && yum clean all"- onBuildRuleCatches yumVersionPinned "RUN yum install -y tomcat && yum clean all"- it "yum no clean all" $ do- ruleCatches yumCleanup "RUN yum install -y mariadb-10.4"- onBuildRuleCatches yumCleanup "RUN yum install -y mariadb-10.4"- it "yum non-interactive" $ do- ruleCatches yumYes "RUN yum install httpd-2.4.24 && yum clean all"- onBuildRuleCatches yumYes "RUN yum install httpd-2.4.24 && yum clean all"+ it "yum update" $ do+ ruleCatches noYumUpdate "RUN yum update"+ ruleCatchesNot noYumUpdate "RUN yum install -y httpd-2.4.42 && yum clean all"+ ruleCatchesNot noYumUpdate "RUN bash -c `# not even a yum command`"+ onBuildRuleCatches noYumUpdate "RUN yum update"+ onBuildRuleCatchesNot noYumUpdate "RUN yum install -y httpd-2.4.42 && yum clean all"+ onBuildRuleCatchesNot noYumUpdate "RUN bash -c `# not even a yum command`"+ it "yum version pinning" $ do+ ruleCatches yumVersionPinned "RUN yum install -y tomcat && yum clean all"+ ruleCatchesNot yumVersionPinned "RUN yum install -y tomcat-9.2 && yum clean all"+ ruleCatchesNot yumVersionPinned "RUN bash -c `# not even a yum command`"+ onBuildRuleCatches yumVersionPinned "RUN yum install -y tomcat && yum clean all"+ onBuildRuleCatchesNot yumVersionPinned "RUN yum install -y tomcat-9.2 && yum clean all"+ onBuildRuleCatchesNot yumVersionPinned "RUN bash -c `# not even a yum command`"+ it "yum no clean all" $ do+ ruleCatches yumCleanup "RUN yum install -y mariadb-10.4"+ ruleCatchesNot yumCleanup "RUN yum install -y mariadb-10.4 && yum clean all"+ ruleCatchesNot yumCleanup "RUN bash -c `# not even a yum command`"+ onBuildRuleCatches yumCleanup "RUN yum install -y mariadb-10.4"+ onBuildRuleCatchesNot yumCleanup "RUN yum install -y mariadb-10.4 && yum clean all"+ onBuildRuleCatchesNot yumCleanup "RUN bash -c `# not even a yum command`"+ it "yum non-interactive" $ do+ ruleCatches yumYes "RUN yum install httpd-2.4.24 && yum clean all"+ ruleCatchesNot yumYes "RUN yum install -y httpd-2.4.24 && yum clean all"+ ruleCatchesNot yumYes "RUN bash -c `# not even a yum command`"+ onBuildRuleCatches yumYes "RUN yum install httpd-2.4.24 && yum clean all"+ onBuildRuleCatchesNot yumYes "RUN yum install -y httpd-2.4.24 && yum clean all"+ onBuildRuleCatchesNot yumYes "RUN bash -c `# not even a yum command`" --+ describe "zypper rules" $ do+ it "zupper update" $ do+ ruleCatches noZypperUpdate "RUN zypper update"+ ruleCatches noZypperUpdate "RUN zypper up"+ ruleCatches noZypperUpdate "RUN zypper dist-upgrade"+ ruleCatches noZypperUpdate "RUN zypper dup"+ onBuildRuleCatches noZypperUpdate "RUN zypper update"+ onBuildRuleCatches noZypperUpdate "RUN zypper up"+ onBuildRuleCatches noZypperUpdate "RUN zypper dist-upgrade"+ onBuildRuleCatches noZypperUpdate "RUN zypper dup"+ it "zypper version pinning" $ do+-- NOTE: In Haskell strings, '\' has to be escaped. And in shell commands, '>'+-- and '<' have to be escaped. Hence the double escaping.+ ruleCatches zypperVersionPinned "RUN zypper install -y tomcat && zypper clean"+ ruleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat=9.0.39 && zypper clean"+ ruleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\>=9.0 && zypper clean"+ ruleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\>9.0 && zypper clean"+ ruleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\<=9.0 && zypper clean"+ ruleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\<9.0 && zypper clean"+ ruleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat-9.0.39-1.rpm && zypper clean"+ onBuildRuleCatches zypperVersionPinned "RUN zypper install -y tomcat && zypper clean"+ onBuildRuleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat=9.0.39 && zypper clean"+ onBuildRuleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\>=9.0 && zypper clean"+ onBuildRuleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\>9.0 && zypper clean"+ onBuildRuleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\<=9.0 && zypper clean"+ onBuildRuleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat\\<9.0 && zypper clean"+ onBuildRuleCatchesNot zypperVersionPinned "RUN zypper install -y tomcat-9.0.39-1.rpm && zypper clean"+ it "zypper no clean all" $ do+ ruleCatches zypperCleanup "RUN zypper install -y mariadb=10.4"+ ruleCatchesNot zypperCleanup "RUN zypper install -y mariadb=10.4 && zypper clean"+ ruleCatchesNot zypperCleanup "RUN zypper install -y mariadb=10.4 && zypper cc"+ onBuildRuleCatches zypperCleanup "RUN zypper install -y mariadb=10.4"+ onBuildRuleCatchesNot zypperCleanup "RUN zypper install -y mariadb=10.4 && zypper clean"+ onBuildRuleCatchesNot zypperCleanup "RUN zypper install -y mariadb=10.4 && zypper cc"+ it "zypper non-interactive" $ do+ ruleCatches zypperYes "RUN zypper install httpd=2.4.24 && zypper clean"+ ruleCatchesNot zypperYes "RUN zypper install -y httpd=2.4.24 && zypper clean"+ ruleCatchesNot zypperYes "RUN zypper install --no-confirm httpd=2.4.24 && zypper clean"+ onBuildRuleCatches zypperYes "RUN zypper install httpd=2.4.24 && zypper clean"+ onBuildRuleCatchesNot zypperYes "RUN zypper install -y httpd=2.4.24 && zypper clean"+ onBuildRuleCatchesNot zypperYes "RUN zypper install --no-confirm httpd=2.4.24 && zypper clean"+ --+ describe "dnf rules" $ do+ it "dnf update" $ do+ ruleCatches noDnfUpdate "RUN dnf upgrade"+ onBuildRuleCatches noDnfUpdate "RUN dnf upgrade"+ it "dnf version pinning" $ do+ ruleCatches dnfVersionPinned "RUN dnf install -y tomcat && dnf clean all"+ ruleCatchesNot dnfVersionPinned "RUN dnf install -y tomcat-9.0.1 && dnf clean all"+ onBuildRuleCatches dnfVersionPinned "RUN dnf install -y tomcat && dnf clean all"+ onBuildRuleCatchesNot dnfVersionPinned "RUN dnf install -y tomcat-9.0.1 && dnf clean all"+ it "dnf no clean all" $ do+ ruleCatches dnfCleanup "RUN dnf install -y mariadb-10.4"+ ruleCatchesNot dnfCleanup "RUN dnf install -y mariadb-10.4 && dnf clean all"+ onBuildRuleCatches dnfCleanup "RUN dnf install -y mariadb-10.4"+ onBuildRuleCatchesNot dnfCleanup "RUN dnf install -y mariadb-10.4 && dnf clean all"+ it "dnf non-interactive" $ do+ ruleCatches dnfYes "RUN dnf install httpd-2.4.24 && dnf clean all"+ ruleCatchesNot dnfYes "RUN dnf install -y httpd-2.4.24 && dnf clean all"+ onBuildRuleCatches dnfYes "RUN dnf install httpd-2.4.24 && dnf clean all"+ onBuildRuleCatchesNot dnfYes "RUN dnf install -y httpd-2.4.24 && dnf clean all"+ --+ describe "dnf rules" $ do+ it "dnf update" $ do+ ruleCatches noDnfUpdate "RUN dnf upgrade"+ ruleCatches noDnfUpdate "RUN dnf upgrade-minimal"+ ruleCatchesNot noDnfUpdate "RUN notdnf upgrade"+ onBuildRuleCatches noDnfUpdate "RUN dnf upgrade"+ onBuildRuleCatches noDnfUpdate "RUN dnf upgrade-minimal"+ onBuildRuleCatchesNot noDnfUpdate "RUN notdnf upgrade"+ it "dnf version pinning" $ do+ ruleCatches dnfVersionPinned "RUN dnf install -y tomcat && dnf clean all"+ ruleCatchesNot dnfVersionPinned "RUN dnf install -y tomcat-9.0.1 && dnf clean all"+ ruleCatchesNot dnfVersionPinned "RUN notdnf install tomcat"+ onBuildRuleCatches dnfVersionPinned "RUN dnf install -y tomcat && dnf clean all"+ onBuildRuleCatchesNot dnfVersionPinned "RUN dnf install -y tomcat-9.0.1 && dnf clean all"+ onBuildRuleCatchesNot dnfVersionPinned "RUN notdnf install tomcat"+ it "dnf no clean all" $ do+ ruleCatches dnfCleanup "RUN dnf install -y mariadb-10.4"+ ruleCatchesNot dnfCleanup "RUN dnf install -y mariadb-10.4 && dnf clean all"+ ruleCatchesNot dnfCleanup "RUN notdnf install mariadb"+ onBuildRuleCatches dnfCleanup "RUN dnf install -y mariadb-10.4"+ onBuildRuleCatchesNot dnfCleanup "RUN dnf install -y mariadb-10.4 && dnf clean all"+ onBuildRuleCatchesNot dnfCleanup "RUN notdnf install mariadb"+ it "dnf non-interactive" $ do+ ruleCatches dnfYes "RUN dnf install httpd-2.4.24 && dnf clean all"+ ruleCatchesNot dnfYes "RUN dnf install -y httpd-2.4.24 && dnf clean all"+ ruleCatchesNot dnfYes "RUN notdnf install httpd"+ onBuildRuleCatches dnfYes "RUN dnf install httpd-2.4.24 && dnf clean all"+ onBuildRuleCatchesNot dnfYes "RUN dnf install -y httpd-2.4.24 && dnf clean all"+ onBuildRuleCatchesNot dnfYes "RUN notdnf install httpd"+ -- describe "apt-get rules" $ do it "apt" $ let dockerFile =@@ -410,6 +518,11 @@ it "pip3 version pinned" $ do ruleCatchesNot pipVersionPinned "RUN pip3 install MySQL_python==1.2.2" onBuildRuleCatchesNot pipVersionPinned "RUN pip3 install MySQL_python==1.2.2"+ it "pip3 install from local package" $ do+ ruleCatchesNot pipVersionPinned "RUN pip3 install mypkg.whl"+ ruleCatchesNot pipVersionPinned "RUN pip3 install mypkg.tar.gz"+ onBuildRuleCatchesNot pipVersionPinned "RUN pip3 install mypkg.whl"+ onBuildRuleCatchesNot pipVersionPinned "RUN pip3 install mypkg.tar.gz" it "pip install requirements" $ do ruleCatchesNot pipVersionPinned "RUN pip install -r requirements.txt" onBuildRuleCatchesNot pipVersionPinned "RUN pip install -r requirements.txt"@@ -898,9 +1011,18 @@ "ENTRYPOINT another" ] in ruleCatches multipleEntrypoints $ Text.unlines dockerFile+ it "single entry" $ ruleCatchesNot multipleEntrypoints "ENTRYPOINT /bin/true" it "no entry" $ ruleCatchesNot multipleEntrypoints "FROM busybox"+ it "workdir relative" $ ruleCatches absoluteWorkdir "WORKDIR relative/dir"+ it "workdir absolute" $ ruleCatchesNot absoluteWorkdir "WORKDIR /usr/local" it "workdir variable" $ ruleCatchesNot absoluteWorkdir "WORKDIR ${work}"+ it "workdir relative single quotes" $ ruleCatches absoluteWorkdir "WORKDIR \'relative/dir\'"+ it "workdir absolute single quotes" $ ruleCatchesNot absoluteWorkdir "WORKDIR \'/usr/local\'"+ -- no test for variable/single quotes since the variable would not expand.+ it "workdir relative double quotes" $ ruleCatches absoluteWorkdir "WORKDIR \"relative/dir\""+ it "workdir absolute double quotes" $ ruleCatchesNot absoluteWorkdir "WORKDIR \"/usr/local\""+ it "workdir variable double quotes" $ ruleCatchesNot absoluteWorkdir "WORKDIR \"${dir}\"" it "scratch" $ ruleCatchesNot noUntagged "FROM scratch" -- describe "add files and archives" $ do