{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
module Aura.Pkgbuild.Security
( BannedTerm(..), BanCategory(..)
, parsedPB, bannedTerms
, reportExploit
) where
import Aura.Languages
import Aura.Types (Language, Pkgbuild(..))
import Control.Error.Util (hush)
import Data.Generics.Product (typed, types)
import Data.Text.Prettyprint.Doc (Doc)
import Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle)
import Language.Bash.Parse (parse)
import Language.Bash.Syntax
import Language.Bash.Word (Span(..), Word, unquote)
import Lens.Micro (each, (.~), (^..), _Just)
import RIO hiding (Word)
import qualified RIO.Map as M
import qualified RIO.Text as T
data BannedTerm = BannedTerm Text BanCategory deriving (Eq, Ord, Show, Generic)
data BanCategory = Downloading
| ScriptRunning
| Permissions
| InlinedBash
| StrangeBashism
| CleverRedirect
deriving (Eq, Ord, Show)
blacklist :: Map Text BannedTerm
blacklist = M.fromList $ downloading <> running <> permissions
where
downloading = map (\t -> (t, BannedTerm t Downloading)) ["curl", "wget", "rsync", "scp"]
running = map (\t -> (t, BannedTerm t ScriptRunning)) ["sh", "bash", "eval", "zsh", "fish"]
permissions = map (\t -> (t, BannedTerm t Permissions)) ["sudo", "ssh"]
parsedPB :: Pkgbuild -> Maybe List
parsedPB (Pkgbuild pb) = hush . parse "PKGBUILD" . T.unpack $ decodeUtf8Lenient pb
bannedTerms :: List -> [(ShellCommand, BannedTerm)]
bannedTerms = simpleCommands >=> bannedCommand
banned :: Word -> Maybe BannedTerm
banned w = M.lookup (T.pack $ unquote w) blacklist
simpleCommands :: List -> [ShellCommand]
simpleCommands l = l ^.. types @ShellCommand . to p . each
where p sc@(SimpleCommand _ _) = [sc]
p sc = sc ^.. types @List . to simpleCommands . each
bannedCommand :: ShellCommand -> [(ShellCommand, BannedTerm)]
bannedCommand s@(SimpleCommand [] (g:c:_))
| g == [Char 'g', Char 'i', Char 't'] &&
c == [Char 'c', Char 'l', Char 'o', Char 'n', Char 'e'] = [(s, BannedTerm "git" Downloading)]
bannedCommand s@(SimpleCommand [] (c:_)) = maybeToList $ (s,) <$> banned c
bannedCommand s@(SimpleCommand as _) = as ^.. each . typed @RValue . to r . each
where
r rv@(RValue w) = maybeToList ((s,) <$> (banned w & _Just . typed @BanCategory .~ CleverRedirect)) <> q rv
r rv = q rv
q :: RValue -> [(ShellCommand, BannedTerm)]
q rv = rv ^.. types @Word . each . to p . each . to (s,)
p (CommandSubst str) = maybeToList (hush $ parse "CommandSubst" str) >>= simpleCommands >>= map snd . bannedCommand
p (ArithSubst str) = [BannedTerm (T.pack str) StrangeBashism]
p (ProcessSubst _ str) = [BannedTerm (T.pack str) StrangeBashism]
p sp = sp ^.. types @Word . each . to p . each
bannedCommand _ = []
reportExploit :: BannedTerm -> (Language -> Doc AnsiStyle)
reportExploit (BannedTerm t bc) = case bc of
Downloading -> security_2 t
ScriptRunning -> security_3 t
Permissions -> security_4 t
InlinedBash -> security_8 t
StrangeBashism -> security_9 t
CleverRedirect -> security_10 t