packages feed

hadolint 1.3.0 → 1.4.0

raw patch · 4 files changed

+171/−12 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Hadolint.Rules: aliasMustBe :: (String -> Bool) -> Instruction -> Bool
+ Hadolint.Rules: allAliasedImages :: Dockerfile -> [(Linenumber, ImageAlias)]
+ Hadolint.Rules: copyFromAnother :: Rule
+ Hadolint.Rules: copyFromExists :: Dockerfile -> [RuleCheck]
+ Hadolint.Rules: fromAliasUnique :: Dockerfile -> [RuleCheck]
+ Hadolint.Rules: instructionRuleLine :: String -> Severity -> String -> (Linenumber -> Instruction -> Bool) -> Rule
+ Hadolint.Rules: instructionRuleState :: String -> Severity -> String -> (state -> Linenumber -> Instruction -> (state, Bool)) -> state -> Rule
+ Hadolint.Rules: previouslyDefinedAliases :: Linenumber -> Dockerfile -> [String]
- Hadolint.Rules: mapInstructions :: Metadata -> (Instruction -> Bool) -> Rule
+ Hadolint.Rules: mapInstructions :: Metadata -> (state -> Linenumber -> Instruction -> (state, Bool)) -> state -> Rule
- Hadolint.Rules: noUntagged :: Rule
+ Hadolint.Rules: noUntagged :: Dockerfile -> [RuleCheck]

Files

