| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Rules
Description
The policy rules engine.
A rule set is evaluated against a single PackageDetails snapshot to produce a
Decision. The model is deny by default; precedence decides: each rule
carries an integer precedence (PrecededRule), and the highest-precedence rule
that does not abstain wins. At equal precedence a deny beats an allow, and the
built-in deny defaults sit strictly above the allow defaults
(defaultPrecedence), so "any deny overrides any allow" holds out of the box.
If every rule abstains the package is denied by default. Because precedence —
not list order — decides, and every remaining equal-precedence tie is broken by
rule identity (ruleName) rather than list position, the credited rule is fully
order-independent; only the order in which abstain reasons are gathered for the
audit trail follows the input list.
The initial rule set is pure (no IO). Effectful rules (CVE lookups, etc.) are a
later tier layered on top of this one; see docs/architecture.md. The rule
data types live in Ecluse.Rules.Types.
Synopsis
- ruleName :: Rule -> Text
- evalRule :: EvalContext -> Rule -> PackageDetails -> RuleOutcome
- evalRules :: EvalContext -> [PrecededRule] -> PackageDetails -> Decision
- evalRulesWithPrecedence :: EvalContext -> [PrecededRule] -> PackageDetails -> (Maybe Int, Decision)
- renderDecision :: PackageDetails -> Decision -> Text
- renderDuration :: NominalDiffTime -> Text
Documentation
ruleName :: Rule -> Text Source #
A stable, human-facing name for a rule (for logs and denial messages).
evalRule :: EvalContext -> Rule -> PackageDetails -> RuleOutcome Source #
Evaluate a single rule against a single package version. Total — a malformed rule or package yields an outcome, never an exception, so hostile metadata cannot crash the gate.
evalRules :: EvalContext -> [PrecededRule] -> PackageDetails -> Decision Source #
Evaluate a package version against a rule set.
Precedence decides. Every rule that does not abstain is a candidate, and the
highest-precedence candidate wins; at equal precedence a Deny beats an
Allow, and any remaining equal-precedence tie (e.g. two allows) is broken by
rule identity — the lexicographically-smallest ruleName is credited — not by
list position. The winner yields Approved or Denied. If no rule takes a
position — every rule abstains, including the empty rule set — the package is
DeniedByDefault, with every abstain reason collected in list order for the
audit trail and denial message. The decision, and the rule credited for it, are
independent of the input rule order; only the gathered abstain-reason order
follows the list.
evalRulesWithPrecedence :: EvalContext -> [PrecededRule] -> PackageDetails -> (Maybe Int, Decision) Source #
Evaluate a rule set, returning the winning rule's precedence alongside the
Decision. The precedence is Nothing for a deny-by-default (no rule took a
position), and for the position-taking rule that won at precedence Just pp.
This is the pure tier as the effectful tier consults it: the winning precedence is
what the effectful tier compares its own rules against to decide which — if any —
could still change the outcome, so a rule ranked below the pure winner is skipped
(see Ecluse.Rules.Effectful). evalRules is this with the precedence dropped.
renderDecision :: PackageDetails -> Decision -> Text Source #
A human-readable summary of a decision, suitable for logs and the denial response body.
renderDuration :: NominalDiffTime -> Text Source #
Render a duration as an approximate, human-friendly string for use in decision messages. Always non-negative.
>>>renderDuration 604800"7 days"
>>>renderDuration 90"1 minute"