ecluse
Safe HaskellNone
LanguageGHC2021

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

Rules

data Rule Source #

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.

Instances

Instances details
Show Rule Source # 
Instance details

Defined in Ecluse.Rules.Types

Methods

showsPrec :: Int -> Rule -> ShowS #

show :: Rule -> String #

showList :: [Rule] -> ShowS #

Eq Rule Source # 
Instance details

Defined in Ecluse.Rules.Types

Methods

(==) :: Rule -> Rule -> Bool #

(/=) :: Rule -> Rule -> Bool #

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

Instances details
Show PrecededRule Source # 
Instance details

Defined in Ecluse.Rules.Types

Eq PrecededRule Source # 
Instance details

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.

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 

Fields

Instances

Instances details
Show EvalContext Source # 
Instance details

Defined in Ecluse.Rules.Types

Eq EvalContext Source # 
Instance details

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 Deny beats an Allow.

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 Transience records whether a retry can help (transient outage) or not (a permanent inability); the Text is the audit reason. A pure rule never yields this — only the effectful tier does, and only when a rule whose verdict could still change the outcome was unreachable.

Instances

Instances details
Show RuleOutcome Source # 
Instance details

Defined in Ecluse.Rules.Types

Eq RuleOutcome Source # 
Instance details

Defined in Ecluse.Rules.Types

data Decision Source #

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 503/500 by the serve error model). The Transience carries whether a retry can help; the Text is the audit reason.

Instances

Instances details
Show Decision Source # 
Instance details

Defined in Ecluse.Rules.Types

Eq Decision Source # 
Instance details

Defined in Ecluse.Rules.Types

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 RetryAfter is the delay to suggest to the client.

WontResolve

Not expected to self-heal (an internal or parse error). Retrying cannot help, so the request is a 500, never a 503.

Instances

Instances details
Show Transience Source # 
Instance details

Defined in Ecluse.Rules.Types

Eq Transience Source # 
Instance details

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