hadolint 2.13.1 → 2.14.0
raw patch · 24 files changed
+387/−96 lines, 24 filesdep −ilistdep ~language-dockerdep ~optparse-applicativePVP ok
version bump matches the API change (PVP)
Dependencies removed: ilist
Dependency ranges changed: language-docker, optparse-applicative
API changes (from Hackage documentation)
+ Hadolint.Rule.DL3062: rule :: Rule ParsedShell
+ Hadolint.Utils: hasCacheOrTmpfsMount :: RunFlags -> Bool
+ Hadolint.Utils: hasCacheOrTmpfsMountWith :: Text -> RunFlags -> Bool
Files
- README.md +3/−2
- hadolint.cabal +7/−5
- src/Hadolint/Process.hs +2/−0
- src/Hadolint/Rule/DL3009.hs +4/−12
- src/Hadolint/Rule/DL3019.hs +5/−16
- src/Hadolint/Rule/DL3033.hs +25/−2
- src/Hadolint/Rule/DL3036.hs +7/−5
- src/Hadolint/Rule/DL3040.hs +8/−3
- src/Hadolint/Rule/DL3041.hs +5/−1
- src/Hadolint/Rule/DL3042.hs +4/−3
- src/Hadolint/Rule/DL3044.hs +5/−3
- src/Hadolint/Rule/DL3060.hs +2/−18
- src/Hadolint/Rule/DL3062.hs +46/−0
- src/Hadolint/Shell.hs +2/−4
- src/Hadolint/Utils.hs +31/−0
- test/Hadolint/Rule/DL3009Spec.hs +44/−0
- test/Hadolint/Rule/DL3019Spec.hs +41/−15
- test/Hadolint/Rule/DL3033Spec.hs +15/−0
- test/Hadolint/Rule/DL3036Spec.hs +14/−0
- test/Hadolint/Rule/DL3040Spec.hs +15/−4
- test/Hadolint/Rule/DL3041Spec.hs +25/−3
- test/Hadolint/Rule/DL3042Spec.hs +12/−0
- test/Hadolint/Rule/DL3060Spec.hs +24/−0
- test/Hadolint/Rule/DL3062Spec.hs +41/−0
README.md view
@@ -472,7 +472,7 @@ <!--lint disable maximum-line-length--> | Rule | Default Severity | Description |-| :----------------------------------------------------------- | :--------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |+|:-------------------------------------------------------------|:-----------------| :-------------------------------------------------------------------------------------------------------------------------------------------------- | | [DL1001](https://github.com/hadolint/hadolint/wiki/DL1001) | Ignore | Please refrain from using inline ignore pragmas `# hadolint ignore=DLxxxx`. | | [DL3000](https://github.com/hadolint/hadolint/wiki/DL3000) | Error | Use absolute WORKDIR. | | [DL3001](https://github.com/hadolint/hadolint/wiki/DL3001) | Info | For some bash commands it makes no sense running them in a Docker container like ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig. |@@ -532,6 +532,7 @@ | [DL3059](https://github.com/hadolint/hadolint/wiki/DL3059) | Info | Multiple consecutive `RUN` instructions. Consider consolidation. | | [DL3060](https://github.com/hadolint/hadolint/wiki/DL3060) | Info | `yarn cache clean` missing after `yarn install` was run. | | [DL3061](https://github.com/hadolint/hadolint/wiki/DL3061) | Error | Invalid instruction order. Dockerfile must begin with `FROM`, `ARG` or comment. |+| [DL3062](https://github.com/hadolint/hadolint/wiki/DL3061) | Warning | Pin versions in go install. Instead of `go install <package>` use `go install <package>@<version>` | | [DL4000](https://github.com/hadolint/hadolint/wiki/DL4000) | Error | `MAINTAINER` is deprecated. | | [DL4001](https://github.com/hadolint/hadolint/wiki/DL4001) | Warning | Either use Wget or Curl but not both. | | [DL4003](https://github.com/hadolint/hadolint/wiki/DL4003) | Warning | Multiple `CMD` instructions found. |@@ -559,7 +560,7 @@ | [SC1095](https://github.com/koalaman/shellcheck/wiki/SC1095) | | You need a space or linefeed between the function name and body. | | [SC1097](https://github.com/koalaman/shellcheck/wiki/SC1097) | | Unexpected `==`. For assignment, use `=`. For comparison, use `[ .. ]` or `[[ .. ]]`. | | [SC1098](https://github.com/koalaman/shellcheck/wiki/SC1098) | | Quote/escape special characters when using `eval`, e.g. `eval "a=(b)"`. |-| [SC1099](https://github.com/koalaman/shellcheck/wiki/SC1099) | | You need a space before the `#`. |+| [SC1099](https://github.com/koalaman/shellcheck/wiki/SC1099) | | You need a space before the `#`. | | [SC2002](https://github.com/koalaman/shellcheck/wiki/SC2002) | | Useless cat. Consider <code>cmd < file | ..</code> or <code>cmd file | ..</code> instead. | | [SC2015](https://github.com/koalaman/shellcheck/wiki/SC2015) | | Note that <code>A && B || C</code> is not if-then-else. C may run when A is true. | | [SC2026](https://github.com/koalaman/shellcheck/wiki/SC2026) | | This word is outside of quotes. Did you intend to 'nest '"'single quotes'"' instead'? |
hadolint.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: hadolint-version: 2.13.1+version: 2.14.0 synopsis: Dockerfile Linter JavaScript API description: A smarter Dockerfile linter that helps you build best practice Docker images.@@ -101,6 +101,7 @@ Hadolint.Rule.DL3059 Hadolint.Rule.DL3060 Hadolint.Rule.DL3061+ Hadolint.Rule.DL3062 Hadolint.Rule.DL4000 Hadolint.Rule.DL4001 Hadolint.Rule.DL4003@@ -109,6 +110,7 @@ Hadolint.Rule.DL4006 Hadolint.Rule.Shellcheck Hadolint.Shell+ Hadolint.Utils other-modules: Paths_hadolint autogen-modules: Paths_hadolint@@ -147,12 +149,11 @@ , foldl >=1.4.18 && <1.5 , gitrev >=1.3.1 && <1.4 , HsYAML >=0.2.1 && <0.3- , ilist >=0.3.1 && <0.4- , language-docker >=14.0.1 && <15+ , language-docker >=15.0.0 && <16 , megaparsec >=9.0.0 && <9.8 , mtl >=2.3.1 && <2.4 , network-uri >=2.6.4 && <2.7- , optparse-applicative >=0.19.0 && <0.20+ , optparse-applicative >=0.18.1 && <0.20 , parallel >=3.2.2 && <3.3 , parsec >=3.1.18 && <3.2 , prettyprinter >=1.7.1 && <1.8@@ -196,7 +197,7 @@ , containers >=0.7 && <0.8 , data-default >=0.8.0 && <0.9 , hadolint- , language-docker >=14.0.1 && <15+ , language-docker , megaparsec >=9.7.0 && <9.8 , optparse-applicative >=0.19.0 && <0.20 , prettyprinter >=1.7.1 && <1.8@@ -277,6 +278,7 @@ Hadolint.Rule.DL3059Spec Hadolint.Rule.DL3060Spec Hadolint.Rule.DL3061Spec+ Hadolint.Rule.DL3062Spec Hadolint.Rule.DL4000Spec Hadolint.Rule.DL4001Spec Hadolint.Rule.DL4003Spec
src/Hadolint/Process.hs view
@@ -68,6 +68,7 @@ import qualified Hadolint.Rule.DL3059 import qualified Hadolint.Rule.DL3060 import qualified Hadolint.Rule.DL3061+import qualified Hadolint.Rule.DL3062 import qualified Hadolint.Rule.DL4000 import qualified Hadolint.Rule.DL4001 import qualified Hadolint.Rule.DL4003@@ -172,6 +173,7 @@ <> Hadolint.Rule.DL3059.rule <> Hadolint.Rule.DL3060.rule <> Hadolint.Rule.DL3061.rule+ <> Hadolint.Rule.DL3062.rule <> Hadolint.Rule.DL4000.rule <> Hadolint.Rule.DL4001.rule <> Hadolint.Rule.DL4003.rule
src/Hadolint/Rule/DL3009.hs view
@@ -3,9 +3,9 @@ import Hadolint.Rule import Language.Docker.Syntax import qualified Data.Map.Strict as Map-import qualified Data.Set as Set import qualified Data.Text as Text import qualified Hadolint.Shell as Shell+import qualified Hadolint.Utils as Utils data Acc@@ -36,9 +36,9 @@ if foldArguments disabledDockerClean args then st |> modify rememberDockerClean else st- | hasCacheDirectory "/var/lib/apt/lists" flags = st- | hasCacheDirectory "/var/lib/apt" flags- && hasCacheDirectory "/var/cache/apt" flags = st+ | Utils.hasCacheOrTmpfsMountWith "/var/lib/apt/lists" flags = st+ | Utils.hasCacheOrTmpfsMountWith "/var/lib/apt" flags+ && Utils.hasCacheOrTmpfsMountWith "/var/cache/apt" flags = st | otherwise = st |> modify (rememberLine line) check _ st _ = st @@ -99,14 +99,6 @@ ["\'Binary::apt::APT::Keep-Downloaded-Packages \"true\";\'"] ) (Shell.presentCommands args)--hasCacheDirectory :: Text.Text -> RunFlags -> Bool-hasCacheDirectory dir RunFlags { mount } =- not ( null $ Set.filter (isCacheMount dir) mount)--isCacheMount :: Text.Text -> RunMount -> Bool-isCacheMount dir (CacheMount CacheOpts {cTarget = TargetPath {unTargetPath = t}}) = dir `Text.isPrefixOf` t-isCacheMount _ _ = False -- | Even though dockerfiles without a FROM are not valid, we still want to provide some feedback for this rule -- so we pretend there is a base image at the start of the file if there is none
src/Hadolint/Rule/DL3019.hs view
@@ -3,9 +3,8 @@ import Hadolint.Rule import Hadolint.Shell (ParsedShell) import Language.Docker.Syntax-import qualified Data.Set as Set-import qualified Data.Text as Text import qualified Hadolint.Shell as Shell+import qualified Hadolint.Utils as Utils rule :: Rule ParsedShell@@ -20,22 +19,12 @@ message = "Use the `--no-cache` switch to avoid the need to use `--update` and \ \remove `/var/cache/apk/*` when done installing packages"- check (Run (RunArgs args flags)) = hasCacheMount flags- || foldArguments (Shell.noCommands forgotCacheOption) args+ check (Run (RunArgs args flags))+ | Utils.hasCacheOrTmpfsMountWith "/var/cache/apk" flags = True+ | foldArguments (Shell.noCommands forgotCacheOption) args = True+ | otherwise = False check _ = True {-# INLINEABLE dl3019 #-}--hasCacheMount :: RunFlags -> Bool-hasCacheMount RunFlags { mount } =- not (null $ Set.filter isCacheMount mount)--isCacheMount :: RunMount -> Bool-isCacheMount (CacheMount CacheOpts { cTarget = t })= isVarCacheApk t-isCacheMount _ = False--isVarCacheApk :: TargetPath -> Bool-isVarCacheApk TargetPath { unTargetPath = p }- = Text.dropWhileEnd (=='/') p == "/var/cache/apk" forgotCacheOption :: Shell.Command -> Bool forgotCacheOption cmd = Shell.cmdHasArgs "apk" ["add"] cmd
src/Hadolint/Rule/DL3033.hs view
@@ -4,6 +4,7 @@ import Hadolint.Rule import qualified Hadolint.Shell as Shell import Language.Docker.Syntax+import Data.Char (isDigit, isAsciiUpper, isAsciiLower) rule :: Rule Shell.ParsedShell@@ -32,8 +33,30 @@ ] packageVersionFixed :: Text.Text -> Bool-packageVersionFixed package =- "-" `Text.isInfixOf` package || ".rpm" `Text.isSuffixOf` package+packageVersionFixed package+ | length parts <= 1 = False -- No dashes, definitively no version+ | ".rpm" `Text.isSuffixOf` package = True -- rpm files always have a version+ | otherwise = isVersionLike $ drop 1 parts+ where+ parts = Text.splitOn "-" package++isVersionLike :: [Text.Text] -> Bool+isVersionLike parts =+ case parts of+ [] -> False -- No parts after splitting by hyphen+ _ -> all partIsValid parts && any partStartsWithDigit parts+ where+ partIsValid part = Text.all isVersionChar part+ partStartsWithDigit part = case Text.uncons part of+ Just (c, _) -> isDigit c+ Nothing -> False -- Empty Text++isVersionChar :: Char -> Bool+isVersionChar c =+ isDigit c+ || isAsciiUpper c+ || isAsciiLower c+ || c `elem` ['.', '~', '^', '_', ':', '+'] yumModules :: Shell.ParsedShell -> [Text.Text] yumModules args =
src/Hadolint/Rule/DL3036.hs view
@@ -2,6 +2,7 @@ import Hadolint.Rule import qualified Hadolint.Shell as Shell+import qualified Hadolint.Utils as Utils import Language.Docker.Syntax @@ -16,11 +17,12 @@ severity = DLWarningC message = "`zypper clean` missing after zypper use." - check (Run (RunArgs args _)) =- foldArguments (Shell.noCommands zypperInstall) args- || ( foldArguments (Shell.anyCommands zypperInstall) args- && foldArguments (Shell.anyCommands zypperClean) args- )+ check (Run (RunArgs args flags))+ | foldArguments (Shell.noCommands zypperInstall) args = True+ | Utils.hasCacheOrTmpfsMountWith "/var/cache/zypp" flags = True+ | foldArguments (Shell.anyCommands zypperInstall) args+ && foldArguments (Shell.anyCommands zypperClean) args = True+ | otherwise = False check _ = True zypperInstall = Shell.cmdHasArgs "zypper" ["install", "in"]
src/Hadolint/Rule/DL3040.hs view
@@ -2,6 +2,7 @@ import Hadolint.Rule import qualified Hadolint.Shell as Shell+import qualified Hadolint.Utils as Utils import Language.Docker.Syntax @@ -16,7 +17,11 @@ severity = DLWarningC message = "`dnf clean all` missing after dnf command." - check (Run (RunArgs args _)) = all (checkMissingClean args) dnfCmds+ check (Run (RunArgs args flags))+ | all (checkMissingClean args) dnfCmds = True+ | Utils.hasCacheOrTmpfsMountWith "/var/cache/libdnf5" flags = True+ | Utils.hasCacheOrTmpfsMountWith ".cache/libdnf5" flags = True+ | otherwise = False check _ = True checkMissingClean args cmdName =@@ -26,7 +31,7 @@ ) dnfInstall cmdName = Shell.cmdHasArgs cmdName ["install"]- dnfClean cmdName args = Shell.cmdHasArgs cmdName ["clean", "all"] args - || Shell.cmdHasArgs "rm" ["-rf", "/var/cache/yum/*"] args+ dnfClean cmdName args = Shell.cmdHasArgs cmdName ["clean", "all"] args+ || Shell.cmdHasArgs "rm" ["-rf", "/var/cache/libdnf5*"] args dnfCmds = ["dnf", "microdnf"] {-# INLINEABLE dl3040 #-}
src/Hadolint/Rule/DL3041.hs view
@@ -54,7 +54,11 @@ Nothing -> False -- Empty Text isVersionChar :: Char -> Bool-isVersionChar c = isDigit c || isAsciiUpper c || isAsciiLower c || c `elem` ['.', '~', '^', '_']+isVersionChar c =+ isDigit c+ || isAsciiUpper c+ || isAsciiLower c+ || c `elem` ['.', '~', '^', '_', ':', '+'] dnfModules :: Shell.ParsedShell -> [Text.Text] dnfModules args =
src/Hadolint/Rule/DL3042.hs view
@@ -4,12 +4,12 @@ import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Maybe (fromMaybe, isNothing, isJust, fromJust)-import Data.Set (Set) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as Text import Hadolint.Rule import qualified Hadolint.Shell as Shell+import qualified Hadolint.Utils as Utils import Language.Docker.Syntax @@ -38,10 +38,11 @@ \ Use `pip install --no-cache-dir <package>`" check _ st (From from) = st |> modify (rememberStage from) check _ st (Env pairs) = st |> modify (registerEnv pairs)- check line st (Run (RunArgs args _))+ check line st (Run (RunArgs args flags)) | Acc s ncm <- state st, Just True <- Map.lookup s ncm = st | foldArguments pipNoCacheDirIsSet args = st | foldArguments (Shell.noCommands forgotNoCacheDir) args = st+ | Utils.hasCacheOrTmpfsMountWith ".cache/pip" flags = st | otherwise = st |> addFail CheckFailure {..} check _ st _ = st {-# INLINEABLE dl3042 #-}@@ -119,5 +120,5 @@ isJust val && fromJust val `notElem` truthy = False | otherwise = True -truthy :: Set Text+truthy :: Set.Set Text truthy = Set.fromList ["1", "true", "True", "TRUE", "on", "On", "ON", "yes", "Yes", "YES"]
src/Hadolint/Rule/DL3044.hs view
@@ -1,6 +1,5 @@ module Hadolint.Rule.DL3044 (rule) where -import Data.List.Index (indexed) import qualified Data.Set as Set import qualified Data.Text as Text import Hadolint.Rule@@ -28,9 +27,12 @@ listOfReferences :: Pairs -> [Text.Text] listOfReferences prs = [ var- | (idx, (var, _)) <- indexed prs,- var `isSubstringOfAny` map (snd . snd) (filter ((/= idx) . fst) (indexed prs))+ | (idx, (var, _)) <- enum prs,+ var `isSubstringOfAny` map (snd . snd) (filter ((/= idx) . fst) (enum prs)) ]+ where+ enum :: Pairs -> [(Integer, (Text.Text, Text.Text))]+ enum = zip [0..] -- | is a reference of a variable substring of any text? -- matches ${var_name} and $var_name, but not $var_nameblafoo
src/Hadolint/Rule/DL3060.hs view
@@ -4,8 +4,8 @@ import qualified Data.Text as Text import Hadolint.Rule import qualified Hadolint.Shell as Shell+import qualified Hadolint.Utils as Utils import Language.Docker.Syntax-import Data.Set data Acc = Acc@@ -16,22 +16,6 @@ | Empty deriving (Show) --- check if a RunMount is a CacheMount-isCacheMount :: RunMount -> Bool-isCacheMount (CacheMount _) = True-isCacheMount _ = False---- Set of RunMounts must contain at least one CacheMount--containsOneCacheMount :: Set RunMount -> Bool-containsOneCacheMount = Data.Set.foldl foldable False- where - foldable a b = a || isCacheMount b---- do not flag this as a problem if using a cacheMount-cacheMount :: RunFlags -> Bool-cacheMount RunFlags {mount} = containsOneCacheMount mount- rule :: Rule Shell.ParsedShell rule = dl3060 <> onbuild dl3060 {-# INLINEABLE rule #-}@@ -48,7 +32,7 @@ check line st (Run (RunArgs args flags)) | foldArguments (Shell.anyCommands yarnInstall) args && foldArguments (Shell.noCommands yarnCacheClean) args- && not (cacheMount flags) =+ && not (Utils.hasCacheOrTmpfsMountWith ".cache/yarn" flags) = st |> modify (rememberLine line) | otherwise = st check _ st _ = st
+ src/Hadolint/Rule/DL3062.hs view
@@ -0,0 +1,46 @@+module Hadolint.Rule.DL3062 (rule) where++import Data.Text qualified as Text+import Hadolint.Rule+import Hadolint.Shell qualified as Shell+import Language.Docker.Syntax++rule :: Rule Shell.ParsedShell+rule = dl3062 <> onbuild dl3062+{-# INLINEABLE rule #-}++dl3062 :: Rule Shell.ParsedShell+dl3062 = simpleRule code severity message check+ where+ code = "DL3062"+ severity = DLWarningC+ message = "Pin versions in go. Instead of `go install <package>` use `go install <package>@<version>`"++ check (Run (RunArgs args _)) = foldArguments (all checkGoPackageVersion . getGoPackage) args+ check _ = True+{-# INLINEABLE dl3062 #-}++goCommands :: [Text.Text]+goCommands = ["install", "get", "run"]++isGoCommand :: Shell.Command -> Bool+isGoCommand = Shell.cmdsHaveArgs ["go"] goCommands++getGoPackage :: Shell.ParsedShell -> [Text.Text]+getGoPackage args =+ [ arg+ | cmd <- Shell.presentCommands args,+ isGoCommand cmd,+ arg <- Shell.getArgsNoFlags cmd,+ arg `notElem` goCommands+ ]++hasVersionSymbol :: Text.Text -> Bool+hasVersionSymbol package = "@" `Text.isInfixOf` package++isTagsVersion :: Text.Text -> Bool+isTagsVersion package =+ or [("@" <> tag) `Text.isSuffixOf` package | tag <- ["latest", "none"]]++checkGoPackageVersion :: Text.Text -> Bool+checkGoPackageVersion package = hasVersionSymbol package && not (isTagsVersion package)
src/Hadolint/Shell.hs view
@@ -162,9 +162,7 @@ findCommandNames script = map name (presentCommands script) cmdHasArgs :: Text.Text -> [Text.Text] -> Command -> Bool-cmdHasArgs expectedName expectedArgs (Command n args _)- | expectedName /= n = False- | otherwise = not $ null [arg | CmdPart arg _ <- args, arg `elem` expectedArgs]+cmdHasArgs expectedName = cmdsHaveArgs [expectedName] cmdsHaveArgs :: [Text.Text] -> [Text.Text] -> Command -> Bool cmdsHaveArgs expectedNames expectedArgs (Command n args _)@@ -204,7 +202,7 @@ notAFlagId pId = pId `notElem` map partId (flags args) hasFlag :: Text.Text -> Command -> Bool-hasFlag flag Command {flags} = not $ null [f | CmdPart f _ <- flags, f == flag]+hasFlag flag cmd = countFlag flag cmd > 0 countFlag :: Text.Text -> Command -> Int countFlag flag Command {flags} = length [ f | CmdPart f _ <- flags, f == flag ]
+ src/Hadolint/Utils.hs view
@@ -0,0 +1,31 @@+module Hadolint.Utils+ ( hasCacheOrTmpfsMount,+ hasCacheOrTmpfsMountWith+ )+where++import qualified Data.Text as Text+import qualified Data.Set as Set+import Language.Docker.Syntax+++-- Returns true if the given RunFlags contain a mount of type cache or tmpfs at+-- a location that contains the path fragment `frag`.+hasCacheOrTmpfsMountWith :: Text.Text -> RunFlags -> Bool+hasCacheOrTmpfsMountWith frag RunFlags { mount } =+ not ( null $ Set.filter (isCacheOrTmpfsMountWith frag) mount)++isCacheOrTmpfsMountWith :: Text.Text -> RunMount -> Bool+isCacheOrTmpfsMountWith frag (CacheMount CacheOpts {cTarget = TargetPath {unTargetPath = t}}) = frag `Text.isInfixOf` t+isCacheOrTmpfsMountWith frag (TmpfsMount TmpOpts {tTarget = TargetPath {unTargetPath = t}}) = frag `Text.isInfixOf` t+isCacheOrTmpfsMountWith _ _ = False++-- Returns true if the given RunFlags contain a mount of type cache or tmpfs+hasCacheOrTmpfsMount :: RunFlags -> Bool+hasCacheOrTmpfsMount RunFlags { mount } =+ not ( null $ Set.filter isCacheOrTmpfsMount mount)++isCacheOrTmpfsMount :: RunMount -> Bool+isCacheOrTmpfsMount (CacheMount _) = True+isCacheOrTmpfsMount (TmpfsMount _) = True+isCacheOrTmpfsMount _ = False
test/Hadolint/Rule/DL3009Spec.hs view
@@ -132,6 +132,50 @@ ruleCatches "DL3009" $ Text.unlines dockerFile onBuildRuleCatches "DL3009" $ Text.unlines dockerFile + it "don't warn: tmpfs mount to apt cache and lists directory" $+ let dockerFile =+ [ "RUN \\",+ " --mount=type=tmpfs,target=/var/cache/apt \\",+ " --mount=type=tmpfs,target=/var/lib/apt \\",+ " apt-get update"+ ]+ in do+ ruleCatchesNot "DL3009" $ Text.unlines dockerFile+ onBuildRuleCatchesNot "DL3009" $ Text.unlines dockerFile++ it "don't warn: tmpfs mount to apt cache and cache mount to lists directory" $+ let dockerFile =+ [ "RUN \\",+ " --mount=type=tmpfs,target=/var/cache/apt \\",+ " --mount=type=cache,target=/var/lib/apt \\",+ " apt-get update"+ ]+ in do+ ruleCatchesNot "DL3009" $ Text.unlines dockerFile+ onBuildRuleCatchesNot "DL3009" $ Text.unlines dockerFile++ it "don't warn: cache mount to apt cache and tmpfs mount to lists directory" $+ let dockerFile =+ [ "RUN \\",+ " --mount=type=cache,target=/var/cache/apt \\",+ " --mount=type=tmpfs,target=/var/lib/apt \\",+ " apt-get update"+ ]+ in do+ ruleCatchesNot "DL3009" $ Text.unlines dockerFile+ onBuildRuleCatchesNot "DL3009" $ Text.unlines dockerFile++ it "don't warn: cache mount to apt cache and lists directory" $+ let dockerFile =+ [ "RUN \\",+ " --mount=type=cache,target=/var/cache/apt \\",+ " --mount=type=cache,target=/var/lib/apt \\",+ " apt-get update"+ ]+ in do+ ruleCatchesNot "DL3009" $ Text.unlines dockerFile+ onBuildRuleCatchesNot "DL3009" $ Text.unlines dockerFile+ it "apt no cleanup" $ let dockerFile = [ "FROM scratch",
test/Hadolint/Rule/DL3019Spec.hs view
@@ -10,18 +10,44 @@ let ?config = def describe "DL3019 - Use the --no-cache switch." $ do- it "apk add with --no-cache" $ do- ruleCatches "DL3019" "RUN apk add flex=2.6.4-r1"- onBuildRuleCatches "DL3019" "RUN apk add flex=2.6.4-r1"- it "apk add without --no-cache" $ do- ruleCatchesNot "DL3019" "RUN apk add --no-cache flex=2.6.4-r1"- onBuildRuleCatchesNot "DL3019" "RUN apk add --no-cache flex=2.6.4-r1"- it "don't warn: apk add with BuildKit cache mount" $ do- ruleCatchesNot "DL3019" "RUN --mount=type=cache,target=/var/cache/apk apk add -U curl=7.77.0"- onBuildRuleCatchesNot "DL3019" "RUN --mount=type=cache,target=/var/cache/apk apk add -U curl=7.77.0"- it "don't warn: apk add with BuildKit cache mount in wrong dir and --no-cache" $ do- ruleCatchesNot "DL3019" "RUN --mount=type=cache,target=/var/cache/foo apk add --no-cache -U curl=7.77.0"- onBuildRuleCatchesNot "DL3019" "RUN --mount=type=cache,target=/var/cache/foo apk add --no-cache -U curl=7.77.0"- it "warn: apk add with BuildKit cache mount to wrong dir" $ do- ruleCatches "DL3019" "RUN --mount=type=cache,target=/var/cache/foo apk add -U curl=7.77.0"- onBuildRuleCatches "DL3019" "RUN --mount=type=cache,target=/var/cache/foo apk add -U curl=7.77.0"+ it "warn: apk add without --no-cache" $+ let line = "RUN apk add flex=2.6.4-r1"+ in do+ ruleCatches "DL3019" line+ onBuildRuleCatches "DL3019" line++ it "don't warn: apk add with --no-cache" $+ let line = "RUN apk add --no-cache flex=2.6.4-r1"+ in do+ ruleCatchesNot "DL3019" line+ onBuildRuleCatchesNot "DL3019" line++ it "don't warn: apk add with BuildKit cache mount" $+ let line = "RUN --mount=type=cache,target=/var/cache/apk apk add -U curl=7.77.0"+ in do+ ruleCatchesNot "DL3019" line+ onBuildRuleCatchesNot "DL3019" line++ it "don't warn: apk add with BuildKit tmpfs mount" $+ let line = "RUN --mount=type=tmpfs,target=/var/cache/apk apk add -U curl=7.77.0"+ in do+ ruleCatchesNot "DL3019" line+ onBuildRuleCatchesNot "DL3019" line++ it "don't warn: apk add with BuildKit cache mount in wrong dir and --no-cache" $+ let line = "RUN --mount=type=cache,target=/var/cache/foo apk add --no-cache -U curl=7.77.0"+ in do+ ruleCatchesNot "DL3019" line+ onBuildRuleCatchesNot "DL3019" line++ it "warn: apk add with BuildKit cache mount to wrong dir" $+ let line = "RUN --mount=type=cache,target=/var/cache/foo apk add -U curl=7.77.0"+ in do+ ruleCatches "DL3019" line+ onBuildRuleCatches "DL3019" line++ it "warn: apk add with BuildKit tmpfs mount to wrong dir" $+ let line = "RUN --mount=type=tmpfs,target=/var/cache/foo apk add -U curl=7.77.0"+ in do+ ruleCatches "DL3019" line+ onBuildRuleCatches "DL3019" line
test/Hadolint/Rule/DL3033Spec.hs view
@@ -13,14 +13,29 @@ it "not ok without yum version pinning" $ do ruleCatches "DL3033" "RUN yum install -y tomcat && yum clean all" onBuildRuleCatches "DL3033" "RUN yum install -y tomcat && yum clean all"+ it "ok with yum version pinning" $ do ruleCatchesNot "DL3033" "RUN yum install -y tomcat-9.2 && yum clean all" ruleCatchesNot "DL3033" "RUN bash -c `# not even a yum command`" onBuildRuleCatchesNot "DL3033" "RUN yum install -y tomcat-9.2 && yum clean all" onBuildRuleCatchesNot "DL3033" "RUN bash -c `# not even a yum command`"++ it "ok with yum version pinning - version contains epoch" $ do+ ruleCatchesNot "DL3033" "RUN yum install -y openssl-1:1.1.1k"+ onBuildRuleCatchesNot "DL3033" "RUN yum install -y openssl-1:1.1.1k"++ it "ok with yum version pinning - package name contains `-`" $ do+ ruleCatchesNot "DL3033" "RUN yum install -y rpm-sign-4.16.1.3"+ onBuildRuleCatchesNot "DL3033" "RUN yum install -y rpm-sign-4.16.1.3"++ it "ok with yum version pinning - package name contains `-` and `+`" $ do+ ruleCatchesNot "DL3033" "RUN yum install -y gcc-c++-1.1.1"+ onBuildRuleCatchesNot "DL3033" "RUN yum install -y gcc-c++-1.1.1"+ it "not ok without yum version pinning - modules" $ do ruleCatches "DL3033" "RUN yum module install -y tomcat && yum clean all" onBuildRuleCatches "DL3033" "RUN yum module install -y tomcat && yum clean all"+ it "ok with yum version pinning - modules" $ do ruleCatchesNot "DL3033" "RUN yum module install -y tomcat:9 && yum clean all" ruleCatchesNot "DL3033" "RUN bash -c `# not even a yum command`"
test/Hadolint/Rule/DL3036Spec.hs view
@@ -10,11 +10,25 @@ let ?config = def describe "DL3036 - `zypper clean` missing after zypper use." $ do+ it "not ok without zypper clean" $ do ruleCatches "DL3036" "RUN zypper install -y mariadb=10.4" onBuildRuleCatches "DL3036" "RUN zypper install -y mariadb=10.4"+ it "ok with zypper clean" $ do ruleCatchesNot "DL3036" "RUN zypper install -y mariadb=10.4 && zypper clean" ruleCatchesNot "DL3036" "RUN zypper install -y mariadb=10.4 && zypper cc" onBuildRuleCatchesNot "DL3036" "RUN zypper install -y mariadb=10.4 && zypper clean" onBuildRuleCatchesNot "DL3036" "RUN zypper install -y mariadb=10.4 && zypper cc"++ it "ok when mount type cache is used" $+ let line = "RUN --mount=type=cache,target=/var/cache/zypp zypper install -y mariadb"+ in do+ ruleCatchesNot "DL3036" line+ onBuildRuleCatchesNot "DL3036" line++ it "ok when mount type tmpfs is used" $+ let line = "RUN --mount=type=tmpfs,target=/var/cache/zypp zypper install -y mariadb"+ in do+ ruleCatchesNot "DL3036" line+ onBuildRuleCatchesNot "DL3036" line
test/Hadolint/Rule/DL3040Spec.hs view
@@ -24,7 +24,18 @@ onBuildRuleCatchesNot "DL3040" "RUN microdnf install -y mariadb-10.4 && microdnf clean all" onBuildRuleCatchesNot "DL3040" "RUN notdnf install mariadb" it "ok with rm /var/cache/yum" $ do- ruleCatchesNot "DL3040" "RUN dnf install -y mariadb-10.4 && rm -rf /var/cache/yum/*"- ruleCatchesNot "DL3040" "RUN microdnf install -y mariadb-10.4 && rm -rf /var/cache/yum/*"- onBuildRuleCatchesNot "DL3040" "RUN dnf install -y mariadb-10.4 && rm -rf /var/cache/yum/*"- onBuildRuleCatchesNot "DL3040" "RUN microdnf install -y mariadb-10.4 && rm -rf /var/cache/yum/*"+ ruleCatchesNot "DL3040" "RUN dnf install -y mariadb-10.4 && rm -rf /var/cache/libdnf5"+ ruleCatchesNot "DL3040" "RUN microdnf install -y mariadb-10.4 && rm -rf /var/cache/libdnf5"+ onBuildRuleCatchesNot "DL3040" "RUN dnf install -y mariadb-10.4 && rm -rf /var/cache/libdnf5"+ onBuildRuleCatchesNot "DL3040" "RUN microdnf install -y mariadb-10.4 && rm -rf /var/cache/libdnf5"++ it "ok with cache mount at /var/cache/yum" $ do+ ruleCatchesNot "DL3040" "RUN --mount=type=cache,target=/var/cache/libdnf5 dnf install -y mariadb-10.4"+ ruleCatchesNot "DL3040" "RUN --mount=type=cache,target=/var/cache/libdnf5 microdnf install -y mariadb-10.4"+ onBuildRuleCatchesNot "DL3040" "RUN --mount=type=cache,target=/var/cache/libdnf5 dnf install -y mariadb-10.4"+ onBuildRuleCatchesNot "DL3040" "RUN --mount=type=cache,target=/var/cache/libdnf5 microdnf install -y mariadb-10.4"+ it "ok with tmpfs mount at /var/cache/yum" $ do+ ruleCatchesNot "DL3040" "RUN --mount=type=tmpfs,target=/var/cache/libdnf5 dnf install -y mariadb-10.4"+ ruleCatchesNot "DL3040" "RUN --mount=type=tmpfs,target=/var/cache/libdnf5 microdnf install -y mariadb-10.4"+ onBuildRuleCatchesNot "DL3040" "RUN --mount=type=tmpfs,target=/var/cache/libdnf5 dnf install -y mariadb-10.4"+ onBuildRuleCatchesNot "DL3040" "RUN --mount=type=tmpfs,target=/var/cache/libdnf5 microdnf install -y mariadb-10.4"
test/Hadolint/Rule/DL3041Spec.hs view
@@ -10,32 +10,54 @@ let ?config = def describe "DL3041 - Specify version with `dnf install -y <package>-<version>`" $ do+ it "not ok without dnf version pinning" $ do ruleCatches "DL3041" "RUN dnf install -y tomcat && dnf clean all" ruleCatches "DL3041" "RUN microdnf install -y tomcat && microdnf clean all" onBuildRuleCatches "DL3041" "RUN dnf install -y tomcat && dnf clean all"+ it "not ok without dnf version pinning - package name with `-`" $ do ruleCatches "DL3041" "RUN dnf install -y rpm-sign && dnf clean all" ruleCatches "DL3041" "RUN microdnf install -y rpm-sign && microdnf clean all" onBuildRuleCatches "DL3041" "RUN dnf install -y rpm-sign && dnf clean all"+ it "ok with dnf version pinning" $ do ruleCatchesNot "DL3041" "RUN dnf install -y tomcat-9.0.1 && dnf clean all" ruleCatchesNot "DL3041" "RUN microdnf install -y tomcat-9.0.1 && microdnf clean all"- ruleCatchesNot "DL3041" "RUN notdnf install tomcat" onBuildRuleCatchesNot "DL3041" "RUN dnf install -y tomcat-9.0.1 && dnf clean all" onBuildRuleCatchesNot "DL3041" "RUN microdnf install -y tomcat-9.0.1 && microdnf clean all"++ it "ok with version pinning if command is not `dnf` or `microdnf`" $ do+ ruleCatchesNot "DL3041" "RUN notdnf install openssl-1:1.1.1k"+ onBuildRuleCatchesNot "DL3041" "RUN notdnf install openssl-1:1.1.1k"++ it "ok without version pinning if command is not `dnf` or `microdnf`" $ do+ ruleCatchesNot "DL3041" "RUN notdnf install tomcat" onBuildRuleCatchesNot "DL3041" "RUN notdnf install tomcat"+ it "ok with dnf version pinning - package name with `-`" $ do ruleCatchesNot "DL3041" "RUN dnf install -y rpm-sign-4.16.1.3 && dnf clean all" ruleCatchesNot "DL3041" "RUN microdnf install -y rpm-sign-4.16.1.3 && microdnf clean all"- ruleCatchesNot "DL3041" "RUN notdnf install rpm-sign" onBuildRuleCatchesNot "DL3041" "RUN dnf install -y rpm-sign-4.16.1.3 && dnf clean all" onBuildRuleCatchesNot "DL3041" "RUN microdnf install -y rpm-sign-4.16.1.3 && microdnf clean all"- onBuildRuleCatchesNot "DL3041" "RUN notdnf install rpm-sign"++ it "ok with dnf version pinning - package name with `-` and `+`" $ do+ ruleCatchesNot "DL3041" "RUN dnf install -y gcc-c++-1.1.1"+ ruleCatchesNot "DL3041" "RUN microdnf install -y gcc-c++-1.1.1"+ onBuildRuleCatchesNot "DL3041" "RUN dnf install -y gcc-c++-1.1.1"+ onBuildRuleCatchesNot "DL3041" "RUN microdnf install -y gcc-c++-1.1.1"++ it "ok with dnf version pinning - package version with epoch" $ do+ ruleCatchesNot "DL3041" "RUN dnf install -y openssl-1:1.1.1k"+ ruleCatchesNot "DL3041" "RUN microdnf install -y openssl-1:1.1.1k"+ onBuildRuleCatchesNot "DL3041" "RUN dnf install -y openssl-1:1.1.1k"+ onBuildRuleCatchesNot "DL3041" "RUN microdnf install -y openssl-1:1.1.1k"+ it "not ok without dnf version pinning - modules" $ do ruleCatches "DL3041" "RUN dnf module install -y tomcat && dnf clean all" ruleCatches "DL3041" "RUN microdnf module install -y tomcat && microdnf clean all" onBuildRuleCatches "DL3041" "RUN dnf module install -y tomcat && dnf clean all"+ it "ok with dnf version pinning - modules" $ do ruleCatchesNot "DL3041" "RUN dnf module install -y tomcat:9 && dnf clean all" ruleCatchesNot "DL3041" "RUN microdnf module install -y tomcat:9 && microdnf clean all"
test/Hadolint/Rule/DL3042Spec.hs view
@@ -86,3 +86,15 @@ ] in do ruleCatches "DL3042" dockerFile++ it "ok with cache mount in cache dir" $+ let line = "RUN --mount=type=cache,target=/root/.cache/pip pip install foobar"+ in do+ ruleCatchesNot "DL3042" line+ onBuildRuleCatchesNot "DL3042" line++ it "ok with tmpfs mount in cache dir" $+ let line = "RUN --mount=type=tmpfs,target=/root/.cache/pip pip install foobar"+ in do+ ruleCatchesNot "DL3042" line+ onBuildRuleCatchesNot "DL3042" line
test/Hadolint/Rule/DL3060Spec.hs view
@@ -21,6 +21,30 @@ ruleCatchesNot "DL3060" "RUN yarn install bar && yarn cache clean" onBuildRuleCatchesNot "DL3060" "RUN yarn install bar && yarn cache clean" + it "ok when cache mount is used" $+ let line = "RUN --mount=type=cache,target=/root/.cache/yarn yarn install foobar"+ in do+ ruleCatchesNot "DL3060" line+ onBuildRuleCatchesNot "DL3060" line++ it "ok when tmpfs mount is used" $+ let line = "RUN --mount=type=tmpfs,target=/root/.cache/yarn yarn install foobar"+ in do+ ruleCatchesNot "DL3060" line+ onBuildRuleCatchesNot "DL3060" line++ it "not ok when cache mount is in wrong location" $+ let line = "RUN --mount=type=cache,target=/var/lib/foobar yarn install foobar"+ in do+ ruleCatches "DL3060" line+ onBuildRuleCatches "DL3060" line++ it "not ok when tmpfs mount is in wrong location" $+ let line = "RUN --mount=type=tmpfs,target=/var/lib/foobar yarn install foobar"+ in do+ ruleCatches "DL3060" line+ onBuildRuleCatches "DL3060" line+ it "not ok when yarn install is in last stage w/o yarn clean" $ let dockerFile = Text.unlines
+ test/Hadolint/Rule/DL3062Spec.hs view
@@ -0,0 +1,41 @@+module Hadolint.Rule.DL3062Spec (spec) where++import Data.Default+import Helpers+import Test.Hspec++spec :: SpecWith ()+spec = do+ let ?config = def++ describe "DL3062 - Pin versions in go install." $ do+ describe "go install" $ do+ it "go version not pinned" $ do+ ruleCatches "DL3062" "RUN go install example.com/pkg"+ onBuildRuleCatches "DL3062" "RUN go install example.com/pkg"+ it "go version pinned as latest" $ do+ ruleCatches "DL3062" "RUN go install example.com/pkg@latest"+ onBuildRuleCatches "DL3062" "RUN go install example.com/pkg@latest"+ it "go version pinned" $ do+ ruleCatchesNot "DL3062" "RUN go install example.com/pkg@v1.2.3"+ onBuildRuleCatchesNot "DL3062" "RUN go install example.com/pkg@v1.2.3"+ describe "go get" $ do+ it "go version not pinned" $ do+ ruleCatches "DL3062" "RUN go get example.com/pkg"+ onBuildRuleCatches "DL3062" "RUN go get example.com/pkg"+ it "go version pinned as latest" $ do+ ruleCatches "DL3062" "RUN go get example.com/pkg@latest"+ onBuildRuleCatches "DL3062" "RUN go get example.com/pkg@latest"+ it "go version pinned" $ do+ ruleCatchesNot "DL3062" "RUN go get example.com/pkg@v1.2.3"+ onBuildRuleCatchesNot "DL3062" "RUN go get example.com/pkg@v1.2.3"+ describe "go run" $ do+ it "go version not pinned" $ do+ ruleCatches "DL3062" "RUN go run example.com/pkg"+ onBuildRuleCatches "DL3062" "RUN go run example.com/pkg"+ it "go version pinned as latest" $ do+ ruleCatches "DL3062" "RUN go run example.com/pkg@latest"+ onBuildRuleCatches "DL3062" "RUN go run example.com/pkg@latest"+ it "go version pinned" $ do+ ruleCatchesNot "DL3062" "RUN go run example.com/pkg@v1.2.3"+ onBuildRuleCatchesNot "DL3062" "RUN go run example.com/pkg@v1.2.3"