feat: Add archived advisories and implement smart-diff as a core evidence primitive

- Introduced new advisory documents for archived superseded advisories, including detailed descriptions of features already implemented or covered by existing sprints.
- Added "Smart-Diff as a Core Evidence Primitive" advisory outlining the treatment of SBOM diffs as first-class evidence objects, enhancing vulnerability verdicts with deterministic replayability.
- Created "Visual Diffs for Explainable Triage" advisory to improve user experience in understanding policy decisions and reachability changes through visual diffs.
- Implemented "Weighted Confidence for VEX Sources" advisory to rank conflicting vulnerability evidence based on freshness and confidence, facilitating better decision-making.
- Established a signer module charter detailing the mission, expectations, key components, and signing modes for cryptographic signing services in StellaOps.
- Consolidated overlapping concepts from triage UI, visual diffs, and risk budget visualization advisories into a unified specification for better clarity and implementation tracking.
This commit is contained in:
StellaOps Bot
2025-12-26 13:01:43 +02:00
parent 22390057fc
commit 7792749bb4
50 changed files with 6844 additions and 130 deletions

View File

@@ -1,104 +1,146 @@
Heres a tight, practical blueprint for evolving StellaOpss policy engine into a **fully deterministic verdict engine**—so the *same SBOM + VEX + reachability subgraph ⇒ the exact same, replayable verdict* every time, with auditorgrade trails and signed “delta verdicts.”
# Building a Deterministic Verdict Engine
# Why this matters (quick)
> **Status:** PLANNED — Implementation in progress
> **Date:** 2025-12-25
> **Updated:** 2025-12-26
> **Related Sprints:** [`SPRINT_20251226_007_BE_determinism_gaps.md`](../implplan/SPRINT_20251226_007_BE_determinism_gaps.md)
> **Merged Advisories:** [`25-Dec-2025 - Enforcing Canonical JSON for Stable Verdicts.md`](./25-Dec-2025%20-%20Enforcing%20Canonical%20JSON%20for%20Stable%20Verdicts.md) (SUPERSEDED)
---
## Implementation Status
| Component | Status | Location |
|-----------|--------|----------|
| Canonical JSON (JCS) | COMPLETE | `StellaOps.Canonical.Json` |
| NFC String Normalization | COMPLETE | `StellaOps.Resolver.NfcStringNormalizer` |
| Content-Addressed IDs | COMPLETE | `Attestor.ProofChain/Identifiers/` |
| DSSE Signing | COMPLETE | `Signer/`, `Attestor/` |
| Delta Verdict | COMPLETE | `Policy/Deltas/DeltaVerdict.cs` |
| Merkle Trees | COMPLETE | `ProofChain/Merkle/` |
| Determinism Guards | COMPLETE | `Policy.Engine/DeterminismGuard/` |
| Replay Manifest | COMPLETE | `StellaOps.Replay.Core` |
| Feed Snapshot Coordinator | TODO | SPRINT_20251226_007 DET-GAP-01..04 |
| Keyless Signing | TODO | SPRINT_20251226_001 |
| Cross-Platform Testing | TODO | SPRINT_20251226_007 DET-GAP-11..13 |
**Overall Progress:** ~85% complete
---
## Advisory Content
Here's a tight, practical blueprint for evolving Stella Ops's policy engine into a **fully deterministic verdict engine**—so the *same SBOM + VEX + reachability subgraph ⇒ the exact same, replayable verdict* every time, with auditorgrade trails and signed "delta verdicts."
## Why this matters (quick)
* **Reproducibility:** auditors can replay any scan and get identical results.
* **Trust & scale:** crossagent consensus via contentaddressed inputs and signed outputs.
* **Operational clarity:** diffs between builds become crisp, machineverifiable artifacts.
# Core principles
## Core principles
* **Determinism-first:** no wallclock time, no random iteration order, no network during evaluation.
* **Contentaddressing:** hash every *input* (SBOM, VEX docs, reachability subgraph, policy set, rule versions, feed snapshots).
* **Declarative state:** a compact **Scan Manifest** lists input hashes + policy bundle hash + engine version.
* **Pure evaluation:** the verdict function is referentially transparent: `Verdict = f(Manifest)`.
# Data artifacts
## Canonical JSON (Merged from Canonical JSON Advisory)
All JSON artifacts must use **RFC 8785 JCS** canonicalization with optional **Unicode NFC** normalization:
```csharp
// Existing implementation
using StellaOps.Canonical.Json;
var canonical = CanonJson.Canonicalize(myObject);
var hash = CanonJson.Hash(myObject);
var versionedHash = CanonJson.HashVersioned(myObject, CanonVersion.V1);
```
**Canonicalization Rules:**
1. Object keys sorted lexicographically (Ordinal)
2. No whitespace or formatting variations
3. UTF-8 encoding without BOM
4. IEEE 754 number formatting
5. Version markers for migration safety (`_canonVersion: "stella:canon:v1"`)
## Data artifacts
* **Scan Manifest (`manifest.jsonc`)**
* `sbom_sha256`, `vex_set_sha256[]`, `reach_subgraph_sha256`, `feeds_snapshot_sha256`, `policy_bundle_sha256`, `engine_version`, `policy_semver`, `options_hash`
* **Verdict (`verdict.json`)**
* canonical JSON (stable key order); includes:
* `risk_score`, `status` (pass/warn/fail), `unknowns_count`
* **evidence_refs:** content IDs for cited VEX statements, nodes/edges from reachability, CVE records, featureflags, envguards
* **explanations:** stable, templatedriven strings (+ machine reasons)
* **Delta Verdict (`delta.json`)**
* computed between two manifests/verdicts:
* `added_findings[]`, `removed_findings[]`, `severity_shift[]`, `unknowns_delta`, `policy_effects[]`
* signed (DSSE/COSE/JWS), timestamped, and linkable to both verdicts
# Engine architecture (deterministic path)
## Engine architecture (deterministic path)
1. **Normalize inputs**
* SBOM: sort by `packageUrl`/`name@version`; resolve aliases; freeze semver comparison rules.
* VEX: normalize provider → `vex_id`, `product_ref`, `status` (`affected`, `not_affected`, …), *with* source trust score precomputed from a **trust registry** (strict, versioned).
* Reachability: store subgraph as adjacency lists sorted by node ID; hash after topological stable ordering.
* Feeds: lock to a **snapshot** (timestamp + commit/hash); no live calls.
2. **Policy bundle**
2. **Policy bundle**
* Declarative rules (e.g., lattice/merge semantics), compiled to a **canonical IR** (e.g., OPARego → sorted DNF).
* Merge precedence is explicit (e.g., `vendor > distro > internal` can be replaced by a latticemerge table).
* Unknowns policy baked in: e.g., `fail_if_unknowns > N in prod`.
3. **Evaluation**
3. **Evaluation**
* Build a **finding set**: `(component, vuln, context)` tuples with deterministic IDs.
* Apply **latticebased VEX merge** (proofcarrying): each suppression must carry an evidence pointer (feature flag off, code path unreachable, patchedbackport proof).
* Compute final `status` and `risk_score` using fixedprecision math; round rules are part of the bundle.
4. **Emit**
4. **Emit**
* Canonicalize verdict JSON; attach **evidence map** (content IDs only).
* Sign verdict; attach as **OCI attestation** to image/digest.
# APIs (minimal but complete)
## APIs (minimal but complete)
* `POST /evaluate` → returns `verdict.json` + attestation
* `POST /delta` with `{base_verdict, head_verdict}``delta.json` (signed)
* `GET /replay?manifest_sha=` → reexecutes using cached snapshot bundles, returns the same `verdict_sha`
* `GET /evidence/:cid` → fetches immutable evidence blobs (offlineready)
# Storage & indexing
## Storage & indexing
* **CAS (contentaddressable store):** `/evidence/<sha256>` for SBOM/VEX/graphs/feeds/policies.
* **Verdict registry:** keyed by `(image_digest, manifest_sha, engine_version)`.
* **Delta ledger:** appendonly, signed; supports crossagent consensus (multiple engines can cosign identical deltas).
# UI slices (where it lives)
## UI slices (where it lives)
* **Run details → Verdict tab:** status, risk score, unknowns, top evidence links.
* **Diff tab:** render **Delta Verdict** (added/removed/changed), with drilldown to proofs.
* **Replay button:** shows the exact manifest & engine version; oneclick reevaluation (offline possible).
* **Run details → "Verdict" tab:** status, risk score, unknowns, top evidence links.
* **"Diff" tab:** render **Delta Verdict** (added/removed/changed), with drilldown to proofs.
* **"Replay" button:** shows the exact manifest & engine version; oneclick reevaluation (offline possible).
* **Audit export:** zip of `manifest.jsonc`, `verdict.json`, `delta.json` (if any), attestation, and referenced evidence.
# Testing & QA (musthave)
## Testing & QA (musthave)
* **Golden tests:** fixtures of manifests → frozen verdict JSONs (byteforbyte).
* **Chaos determinism tests:** vary thread counts, env vars, map iteration seeds; assert identical verdicts.
* **Crossengine roundtrips:** two independent builds of the engine produce the same verdict for the same manifest.
* **Timetravel tests:** replay older feed snapshots to ensure stability.
# Rollout plan
## Rollout plan
1. **Phase 1:** Introduce Manifest + canonical verdict format alongside existing policy engine (shadow mode).
2. **Phase 2:** Make verdicts the **firstclass artifact** (OCIattached); ship UI Verdict/Diff.
2. **Phase 2:** Make verdicts the **firstclass artifact** (OCIattached); ship UI "Verdict/Diff".
3. **Phase 3:** Enforce **deltagates** in CI/CD (risk budgets + exception packs referenceable by content ID).
4. **Phase 4:** Open **consensus mode**—accept externally signed identical delta verdicts to strengthen trust.
# Notes for Stella modules
## Notes for Stella modules
* **scanner.webservice:** keep lattice algorithms here (per your standing rule). Concelier/Excitors preserveprune source.
* **scanner.webservice:** keep lattice algorithms here (per your standing rule). Concelier/Excitors "preserveprune source."
* **Authority/Attestor:** handle DSSE signing, key management, regional crypto profiles (eIDAS/FIPS/GOST/SM).
* **Feedser/Vexer:** produce immutable **snapshot bundles**; never query live during evaluation.
* **Router/Scheduler:** schedule replay jobs; cache manifests to speed up audits.
* **Db:** Postgres as SoR; Valkey only for ephemeral queues/caches (per your BSDonly profile).
If you want, I can generate:
* a sample **Manifest + Verdict + Delta** trio,
* the **canonical JSON schema**,
* and a **.NET10** reference evaluator (deterministic LINQ pipeline + fixedprecision math) you can drop into `scanner.webservice`.