9.3 KiB
Policy Exception Effects
Audience: Policy authors, reviewers, operators, and governance owners.
Scope: How exception definitions are authored, resolved, and surfaced by the Policy Engine during evaluation, including precedence rules, metadata flow, and simulation/diff behaviour.
Exception effects let teams codify governed waivers without compromising determinism. This guide explains the artefacts involved, how the evaluator selects a single winning exception, and where downstream consumers observe the applied override.
1 · Exception Building Blocks
| Artefact | Description |
|---|---|
| Exception Effect | Declared inside a policy pack (exceptions.effects). Defines the override behaviour plus governance metadata. See effect fields in §2. |
| Routing Template | Optional mapping (exceptions.routingTemplates) used by Authority to route approvals/MFA. Effects reference templates by id. |
| Exception Instance | Stored outside the policy pack (Authority/API). Captures who requested the waiver, scope filters, metadata, and creation time. |
Effects are validated at bind time (PolicyBinder), while instances are ingested alongside policy evaluation inputs. Both are normalized to case-insensitive identifiers to avoid duplicate conflicts.
2 · Effect Fields
| Field | Required | Purpose | Notes |
|---|---|---|---|
id |
✅ | Stable identifier ([A-Za-z0-9-_]+). |
Must be unique per policy pack. |
name |
— | Friendly label for consoles/reports. | Forwarded to verdict metadata if present. |
effect |
✅ | Behaviour enum: suppress, defer, downgrade, requireControl. |
Case-insensitive. |
downgradeSeverity |
⚠️ | Target severity for downgrade. |
Must map to DSL severities (high, medium, etc.). Validation enforced in PolicyBinder (policy.exceptions.effect.downgrade.missingSeverity). |
requiredControlId |
⚠️ | Control catalogue key for requireControl. |
Required when effect is requireControl. |
routingTemplate |
— | Connects to an Authority approval flow. | CLI/Console resolve to authorityRouteId. |
maxDurationDays |
— | Soft limit for temporary waivers. | Must be > 0 when provided. |
description |
— | Rich-text rationale. | Displayed in approvals centre (optional). |
Authoring invalid combinations returns structured errors with JSON paths, preventing packs from compiling (see src/Policy/__Tests/StellaOps.Policy.Tests/PolicyBinderTests.cs:33). Routing templates additionally declare authorityRouteId and requireMfa flags for governance routing.
3 · Exception Instances & Scope
Instances are resolved from Authority or API collections and injected into the evaluation context (PolicyEvaluationExceptions). Each instance contains:
| Field | Source | Usage |
|---|---|---|
id |
Authority storage | Propagated to annotations and appliedException.exceptionId. |
effectId |
Links to pack-defined effect | Must resolve to a known effect; otherwise ignored. |
scope.ruleNames |
Optional list | Limits to specific rule identifiers. |
scope.severities |
Optional list (severity.normalized) |
Normalized against the evaluator’s severity string. |
scope.sources |
Optional advisory sources (GHSA, NVD, …) |
Compared against the advisory context. |
scope.tags |
Optional SBOM tags | Matched using sbom.has_tag(...). |
createdAt |
RFC3339 UTC timestamp | Used as tie-breaker when specificity scores match. |
metadata |
Arbitrary key/value bag | Copied to verdict annotations (exception.meta.*). |
Scopes are case-insensitive and trimmed. Empty scopes behave as global waivers but still require routing and metadata supplied by Authority workflows.
4 · Resolution & Specificity
Only one exception effect is applied per finding. Evaluation proceeds as follows:
- Filter instances whose
effectIdresolves to a known effect. - Discard instances whose scope does not match the candidate finding (rule name, severity, advisory source, SBOM tags).
- Score remaining instances for specificity:
ruleNames⇒1000 + (count × 25)severities⇒500 + (count × 10)sources⇒250 + (count × 10)tags⇒100 + (count × 5)
- Highest score wins. Ties fall back to the newest
createdAt, then lexicalid(stable sorting).
These rules guarantee deterministic selection even when multiple waivers overlap. See src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:209 for tie-break coverage.
5 · Effect Behaviours
| Effect | Status impact | Severity impact | Warnings / metadata |
|---|---|---|---|
suppress |
Forces status suppressed. |
No change. | exception.status=suppressed. |
defer |
Forces status deferred. |
No change. | exception.status=deferred. |
downgrade |
No change. | Sets severity to configured downgradeSeverity. |
exception.severity annotation. |
requireControl |
No change. | No change. | Adds warning Exception '<id>' requires control '<requiredControlId>'. Annotation exception.requiredControl. |
All effects stamp shared annotations: exception.id, exception.effectId, exception.effectType, optional exception.effectName, optional exception.routingTemplate, plus exception.maxDurationDays. Instance metadata is surfaced both in annotations (exception.meta.<key>) and the structured AppliedException.Metadata payload for downstream APIs. Behaviour is validated by unit tests (src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:130 & src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:169).
6 · Explain, Simulation & Outputs
- Explain traces / CLI simulate – Verdict payloads include
appliedExceptioncapturing original vs applied status/severity, enabling diff visualisation in Console and CLI previews. - Annotations – Deterministic keys make it trivial for exports or alerting pipelines to flag waived findings.
- Warnings –
requireControladds runtime warnings so operators can enforce completion of compensating controls. - Routing – When
routingTemplateis populated, verdict metadata includesroutingTemplate, allowing UI surfaces to deep-link into the approvals centre.
Example verdict excerpt (JSON):
{
"status": "suppressed",
"severity": "Critical",
"annotations": {
"exception.id": "exc-001",
"exception.effectId": "suppress-critical",
"exception.effectType": "Suppress",
"exception.status": "suppressed",
"exception.meta.requestedBy": "alice"
},
"appliedException": {
"exceptionId": "exc-001",
"effectId": "suppress-critical",
"effectType": "Suppress",
"originalStatus": "blocked",
"appliedStatus": "suppressed",
"metadata": {
"effectName": "Rule Critical Suppress",
"requestedBy": "alice"
}
}
}
7 · Operational Notes
- Authoring – Policy packs must ship effect definitions before Authority can issue instances. CLI validation (
stella policy lint) fails if required fields are missing. - Approvals & MFA – Effects referencing routing templates inherit
requireMfarules fromexceptions.routingTemplates. When a template requires MFA, Authority will refuse to mint tokens containingexceptions:approveunless the authenticating identity provider exposes MFA capability; the failure is logged asauthority.password.grantwithreason="Exception approval scope requires an MFA-capable identity provider."Review/docs/security/authority-scopes.mdfor scope/role assignments and/docs/11_AUTHORITY.mdfor configuration samples. - Presence in exports – Even when an exception suppresses a finding, explain traces and effective findings retain the applied exception metadata for audit parity.
- Determinism – Specificity scoring plus tie-breakers ensure repeatable outcomes across runs, supporting sealed/offline replay.
8 · Testing References
src/Policy/__Tests/StellaOps.Policy.Tests/PolicyBinderTests.cs:33– Validates schema rules for defining effects, routing templates, and downgrade guardrails.src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:130– Covers suppression, downgrade, and metadata propagation.src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:209– Confirms specificity ordering and metadata forwarding for competing exceptions.
9 · Compliance Checklist
- Effect catalogue maintained: Each policy pack documents available effects and routing templates for auditors.
- Authority alignment: Approval routes in Authority mirror
routingTemplatedefinitions and enforce MFA where required. - Explain coverage: Console/CLI surfaces display
appliedExceptiondetails andexception.*annotations for every waived verdict. - Simulation parity:
stella policy simulateoutputs include exception metadata, ensuring PR/CI reviews catch unintended waivers. - Audit retention: Effective findings history retains
appliedExceptionpayloads so exception lifecycle reviews remain replayable. - Tests locked: Binder and evaluator tests covering exception paths remain green before publishing documentation updates.
Last updated: 2025-10-27 (Sprint 25).