diff --git a/hadolint.cabal b/hadolint.cabal
--- a/hadolint.cabal
+++ b/hadolint.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: a3ad665f70a51301b8daa833c740191a21e0230d890d804af8bdbb21c93e5a18
+-- hash: 87e6b48cec5e99eb4cf60b8c1d95138858601ec3516c35a5f36c2ff74e2d7f0e
 
 name:           hadolint
-version:        1.14.0
+version:        1.15.0
 synopsis:       Dockerfile Linter JavaScript API
 description:    A smarter Dockerfile linter that helps you build best practice Docker images.
 category:       Development
@@ -51,8 +51,8 @@
     , containers
     , directory >=1.3.0
     , filepath
-    , language-docker >=7.0.0 && <8
-    , megaparsec >=6.4
+    , language-docker >=8.0.0 && <9
+    , megaparsec >=7.0
     , mtl
     , split >=0.2
     , text
@@ -72,8 +72,8 @@
     , containers
     , gitrev >=1.3.1
     , hadolint
-    , language-docker >=7.0.0 && <8
-    , megaparsec >=6.4
+    , language-docker >=8.0.0 && <9
+    , megaparsec >=7.0
     , optparse-applicative >=0.14.0
     , text
   if !(os(osx))
@@ -96,8 +96,8 @@
     , bytestring >=0.10
     , hadolint
     , hspec
-    , language-docker >=7.0.0 && <8
-    , megaparsec >=6.4
+    , language-docker >=8.0.0 && <9
+    , megaparsec >=7.0
     , split >=0.2
     , text
   default-language: Haskell2010
diff --git a/src/Hadolint/Formatter/Checkstyle.hs b/src/Hadolint/Formatter/Checkstyle.hs
--- a/src/Hadolint/Formatter/Checkstyle.hs
+++ b/src/Hadolint/Formatter/Checkstyle.hs
@@ -11,17 +11,14 @@
 import Data.Char
 import Data.Foldable (toList)
 import Data.List (groupBy)
-import qualified Data.List.NonEmpty as NE
 import Data.Monoid ((<>), mconcat)
 import qualified Data.Text as Text
 import Hadolint.Formatter.Format
 import Hadolint.Rules (Metadata(..), RuleCheck(..))
 import ShellCheck.Interface
+import Text.Megaparsec (Stream)
 import Text.Megaparsec.Error
-       (ParseError, ShowErrorComponent, ShowToken, errorPos,
-        parseErrorTextPretty)
-import Text.Megaparsec.Pos
-       (sourceColumn, sourceLine, sourceName, unPos)
+import Text.Megaparsec.Pos (sourceColumn, sourceLine, sourceName, unPos)
 
 data CheckStyle = CheckStyle
     { file :: String
@@ -32,29 +29,29 @@
     , source :: String
     }
 
-errorToCheckStyle :: (ShowToken t, Ord t, ShowErrorComponent e) => ParseError t e -> CheckStyle
+errorToCheckStyle :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> CheckStyle
 errorToCheckStyle err =
     CheckStyle
-    { file = sourceName pos
-    , line = unPos (sourceLine pos)
-    , column = unPos (sourceColumn pos)
-    , impact = severityText ErrorC
-    , msg = stripNewlines (parseErrorTextPretty err)
-    , source = "DL1000"
-    }
+        { file = sourceName pos
+        , line = unPos (sourceLine pos)
+        , column = unPos (sourceColumn pos)
+        , impact = severityText ErrorC
+        , msg = errorBundlePretty err
+        , source = "DL1000"
+        }
   where
-    pos = NE.head (errorPos err)
+    pos = errorPosition err
 
 ruleToCheckStyle :: RuleCheck -> CheckStyle
 ruleToCheckStyle RuleCheck {..} =
     CheckStyle
