module Hadolint.Rule.DL3033 (rule) where
import qualified Data.Text as Text
import Hadolint.Rule
import qualified Hadolint.Shell as Shell
import Language.Docker.Syntax
import Data.Char (isDigit, isAsciiUpper, isAsciiLower)
rule :: Rule Shell.ParsedShell
rule :: Rule ParsedShell
rule = Rule ParsedShell
dl3033 Rule ParsedShell -> Rule ParsedShell -> Rule ParsedShell
forall a. Semigroup a => a -> a -> a
<> Rule ParsedShell -> Rule ParsedShell
forall args. Rule args -> Rule args
onbuild Rule ParsedShell
dl3033
{-# INLINEABLE rule #-}
dl3033 :: Rule Shell.ParsedShell
dl3033 :: Rule ParsedShell
dl3033 = RuleCode
-> DLSeverity
-> Text
-> (Instruction ParsedShell -> Bool)
-> Rule ParsedShell
forall args.
RuleCode
-> DLSeverity -> Text -> (Instruction args -> Bool) -> Rule args
simpleRule RuleCode
code DLSeverity
severity Text
message Instruction ParsedShell -> Bool
check
where
code :: RuleCode
code = RuleCode
"DL3033"
severity :: DLSeverity
severity = DLSeverity
DLWarningC
message :: Text
message = Text
"Specify version with `yum install -y <package>-<version>`."
check :: Instruction ParsedShell -> Bool
check (Run (RunArgs Arguments ParsedShell
args RunFlags
_)) =
(ParsedShell -> Bool) -> Arguments ParsedShell -> Bool
forall a b. (a -> b) -> Arguments a -> b
foldArguments ((Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Text -> Bool
packageVersionFixed ([Text] -> Bool) -> (ParsedShell -> [Text]) -> ParsedShell -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParsedShell -> [Text]
yumPackages) Arguments ParsedShell
args
Bool -> Bool -> Bool
&& (ParsedShell -> Bool) -> Arguments ParsedShell -> Bool
forall a b. (a -> b) -> Arguments a -> b
foldArguments ((Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Text -> Bool
moduleVersionFixed ([Text] -> Bool) -> (ParsedShell -> [Text]) -> ParsedShell -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParsedShell -> [Text]
yumModules) Arguments ParsedShell
args
check Instruction ParsedShell
_ = Bool
True
{-# INLINEABLE dl3033 #-}
yumPackages :: Shell.ParsedShell -> [Text.Text]
yumPackages :: ParsedShell -> [Text]
yumPackages ParsedShell
args =
[ Text
arg
| Command
cmd <- ParsedShell -> [Command]
Shell.presentCommands ParsedShell
args,
Bool -> Bool
not (Text -> [Text] -> Command -> Bool
Shell.cmdHasArgs Text
"yum" [Text
"module"] Command
cmd),
Text
arg <- Command -> [Text]
installFilter Command
cmd
]
packageVersionFixed :: Text.Text -> Bool
packageVersionFixed :: Text -> Bool
packageVersionFixed Text
package
| [Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
parts Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
1 = Bool
False
| Text
".rpm" Text -> Text -> Bool
`Text.isSuffixOf` Text
package = Bool
True
| Bool
otherwise = [Text] -> Bool
isVersionLike ([Text] -> Bool) -> [Text] -> Bool
forall a b. (a -> b) -> a -> b
$ Int -> [Text] -> [Text]
forall a. Int -> [a] -> [a]
drop Int
1 [Text]
parts
where
parts :: [Text]
parts = HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
Text.splitOn Text
"-" Text
package
isVersionLike :: [Text.Text] -> Bool
isVersionLike :: [Text] -> Bool
isVersionLike [Text]
parts =
case [Text]
parts of
[] -> Bool
False
[Text]
_ -> (Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Text -> Bool
partIsValid [Text]
parts Bool -> Bool -> Bool
&& (Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Text -> Bool
partStartsWithDigit [Text]
parts
where
partIsValid :: Text -> Bool
partIsValid Text
part = (Char -> Bool) -> Text -> Bool
Text.all Char -> Bool
isVersionChar Text
part
partStartsWithDigit :: Text -> Bool
partStartsWithDigit Text
part = case Text -> Maybe (Char, Text)
Text.uncons Text
part of
Just (Char
c, Text
_) -> Char -> Bool
isDigit Char
c
Maybe (Char, Text)
Nothing -> Bool
False
isVersionChar :: Char -> Bool
isVersionChar :: Char -> Bool
isVersionChar Char
c =
Char -> Bool
isDigit Char
c
Bool -> Bool -> Bool
|| Char -> Bool
isAsciiUpper Char
c
Bool -> Bool -> Bool
|| Char -> Bool
isAsciiLower Char
c
Bool -> Bool -> Bool
|| Char
c Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'.', Char
'~', Char
'^', Char
'_', Char
':', Char
'+']
yumModules :: Shell.ParsedShell -> [Text.Text]
yumModules :: ParsedShell -> [Text]
yumModules ParsedShell
args =
[ Text
arg
| Command
cmd <- ParsedShell -> [Command]
Shell.presentCommands ParsedShell
args,
Text -> [Text] -> Command -> Bool
Shell.cmdHasArgs Text
"yum" [Text
"module"] Command
cmd,
Text
arg <- Command -> [Text]
installFilter Command
cmd
]
moduleVersionFixed :: Text.Text -> Bool
moduleVersionFixed :: Text -> Bool
moduleVersionFixed = Text -> Text -> Bool
Text.isInfixOf Text
":"
installFilter :: Shell.Command -> [Text.Text]
installFilter :: Command -> [Text]
installFilter Command
cmd =
[ Text
arg
| Text -> [Text] -> Command -> Bool
Shell.cmdHasArgs Text
"yum" [Text
"install"] Command
cmd,
Text
arg <- Command -> [Text]
Shell.getArgsNoFlags Command
cmd,
Text
arg Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"install",
Text
arg Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"module"
]