packages feed

hadolint 2.8.0 → 2.9.0

raw patch · 35 files changed

+639/−279 lines, 35 filesdep ~ShellCheckdep ~language-dockerPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: ShellCheck, language-docker

API changes (from Hackage documentation)

+ Hadolint.Config.Configuration: [disableIgnorePragma] :: Configuration -> Bool
+ Hadolint.Config.Configuration: [partialDisableIgnorePragma] :: PartialConfiguration -> Maybe Bool
+ Hadolint.Pragma: parseIgnorePragma :: Text -> Maybe [Text]
+ Hadolint.Rule.DL1001: rule :: Rule ParsedShell
+ Hadolint.Rule.DL3061: rule :: Rule args
+ Hadolint.Shell: cmdsHaveArgs :: [Text] -> [Text] -> Command -> Bool
- Hadolint.Config.Configuration: Configuration :: Bool -> Bool -> Bool -> OutputFormat -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> Set Registry -> LabelSchema -> Bool -> DLSeverity -> Configuration
+ Hadolint.Config.Configuration: Configuration :: Bool -> Bool -> Bool -> OutputFormat -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> Set Registry -> LabelSchema -> Bool -> Bool -> DLSeverity -> Configuration
- Hadolint.Config.Configuration: PartialConfiguration :: Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe OutputFormat -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> Set Registry -> LabelSchema -> Maybe Bool -> Maybe DLSeverity -> PartialConfiguration
+ Hadolint.Config.Configuration: PartialConfiguration :: Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe OutputFormat -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> [RuleCode] -> Set Registry -> LabelSchema -> Maybe Bool -> Maybe Bool -> Maybe DLSeverity -> PartialConfiguration

Files

