sprints and audit work
This commit is contained in:
290
docs/modules/policy/guides/verdict-rationale.md
Normal file
290
docs/modules/policy/guides/verdict-rationale.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# Verdict Rationale Template
|
||||
|
||||
> **Status:** Implemented (SPRINT_20260106_001_001_LB)
|
||||
> **Library:** `StellaOps.Policy.Explainability`
|
||||
> **API Endpoint:** `GET /api/v1/triage/findings/{findingId}/rationale`
|
||||
> **CLI Command:** `stella verdict rationale <finding-id>`
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**Verdict Rationales** provide human-readable explanations for policy verdicts using a standardized 4-line template. Each rationale explains:
|
||||
|
||||
1. **Evidence:** What vulnerability was found and where
|
||||
2. **Policy Clause:** Which policy rule triggered the decision
|
||||
3. **Attestations:** What proofs support the verdict
|
||||
4. **Decision:** Final verdict with recommendation
|
||||
|
||||
Rationales are content-addressed (same inputs produce same rationale ID), enabling caching and deduplication.
|
||||
|
||||
---
|
||||
|
||||
## 4-Line Template
|
||||
|
||||
Every verdict rationale follows this structure:
|
||||
|
||||
```
|
||||
Line 1 - Evidence: CVE-2024-XXXX in `libxyz` 1.2.3; symbol `foo_read` reachable from `/usr/bin/tool`.
|
||||
Line 2 - Policy: Policy S2.1: reachable+EPSS>=0.2 => triage=P1.
|
||||
Line 3 - Attestations: Build-ID match to vendor advisory; call-path: `main->parse->foo_read`.
|
||||
Line 4 - Decision: Affected (score 0.72). Mitigation recommended: upgrade or backport KB-123.
|
||||
```
|
||||
|
||||
### Template Components
|
||||
|
||||
| Line | Purpose | Content |
|
||||
|------|---------|---------|
|
||||
| **Evidence** | What was found | CVE ID, component PURL, version, reachability info |
|
||||
| **Policy Clause** | Why decision was made | Policy rule ID, expression, triage priority |
|
||||
| **Attestations** | Supporting proofs | Build-ID matches, call paths, VEX statements, provenance |
|
||||
| **Decision** | What to do | Verdict status, risk score, recommendation, mitigation |
|
||||
|
||||
---
|
||||
|
||||
## API Usage
|
||||
|
||||
### Get Rationale (JSON)
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://scanner.example.com/api/v1/triage/findings/12345/rationale?format=json"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"finding_id": "12345",
|
||||
"rationale_id": "rationale:sha256:abc123...",
|
||||
"schema_version": "1.0",
|
||||
"evidence": {
|
||||
"cve": "CVE-2024-1234",
|
||||
"component_purl": "pkg:npm/lodash@4.17.20",
|
||||
"component_version": "4.17.20",
|
||||
"vulnerable_function": "template",
|
||||
"entry_point": "/app/src/index.js",
|
||||
"text": "CVE-2024-1234 in `pkg:npm/lodash@4.17.20` 4.17.20; symbol `template` reachable from `/app/src/index.js`."
|
||||
},
|
||||
"policy_clause": {
|
||||
"clause_id": "S2.1",
|
||||
"rule_description": "High severity with reachability",
|
||||
"conditions": ["severity>=high", "reachable=true"],
|
||||
"text": "Policy S2.1: severity>=high AND reachable=true => triage=P1."
|
||||
},
|
||||
"attestations": {
|
||||
"path_witness": {
|
||||
"id": "witness-789",
|
||||
"type": "path-witness",
|
||||
"digest": "sha256:def456...",
|
||||
"summary": "Path witness from scanner"
|
||||
},
|
||||
"vex_statements": [
|
||||
{
|
||||
"id": "vex-001",
|
||||
"type": "vex",
|
||||
"digest": "sha256:ghi789...",
|
||||
"summary": "Affected: from vendor.example.com"
|
||||
}
|
||||
],
|
||||
"provenance": null,
|
||||
"text": "Path witness from scanner; VEX statement: Affected from vendor.example.com."
|
||||
},
|
||||
"decision": {
|
||||
"verdict": "Affected",
|
||||
"score": 0.72,
|
||||
"recommendation": "Upgrade to version 4.17.21",
|
||||
"mitigation": {
|
||||
"action": "upgrade",
|
||||
"details": "Upgrade to 4.17.21 or later"
|
||||
},
|
||||
"text": "Affected (score 0.72). Mitigation recommended: Upgrade to version 4.17.21."
|
||||
},
|
||||
"generated_at": "2026-01-07T12:00:00Z",
|
||||
"input_digests": {
|
||||
"verdict_digest": "sha256:abc123...",
|
||||
"policy_digest": "sha256:def456...",
|
||||
"evidence_digest": "sha256:ghi789..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get Rationale (Plain Text)
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://scanner.example.com/api/v1/triage/findings/12345/rationale?format=plaintext"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"finding_id": "12345",
|
||||
"rationale_id": "rationale:sha256:abc123...",
|
||||
"format": "plaintext",
|
||||
"content": "CVE-2024-1234 in `pkg:npm/lodash@4.17.20` 4.17.20; symbol `template` reachable from `/app/src/index.js`.\nPolicy S2.1: severity>=high AND reachable=true => triage=P1.\nPath witness from scanner; VEX statement: Affected from vendor.example.com.\nAffected (score 0.72). Mitigation recommended: Upgrade to version 4.17.21."
|
||||
}
|
||||
```
|
||||
|
||||
### Get Rationale (Markdown)
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://scanner.example.com/api/v1/triage/findings/12345/rationale?format=markdown"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"finding_id": "12345",
|
||||
"rationale_id": "rationale:sha256:abc123...",
|
||||
"format": "markdown",
|
||||
"content": "**Evidence:** CVE-2024-1234 in `pkg:npm/lodash@4.17.20` 4.17.20; symbol `template` reachable from `/app/src/index.js`.\n\n**Policy:** Policy S2.1: severity>=high AND reachable=true => triage=P1.\n\n**Attestations:** Path witness from scanner; VEX statement: Affected from vendor.example.com.\n\n**Decision:** Affected (score 0.72). Mitigation recommended: Upgrade to version 4.17.21."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Usage
|
||||
|
||||
### Table Output (Default)
|
||||
|
||||
```bash
|
||||
stella verdict rationale 12345
|
||||
```
|
||||
|
||||
```
|
||||
Finding: 12345
|
||||
Rationale ID: rationale:sha256:abc123...
|
||||
Generated: 2026-01-07T12:00:00Z
|
||||
|
||||
+--------------------------------------+
|
||||
| 1. Evidence |
|
||||
+--------------------------------------+
|
||||
| CVE-2024-1234 in `pkg:npm/lodash... |
|
||||
+--------------------------------------+
|
||||
|
||||
+--------------------------------------+
|
||||
| 2. Policy Clause |
|
||||
+--------------------------------------+
|
||||
| Policy S2.1: severity>=high AND... |
|
||||
+--------------------------------------+
|
||||
|
||||
+--------------------------------------+
|
||||
| 3. Attestations |
|
||||
+--------------------------------------+
|
||||
| Path witness from scanner; VEX... |
|
||||
+--------------------------------------+
|
||||
|
||||
+--------------------------------------+
|
||||
| 4. Decision |
|
||||
+--------------------------------------+
|
||||
| Affected (score 0.72). Mitigation... |
|
||||
+--------------------------------------+
|
||||
```
|
||||
|
||||
### JSON Output
|
||||
|
||||
```bash
|
||||
stella verdict rationale 12345 --output json
|
||||
```
|
||||
|
||||
### Markdown Output
|
||||
|
||||
```bash
|
||||
stella verdict rationale 12345 --output markdown
|
||||
```
|
||||
|
||||
### Plain Text Output
|
||||
|
||||
```bash
|
||||
stella verdict rationale 12345 --output text
|
||||
```
|
||||
|
||||
### With Tenant
|
||||
|
||||
```bash
|
||||
stella verdict rationale 12345 --tenant acme-corp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration
|
||||
|
||||
### Service Registration
|
||||
|
||||
```csharp
|
||||
// In Program.cs or service configuration
|
||||
services.AddVerdictExplainability();
|
||||
services.AddScoped<IFindingRationaleService, FindingRationaleService>();
|
||||
```
|
||||
|
||||
### Programmatic Usage
|
||||
|
||||
```csharp
|
||||
// Inject IVerdictRationaleRenderer
|
||||
public class MyService
|
||||
{
|
||||
private readonly IVerdictRationaleRenderer _renderer;
|
||||
|
||||
public MyService(IVerdictRationaleRenderer renderer)
|
||||
{
|
||||
_renderer = renderer;
|
||||
}
|
||||
|
||||
public string GetExplanation(VerdictRationaleInput input)
|
||||
{
|
||||
var rationale = _renderer.Render(input);
|
||||
return _renderer.RenderPlainText(rationale);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Requirements
|
||||
|
||||
The `VerdictRationaleInput` requires:
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `VerdictRef` | `VerdictReference` | Yes | Reference to verdict attestation |
|
||||
| `Cve` | `string` | Yes | CVE identifier |
|
||||
| `Component` | `ComponentIdentity` | Yes | Component PURL, name, version |
|
||||
| `Reachability` | `ReachabilityDetail` | No | Vulnerable function, entry point |
|
||||
| `PolicyClauseId` | `string` | Yes | Policy clause that triggered verdict |
|
||||
| `PolicyRuleDescription` | `string` | Yes | Human-readable rule description |
|
||||
| `PolicyConditions` | `List<string>` | No | Matched conditions |
|
||||
| `PathWitness` | `AttestationReference` | No | Path witness attestation |
|
||||
| `VexStatements` | `List<AttestationReference>` | No | VEX statement references |
|
||||
| `Provenance` | `AttestationReference` | No | Provenance attestation |
|
||||
| `Verdict` | `string` | Yes | Final verdict status |
|
||||
| `Score` | `double?` | No | Risk score (0-1) |
|
||||
| `Recommendation` | `string` | Yes | Recommended action |
|
||||
| `Mitigation` | `MitigationGuidance` | No | Specific mitigation guidance |
|
||||
|
||||
---
|
||||
|
||||
## Determinism
|
||||
|
||||
Rationales are **content-addressed**: the same inputs always produce the same `rationale_id`. This enables:
|
||||
|
||||
- **Caching:** Store and retrieve rationales by ID
|
||||
- **Deduplication:** Avoid regenerating identical rationales
|
||||
- **Verification:** Confirm rationale wasn't modified after generation
|
||||
|
||||
The rationale ID is computed as:
|
||||
```
|
||||
sha256(canonical_json(verdict_id + witness_id + score_factors))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documents
|
||||
|
||||
- [Verdict Attestations](verdict-attestations.md) - Cryptographic verdict proofs
|
||||
- [Policy DSL](dsl.md) - Policy rule syntax
|
||||
- [Scoring Profiles](scoring-profiles.md) - Risk score computation
|
||||
- [VEX Trust Model](vex-trust-model.md) - VEX statement handling
|
||||
Reference in New Issue
Block a user