-    { file = Text.unpack filename
-    , line = linenumber
-    , column = 1
-    , impact = severityText (severity metadata)
-    , msg = Text.unpack (message metadata)
-    , source = Text.unpack (code metadata)
-    }
+        { file = Text.unpack filename
+        , line = linenumber
+        , column = 1
+        , impact = severityText (severity metadata)
+        , msg = Text.unpack (message metadata)
+        , source = Text.unpack (code metadata)
+        }
 
 toXml :: [CheckStyle] -> Builder.Builder
 toXml checks = wrap fileName (foldMap convert checks)
@@ -85,7 +82,7 @@
             else "&#" ++ show (ord c) ++ ";"
     isOk x = any (\check -> check x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])]
 
-formatResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> Builder.Builder
+formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Builder.Builder
 formatResult (Result errors checks) =
     "<?xml version='1.0' encoding='UTF-8'?><checkstyle version='4.3'>" <> xmlBody <> "</checkstyle>"
   where
@@ -96,5 +93,5 @@
     checkstyleChecks = fmap ruleToCheckStyle checks
     sameFileName CheckStyle {file = f1} CheckStyle {file = f2} = f1 == f2
 
-printResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> IO ()
+printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()
 printResult result = B.putStr (Builder.toLazyByteString (formatResult result))
diff --git a/src/Hadolint/Formatter/Codacy.hs b/src/Hadolint/Formatter/Codacy.hs
--- a/src/Hadolint/Formatter/Codacy.hs
+++ b/src/Hadolint/Formatter/Codacy.hs
@@ -8,17 +8,14 @@
 
 import Data.Aeson hiding (Result)
 import qualified Data.ByteString.Lazy.Char8 as B
-import qualified Data.List.NonEmpty as NE
 import Data.Monoid ((<>))
 import Data.Sequence (Seq)
 import qualified Data.Text as Text
-import Hadolint.Formatter.Format (Result(..))
+import Hadolint.Formatter.Format (Result(..), errorPosition)
 import Hadolint.Rules (Metadata(..), RuleCheck(..))
+import Text.Megaparsec (Stream)
 import Text.Megaparsec.Error