README.md view
@@ -122,6 +122,9 @@ | [DL3019](https://github.com/hadolint/hadolint/wiki/DL3019)   | Use the `--no-cache` switch to avoid the need to use `--update` and remove `/var/cache/apk/*` when done installing packages.                        | | [DL3020](https://github.com/hadolint/hadolint/wiki/DL3020)   | Use `COPY` instead of `ADD` for files and folders.                                                                                                  | | [DL3021](https://github.com/hadolint/hadolint/wiki/DL3021)   | `COPY` with more than 2 arguments requires the last argument to end with `/`                                                                                                |+| [DL3022](https://github.com/hadolint/hadolint/wiki/DL3022)   | `COPY --from` should reference a previously defined `FROM` alias                                                                                    |+| [DL3023](https://github.com/hadolint/hadolint/wiki/DL3023)   | `COPY --from` cannot reference its own `FROM` alias                                                                                                 |+| [DL3024](https://github.com/hadolint/hadolint/wiki/DL3024)   | `FROM` aliases (stage names) must be unique                                                                                                         | | [DL4000](https://github.com/hadolint/hadolint/wiki/DL4000)   | MAINTAINER is deprecated.                                                                                                                           | | [DL4001](https://github.com/hadolint/hadolint/wiki/DL4001)   | Either use Wget or Curl but not both.                                                                                                               | | [DL4003](https://github.com/hadolint/hadolint/wiki/DL4003)   | Multiple `CMD` instructions found.                                                                                                                  |
hadolint.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 36374cf97e8c22f6971eb0142316c4cb1e42f9d83fba7b06c6558bb0ce1ccf24+-- hash: 27e3b32590f30c0792f229a5aebdd2796f20c9ef9889029a1f2c6dbd2d986bcf  name:           hadolint-version:        1.3.0+version:        1.4.0 synopsis:       Dockerfile Linter JavaScript API description:    A smarter Dockerfile linter that helps you build best practice Docker images. category:       Development
src/Hadolint/Rules.hs view
@@ -1,6 +1,8 @@ module Hadolint.Rules where -import Data.List (intercalate, isInfixOf, isPrefixOf, isSuffixOf)+import Control.Arrow ((&&&))+import Data.List+       (intercalate, isInfixOf, isPrefixOf, isSuffixOf, mapAccumL) import Data.List.NonEmpty (toList) import Data.List.Split (splitOn, splitOneOf) import Data.Maybe (fromMaybe, isJust, mapMaybe)@@ -41,14 +43,35 @@  -- Apply a function on each instruction and create a check -- for the according line number-mapInstructions :: Metadata -> (Instruction -> Bool) -> Rule-mapInstructions metadata f = map applyRule+mapInstructions ::+       Metadata -> (state -> Linenumber -> Instruction -> (state, Bool)) -> state -> Rule+mapInstructions metadata f initialState dockerfile =+    let (_, results) = mapAccumL applyRule initialState dockerfile+    in results   where-    applyRule (InstructionPos i source linenumber) = RuleCheck metadata source linenumber (f i)+    applyRule state (InstructionPos i source linenumber) =+        let (newState, res) = f state linenumber i+        in (newState, RuleCheck metadata source linenumber res)  instructionRule :: String -> Severity -> String -> (Instruction -> Bool) -> Rule-instructionRule code severity message = mapInstructions $ Metadata code severity message+instructionRule code severity message check =+    instructionRuleLine code severity message (const check) +instructionRuleLine :: String -> Severity -> String -> (Linenumber -> Instruction -> Bool) -> Rule+instructionRuleLine code severity message check =+    instructionRuleState code severity message checkAndDropState ()+  where+    checkAndDropState state line instr = (state, check line instr)++instructionRuleState ::+       String+    -> Severity+    -> String+    -> (state -> Linenumber -> Instruction -> (state, Bool))+    -> state+    -> Rule+instructionRuleState code severity message = mapInstructions (Metadata code severity message)+ dockerfileRule :: String -> Severity -> String -> ([Instruction] -> Bool) -> Rule dockerfileRule code severity message f = rule   where@@ -69,6 +92,9 @@     , invalidCmd     , copyInsteadAdd     , copyEndingSlash+    , copyFromExists+    , copyFromAnother+    , fromAliasUnique     , noRootUser     , noCd     , noSudo@@ -112,6 +138,31 @@ bashCommands :: [String] -> [[String]] bashCommands = splitOneOf [";", "|", "&&"] +allAliasedImages :: Dockerfile -> [(Linenumber, ImageAlias)]+allAliasedImages dockerfile =+    [(l, a) | (l, From (UntaggedImage _ (Just a))) <- instr] +++    [(l, a) | (l, From (TaggedImage _ _ (Just a))) <- instr] +++    [(l, a) | (l, From (DigestedImage _ _ (Just a))) <- instr]+  where+    instr = fmap (lineNumber &&& instruction) dockerfile++-- | Returns a list of all image aliases in FROM instructions that+--  are defined before the given line number.+previouslyDefinedAliases :: Linenumber -> Dockerfile -> [String]+previouslyDefinedAliases line dockerfile =+    [i | (l, ImageAlias i) <- allAliasedImages dockerfile, l < line]++-- | Returns the result of running the check function on the image alias+--   name, if the passed instruction is a FROM instruction with a stage alias.+--   Otherwise, returns True.+aliasMustBe :: (String -> Bool) -> Instruction -> Bool+aliasMustBe predicate fromInstr =+    case fromInstr of+        From (UntaggedImage _ (Just (ImageAlias alias))) -> predicate alias+        From (TaggedImage _ _ (Just (ImageAlias alias))) -> predicate alias+        From (DigestedImage _ _ (Just (ImageAlias alias))) -> predicate alias+        _ -> True+ absoluteWorkdir = instructionRule code severity message check   where     code = "DL3000"@@ -211,14 +262,14 @@     check (Run args) = not $ isInfixOf ["apt-get", "upgrade"] args     check _ = True -noUntagged = instructionRule code severity message check+noUntagged dockerfile = instructionRuleLine code severity message check dockerfile   where     code = "DL3006"     severity = WarningC     message = "Always tag the version of an image explicitly."-    check (From (UntaggedImage image _)) = image == "scratch"-    check (From TaggedImage {}) = True-    check _ = True+    check line (From (UntaggedImage "scratch" _)) = True+    check line (From (UntaggedImage i _)) = i `elem` previouslyDefinedAliases line dockerfile+    check _ _ = True  noLatestTag = instructionRule code severity message check   where@@ -474,6 +525,36 @@         | otherwise = True     check _ = True     endsWithSlash (TargetPath t) = last t == '/' -- it is safe to use last, as the target is never empty++copyFromExists dockerfile = instructionRuleLine code severity message check dockerfile+  where+    code = "DL3022"+    severity = WarningC+    message = "COPY --from should reference a previously defined FROM alias"+    check l (Copy (CopyArgs _ _ _ (CopySource s))) = s `elem` previouslyDefinedAliases l dockerfile+    check _ _ = True++copyFromAnother = instructionRuleState code severity message check Nothing+  where+    code = "DL3023"+    severity = ErrorC+    message = "COPY --from should reference a previously defined FROM alias"+    withState st res = (st, res)+    -- | 'check' returns a tuple (state, check_result)+    --   The state in this case is the FROM instruction where the current instruction we are+    --   inspecting is nested in.+    check _ _ f@(From _) = withState (Just f) True -- Remember the last FROM instruction found+    check st@(Just fromInstr) _ (Copy (CopyArgs _ _ _ (CopySource stageName))) =+        withState st (aliasMustBe (/= stageName) fromInstr) -- Cannot copy from itself!+    check state _ _ = withState state True++fromAliasUnique dockerfile = instructionRuleLine code severity message check dockerfile+  where+    code = "DL3024"+    severity = ErrorC+    message = "FROM aliases (stage names) must be unique"+    check line = aliasMustBe (not . alreadyTaken line)+    alreadyTaken line alias = alias `elem` previouslyDefinedAliases line dockerfile  useShell = instructionRule code severity message check   where
test/Spec.hs view
@@ -23,7 +23,27 @@                     "FROM debian@sha256:\                     \7959ed6f7e35f8b1aaa06d1d8259d4ee25aa85a086d5c125480c333183f9deeb"             it "explicit tagged with name" $-                ruleCatchesNot noLatestTag "FROM debian:jessie AS builder"+              ruleCatchesNot noLatestTag "FROM debian:jessie AS builder"+            it "local aliases are OK to be untagged" $+                let dockerfile =+                        [ "FROM golang:1.9.3-alpine3.7 AS build"+                        , "RUN foo"+                        , "FROM build as unit-test"+                        , "RUN bar"+                        , "FROM alpine:3.7"+                        , "RUN baz"+                        ]+                in ruleCatchesNot noUntagged $ unlines dockerfile+            it "other untagged cases are not ok" $+                let dockerfile =+                        [ "FROM golang:1.9.3-alpine3.7 AS build"+                        , "RUN foo"+                        , "FROM node as unit-test"+                        , "RUN bar"+                        , "FROM alpine:3.7"+                        , "RUN baz"+                        ]+                in ruleCatches noUntagged $ unlines dockerfile         --         describe "no root or sudo rules" $ do             it "sudo" $ ruleCatches noSudo "RUN sudo apt-get update"@@ -266,6 +286,61 @@             it "no warn on 2 args" $ ruleCatchesNot copyEndingSlash "COPY foo bar"             it "warn on 3 args" $ ruleCatches copyEndingSlash "COPY foo bar baz"             it "no warn on 3 args" $ ruleCatchesNot copyEndingSlash "COPY foo bar baz/"+        --+        describe "copy from existing alias" $ do+            it "warn on missing alias" $ ruleCatches copyFromExists "COPY --from=foo bar ."+            it "warn on alias defined after" $+                let dockerfile =+                        [ "FROM scratch"+                        , "COPY --from=build foo ."+                        , "FROM node as build"+                        , "RUN baz"+                        ]+                in ruleCatches copyFromExists $ unlines dockerfile+            it "don't warn on correctly defined aliases" $+                let dockerfile =+                        [ "FROM scratch as build"+                        , "RUN foo"+                        , "FROM node"+                        , "COPY --from=build foo ."+                        , "RUN baz"+                        ]+                in ruleCatchesNot copyFromExists $ unlines dockerfile+        --+        describe "copy from own FROM" $ do+            it "warn on copying from your the same FROM" $+                let dockerfile =+                        [ "FROM node as foo"+                        , "COPY --from=foo bar ."+                        ]+                in ruleCatches copyFromAnother $ unlines dockerfile+            it "don't warn on copying form other sources" $+                let dockerfile =+                        [ "FROM scratch as build"+                        , "RUN foo"+                        , "FROM node as run"+                        , "COPY --from=build foo ."+                        , "RUN baz"+                        ]+                in ruleCatchesNot copyFromAnother $ unlines dockerfile+        --+        describe "Duplicate aliases" $ do+            it "warn on duplicate aliases" $+                let dockerfile =+                        [ "FROM node as foo"+                        , "RUN something"+                        , "FROM scratch as foo"+                        , "RUN something"+                        ]+                in ruleCatches fromAliasUnique $ unlines dockerfile+            it "don't warn on unique aliases" $+                let dockerfile =+                        [ "FROM scratch as build"+                        , "RUN foo"+                        , "FROM node as run"+                        , "RUN baz"+                        ]+                in ruleCatchesNot fromAliasUnique $ unlines dockerfile         --         describe "format error" $             it "display error after line pos" $ do