| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Rules.Types
Description
Data types for the policy rules engine.
The evaluation model lives in Ecluse.Rules; this module holds only the types it operates on.
Synopsis
- data Rule
- data PrecededRule = PrecededRule {
- rulePrecedence :: Int
- prRule :: Rule
- defaultPrecedence :: Rule -> Int
- atDefaultPrecedence :: Rule -> PrecededRule
- defaultAllowIfPublishedBeforePrecedence :: Int
- defaultAllowScopePrecedence :: Int
- defaultDenyInstallTimeExecutionPrecedence :: Int
- newtype EvalContext = EvalContext {}
- data RuleOutcome
- data Decision
- data Transience
- newtype RetryAfter = RetryAfter Int
Rules
A single policy rule.
Rules come in two flavours. Allow rules either allow a package or abstain
(they never deny), so that a later rule still gets the chance to allow. Deny
rules either deny a package or abstain. Selection is by precedence, not list
order — see evalRules and PrecededRule.
Constructors
| AllowScope Scope | Unconditionally allow every package under the given scope. |
| AllowIfPublishedBefore NominalDiffTime | Allow a version only if it was published at least this long ago. Guards against race-to-publish supply-chain attacks where an attacker publishes a malicious version and hopes it is consumed before takedown. |
| DenyInstallTimeExecution | Deny any package version that runs code at install time — npm install scripts, a RubyGems native-extension build, a PyPI sdist build backend — a common arbitrary-code-execution vector. Abstains otherwise. |
Precedence
data PrecededRule Source #
A Rule paired with the integer precedence at which it competes (higher
wins). evalRules selects the highest-precedence non-abstaining
rule; at equal precedence a deny beats an allow, and any remaining tie is broken
by rule identity rather than list order (see evalRules).
Precedence is a field, not an Ord Rule instance: equal precedence between
two rules is legal (it is the deny-over-allow tiebreak), so a total derived Ord
would be non-antisymmetric — unlawful and misleading. This mirrors
Version, whose ordering likewise goes through a function rather
than a derived instance.
Constructors
| PrecededRule | |
Fields
| |
Instances
| Show PrecededRule Source # | |
Defined in Ecluse.Rules.Types Methods showsPrec :: Int -> PrecededRule -> ShowS # show :: PrecededRule -> String # showList :: [PrecededRule] -> ShowS # | |
| Eq PrecededRule Source # | |
Defined in Ecluse.Rules.Types | |
defaultPrecedence :: Rule -> Int Source #
The default precedence for a rule type — used when a policy omits an explicit precedence for a rule.
Every deny type defaults strictly above every allow type, so "any deny
overrides any allow" holds out of the box. The three rule types occupy two
bands: the allow band (AllowIfPublishedBefore <
defaultAllowScopePrecedence), then the deny band
(defaultDenyInstallTimeExecutionPrecedence) strictly above both. An operator may
still elevate a specific allow above a specific deny by giving it a higher
explicit precedence — the per-type defaults set only the out-of-the-box ordering.
atDefaultPrecedence :: Rule -> PrecededRule Source #
Pair a rule with its type's defaultPrecedence.
defaultAllowIfPublishedBeforePrecedence :: Int Source #
Default precedence of AllowIfPublishedBefore: the lowest band, a passive
quarantine that yields to an explicit allow-list and to every deny.
defaultAllowScopePrecedence :: Int Source #
Default precedence of AllowScope: above the passive age quarantine — an
explicit allow-list of a trusted internal scope is a stronger statement than the
time gate — but still below every deny.
defaultDenyInstallTimeExecutionPrecedence :: Int Source #
Default precedence of DenyInstallTimeExecution: the deny band, strictly above
every allow default, so a matching deny overrides any allow out of the box.
Evaluation
newtype EvalContext Source #
Ambient information a rule may need that is not part of the package itself (the wall-clock "now" for age calculations).
Constructors
| EvalContext | |
Instances
| Show EvalContext Source # | |
Defined in Ecluse.Rules.Types Methods showsPrec :: Int -> EvalContext -> ShowS # show :: EvalContext -> String # showList :: [EvalContext] -> ShowS # | |
| Eq EvalContext Source # | |
Defined in Ecluse.Rules.Types | |
data RuleOutcome Source #
The verdict of a single rule against a single package version.
Constructors
| Allow Text | This rule explicitly allows the package (with a human reason). |
| Deny Text | This rule explicitly denies the package (with a human reason). At
equal precedence a |
| Abstain Text | This rule has no opinion; the reason is kept for the audit trail. |
| Unavailable Transience Text | An effectful rule the evaluator needed could not be consulted — its IO
failed, timed out, or its source circuit breaker is open. This is
fail-closed: a version a needed rule could not vet is not admitted just
because the scanner is unreachable. The |
Instances
| Show RuleOutcome Source # | |
Defined in Ecluse.Rules.Types Methods showsPrec :: Int -> RuleOutcome -> ShowS # show :: RuleOutcome -> String # showList :: [RuleOutcome] -> ShowS # | |
| Eq RuleOutcome Source # | |
Defined in Ecluse.Rules.Types | |
The overall decision for a package version against a whole rule set.
The first three arms are the pure tier's; the effectful tier (Ecluse.Rules.Effectful)
adds the last three. An effectful approval/denial carries the deciding rule's
name (Text) rather than a pure Rule, because an effectful rule is not a
member of the pure Rule enumeration — its identity is just the name it logs under.
Constructors
| Approved Rule Text | Allowed by a specific pure rule, with its reason. |
| Denied Rule Text | Denied by a specific pure (deny) rule, with its reason. |
| DeniedByDefault [Text] | No rule allowed it. Deny-by-default; carries every rule's reason so the denial response can explain what was considered. |
| ApprovedEffectful Text Text | Allowed by an effectful rule (named), with its reason. |
| DeniedEffectful Text Text | Denied by an effectful rule (named), with its reason. |
| Undecidable Transience Text | Undecidable: an effectful rule whose verdict could still have changed the
outcome could not be consulted, so the version could not be vetted. This is
fail-closed — it is not admitted (a packument filters it out like a denial;
a concrete artifact surfaces a |
Instances
Unavailability
data Transience Source #
Whether an unavailability is expected to resolve on its own.
This is the single distinction the serve status mapping turns on: a transient cause
(WillResolve) is worth retrying (a 503); a permanent or internal one
(WontResolve) is not, so it must not be dressed up as a retryable 503 (it is a
500). The effectful tier sets it from the nature of the failure: an upstream
outage, rate limit, timeout, or open breaker is transient; an internal or parse
fault is not.
Constructors
| WillResolve (Maybe RetryAfter) | Transient — a retry may succeed (an advisory source briefly down, a
timeout, an open circuit breaker). The optional |
| WontResolve | Not expected to self-heal (an internal or parse error). Retrying cannot
help, so the request is a |
Instances
| Show Transience Source # | |
Defined in Ecluse.Rules.Types Methods showsPrec :: Int -> Transience -> ShowS # show :: Transience -> String # showList :: [Transience] -> ShowS # | |
| Eq Transience Source # | |
Defined in Ecluse.Rules.Types | |
newtype RetryAfter Source #
A Retry-After delay, in whole seconds. A 'newtype' so a raw count of seconds
is never confused with some other integer when it reaches the response header.
Constructors
| RetryAfter Int |
Instances
| Show RetryAfter Source # | |
Defined in Ecluse.Rules.Types Methods showsPrec :: Int -> RetryAfter -> ShowS # show :: RetryAfter -> String # showList :: [RetryAfter] -> ShowS # | |
| Eq RetryAfter Source # | |
Defined in Ecluse.Rules.Types | |
| Ord RetryAfter Source # | |
Defined in Ecluse.Rules.Types Methods compare :: RetryAfter -> RetryAfter -> Ordering # (<) :: RetryAfter -> RetryAfter -> Bool # (<=) :: RetryAfter -> RetryAfter -> Bool # (>) :: RetryAfter -> RetryAfter -> Bool # (>=) :: RetryAfter -> RetryAfter -> Bool # max :: RetryAfter -> RetryAfter -> RetryAfter # min :: RetryAfter -> RetryAfter -> RetryAfter # | |