Files
git.stella-ops.org/docs/policy/exception-effects.md
root 68da90a11a
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Restructure solution layout by module
2025-10-28 15:10:40 +02:00

153 lines
9.3 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 evaluators 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:
1. Filter instances whose `effectId` resolves to a known effect.
2. Discard instances whose scope does not match the candidate finding (rule name, severity, advisory source, SBOM tags).
3. Score remaining instances for **specificity**:
- `ruleNames``1000 + (count × 25)`
- `severities``500 + (count × 10)`
- `sources``250 + (count × 10)`
- `tags``100 + (count × 5)`
4. Highest score wins. Ties fall back to the newest `createdAt`, then lexical `id` (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 `appliedException` capturing 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** `requireControl` adds runtime warnings so operators can enforce completion of compensating controls.
- **Routing** When `routingTemplate` is populated, verdict metadata includes `routingTemplate`, allowing UI surfaces to deep-link into the approvals centre.
Example verdict excerpt (JSON):
```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 `requireMfa` rules from `exceptions.routingTemplates`. When a template requires MFA, Authority will refuse to mint tokens containing `exceptions:approve` unless the authenticating identity provider exposes MFA capability; the failure is logged as `authority.password.grant` with `reason="Exception approval scope requires an MFA-capable identity provider."` Review `/docs/security/authority-scopes.md` for scope/role assignments and `/docs/11_AUTHORITY.md` for 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 `routingTemplate` definitions and enforce MFA where required.
- [ ] **Explain coverage:** Console/CLI surfaces display `appliedException` details and `exception.*` annotations for every waived verdict.
- [ ] **Simulation parity:** `stella policy simulate` outputs include exception metadata, ensuring PR/CI reviews catch unintended waivers.
- [ ] **Audit retention:** Effective findings history retains `appliedException` payloads 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).*