never executed always true always false
1 module PureClaw.Security.Policy
2 ( -- * Policy type
3 SecurityPolicy (..)
4 -- * Constructors and combinators
5 , defaultPolicy
6 , allowCommand
7 , denyCommand
8 , withAutonomy
9 -- * Pure evaluation
10 , isCommandAllowed
11 ) where
12
13 import Data.Set qualified as Set
14
15 import PureClaw.Core.Types
16
17 -- | Security policy governing what an agent can do.
18 -- Policy evaluation is pure — no IO, fully testable with QuickCheck.
19 data SecurityPolicy = SecurityPolicy
20 { _sp_allowedCommands :: AllowList CommandName
21 , _sp_autonomy :: AutonomyLevel
22 }
23 deriving stock (Show, Eq)
24
25 -- | Default policy: deny everything. Start here and open up explicitly.
26 defaultPolicy :: SecurityPolicy
27 defaultPolicy = SecurityPolicy
28 { _sp_allowedCommands = AllowList Set.empty
29 , _sp_autonomy = Deny
30 }
31
32 -- | Add a command to the allowed set.
33 -- If the policy already uses 'AllowAll', this is a no-op.
34 allowCommand :: CommandName -> SecurityPolicy -> SecurityPolicy
35 allowCommand cmd policy =
36 case _sp_allowedCommands policy of
37 AllowAll -> policy
38 AllowList s -> policy { _sp_allowedCommands = AllowList (Set.insert cmd s) }
39
40 -- | Remove a command from the allowed set.
41 -- If the policy uses 'AllowAll', this has no effect (you cannot deny
42 -- individual commands from an AllowAll policy — switch to explicit list first).
43 denyCommand :: CommandName -> SecurityPolicy -> SecurityPolicy
44 denyCommand cmd policy =
45 case _sp_allowedCommands policy of
46 AllowAll -> policy
47 AllowList s -> policy { _sp_allowedCommands = AllowList (Set.delete cmd s) }
48
49 -- | Set the autonomy level on a policy.
50 withAutonomy :: AutonomyLevel -> SecurityPolicy -> SecurityPolicy
51 withAutonomy level policy = policy { _sp_autonomy = level }
52
53 -- | Check whether a command is allowed by this policy. Pure.
54 isCommandAllowed :: SecurityPolicy -> CommandName -> Bool
55 isCommandAllowed policy = isAllowed (_sp_allowedCommands policy)