-       (ParseError, ShowErrorComponent, ShowToken, errorPos,
-        parseErrorTextPretty)
-import Text.Megaparsec.Pos
-       (sourceLine, sourceName, unPos)
+import Text.Megaparsec.Pos (sourceLine, sourceName, unPos)
 
 data Issue = Issue
     { filename :: String
@@ -29,42 +26,37 @@
 
 instance ToJSON Issue where
     toJSON Issue {..} =
-        object
-            [ "filename" .= filename
-            , "patternId" .= patternId
-            , "message" .= msg
-            , "line" .= line
-            ]
+        object ["filename" .= filename, "patternId" .= patternId, "message" .= msg, "line" .= line]
 
-errorToIssue :: (ShowToken t, Ord t, ShowErrorComponent e) => ParseError t e -> Issue
+errorToIssue :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> Issue
 errorToIssue err =
     Issue
-    { filename = sourceName pos
-    , patternId = "DL1000"
-    , msg = parseErrorTextPretty err
-    , line = linenumber
-    }
+        { filename = sourceName pos
+        , patternId = "DL1000"
+        , msg = errorBundlePretty err
+        , line = linenumber
+        }
   where
-    pos = NE.head (errorPos err)
+    pos = errorPosition err
     linenumber = unPos (sourceLine pos)
-    
+
 checkToIssue :: RuleCheck -> Issue
 checkToIssue RuleCheck {..} =
     Issue
-    { filename = Text.unpack filename
-    , patternId = Text.unpack (code metadata)
-    , msg = Text.unpack (message metadata)
-    , line = linenumber
-    }
+        { filename = Text.unpack filename
+        , patternId = Text.unpack (code metadata)
+        , msg = Text.unpack (message metadata)
+        , line = linenumber
+        }
 
-formatResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> Seq Issue
+formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Seq Issue
 formatResult (Result errors checks) = allIssues
   where
     allIssues = errorMessages <> checkMessages
     errorMessages = fmap errorToIssue errors
     checkMessages = fmap checkToIssue checks
 
-printResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> IO ()
+printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()
 printResult result = mapM_ output (formatResult result)
   where
     output value = B.putStrLn (encode value)
diff --git a/src/Hadolint/Formatter/Codeclimate.hs b/src/Hadolint/Formatter/Codeclimate.hs
--- a/src/Hadolint/Formatter/Codeclimate.hs
+++ b/src/Hadolint/Formatter/Codeclimate.hs
@@ -9,19 +9,16 @@
 
 import Data.Aeson hiding (Result)
 import qualified Data.ByteString.Lazy as B
-import qualified Data.List.NonEmpty as NE
 import Data.Monoid ((<>))
 import Data.Sequence (Seq)
 import qualified Data.Text as Text
 import GHC.Generics
-import Hadolint.Formatter.Format (Result(..))
+import Hadolint.Formatter.Format (Result(..), errorPosition)
 import Hadolint.Rules (Metadata(..), RuleCheck(..))
 import ShellCheck.Interface
+import Text.Megaparsec (Stream)
 import Text.Megaparsec.Error
-       (ParseError, ShowErrorComponent, ShowToken, errorPos,
-        parseErrorTextPretty)
-import Text.Megaparsec.Pos
-       (sourceColumn, sourceLine, sourceName, unPos)
+import Text.Megaparsec.Pos (sourceColumn, sourceLine, sourceName, unPos)
 
 data Issue = Issue
     { checkName :: String
@@ -59,27 +56,27 @@
             , "severity" .= impact
             ]
 
-errorToIssue :: (ShowToken t, Ord t, ShowErrorComponent e) => ParseError t e -> Issue
+errorToIssue :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> Issue
 errorToIssue err =
     Issue
-    { checkName = "DL1000"
-    , description = parseErrorTextPretty err
-    , location = LocPos (sourceName pos) Pos {..}
-    , impact = severityText ErrorC
-    }
+        { checkName = "DL1000"
+        , description = errorBundlePretty err
+        , location = LocPos (sourceName pos) Pos {..}
+        , impact = severityText ErrorC
+        }
   where
-    pos = NE.head (errorPos err)
+    pos = errorPosition err
     line = unPos (sourceLine pos)
     column = unPos (sourceColumn pos)
 
 checkToIssue :: RuleCheck -> Issue
 checkToIssue RuleCheck {..} =
     Issue
-    { checkName = Text.unpack (code metadata)
-    , description = Text.unpack (message metadata)
-    , location = LocLine (Text.unpack filename) linenumber
-    , impact = severityText (severity metadata)
-    }
+        { checkName = Text.unpack (code metadata)
+        , description = Text.unpack (message metadata)
+        , location = LocLine (Text.unpack filename) linenumber
+        , impact = severityText (severity metadata)
+        }
 
 severityText :: Severity -> String
 severityText severity =
@@ -89,14 +86,14 @@
         InfoC -> "info"
         StyleC -> "minor"
 
-formatResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> Seq Issue
+formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Seq Issue
 formatResult (Result errors checks) = allIssues
   where
     allIssues = errorMessages <> checkMessages
     errorMessages = fmap errorToIssue errors
     checkMessages = fmap checkToIssue checks
 
-printResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> IO ()
+printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()
 printResult result = mapM_ output (formatResult result)
   where
     output value = do
diff --git a/src/Hadolint/Formatter/Format.hs b/src/Hadolint/Formatter/Format.hs
--- a/src/Hadolint/Formatter/Format.hs
+++ b/src/Hadolint/Formatter/Format.hs
@@ -1,35 +1,48 @@
 module Hadolint.Formatter.Format
     ( severityText
     , stripNewlines
+    , errorMessageLine
+    , errorPosition
+    , errorPositionPretty
+    , Text.Megaparsec.Error.errorBundlePretty
     , Result(..)
+    , isEmpty
     , toResult
     ) where
 
 import Data.List (sort)
+import qualified Data.List.NonEmpty as NE
 import Data.Monoid (Monoid)
 import Data.Semigroup
-import Data.Sequence (Seq, fromList, singleton)
+import qualified Data.Sequence as Seq
+import Data.Sequence (Seq)
 import Hadolint.Rules
 import ShellCheck.Interface
-import Text.Megaparsec.Error (ParseError)
+import Text.Megaparsec (Stream(..))
+import Text.Megaparsec.Error
+import Text.Megaparsec.Pos (SourcePos, sourcePosPretty)
 
-data Result t e = Result
-    { errors :: Seq (ParseError t e)
+data Result s e = Result
+    { errors :: Seq (ParseErrorBundle s e)
     , checks :: Seq RuleCheck
-    } deriving (Eq)
+    }
 
-instance Semigroup (Result t e) where
+instance Semigroup (Result s e) where
     (Result e1 c1) <> (Result e2 c2) = Result (e1 <> e2) (c1 <> c2)
 
-instance Monoid (Result t e) where
+instance Monoid (Result s e) where
     mappend = (<>)
     mempty = Result mempty mempty
 
-toResult :: Either (ParseError t e) [RuleCheck] -> Result t e
+isEmpty :: Result s e -> Bool
+isEmpty (Result Seq.Empty Seq.Empty) = True
+isEmpty _ = False
+
+toResult :: Either (ParseErrorBundle s e) [RuleCheck] -> Result s e
 toResult res =
     case res of
-        Left err -> Result (singleton err) mempty
-        Right c -> Result mempty (fromList (sort c))
+        Left err -> Result (Seq.singleton err) mempty
+        Right c -> Result mempty (Seq.fromList (sort c))
 
 severityText :: Severity -> String
 severityText s =
@@ -41,8 +54,19 @@
 
 stripNewlines :: String -> String
 stripNewlines =
-    map
-        (\c ->
+    map (\c ->
              if c == '\n'
                  then ' '
                  else c)
+
+errorMessageLine :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> String
+errorMessageLine err@(ParseErrorBundle e _) =
+    errorPositionPretty err ++ " " ++ parseErrorTextPretty (NE.head e)
+
+errorPositionPretty :: Stream s => ParseErrorBundle s e -> String
+errorPositionPretty err = sourcePosPretty (errorPosition err)
+
+errorPosition :: Stream s => ParseErrorBundle s e -> Text.Megaparsec.Pos.SourcePos
+errorPosition (ParseErrorBundle e s) =
+    let (pos, _, _) = reachOffset (errorOffset (NE.head e)) s
+     in pos
diff --git a/src/Hadolint/Formatter/Json.hs b/src/Hadolint/Formatter/Json.hs
--- a/src/Hadolint/Formatter/Json.hs
+++ b/src/Hadolint/Formatter/Json.hs
@@ -8,22 +8,19 @@
 
 import Data.Aeson hiding (Result)
 import qualified Data.ByteString.Lazy.Char8 as B
-import qualified Data.List.NonEmpty as NE
 import Data.Monoid ((<>))
-import Hadolint.Formatter.Format (Result(..), severityText)
+import Hadolint.Formatter.Format (Result(..), errorPosition, severityText)
 import Hadolint.Rules (Metadata(..), RuleCheck(..))
 import ShellCheck.Interface
+import Text.Megaparsec (Stream)
 import Text.Megaparsec.Error
-       (ParseError, ShowErrorComponent, ShowToken, errorPos,
-        parseErrorTextPretty)
-import Text.Megaparsec.Pos
-       (sourceColumn, sourceLine, sourceName, unPos)
+import Text.Megaparsec.Pos (sourceColumn, sourceLine, sourceName, unPos)
 
-data JsonFormat t e
+data JsonFormat s e
     = JsonCheck RuleCheck
-    | JsonParseError (ParseError t e)
+    | JsonParseError (ParseErrorBundle s e)
 
-instance (ShowToken t, Ord t, ShowErrorComponent e) => ToJSON (JsonFormat t e) where
+instance (Stream s, ShowErrorComponent e) => ToJSON (JsonFormat s e) where
     toJSON (JsonCheck RuleCheck {..}) =
         object
             [ "file" .= filename
@@ -40,17 +37,17 @@
             , "column" .= unPos (sourceColumn pos)
             , "level" .= severityText ErrorC
             , "code" .= ("DL1000" :: String)
-            , "message" .= parseErrorTextPretty err
+            , "message" .= errorBundlePretty err
             ]
       where
-        pos = NE.head (errorPos err)
+        pos = errorPosition err
 
-formatResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> Value
+formatResult :: (Stream s, ShowErrorComponent e) => Result s e -> Value
 formatResult (Result errors checks) = toJSON allMessages
   where
     allMessages = errorMessages <> checkMessages
     errorMessages = fmap JsonParseError errors
     checkMessages = fmap JsonCheck checks
 
-printResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> IO ()
+printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()
 printResult result = B.putStrLn (encode (formatResult result))
diff --git a/src/Hadolint/Formatter/TTY.hs b/src/Hadolint/Formatter/TTY.hs
--- a/src/Hadolint/Formatter/TTY.hs
+++ b/src/Hadolint/Formatter/TTY.hs
@@ -4,28 +4,22 @@
 module Hadolint.Formatter.TTY
     ( printResult
     , formatError
+    , formatChecks
     ) where
 
-import qualified Data.List.NonEmpty as NE
 import Data.Semigroup ((<>))
 import qualified Data.Text as Text
 import Hadolint.Formatter.Format
 import Hadolint.Rules
 import Language.Docker.Syntax
+import Text.Megaparsec (Stream(..))
 import Text.Megaparsec.Error
-       (ParseError, ShowErrorComponent, ShowToken, errorPos,
-        parseErrorTextPretty)
-import Text.Megaparsec.Pos (sourcePosPretty)
 
-formatErrors ::
-       (ShowToken t, Ord t, ShowErrorComponent e, Functor f) => f (ParseError t e) -> f String
+formatErrors :: (Stream s, ShowErrorComponent e, Functor f) => f (ParseErrorBundle s e) -> f String
 formatErrors = fmap formatError
 
-formatError :: (ShowToken t, Ord t, ShowErrorComponent e) => ParseError t e -> String
-formatError err = posPart ++ " " ++ stripNewlines (parseErrorTextPretty err)
-  where
-    pos = NE.head (errorPos err)
-    posPart = sourcePosPretty pos
+formatError :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> String
+formatError err = stripNewlines (errorMessageLine err)
 
 formatChecks :: Functor f => f RuleCheck -> f Text.Text
 formatChecks = fmap formatCheck
@@ -36,7 +30,7 @@
 formatPos :: Filename -> Linenumber -> Text.Text
 formatPos source line = source <> ":" <> Text.pack (show line) <> " "
 
-printResult :: (ShowToken t, Ord t, ShowErrorComponent e) => Result t e -> IO ()
+printResult :: (Stream s, ShowErrorComponent e) => Result s e -> IO ()
 printResult Result {errors, checks} = printErrors >> printChecks
   where
     printErrors = mapM_ putStrLn (formatErrors errors)
diff --git a/src/Hadolint/Lint.hs b/src/Hadolint/Lint.hs
--- a/src/Hadolint/Lint.hs
+++ b/src/Hadolint/Lint.hs
@@ -34,10 +34,10 @@
     | Codacy
     deriving (Show, Eq)
 
-printResultsAndExit :: OutputFormat -> Format.Result Char DockerfileError -> IO ()
+printResultsAndExit :: OutputFormat -> Format.Result Text DockerfileError -> IO ()
 printResultsAndExit format allResults = do
     printResult allResults
-    if allResults /= mempty
+    if not . Format.isEmpty $ allResults
         then exitFailure
         else exitSuccess
   where
@@ -52,7 +52,7 @@
 -- | Performs the process of parsing the dockerfile and analyzing it with all the applicable
 -- rules, depending on the list of ignored rules.
 -- Depending on the preferred printing format, it will output the results to stdout
-lint :: LintOptions -> NonEmpty.NonEmpty String -> IO (Format.Result Char DockerfileError)
+lint :: LintOptions -> NonEmpty.NonEmpty String -> IO (Format.Result Text DockerfileError)
 lint LintOptions {ignoreRules = ignoreList, rulesConfig} dFiles = do
     processedFiles <- mapM (lintDockerfile ignoreList) (NonEmpty.toList dFiles)
     return (results processedFiles)
@@ -69,8 +69,7 @@
         ignoredRules = ignoreFilter ignoreRules
         -- | Returns true if the rule should be ignored
         ignoreFilter :: [IgnoreRule] -> Rules.RuleCheck -> Bool
-        ignoreFilter rules (Rules.RuleCheck (Rules.Metadata code _ _) _ _ _) =
-            code `notElem` rules
+        ignoreFilter rules (Rules.RuleCheck (Rules.Metadata code _ _) _ _ _) = code `notElem` rules
         -- | Support UNIX convention of passing "-" instead of "/dev/stdin"
         parseFilename :: String -> IO (Either Error Dockerfile)
         parseFilename "-" = Docker.parseStdin
diff --git a/src/Hadolint/Rules.hs b/src/Hadolint/Rules.hs
--- a/src/Hadolint/Rules.hs
+++ b/src/Hadolint/Rules.hs
@@ -4,8 +4,7 @@
 module Hadolint.Rules where
 
 import Control.Arrow ((&&&))
-import Data.List
-       (dropWhile, foldl', isInfixOf, isPrefixOf, mapAccumL, nub)
+import Data.List (dropWhile, foldl', isInfixOf, isPrefixOf, mapAccumL, nub)
 import Data.List.NonEmpty (toList)
 import qualified Hadolint.Shell as Shell
 import Language.Docker.Syntax
@@ -88,7 +87,7 @@
 mapInstructions :: CheckerWithState state -> state -> Rule
 mapInstructions f initialState dockerfile =
     let (_, results) = mapAccumL applyRule initialState dockerfile
-    in concat results
+     in concat results
   where
     applyRule state (InstructionPos (OnBuild i) source linenumber) =
         applyWithState state source linenumber i -- All rules applying to instructions also apply to ONBUILD,
@@ -98,7 +97,7 @@
         applyWithState state source linenumber i -- Otherwise, normal instructions are not unwrapped
     applyWithState state source linenumber instruction =
         let (newState, res) = f state linenumber instruction
-        in (newState, [RuleCheck m source linenumber False | m <- res])
+         in (newState, [RuleCheck m source linenumber False | m <- res])
 
 instructionRule ::
        Text.Text -> Severity -> Text.Text -> (Instruction Shell.ParsedShell -> Bool) -> Rule
@@ -118,9 +117,9 @@
     meta = Metadata code severity message
     constMetadataCheck st ln instr =
         let (newSt, success) = f st ln instr
-        in if not success
-               then (newSt, [meta])
-               else (newSt, [])
+         in if not success
+                then (newSt, [meta])
+                else (newSt, [])
 
 withState :: a -> b -> (a, b)
 withState st res = (st, res)
@@ -234,17 +233,14 @@
 aliasMustBe :: (Text.Text -> Bool) -> Instruction a -> Bool
 aliasMustBe predicate fromInstr =
     case fromInstr of
-        From (UntaggedImage _ _ (Just (ImageAlias alias))) -> predicate alias
-        From (TaggedImage _ _ _ (Just (ImageAlias alias))) -> predicate alias
+        From BaseImage {alias = Just (ImageAlias as)} -> predicate as
         _ -> True
 
 fromName :: BaseImage -> Text.Text
-fromName (UntaggedImage Image {imageName} _ _) = imageName
-fromName (TaggedImage Image {imageName} _ _ _) = imageName
+fromName BaseImage {image = Image {imageName}} = imageName
 
 fromAlias :: BaseImage -> Maybe ImageAlias
-fromAlias (UntaggedImage _ _ alias) = alias
-fromAlias (TaggedImage _ _ _ alias) = alias
+fromAlias BaseImage {alias} = alias
 
 -------------
 --  RULES  --
@@ -328,7 +324,7 @@
     detectDoubleUsage state args =
         let newArgs = extractCommands args
             newState = Set.union state newArgs
-        in withState newState (Set.size newState < 2)
+         in withState newState (Set.size newState < 2)
     extractCommands args =
         Set.fromList [w | w <- Shell.findCommandNames args, w == "curl" || w == "wget"]
 
@@ -365,7 +361,7 @@
     rootStages =
         let indexedInstructions = map (instruction &&& lineNumber) dockerfile
             (_, usersMap) = foldl' buildMap (Nothing, Map.empty) indexedInstructions
-        in usersMap
+         in usersMap
     --
     --
     buildMap (_, st) (From from, _) = (Just from, st) -- Remember the FROM we are currently inspecting
@@ -414,8 +410,9 @@
     code = "DL3006"
     severity = WarningC
     message = "Always tag the version of an image explicitly"
-    check _ (From (UntaggedImage (Image _ "scratch") _ _)) = True
-    check line (From (UntaggedImage (Image _ i) _ _)) =
+    check _ (From BaseImage {image = (Image _ "scratch")}) = True
+    check _ (From BaseImage {digest = Just _}) = True
+    check line (From BaseImage {image = (Image _ i), tag = Nothing}) =
         i `elem` previouslyDefinedAliases line dockerfile
     check _ _ = True
 
@@ -427,7 +424,7 @@
     message =
         "Using latest is prone to errors if the image will ever update. Pin the version explicitly \
         \to a release tag"
-    check (From (TaggedImage _ tag _ _)) = tag /= "latest"
+    check (From BaseImage {tag = Just t}) = t /= "latest"
     check _ = True
 
 aptGetVersionPinned :: Rule
@@ -566,7 +563,8 @@
         \<package>==<version>`"
     check (Run args) = argumentsRule (Shell.noCommands forgotToPinVersion) args
     check _ = True
-    forgotToPinVersion cmd = isPipInstall cmd && not (hasBuildConstraint cmd) && not (all versionFixed (packages cmd))
+    forgotToPinVersion cmd =
+        isPipInstall cmd && not (hasBuildConstraint cmd) && not (all versionFixed (packages cmd))
     -- Check if the command is a pip* install command, and that specific pacakges are being listed
     isPipInstall cmd =
         case Shell.getCommandName cmd of
@@ -607,7 +605,8 @@
         \<package>@<version>`"
     check (Run args) = argumentsRule (Shell.noCommands forgotToPinVersion) args
     check _ = True
-    forgotToPinVersion cmd = isNpmInstall cmd &&  installIsFirst cmd && not (all versionFixed (packages cmd))
+    forgotToPinVersion cmd =
+        isNpmInstall cmd && installIsFirst cmd && not (all versionFixed (packages cmd))
     isNpmInstall = Shell.cmdHasArgs "npm" ["install"]
     installIsFirst cmd = ["install"] `isPrefixOf` Shell.getArgsNoFlags cmd
     packages cmd = stripInstallPrefix (Shell.getArgsNoFlags cmd)
@@ -637,8 +636,8 @@
     isAptGetInstall = Shell.cmdHasArgs "apt-get" ["install"]
     hasYesOption cmd =
         "y" `elem` allFlags cmd ||
-        "yes" `elem` allFlags cmd || length (filter (== "q") (allFlags cmd)) > 1 ||
-        "assume-yes" `elem` allFlags cmd
+        "yes" `elem` allFlags cmd ||
+        length (filter (== "q") (allFlags cmd)) > 1 || "assume-yes" `elem` allFlags cmd
     allFlags cmd = snd <$> Shell.getAllFlags cmd
 
 aptGetNoRecommends :: Rule
@@ -760,11 +759,11 @@
     message = "Set the SHELL option -o pipefail before RUN with a pipe in it"
     check _ _ From {} = (False, True) -- Reset the state each time we find a new FROM
     check _ _ (Shell args)
-      | argumentsRule isPowerShell args = (True, True)
-      | otherwise = (argumentsRule hasPipefailOption args, True)
+        | argumentsRule isPowerShell args = (True, True)
+        | otherwise = (argumentsRule hasPipefailOption args, True)
     check False _ (Run args) = (False, argumentsRule notHasPipes args)
     check st _ _ = (st, True)
-    isPowerShell (Shell.ParsedShell orig _)= "pwsh" `Text.isPrefixOf` orig
+    isPowerShell (Shell.ParsedShell orig _) = "pwsh" `Text.isPrefixOf` orig
     notHasPipes script = not (Shell.hasPipes script)
     hasPipefailOption script =
         not $
@@ -784,8 +783,7 @@
     code = "DL3026"
     severity = ErrorC
     message = "Use only an allowed registry in the FROM image"
-    check st _ (From (UntaggedImage img _ alias)) = withState (Set.insert alias st) (doCheck st img)
-    check st _ (From (TaggedImage img _ _ alias)) = withState (Set.insert alias st) (doCheck st img)
+    check st _ (From BaseImage {image, alias}) = withState (Set.insert alias st) (doCheck st image)
     check st _ _ = (st, True)
     -- |Transforms an Image into a Maybe ImageAlias by using the Image name
     toImageAlias = Just . ImageAlias . imageName
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -2,8 +2,9 @@
 {-# LANGUAGE OverloadedLists #-}
 import Test.HUnit hiding (Label)
 import Test.Hspec
+import Control.Monad (when, unless)
 
-import Hadolint.Formatter.TTY (formatError)
+import Hadolint.Formatter.TTY (formatError, formatChecks)
 import Hadolint.Rules
 
 import Language.Docker.Parser
@@ -26,6 +27,10 @@
                     \7959ed6f7e35f8b1aaa06d1d8259d4ee25aa85a086d5c125480c333183f9deeb"
             it "explicit tagged with name" $
                 ruleCatchesNot noLatestTag "FROM debian:jessie AS builder"
+            it "untagged digest is not an error" $
+                ruleCatchesNot noUntagged "FROM ruby@sha256:f1dbca0f5dbc9"
+            it "untagged digest is not an error" $
+                ruleCatchesNot noUntagged "FROM ruby:2"
             it "local aliases are OK to be untagged" $
                 let dockerFile =
                         [ "FROM golang:1.9.3-alpine3.7 AS build"
@@ -1009,24 +1014,30 @@
 ruleCatches rule s = assertChecks rule s f
   where
     f checks = do
-      assertEqual "No check for rule found" 1 $ length checks
+      when (length checks /= 1) $
+        assertFailure $ "Too many errors found: \n" ++ (Text.unpack . Text.unlines . formatChecks $ checks)
       assertBool "Incorrect line number for result" $ null [c | c <- checks, linenumber c <= 0]
 
 onBuildRuleCatches :: HasCallStack => Rule -> Text.Text -> Assertion
 onBuildRuleCatches rule s = assertOnBuildChecks rule s f
   where
     f checks = do
-      assertEqual "No check for rule found" 1 $ length checks
+      when (length checks /= 1) $
+        assertFailure (Text.unpack . Text.unlines . formatChecks $ checks)
       assertBool "Incorrect line number for result" $ null [c | c <- checks, linenumber c <= 0]
 
 ruleCatchesNot :: HasCallStack => Rule -> Text.Text -> Assertion
 ruleCatchesNot rule s = assertChecks rule s f
   where
-    f checks = assertEqual ("Error: " ++ errorMessages checks) 0 $ length checks
-    errorMessages checks = Text.unpack . Text.unlines $ map (message . metadata) checks
+    f checks =
+      unless (null checks) $
+        assertFailure $ "Not expecting the following errors: \n" ++
+                        (Text.unpack . Text.unlines . formatChecks $ checks)
 
 onBuildRuleCatchesNot :: HasCallStack => Rule -> Text.Text -> Assertion
 onBuildRuleCatchesNot rule s = assertOnBuildChecks rule s f
   where
-    f checks = assertEqual ("Error: " ++ errorMessages checks) 0 $ length checks
-    errorMessages checks = Text.unpack . Text.unlines $ map (message . metadata) checks
+    f checks =
+      unless (null checks) $
+        assertFailure $ "Not expecting the following errors: \n" ++
+                        (Text.unpack . Text.unlines . formatChecks $ checks)