README.md view
@@ -124,27 +124,34 @@ ```text hadolint - Dockerfile Linter written in Haskell -Usage: hadolint [-v|--version] [--no-fail] [--no-color] [-c|--config FILENAME]-                [-V|--verbose] [-f|--format ARG] [DOCKERFILE...]-                [--error RULECODE] [--warning RULECODE] [--info RULECODE]-                [--style RULECODE] [--ignore RULECODE]+Usage: hadolint [-v|--version] [-c|--config FILENAME] [DOCKERFILE...]+                [--file-path-in-report FILEPATHINREPORT] [--no-fail]+                [--no-color] [-V|--verbose] [-f|--format ARG] [--error RULECODE]+                [--warning RULECODE] [--info RULECODE] [--style RULECODE]+                [--ignore RULECODE]                 [--trusted-registry REGISTRY (e.g. docker.io)]                 [--require-label LABELSCHEMA (e.g. maintainer:text)]-                [--strict-labels] [-t|--failure-threshold THRESHOLD]+                [--strict-labels] [--disable-ignore-pragma]+                [-t|--failure-threshold THRESHOLD]   Lint Dockerfile for errors and best practices  Available options:   -h,--help                Show this help text   -v,--version             Show version+  -c,--config FILENAME     Path to the configuration file+  --file-path-in-report FILEPATHINREPORT+                           The file path referenced in the generated report.+                           This only applies for the 'checkstyle' format and is+                           useful when running Hadolint with Docker to set the+                           correct file path.   --no-fail                Don't exit with a failure status code when any rule                            is violated   --no-color               Don't colorize output-  -c,--config FILENAME     Path to the configuration file   -V,--verbose             Enables verbose logging of hadolint's output to                            stderr   -f,--format ARG          The output format for the results [tty | json |                            checkstyle | codeclimate | gitlab_codeclimate |-                           codacy] (default: tty)+                           codacy | sonarqube | sarif] (default: tty)   --error RULECODE         Make the rule `RULECODE` have the level `error`   --warning RULECODE       Make the rule `RULECODE` have the level `warning`   --info RULECODE          Make the rule `RULECODE` have the level `info`@@ -160,11 +167,13 @@                            format requirement `format`   --strict-labels          Do not permit labels other than specified in                            `label-schema`+  --disable-ignore-pragma  Disable inline ignore pragmas `# hadolint+                           ignore=DLxxxx`   -t,--failure-threshold THRESHOLD                            Exit with failure code only when rules with a-                           severity above THRESHOLD are violated. Accepted-                           values: [error | warning | info | style | ignore |-                           none] (default: info)+                           severity equal to or above THRESHOLD are violated.+                           Accepted values: [error | warning | info | style |+                           ignore | none] (default: info) ```  ## Configure@@ -203,6 +212,7 @@   info: [string]                        # list of rules   style: [string]                       # list of rules strict-labels: boolean                  # true | false+disable-ignore-pragma: boolean          # true | false trustedRegistries: string | [string]    # registry or list of registries ``` @@ -228,6 +238,7 @@ trustedRegistries:   - docker.io   - my-company.com:5000+  - "*.gcr.io" ```  If you want to override the severity of specific rules, you can do that too:@@ -277,7 +288,7 @@ In addition to config files, Hadolint can be configured with environment variables. ```bash-NO_COLOR=1                               # Truthy value e.g. 1, true or yes+NO_COLOR=1                               # Set or unset. See https://no-color.org HADOLINT_NOFAIL=1                        # Truthy value e.g. 1, true or yes HADOLINT_VERBOSE=1                       # Truthy value e.g. 1, true or yes HADOLINT_FORMAT=json                     # Output format (tty | json | checkstyle | codeclimate | gitlab_codeclimate | codacy | sarif )@@ -288,6 +299,7 @@ HADOLINT_OVERRIDE_STYLE=DL3010,DL3020    # comma separated list of rule codes HADOLINT_IGNORE=DL3010,DL3020            # comma separated list of rule codes HADOLINT_STRICT_LABELS=1                 # Truthy value e.g. 1, true or yes+HADOLINT_DISABLE_IGNORE_PRAGMA=1         # Truthy value e.g. 1, true or yes HADOLINT_TRUSTED_REGISTRIES              # comma separated list of registry urls ``` @@ -417,6 +429,7 @@  | 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. | | [DL3002](https://github.com/hadolint/hadolint/wiki/DL3002)   | Warning          | Last user should not be root.                                                                                                                       |@@ -471,10 +484,11 @@ | [DL3054](https://github.com/hadolint/hadolint/wiki/DL3054)   | Warning          | Label `<label>` is not a valid SPDX license identifier.                                                                                             | | [DL3055](https://github.com/hadolint/hadolint/wiki/DL3055)   | Warning          | Label `<label>` is not a valid git hash.                                                                                                            | | [DL3056](https://github.com/hadolint/hadolint/wiki/DL3056)   | Warning          | Label `<label>` does not conform to semantic versioning.                                                                                            |-| [DL3057](https://github.com/hadolint/hadolint/wiki/DL3057)   | IgnoreC          | `HEALTHCHECK` instruction missing.                                                                                                                  |+| [DL3057](https://github.com/hadolint/hadolint/wiki/DL3057)   | Ignore           | `HEALTHCHECK` instruction missing.                                                                                                                  | | [DL3058](https://github.com/hadolint/hadolint/wiki/DL3058)   | Warning          | Label `<label>` is not a valid email format - must be conform to RFC5322.                                                                           | | [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.                                                                     | | [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.                                                                                                                  |@@ -570,6 +584,39 @@ Dockerfile syntax is fully described in the [Dockerfile reference][]. Just take a look at [Syntax.hs][] in the `language-docker` project to see the AST definition.++### Building against custom libraries++Hadolint uses many libraries to do the dirty work. In particular,+language-docker is used to parse Dockerfiles and produce an AST which then can+be analyzed. To build Hadolint against a custom version of such libraries, do+the following. This example uses language-docker, but it would work with any+other libaray as well.++ 1) In the same directory (e.g. `/home/user/repos`) clone Hadolint and+    language-docker git repositories+```bash+cd /home/user/repos+git clone https://github.com/hadolint/hadolint.git+git clone https://github.com/hadolint/language-docker.git+```++ 2) Make your modifications to language-docker++ 3) In the Hadolint repo, edit the `stack.yaml` file, such that the `extra-deps`+    property points to the other repo+```yaml+[...]+extra-deps:+  - ../language-docker+[...]+```++ 4) Recompile Hadolint and run the tests+```bash+cd /home/user/repos/hadolint+stack test+```  ## Alternatives 
hadolint.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           hadolint-version:        2.8.0+version:        2.9.0 synopsis:       Dockerfile Linter JavaScript API description:    A smarter Dockerfile linter that helps you build best practice Docker images. category:       Development@@ -50,6 +50,7 @@       Hadolint.Pragma       Hadolint.Process       Hadolint.Rule+      Hadolint.Rule.DL1001       Hadolint.Rule.DL3000       Hadolint.Rule.DL3001       Hadolint.Rule.DL3002@@ -108,6 +109,7 @@       Hadolint.Rule.DL3058       Hadolint.Rule.DL3059       Hadolint.Rule.DL3060+      Hadolint.Rule.DL3061       Hadolint.Rule.DL4000       Hadolint.Rule.DL4001       Hadolint.Rule.DL4003@@ -133,12 +135,11 @@       StrictData       TemplateHaskell       TupleSections-      PatternSynonyms   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -optP-Wno-nonportable-include-path -flate-dmd-anal   build-depends:       Cabal     , HsYAML-    , ShellCheck >=0.7.1+    , ShellCheck >=0.8.0     , aeson     , base >=4.8 && <5     , bytestring@@ -153,7 +154,7 @@     , foldl     , gitrev >=1.3.1     , ilist-    , language-docker >=10.3.0 && <11+    , language-docker >=10.4.2 && <11     , megaparsec >=9.0.0     , mtl     , network-uri@@ -187,14 +188,13 @@       StrictData       TemplateHaskell       TupleSections-      PatternSynonyms   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -optP-Wno-nonportable-include-path -flate-dmd-anal -O2 -threaded -rtsopts "-with-rtsopts=-N5 -A4m"   build-depends:       base >=4.8 && <5     , containers     , data-default     , hadolint-    , language-docker >=10.3.0 && <11+    , language-docker >=10.4.2 && <11     , megaparsec >=9.0.0     , optparse-applicative >=0.14.0     , prettyprinter >=1.7.0@@ -216,6 +216,7 @@       Hadolint.Formatter.SarifSpec       Hadolint.Formatter.TTYSpec       Hadolint.PragmaSpec+      Hadolint.Rule.DL1001Spec       Hadolint.Rule.DL3000Spec       Hadolint.Rule.DL3001Spec       Hadolint.Rule.DL3002Spec@@ -274,6 +275,7 @@       Hadolint.Rule.DL3058Spec       Hadolint.Rule.DL3059Spec       Hadolint.Rule.DL3060Spec+      Hadolint.Rule.DL3061Spec       Hadolint.Rule.DL4000Spec       Hadolint.Rule.DL4001Spec       Hadolint.Rule.DL4003Spec@@ -298,14 +300,13 @@       StrictData       TemplateHaskell       TupleSections-      PatternSynonyms       ImplicitParams       OverloadedLists   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -optP-Wno-nonportable-include-path -flate-dmd-anal   build-depends:       HUnit >=1.2     , HsYAML-    , ShellCheck >=0.7.1+    , ShellCheck >=0.8.0     , aeson     , base >=4.8 && <5     , bytestring >=0.10@@ -314,7 +315,7 @@     , foldl     , hadolint     , hspec >=2.8.3-    , language-docker >=10.3.0 && <11+    , language-docker >=10.4.2 && <11     , megaparsec >=9.0.0     , optparse-applicative >=0.14.0     , silently
src/Hadolint/Config/Commandline.hs view
@@ -95,6 +95,7 @@         <*> parseAllowedRegistries         <*> parseLabelSchema         <*> parseStrictlabels+        <*> parseDisableIgnorePragma         <*> parseFailureThreshold      -- All optional flags with boolean value must not have a default value. The@@ -226,6 +227,15 @@             ( long "strict-labels"                 <> help "Do not permit labels other than specified in\                         \ `label-schema`"+            )+        )++    parseDisableIgnorePragma =+      optional+        ( flag' True+            ( long "disable-ignore-pragma"+                <> help "Disable inline ignore pragmas \+                        \ `# hadolint ignore=DLxxxx`"             )         ) 
src/Hadolint/Config/Configuration.hs view
@@ -36,6 +36,7 @@       allowedRegistries :: Set.Set Registry,       labelSchema :: LabelSchema,       strictLabels :: Bool,+      disableIgnorePragma :: Bool,       failureThreshold :: DLSeverity     }   deriving (Eq, Show)@@ -55,6 +56,7 @@       mempty       mempty       False+      False       def  applyPartialConfiguration ::@@ -73,6 +75,7 @@     (allowedRegistries config <> partialAllowedRegistries partial)     (labelSchema config <> partialLabelSchema partial)     (fromMaybe (strictLabels config) (partialStrictLabels partial))+    (fromMaybe (disableIgnorePragma config) (partialDisableIgnorePragma partial))     (fromMaybe (failureThreshold config) (partialFailureThreshold partial))  instance Pretty Configuration where@@ -91,6 +94,7 @@             prettyPrintRulelist "ignore" (ignoreRules c),             "strict labels:" <+> pretty (strictLabels c),             prettyPrintLabelSchema (labelSchema c),+            "disable ignore pragma:" <+> pretty (disableIgnorePragma c),             prettyPrintRegistries (allowedRegistries c)           ]       )@@ -137,14 +141,15 @@       partialAllowedRegistries :: Set.Set Registry,       partialLabelSchema :: LabelSchema,       partialStrictLabels :: Maybe Bool,+      partialDisableIgnorePragma :: Maybe Bool,       partialFailureThreshold :: Maybe DLSeverity     }   deriving (Eq, Show)   instance Semigroup PartialConfiguration where-  PartialConfiguration a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13-    <> PartialConfiguration b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 =+  PartialConfiguration a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14+    <> PartialConfiguration b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 =       PartialConfiguration         (b1 <|> a1)         (b2 <|> a2)@@ -158,7 +163,8 @@         (a10 <> b10)         (a11 <> b11)         (b12 <|> a12)-        (a13 <> b13)+        (b13 <|> a13)+        (a14 <> b14)  instance Monoid PartialConfiguration where   mempty =@@ -175,6 +181,7 @@       mempty       mempty       Nothing+      Nothing       mempty  instance Default PartialConfiguration where@@ -188,9 +195,10 @@     partialFormat <- m .:? "output-format"     override <- m .:? "override" .!= mempty     ignored <- m .:? "ignored" .!= mempty-    trusted <- m .:? "trusted-registries" .!= mempty+    trusted <- m .:? "trustedRegistries" .!= mempty     partialLabelSchema <- m .:? "label-schema" .!= mempty     partialStrictLabels <- m .:? "strict-labels" .!= Nothing+    partialDisableIgnorePragma <- m .:? "disable-ignore-pragma" .!= Nothing     let partialIgnoreRules = coerce (ignored :: [Text])         partialErrorRules = overrideErrorRules override         partialWarningRules = overrideWarningRules override
src/Hadolint/Config/Environment.hs view
@@ -19,7 +19,7 @@ getConfigFromEnvironment =   PartialConfiguration     <$> maybeTruthy "HADOLINT_NOFAIL"-    <*> maybeTruthy "NO_COLOR"+    <*> isSet "NO_COLOR"     <*> maybeTruthy "HADOLINT_VERBOSE"     <*> getFormat     <*> getOverrideList "HADOLINT_OVERRIDE_ERROR"@@ -27,11 +27,19 @@     <*> getOverrideList "HADOLINT_OVERRIDE_INFO"     <*> getOverrideList "HADOLINT_OVERRIDE_STYLE"     <*> getOverrideList "HADOLINT_IGNORE"-    <*> getAllowedSet "HADOLINT_ALLOWED_REGISTRIES"+    <*> getAllowedSet "HADOLINT_TRUSTED_REGISTRIES"     <*> getLabelSchema "HADOLINT_REQUIRE_LABELS"     <*> maybeTruthy "HADOLINT_STRICT_LABELS"+    <*> maybeTruthy "HADOLINT_DISABLE_IGNORE_PRAGMA"     <*> getFailureThreshold ++isSet :: String -> IO (Maybe Bool)+isSet name = do+  e <- lookupEnv name+  case e of+    Just _ -> return $ Just True+    Nothing -> return Nothing  maybeTruthy :: String -> IO (Maybe Bool) maybeTruthy name = do
src/Hadolint/Meta.hs view
@@ -4,7 +4,9 @@   ) where -import qualified Development.GitRev+import Data.Version (showVersion)+import Development.GitRev (gitDirty, gitDescribe)+import Paths_hadolint (version)   getVersion :: String@@ -13,9 +15,10 @@ getShortVersion :: String getShortVersion = v ++ d   where-    version = $(Development.GitRev.gitDescribe)-    dirty = $(Development.GitRev.gitDirty)-    v = case version of-      "UNKONWN" -> "-no-git"-      _ -> version+    gitVersion = $(gitDescribe)+    cabalVersion = showVersion version+    dirty = $(gitDirty)+    v = case gitVersion of+      "UNKNOWN" -> cabalVersion ++ "-no-git"+      _ -> gitVersion     d = if dirty then "-dirty" else ""
src/Hadolint/Pragma.hs view
@@ -1,5 +1,6 @@ module Hadolint.Pragma   ( ignored,+    parseIgnorePragma,     parseShell   )   where@@ -20,13 +21,13 @@ ignored = Foldl.Fold parse mempty id   where     parse acc InstructionPos {instruction = Comment comment, lineNumber = line} =-      case parseComment comment of+      case parseIgnorePragma comment of         Just ignores@(_ : _) -> Map.insert (line + 1) (Set.fromList . fmap RuleCode $ ignores) acc         _ -> acc     parse acc _ = acc -parseComment :: Text -> Maybe [Text]-parseComment =+parseIgnorePragma :: Text -> Maybe [Text]+parseIgnorePragma =   Megaparsec.parseMaybe commentParser  commentParser :: Megaparsec.Parsec Void Text [Text]
src/Hadolint/Process.hs view
@@ -9,6 +9,7 @@ import qualified Data.Set as Set import qualified Data.Text as Text import qualified Hadolint.Pragma+import qualified Hadolint.Rule.DL1001 import qualified Hadolint.Rule.DL3000 import qualified Hadolint.Rule.DL3001 import qualified Hadolint.Rule.DL3002@@ -67,6 +68,7 @@ import qualified Hadolint.Rule.DL3058 import qualified Hadolint.Rule.DL3059 import qualified Hadolint.Rule.DL3060+import qualified Hadolint.Rule.DL3061 import qualified Hadolint.Rule.DL4000 import qualified Hadolint.Rule.DL4001 import qualified Hadolint.Rule.DL4003@@ -89,10 +91,11 @@   where     AnalisisResult {..} = Foldl.fold (analyze config) dockerfile -    shouldKeep CheckFailure {line, code} =-      Just True /= do-        ignoreList <- SMap.lookup line ignored-        return $ code `Set.member` ignoreList+    shouldKeep CheckFailure {line, code}+      | disableIgnorePragma config = True+      | otherwise = Just True /= do+          ignoreList <- SMap.lookup line ignored+          return $ code `Set.member` ignoreList  analyze ::   Configuration ->@@ -119,7 +122,8 @@  failures :: Configuration -> Rule Shell.ParsedShell failures Configuration {allowedRegistries, labelSchema, strictLabels} =-  Hadolint.Rule.DL3000.rule+  Hadolint.Rule.DL1001.rule+    <> Hadolint.Rule.DL3000.rule     <> Hadolint.Rule.DL3001.rule     <> Hadolint.Rule.DL3002.rule     <> Hadolint.Rule.DL3003.rule@@ -177,6 +181,7 @@     <> Hadolint.Rule.DL3058.rule labelSchema     <> Hadolint.Rule.DL3059.rule     <> Hadolint.Rule.DL3060.rule+    <> Hadolint.Rule.DL3061.rule     <> Hadolint.Rule.DL4000.rule     <> Hadolint.Rule.DL4001.rule     <> Hadolint.Rule.DL4003.rule
+ src/Hadolint/Rule/DL1001.hs view
@@ -0,0 +1,19 @@+module Hadolint.Rule.DL1001 (rule) where++import Hadolint.Pragma+import Hadolint.Rule+import Hadolint.Shell (ParsedShell)+import Language.Docker.Syntax++rule :: Rule ParsedShell+rule = simpleRule code severity message check+  where+    code = "DL1001"+    severity = DLIgnoreC+    message = "Please refrain from using inline igore pragmas \+              \ `# hadolint ignore=DLxxxx`."+    check (Comment com) =+      case parseIgnorePragma com of+        Just _ -> False+        _ -> True+    check _ = True
src/Hadolint/Rule/DL3007.hs view
@@ -11,6 +11,7 @@     message =       "Using latest is prone to errors if the image will ever update. Pin the version explicitly \       \to a release tag"+    check (From BaseImage {tag = Just _, digest = Just _}) = True     check (From BaseImage {tag = Just t}) = t /= "latest"     check _ = True {-# INLINEABLE rule #-}
src/Hadolint/Rule/DL3021.hs view
@@ -15,6 +15,8 @@       | length sources > 1 = endsWithSlash t       | otherwise = True     check _ = True--    endsWithSlash (TargetPath t) = not (Text.null t) && Text.last t == '/' {-# INLINEABLE rule #-}++endsWithSlash :: TargetPath -> Bool+endsWithSlash (TargetPath t) =+  not (Text.null t) && (Text.last . dropQuotes) t == '/'
src/Hadolint/Rule/DL3026.hs view
@@ -1,6 +1,7 @@ module Hadolint.Rule.DL3026 (rule) where  import qualified Data.Set as Set+import Data.Text (Text, pack, drop, dropEnd, isSuffixOf, isPrefixOf) import Hadolint.Rule import Language.Docker.Syntax @@ -21,9 +22,20 @@     doCheck st img = Set.member (toImageAlias img) st || Set.null allowed || isAllowed img      toImageAlias = Just . ImageAlias . imageName-    isAllowed Image {registryName = Just registry} = Set.member registry allowed+    isAllowed Image {registryName = Just registry} = isRegistryAllowed (unRegistry registry)     isAllowed Image {registryName = Nothing, imageName} =       imageName == "scratch"-        || Set.member "docker.io" allowed-        || Set.member "hub.docker.com" allowed+        || isRegistryAllowed "docker.io"+        || isRegistryAllowed "hub.docker.com"++    isRegistryAllowed registry = any (\p -> matchRegistry (unRegistry p) registry) allowed++    matchRegistry :: Text -> Text -> Bool+    matchRegistry allow registry | allow == star = True+                                 | isPrefixOf star allow = isSuffixOf (Data.Text.drop 1 allow) registry+                                 | isSuffixOf star allow = isPrefixOf (Data.Text.dropEnd 1 allow) registry+                                 | otherwise = registry == allow+                                  where+                                      star = pack "*"+ {-# INLINEABLE rule #-}
src/Hadolint/Rule/DL3029.hs view
@@ -10,6 +10,6 @@     severity = DLWarningC     message = "Do not use --platform flag with FROM" -    check (From BaseImage {platform = Just p}) = p == ""+    check (From BaseImage {platform = Just p}) = p == "$BUILDPLATFORM"     check _ = True {-# INLINEABLE rule #-}
src/Hadolint/Rule/DL3038.hs view
@@ -15,6 +15,7 @@     check _ = True      forgotDnfYesOption cmd = isDnfInstall cmd && not (hasYesOption cmd)-    isDnfInstall = Shell.cmdHasArgs "dnf" ["install", "groupinstall", "localinstall"]+    isDnfInstall = Shell.cmdsHaveArgs dnfCmds ["install", "groupinstall", "localinstall"]     hasYesOption = Shell.hasAnyFlag ["y", "assumeyes"]+    dnfCmds = ["dnf", "microdnf"] {-# INLINEABLE rule #-}
src/Hadolint/Rule/DL3040.hs view
@@ -11,13 +11,16 @@     severity = DLWarningC     message = "`dnf clean all` missing after dnf command." -    check (Run (RunArgs args _)) =-      foldArguments (Shell.noCommands dnfInstall) args-        || ( foldArguments (Shell.anyCommands dnfInstall) args-               && foldArguments (Shell.anyCommands dnfClean) args-           )+    check (Run (RunArgs args _)) = all (checkMissingClean args) dnfCmds     check _ = True -    dnfInstall = Shell.cmdHasArgs "dnf" ["install"]-    dnfClean = Shell.cmdHasArgs "dnf" ["clean", "all"]+    checkMissingClean args cmdName =+      foldArguments (Shell.noCommands $ dnfInstall cmdName) args+        || ( foldArguments (Shell.anyCommands $ dnfInstall cmdName) args+               && foldArguments (Shell.anyCommands $ dnfClean cmdName) args+           )++    dnfInstall cmdName = Shell.cmdHasArgs cmdName ["install"]+    dnfClean cmdName = Shell.cmdHasArgs cmdName ["clean", "all"]+    dnfCmds = ["dnf", "microdnf"] {-# INLINEABLE rule #-}
src/Hadolint/Rule/DL3041.hs view
@@ -18,11 +18,14 @@     check _ = True {-# INLINEABLE rule #-} +dnfCmds :: [Text.Text]+dnfCmds = ["dnf", "microdnf"]+ dnfPackages :: Shell.ParsedShell -> [Text.Text] dnfPackages args =     [ arg       | cmd <- Shell.presentCommands args,-        not (Shell.cmdHasArgs "dnf" ["module"] cmd),+        not (Shell.cmdsHaveArgs dnfCmds ["module"] cmd),         arg <- installFilter cmd     ] @@ -34,7 +37,7 @@ dnfModules args =   [ arg     | cmd <- Shell.presentCommands args,-      Shell.cmdHasArgs "dnf" ["module"] cmd,+      Shell.cmdsHaveArgs dnfCmds ["module"] cmd,       arg <- installFilter cmd   ] @@ -44,7 +47,7 @@ installFilter :: Shell.Command -> [Text.Text] installFilter cmd =   [ arg-    | Shell.cmdHasArgs "dnf" ["install"] cmd,+    | Shell.cmdsHaveArgs dnfCmds ["install"] cmd,       arg <- Shell.getArgsNoFlags cmd,       arg /= "install",       arg /= "module"
+ src/Hadolint/Rule/DL3061.hs view
@@ -0,0 +1,21 @@+module Hadolint.Rule.DL3061 (rule) where++import Hadolint.Rule+import Language.Docker.Syntax+++rule :: Rule args+rule = customRule check (emptyState False)+  where+    code = "DL3061"+    severity = DLErrorC+    message = "Invalid instruction order. Dockerfile must begin with `FROM`,\+              \ `ARG` or comment."++    check _ st (From _) = st |> replaceWith True+    check _ st (Comment _) = st+    check _ st (Arg _ _) = st+    check line st _+      | state st = st+      | otherwise = st |> addFail CheckFailure {..}+{-# INLINEABLE rule #-}
src/Hadolint/Shell.hs view
@@ -165,6 +165,11 @@   | expectedName /= n = False   | otherwise = not $ null [arg | CmdPart arg _ <- args, arg `elem` expectedArgs] +cmdsHaveArgs :: [Text.Text] -> [Text.Text] -> Command -> Bool+cmdsHaveArgs expectedNames expectedArgs (Command n args _)+  | n `notElem` expectedNames = 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
test/Hadolint/Config/CommandlineSpec.hs view
@@ -300,6 +300,18 @@               mempty { partialStrictLabels = Just True }           ) +    describe "parse disable ignore pragma" $ do+      it "parse --disable-ignore-pragma" $ do+        checkCommandline+          ["--disable-ignore-pragma"]+          ( CommandlineConfig+              False+              Nothing+              []+              Nothing+              mempty { partialDisableIgnorePragma = Just True }+          )+     describe "parse failure thresholds" $ do       it "parse -t warning" $ do         checkCommandline ["-t", "warning"] $ CommandlineConfig
test/Hadolint/Config/ConfigfileSpec.hs view
@@ -117,7 +117,7 @@                           { partialIgnoreRules = ["DL3020", "DL3040", "SC1020"] }      it "parse trusted registries" $ do-      let yaml = [ "trusted-registries:",+      let yaml = [ "trustedRegistries:",                    "  - foobar.com",                    "  - barfoo.com"                  ]@@ -149,6 +149,16 @@       let yaml = [ "strict-labels: false" ]           conf = parseYaml yaml       conf `shouldBe` Right mempty { partialStrictLabels = Just False }++    it "parse disable-ignore-pragma: true" $ do+      let yaml = [ "disable-ignore-pragma: true" ]+          conf = parseYaml yaml+      conf `shouldBe` Right mempty { partialDisableIgnorePragma = Just True }++    it "parse disable-ignore-pragma: false" $ do+      let yaml = [ "disable-ignore-pragma: false" ]+          conf = parseYaml yaml+      conf `shouldBe` Right mempty { partialDisableIgnorePragma = Just False }      it "parse `failure-threshold: warning`" $ do       let yaml = ["failure-threshold: warning"]
test/Hadolint/Config/ConfigurationSpec.hs view
@@ -1,219 +1,43 @@ module Hadolint.Config.ConfigurationSpec where  import Data.Default-import qualified Data.Map as Map-import qualified Data.Set as Set import Hadolint import Test.Hspec  spec :: SpecWith () spec = do   describe "Configuration" $ do-    it "default configuration" $ do-      def-        `shouldBe` Configuration-          False-          False-          False-          TTY-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          False-          DLInfoC-     it "override default configuration with empty config" $ do       applyPartialConfiguration def mempty-        `shouldBe` Configuration-          False-          False-          False-          TTY-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          False-          DLInfoC+        `shouldBe` def      it "override default with specific configuration: no-fail" $ do-      let config =-            PartialConfiguration-              (Just True)-              Nothing-              Nothing-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              Nothing-              mempty+      let config = def { partialNoFail = Just True }       applyPartialConfiguration def config-        `shouldBe` Configuration-          True-          False-          False-          TTY-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          False-          DLInfoC+        `shouldBe` def { noFail = True }      it "override default with specific configuration: no-color" $ do-      let config =-            PartialConfiguration-              Nothing-              (Just True)-              Nothing-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              Nothing-              mempty+      let config = def { partialNoColor = Just True }       applyPartialConfiguration def config-        `shouldBe` Configuration-          False-          True-          False-          TTY-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          False-          DLInfoC+        `shouldBe` def { noColor = True }      it "empty should not override: no-color" $ do-      let config =-            PartialConfiguration-              Nothing-              (Just True)-              Nothing-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              Nothing-              mempty-          config2 =-            PartialConfiguration-              Nothing-              Nothing-              Nothing-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              Nothing-              mempty+      let config = def { partialNoColor = Just True }+          config2 = def       applyPartialConfiguration def (config <> config2)-        `shouldBe` Configuration-          False-          True-          False-          TTY-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          False-          DLInfoC+        `shouldBe` def { noColor = True }      it "override default with specific configuration: verbose" $ do-      let config =-            PartialConfiguration-              Nothing-              Nothing-              (Just True)-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              Nothing-              mempty+      let config = def { partialVerbose = Just True }       applyPartialConfiguration def config-        `shouldBe` Configuration-          False-          False-          True-          TTY-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          False-          DLInfoC+        `shouldBe` def { verbose = True }      it "override default with specific configuration: output-format json" $ do-      let config =-            PartialConfiguration-              Nothing-              Nothing-              Nothing-              (Just Json)-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              mempty-              Nothing-              mempty+      let config = def { partialFormat = Just Json }       applyPartialConfiguration def config-        `shouldBe` Configuration-          False-          False-          False-          Json-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          mempty-          False-          DLInfoC+        `shouldBe` def { format = Json }++    it "override default with specific configuration: disable ignore pragma" $ do+      let config = def { partialDisableIgnorePragma = Just True }+      applyPartialConfiguration def config+        `shouldBe` def { disableIgnorePragma = True }
test/Hadolint/Config/EnvironmentSpec.hs view
@@ -1,7 +1,9 @@ module Hadolint.Config.EnvironmentSpec (spec) where  import Hadolint-import System.Environment+import System.Environment hiding (setEnv)+import System.Environment.Blank (setEnv)+import System.Info import Test.Hspec import qualified Data.Map as Map import qualified Data.Set as Set@@ -33,8 +35,22 @@     withJustEnv "NO_COLOR" "n" $ do       it "parse NO_COLOR=n" $ do         conf <- getConfigFromEnvironment-        conf `shouldBe` mempty { partialNoColor = Just False }+        conf `shouldBe` mempty { partialNoColor = Just True } +    -- The expected value in this test changes based on the OS, but in order to+    -- repect the spec of https://no-color.org, an environment variable set to+    -- the empty string must still count as set, at least on platforms that+    -- support setting an environment variable to the empty string (POSIX).+    withJustEnv "NO_COLOR" "" $ do+      it "parse NO_COLOR set to empty string" $ do+        conf <- getConfigFromEnvironment+        conf `shouldBe` mempty { partialNoColor = expectedValueVarEmptyString }++    withoutAll $ do+      it "parse NO_COLOR unset" $ do+        conf <- getConfigFromEnvironment+        conf `shouldBe` mempty { partialNoColor = Nothing }+     withJustEnv "HADOLINT_VERBOSE" "on" $ do       it "parse HADOLINT_VERBOSE=on" $ do         conf <- getConfigFromEnvironment@@ -105,16 +121,16 @@         conf <- getConfigFromEnvironment         conf `shouldBe` mempty { partialIgnoreRules = ["DL3010", "DL3011"] } -    withJustEnv "HADOLINT_ALLOWED_REGISTRIES" "foobar.com" $ do-      it "parse HADOLINT_ALLOWED_REGISTRIES=foobar.com" $ do+    withJustEnv "HADOLINT_TRUSTED_REGISTRIES" "foobar.com" $ do+      it "parse HADOLINT_TRUSTED_REGISTRIES=foobar.com" $ do         conf <- getConfigFromEnvironment         conf `shouldBe` mempty                           { partialAllowedRegistries =                               Set.fromList ["foobar.com"]                           } -    withJustEnv "HADOLINT_ALLOWED_REGISTRIES" "foobar.com,barfoo.com" $ do-      it "parse HADOLINT_ALLOWED_REGISTIRES=foobar.com,barfoo.com" $ do+    withJustEnv "HADOLINT_TRUSTED_REGISTRIES" "foobar.com,barfoo.com" $ do+      it "parse HADOLINT_TRUSTED_REGISTRIES=foobar.com,barfoo.com" $ do         conf <- getConfigFromEnvironment         conf `shouldBe` mempty                           { partialAllowedRegistries =@@ -145,6 +161,16 @@         conf <- getConfigFromEnvironment         conf `shouldBe` mempty { partialStrictLabels = Just False } +    withJustEnv "HADOLINT_DISABLE_IGNORE_PRAGMA" "yes" $ do+      it "parse HADOLINT_DISABLE_IGNORE_PRAGMA=yes" $ do+        conf <- getConfigFromEnvironment+        conf `shouldBe` mempty { partialDisableIgnorePragma = Just True }++    withJustEnv "HADOLINT_DISABLE_IGNORE_PRAGMA" "false" $ do+      it "parse HADOLINT_DISABLE_IGNORE_PRAGMA=false" $ do+        conf <- getConfigFromEnvironment+        conf `shouldBe` mempty { partialDisableIgnorePragma = Just False }+     withJustEnv "HADOLINT_FAILURE_THRESHOLD" "error" $ do       it "parse HADOLINT_FAILURE_THRESHOLD=error" $ do         conf <- getConfigFromEnvironment@@ -167,7 +193,7 @@ setJustEnv :: String -> String -> IO () setJustEnv name value = do   unsetAll-  setEnv name value+  setEnv name value False  unsetAll :: IO () unsetAll = do@@ -180,7 +206,16 @@   unsetEnv "HADOLINT_OVERRIDE_INFO"   unsetEnv "HADOLINT_OVERRIDE_STYLE"   unsetEnv "HADOLINT_IGNORE"-  unsetEnv "HADOLINT_ALLOWED_REGISTRIES"+  unsetEnv "HADOLINT_TRUSTED_REGISTRIES"   unsetEnv "HADOLINT_REQUIRE_LABELS"   unsetEnv "HADOLINT_STRICT_LABELS"   unsetEnv "HADOLINT_FAILURE_THRESHOLD"++-- On Windows setting an environment variable to the empty string is the same as+-- unsetting it. On POSIX, an environment variable can be set and contain the+-- empty string. In order to generate the correct expected value one must check+-- the operating system type.+expectedValueVarEmptyString :: Maybe Bool+expectedValueVarEmptyString+  | os == "mingw32" = Nothing+  | otherwise = Just True
test/Hadolint/Formatter/SarifSpec.hs view
@@ -1,11 +1,10 @@ module Hadolint.Formatter.SarifSpec (spec) where +import Data.Aeson import Helpers import Hadolint   ( CheckFailure (..),     DLSeverity (..),-    Result(..),-    printResults,     OutputFormat (..),     getShortVersion,   )@@ -15,18 +14,200 @@ spec :: SpecWith () spec = do   let ?noColor = True+  let version = String "2.1.0"+      schema = String "http://json.schemastore.org/sarif-2.1.0"+      defSrcLang = String "dockerfile"+      tool =+        Object+          [ "driver" .=+              Object+                [ "name" .= String "Hadolint",+                  "fullName" .= String "Haskell Dockerfile Linter",+                  "downloadUri" .=+                    String "https://github.com/hadolint/hadolint",+                  "version" .= getShortVersion,+                  "shortDescription" .=+                    Object+                      [ "text" .= String "Dockerfile linter, validate inline\+                                         \ bash, written in Haskell"+                      ]+                ]+          ]    describe "Formatter: Sarif" $ do     it "print empty results" $ do       let checkFails = []-          expectation = "{\"runs\":[{\"defaultSourceLanguage\":\"dockerfi\-                        \le\",\"results\":[],\"tool\":{\"driver\":{\"full\-                        \Name\":\"Haskell Dockerfile Linter\",\"downloadU\-                        \ri\":\"https://github.com/hadolint/hadolint\",\"\-                        \shortDescription\":{\"text\":\"Dockerfile linter\-                        \, validate inline bash, written in Haskell\"},\"\-                        \name\":\"Hadolint\",\"version\":\""-                        ++ getShortVersion-                        ++ "\"}}}],\"version\":\"2.1.0\",\"$schema\-                           \\":\"http://json.schemastore.org/sarif-2.1.0\"}"-      assertFormatter Sarif checkFails expectation+          expectation =+            Object+              [ "version" .= version,+                "$schema" .= schema,+                "runs" .=+                  Array+                    [ Object+                        [ "tool" .= tool,+                          "results" .= Array [],+                          "defaultSourceLanguage" .= defSrcLang+                        ]+                    ]+              ]+      assertFormatterJson Sarif checkFails expectation++    it "print one failed rule" $ do+      let checkFails =+            [ CheckFailure+                { code = "DL2001",+                  severity = DLWarningC,+                  message = "test",+                  line = 1+                }+            ]+          expectation =+            Object+              [ "version" .= version,+                "$schema" .= schema,+                "runs" .=+                  Array+                    [ Object+                        [ "tool" .= tool,+                          "results" .=+                            Array+                              [ Object+                                  [ "level" .= String "warning",+                                    "locations" .=+                                      Array+                                        [ Object+                                            [ "physicalLocation" .=+                                                Object+                                                  [ "artifactLocation" .=+                                                      Object+                                                        [ "uri" .=+                                                            String "<string>"+                                                        ],+                                                    "region" .=+                                                      Object+                                                        [ "startLine" .=+                                                            Number 1.0,+                                                          "endLine" .=+                                                            Number 1.0,+                                                          "startColumn" .=+                                                            Number 1.0,+                                                          "endColumn" .=+                                                            Number 1.0,+                                                          "sourceLanguage" .=+                                                            String "dockerfile"+                                                        ]+                                                  ]+                                            ]+                                        ],+                                    "message" .=+                                      Object+                                        [ "text" .= String "test"+                                        ],+                                    "ruleId" .= String "DL2001"+                                  ]+                              ],+                          "defaultSourceLanguage" .= defSrcLang+                        ]+                    ]+              ]+      assertFormatterJson Sarif checkFails expectation++    it "print multiple failed rules" $ do+      let checkFails =+            [ CheckFailure+                { code = "DL2001",+                  severity = DLWarningC,+                  message = "test",+                  line = 1+                },+              CheckFailure+                { code = "DL2003",+                  severity = DLInfoC,+                  message = "test 2",+                  line = 3+                }+            ]+          expectation =+            Object+              [ "version" .= version,+                "$schema" .= schema,+                "runs" .=+                  Array+                    [ Object+                        [ "tool" .= tool,+                          "results" .=+                            Array+                              [ Object+                                  [ "level" .= String "warning",+                                    "locations" .=+                                      Array+                                        [ Object+                                            [ "physicalLocation" .=+                                                Object+                                                  [ "artifactLocation" .=+                                                      Object+                                                        [ "uri" .=+                                                            String "<string>"+                                                        ],+                                                    "region" .=+                                                      Object+                                                        [ "startLine" .=+                                                            Number 1.0,+                                                          "endLine" .=+                                                            Number 1.0,+                                                          "startColumn" .=+                                                            Number 1.0,+                                                          "endColumn" .=+                                                            Number 1.0,+                                                          "sourceLanguage" .=+                                                            String "dockerfile"+                                                        ]+                                                  ]+                                            ]+                                        ],+                                    "message" .=+                                      Object+                                        [ "text" .= String "test"+                                        ],+                                    "ruleId" .= String "DL2001"+                                  ],+                                Object+                                  [ "level" .= String "note",+                                    "locations" .=+                                      Array+                                        [ Object+                                            [ "physicalLocation" .=+                                                Object+                                                  [ "artifactLocation" .=+                                                      Object+                                                        [ "uri" .=+                                                            String "<string>"+                                                        ],+                                                    "region" .=+                                                      Object+                                                        [ "startLine" .=+                                                            Number 3.0,+                                                          "endLine" .=+                                                            Number 3.0,+                                                          "startColumn" .=+                                                            Number 1.0,+                                                          "endColumn" .=+                                                            Number 1.0,+                                                          "sourceLanguage" .=+                                                            String "dockerfile"+                                                        ]+                                                  ]+                                            ]+                                        ],+                                    "message" .=+                                      Object+                                        [ "text" .= String "test 2"+                                        ],+                                    "ruleId" .= String "DL2003"+                                  ]+                              ],+                          "defaultSourceLanguage" .= defSrcLang+                        ]+                    ]+              ]+      assertFormatterJson Sarif checkFails expectation
+ test/Hadolint/Rule/DL1001Spec.hs view
@@ -0,0 +1,41 @@+module Hadolint.Rule.DL1001Spec (spec) where++import Data.Default+import Data.Text as Text+import Helpers+import Test.Hspec+++spec :: SpecWith ()+spec = do+  let ?config = def++  describe "DL1001 - Please don't use inline ignore pragma." $ do+    it "catches inline ignore pragma" $ do+      let file =+            [ "# hadolint ignore=DL3003",+              "RUN foo bar"+            ]+       in do+        ruleCatches "DL1001" $ Text.unlines file+    it "does not catch other pragma" $ do+      let file =+            [ "# hadolint shell=powershell",+              "RUN foo bar"+            ]+       in do+        ruleCatchesNot "DL1001" $ Text.unlines file+    it "does not catch other comment" $ do+      let file =+            [ "# foobar",+              "RUN foo bar"+            ]+       in do+        ruleCatchesNot "DL1001" $ Text.unlines file+    it "does not catch when inline ignore pragma is absent" $ do+      let file =+            [ "FROM debian",+              "RUN foo bar"+            ]+       in do+        ruleCatchesNot "DL1001" $ Text.unlines file
test/Hadolint/Rule/DL3007Spec.hs view
@@ -20,5 +20,10 @@         "DL3007"         "FROM hub.docker.io/debian@sha256:\         \7959ed6f7e35f8b1aaa06d1d8259d4ee25aa85a086d5c125480c333183f9deeb"+    it "explicit tagged and SHA" $+      ruleCatchesNot+        "DL3007"+        "FROM hub.docker.io/debian:latest@sha256:\+        \7959ed6f7e35f8b1aaa06d1d8259d4ee25aa85a086d5c125480c333183f9deeb"     it "explicit tagged with name" $       ruleCatchesNot "DL3007" "FROM debian:jessie AS builder"
test/Hadolint/Rule/DL3021Spec.hs view
@@ -13,3 +13,5 @@     it "no warn on 2 args" $ ruleCatchesNot "DL3021" "COPY foo bar"     it "warn on 3 args" $ ruleCatches "DL3021" "COPY foo bar baz"     it "no warn on 3 args" $ ruleCatchesNot "DL3021" "COPY foo bar baz/"+    it "warn on 3 args with quotes" $ ruleCatches "DL3021" "COPY foo bar \"baz\""+    it "no warn on 3 args with quotes" $ ruleCatchesNot "DL3021" "COPY foo bar \"baz/\""
test/Hadolint/Rule/DL3025Spec.hs view
@@ -36,3 +36,13 @@               "CMD [ \"foo\", \"bar\" ]"             ]        in ruleCatchesNot "DL3025" $ Text.unlines dockerFile++    -- regression: deal with broken long strings in exec format+    it "don't warn on CMD JSON notation with broken long strings" $+      let dockerFile =+            [ "CMD [ \"/bin/sh\", \"-c\", \\",+              "      \"echo foo && \\",+              "       echo bar\" \\",+              "    ]"+            ]+       in ruleCatchesNot "DL3025" $ Text.unlines dockerFile
test/Hadolint/Rule/DL3026Spec.hs view
@@ -60,3 +60,29 @@       let ?config = def { allowedRegistries = ["random.com"] }        ruleCatchesNot "DL3026" $ Text.unlines dockerFile++    it "warn on non-allowed wildcard registry" $ do+      let dockerFile =+            [ "FROM x.com/debian"+            ]+      let ?config = def { allowedRegistries = ["*.random.com"] }++      ruleCatches "DL3026" $ Text.unlines dockerFile++    it "does not warn on allowed wildcard registries" $ do+      let dockerFile =+            [ "FROM foo.random.com/debian"+            ]+      let ?config = def { allowedRegistries = ["x.com", "*.random.com"] }++      ruleCatchesNot "DL3026" $ Text.unlines dockerFile++    it "does not warn on * registry" $ do+      let dockerFile =+            [ "FROM ubuntu:18.04 AS builder1",+              "FROM zemanlx/ubuntu:18.04 AS builder2",+              "FROM docker.io/zemanlx/ubuntu:18.04 AS builder3"+            ]+      let ?config = def { allowedRegistries = ["*"] }++      ruleCatchesNot "DL3026" $ Text.unlines dockerFile
test/Hadolint/Rule/DL3029Spec.hs view
@@ -12,3 +12,4 @@   describe "DL3029 - Do not use --platform flag with FROM." $ do     it "explicit platform flag" $ ruleCatches "DL3029" "FROM --platform=linux debian:jessie"     it "no platform flag" $ ruleCatchesNot "DL3029" "FROM debian:jessie"+    it "allow platform $BUILDPLATFORM flag" $ ruleCatchesNot "DL3029" "FROM --platform=$BUILDPLATFORM debian:jessie"
test/Hadolint/Rule/DL3038Spec.hs view
@@ -12,9 +12,13 @@   describe "DL3038 - Use the `-y` switch to avoid manual input `dnf install -y <package>`" $ do     it "not ok without dnf non-interactive flag" $ do       ruleCatches "DL3038" "RUN dnf install httpd-2.4.24 && dnf clean all"+      ruleCatches "DL3038" "RUN microdnf install httpd-2.4.24 && microdnf clean all"       onBuildRuleCatches "DL3038" "RUN dnf install httpd-2.4.24 && dnf clean all"+      onBuildRuleCatches "DL3038" "RUN microdnf install httpd-2.4.24 && microdnf clean all"     it "ok with dnf non-interactive flag" $ do       ruleCatchesNot "DL3038" "RUN dnf install -y httpd-2.4.24 && dnf clean all"+      ruleCatchesNot "DL3038" "RUN microdnf install -y httpd-2.4.24 && microdnf clean all"       ruleCatchesNot "DL3038" "RUN notdnf install httpd"       onBuildRuleCatchesNot "DL3038" "RUN dnf install -y httpd-2.4.24 && dnf clean all"+      onBuildRuleCatchesNot "DL3038" "RUN microdnf install -y httpd-2.4.24 && microdnf clean all"       onBuildRuleCatchesNot "DL3038" "RUN notdnf install httpd"
test/Hadolint/Rule/DL3040Spec.hs view
@@ -12,9 +12,14 @@   describe "DL3040 - `dnf clean all` missing after dnf command." $ do     it "no ok without dnf clean all" $ do       ruleCatches "DL3040" "RUN dnf install -y mariadb-10.4"+      ruleCatches "DL3040" "RUN microdnf install -y mariadb-10.4"       onBuildRuleCatches "DL3040" "RUN dnf install -y mariadb-10.4"+      onBuildRuleCatches "DL3040" "RUN dnf install -y mariadb-10.4 && microdnf clean all"+      onBuildRuleCatches "DL3040" "RUN microdnf install -y mariadb-10.4"     it "ok with dnf clean all" $ do       ruleCatchesNot "DL3040" "RUN dnf install -y mariadb-10.4 && dnf clean all"+      ruleCatchesNot "DL3040" "RUN microdnf install -y mariadb-10.4 && microdnf clean all"       ruleCatchesNot "DL3040" "RUN notdnf install mariadb"       onBuildRuleCatchesNot "DL3040" "RUN dnf install -y mariadb-10.4 && dnf clean all"+      onBuildRuleCatchesNot "DL3040" "RUN microdnf install -y mariadb-10.4 && microdnf clean all"       onBuildRuleCatchesNot "DL3040" "RUN notdnf install mariadb"
test/Hadolint/Rule/DL3041Spec.hs view
@@ -12,17 +12,23 @@   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 "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"       onBuildRuleCatchesNot "DL3041" "RUN notdnf install tomcat"     it "not ok without dnf version pinning - moudles" $ 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"       ruleCatchesNot "DL3041" "RUN notdnf module install tomcat"       onBuildRuleCatchesNot "DL3041" "RUN dnf module install -y tomcat:9 && dnf clean all"+      onBuildRuleCatchesNot "DL3041" "RUN microdnf module install -y tomcat:9 && microdnf clean all"       onBuildRuleCatchesNot "DL3041" "RUN notdnf module install tomcat"
+ test/Hadolint/Rule/DL3061Spec.hs view
@@ -0,0 +1,27 @@+module Hadolint.Rule.DL3061Spec (spec) where++import Data.Default+import Helpers+import Test.Hspec+++spec :: SpecWith ()+spec = do+  let ?config = def++  describe "DL3061 - Invalid instruction order. Dockerfile must begin with\+           \ `FROM`, `ARG` or comment." $ do+    it "warn: label before from" $ do+      ruleCatches "DL3061" "LABEL foo=bar\nFROM foo"+      onBuildRuleCatches "DL3061" "LABEL foo=bar\nFROM foo"++    it "don't warn: from before label" $ do+      ruleCatchesNot "DL3061" "FROM foo\nLABEL foo=bar"+      onBuildRuleCatchesNot "DL3061" "FROM foo\nLABEL foo=bar"++    it "don't warn: ARG then FROM then LABEL" $ do+      ruleCatchesNot "DL3061" "ARG A=B\nFROM foo\nLABEL foo=bar"+      onBuildRuleCatchesNot "DL3061" "ARG A=B\nFROM foo\nLABEL foo=bar"++    it "don't warn: FROM then ARG then RUN" $ do+      ruleCatchesNot "DL3061" "FROM foo\nARG A=B\nRUN echo bla"
test/Helpers.hs view
@@ -1,6 +1,8 @@ module Helpers where  import Control.Monad (unless, when)+import qualified Data.ByteString.Lazy.Char8 as BSC+import Data.Aeson hiding (Result) import Hadolint (Configuration (..), OutputFormat (..), printResults) import Hadolint.Formatter.Format (Result (..)) import Hadolint.Formatter.TTY (formatCheck)@@ -136,3 +138,16 @@   (cap, _) <- capture                 (printResults formatter ?noColor (Just "<string>") results)   cap `shouldBe` expectation++assertFormatterJson+  :: (HasCallStack, ?noColor :: Bool) =>+  OutputFormat ->+  [CheckFailure] ->+  Value ->+  Assertion+assertFormatterJson formatter failures expectation = do+  let results =+        NonEmpty.fromList [ Result "<string>" mempty (Seq.fromList failures) ]+  (cap, _) <- capture+                (printResults formatter ?noColor (Just "<string>") results)+  decode (BSC.pack cap) `shouldBe` Just expectation
test/RegressionSpec.hs view
@@ -6,9 +6,11 @@ import Test.HUnit hiding (Label) import Test.Hspec + spec :: SpecWith () spec = do   let ?config = def  -- default implicit parameter running the checkers+   describe "Regression Tests" $ do     it "Comments with backslashes at the end are just comments" $       let dockerFile =@@ -21,12 +23,16 @@               "RUN echo \"kaka\" | sed 's/a/o/g' >> /root/afile"             ]        in ruleCatches "DL4006" $ Text.unlines dockerFile+     it "`ARG` can correctly unset variables" $       let dockerFile =-            [ "ARG A_WITHOUT_EQ",-              "ARG A_WITH_EQ=",-              "RUN echo bla"-            ]+            Text.unlines+              [ "FROM alpine:3",  -- to satisfy DL3061+                "ARG A_WITHOUT_EQ",+                "ARG A_WITH_EQ=",+                "HEALTHCHECK NONE",  -- to satisfy DL3057, even though it is ignored by default+                "RUN echo bla"+              ]        in assertChecks-            (Text.unlines dockerFile)-            (assertBool "No Warnings or Errors should be triggered" . null)+            dockerFile+            (assertBool "No Warnings or Errors should be triggered," . null)