module Hadolint.Rule.DL3002 (rule) where

import qualified Data.IntMap.Strict as Map
import qualified Data.Sequence as Seq
import qualified Data.Text as Text
import Hadolint.Rule
import Language.Docker.Syntax (Instruction (..), Linenumber)

type StageLine = Linenumber

type UserLine = Linenumber

data Acc
  = Acc StageLine (Map.IntMap UserLine)
  | Empty
  deriving (Int -> Acc -> ShowS
[Acc] -> ShowS
Acc -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Acc] -> ShowS
$cshowList :: [Acc] -> ShowS
show :: Acc -> String
$cshow :: Acc -> String
showsPrec :: Int -> Acc -> ShowS
$cshowsPrec :: Int -> Acc -> ShowS
Show)

rule :: Rule args
rule :: forall args. Rule args
rule = forall a args.
(Int -> State a -> Instruction args -> State a)
-> State a -> (State a -> Failures) -> Rule args
veryCustomRule forall {args}. Int -> State Acc -> Instruction args -> State Acc
check (forall a. a -> State a
emptyState Acc
Empty) State Acc -> Failures
markFailures
  where
    code :: RuleCode
code = RuleCode
"DL3002"
    severity :: DLSeverity
severity = DLSeverity
DLWarningC
    message :: Text
message = Text
"Last USER should not be root"

    check :: Int -> State Acc -> Instruction args -> State Acc
check Int
line State Acc
st (From BaseImage
_) = State Acc
st forall a b. a -> (a -> b) -> b
|> forall a. (a -> a) -> State a -> State a
modify (Int -> Acc -> Acc
rememberStage Int
line)
    check Int
line State Acc
st (User Text
user)
      | Bool -> Bool
not (Text -> Bool
isRoot Text
user) = State Acc
st forall a b. a -> (a -> b) -> b
|> forall a. (a -> a) -> State a -> State a
modify Acc -> Acc
forgetStage
      | Bool
otherwise = State Acc
st forall a b. a -> (a -> b) -> b
|> forall a. (a -> a) -> State a -> State a
modify (Int -> Acc -> Acc
rememberLine Int
line)
    check Int
_ State Acc
st Instruction args
_ = State Acc
st

    isRoot :: Text -> Bool
isRoot Text
user =
      Text -> Text -> Bool
Text.isPrefixOf Text
"root:" Text
user Bool -> Bool -> Bool
|| Text -> Text -> Bool
Text.isPrefixOf Text
"0:" Text
user Bool -> Bool -> Bool
|| Text
user forall a. Eq a => a -> a -> Bool
== Text
"root" Bool -> Bool -> Bool
|| Text
user forall a. Eq a => a -> a -> Bool
== Text
"0"

    markFailures :: State Acc -> Failures
markFailures (State Failures
fails (Acc Int
_ IntMap Int
st)) = forall a b. (a -> b -> a) -> a -> IntMap b -> a
Map.foldl' forall a. Seq a -> a -> Seq a
(Seq.|>) Failures
fails (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Int -> CheckFailure
makeFail IntMap Int
st)
    markFailures State Acc
st = forall a. State a -> Failures
failures State Acc
st
    makeFail :: Int -> CheckFailure
makeFail Int
line = CheckFailure {Int
Text
RuleCode
DLSeverity
line :: Int
message :: Text
severity :: DLSeverity
code :: RuleCode
line :: Int
message :: Text
severity :: DLSeverity
code :: RuleCode
..}
{-# INLINEABLE rule #-}

rememberStage :: StageLine -> Acc -> Acc
rememberStage :: Int -> Acc -> Acc
rememberStage Int
from (Acc Int
_ IntMap Int
m) = Int -> IntMap Int -> Acc
Acc Int
from IntMap Int
m
rememberStage Int
from Acc
Empty = Int -> IntMap Int -> Acc
Acc Int
from forall a. IntMap a
Map.empty

forgetStage :: Acc -> Acc
forgetStage :: Acc -> Acc
forgetStage (Acc Int
from IntMap Int
m) = Int -> IntMap Int -> Acc
Acc Int
from (IntMap Int
m forall a b. a -> (a -> b) -> b
|> forall a. Int -> IntMap a -> IntMap a
Map.delete Int
from)
forgetStage Acc
Empty = Acc
Empty

rememberLine :: StageLine -> Acc -> Acc
rememberLine :: Int -> Acc -> Acc
rememberLine Int
line (Acc Int
from IntMap Int
m) = Int -> IntMap Int -> Acc
Acc Int
from (IntMap Int
m forall a b. a -> (a -> b) -> b
|> forall a. Int -> a -> IntMap a -> IntMap a
Map.insert Int
from Int
line)
rememberLine Int
_ Acc
Empty = Acc
Empty