never executed always true always false
    1 module PureClaw.Security.Command
    2   ( -- * Authorized command type (constructor intentionally NOT exported)
    3     AuthorizedCommand
    4     -- * Command errors
    5   , CommandError (..)
    6     -- * Authorization (pure — no IO)
    7   , authorize
    8     -- * Read-only accessors
    9   , getCommandProgram
   10   , getCommandArgs
   11   ) where
   12 
   13 import Data.Text (Text)
   14 import Data.Text qualified as T
   15 import System.FilePath
   16 
   17 import PureClaw.Core.Types
   18 import PureClaw.Security.Policy
   19 
   20 -- | A command that has been authorized by the security policy.
   21 -- Constructor is intentionally NOT exported — the only way to obtain an
   22 -- 'AuthorizedCommand' is through 'authorize'.
   23 --
   24 -- Note for downstream: 'ShellHandle.execute' is responsible for stripping
   25 -- the subprocess environment (@setEnv (Just [])@) — environment isolation
   26 -- is an execution-time concern, not a policy concern.
   27 newtype AuthorizedCommand = AuthorizedCommand { unAuthorizedCommand :: (FilePath, [Text]) }
   28 
   29 -- | Errors from command authorization.
   30 data CommandError
   31   = CommandNotAllowed Text    -- ^ The command is not in the policy's allowed set
   32   | CommandInAutonomyDeny     -- ^ The policy's autonomy level is 'Deny'
   33   deriving stock (Show, Eq)
   34 
   35 -- | Authorize a command against a security policy. Pure — no IO.
   36 --
   37 -- Checks:
   38 -- 1. Autonomy level is not 'Deny'
   39 -- 2. Command basename is in the policy's allowed command set
   40 authorize :: SecurityPolicy -> FilePath -> [Text] -> Either CommandError AuthorizedCommand
   41 authorize policy cmd args
   42   | _sp_autonomy policy == Deny =
   43       Left CommandInAutonomyDeny
   44   | not (isCommandAllowed policy (CommandName (T.pack (takeFileName cmd)))) =
   45       Left (CommandNotAllowed (T.pack (takeFileName cmd)))
   46   | otherwise =
   47       Right (AuthorizedCommand (cmd, args))
   48 
   49 -- | Get the program path from an authorized command.
   50 getCommandProgram :: AuthorizedCommand -> FilePath
   51 getCommandProgram = fst . unAuthorizedCommand
   52 
   53 -- | Get the arguments from an authorized command.
   54 getCommandArgs :: AuthorizedCommand -> [Text]
   55 getCommandArgs = snd . unAuthorizedCommand