Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
# Advisory Analysis: Evidence-First Container Security
|
||||
|
||||
**Date:** 2025-12-28
|
||||
**Status:** ANALYZED
|
||||
**Verdict:** HIGHLY ALIGNED - StellaOps is ~90% implemented
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory proposes making container security "evidence-first and audit-ready" with deterministic, replayable verdicts and explainable evidence. After comprehensive analysis of the StellaOps codebase, **we find that ~90% of the recommendations are already implemented**.
|
||||
|
||||
The platform has mature infrastructure for:
|
||||
- Deterministic SBOM generation (RFC 8785 JCS canonicalization)
|
||||
- Verdict replay with drift detection
|
||||
- Delta verdicts with R1-R4 material change detection
|
||||
- Unknowns budgets with two-factor ranking
|
||||
- Air-gap operation with signed feed mirroring
|
||||
- VEX trust scoring with 5-component calculation
|
||||
- DSSE/in-toto attestation with Sigstore integration
|
||||
|
||||
**Remaining gaps (10%):**
|
||||
1. Formalize `replay.json` export format (infrastructure exists)
|
||||
2. OCI artifact attestation attachment workflow
|
||||
3. Frontend UI for evidence subgraph visualization
|
||||
4. eBPF runtime signal integration (optional)
|
||||
|
||||
---
|
||||
|
||||
## Gap Analysis Matrix
|
||||
|
||||
### Fully Implemented (No Gaps)
|
||||
|
||||
| Advisory Recommendation | StellaOps Implementation | Evidence |
|
||||
|------------------------|--------------------------|----------|
|
||||
| Deterministic SBOMs | RFC 8785 JCS via `CanonJson.cs` | `SbomDeterminismTests.cs` |
|
||||
| Reproducibility proofs | `DeterminismManifest`, feed snapshots | `FeedSnapshotCoordinatorService` |
|
||||
| Patch/backport awareness | 4-tier `BackportEvidenceResolver` | `ProvenanceScope.cs` |
|
||||
| Delta verdicts | `DeltaVerdict`, `SecurityStateDelta` | `MaterialRiskChangeDetector` (R1-R4) |
|
||||
| Unknowns budgets | `UnknownsBudgetGate`, two-factor ranking | `UnknownRanker.cs` |
|
||||
| Air-gap mode | `BundleExportService`, signed mirrors | `StellaOpsMirrorConnector` |
|
||||
| Self-hosted feeds | JWS signature verification | `MirrorSignatureVerifier` |
|
||||
| Postgres-only profile | All modules migrated | MongoDB removed |
|
||||
| VEX trust scoring | 5-component calculator | `SourceTrustScoreCalculator` |
|
||||
| DSSE/in-toto attestations | Full implementation | `DsseEnvelope`, `AttestorSigningService` |
|
||||
| Keyless signing | Fulcio integration | `KeylessDsseSigner` |
|
||||
| Offline verification | Bundled trust roots | `OfflineVerifier` |
|
||||
| 32 advisory connectors | National CERTs, distros, vendors | Concelier connectors |
|
||||
| 4 consensus algorithms | HighestWeight, WeightedVote, Lattice, AuthoritativeFirst | `VexConsensusEngine` |
|
||||
| Binary analysis (ELF/PE) | Feature extractors | `ElfFeatureExtractor`, `PeFeatureExtractor` |
|
||||
| Call graph extraction | Multi-language support | `BinaryCallGraphExtractor` |
|
||||
| Gate levels (G0-G4) | Risk-based release gates | `DeltaGateLevel` |
|
||||
| Merkle tree proofs | Evidence bundling | `MerkleTreeCalculator` |
|
||||
|
||||
### Partial Implementation (Low Gaps)
|
||||
|
||||
| Advisory Recommendation | Current State | Remaining Work |
|
||||
|------------------------|---------------|----------------|
|
||||
| `replay.json` export | `replay.schema.json` exists, infrastructure complete | Formalize export format, CI template |
|
||||
| OCI attestation attachment | Signing complete, attachment missing | Implement `OciAttestationAttacher` |
|
||||
| Trust lattice config UI | Backend complete | Admin UI needed |
|
||||
|
||||
### Not Implemented (Medium-High Gaps)
|
||||
|
||||
| Advisory Recommendation | Current State | Required Work |
|
||||
|------------------------|---------------|---------------|
|
||||
| Evidence subgraph UI | Backend exists | Frontend visualization |
|
||||
| Single-action triage cards | API exists | Angular components |
|
||||
| eBPF runtime signals | `RuntimeStaticMerger` exists | Probe implementation |
|
||||
|
||||
---
|
||||
|
||||
## Recommended Sprints
|
||||
|
||||
Based on the gap analysis, only **4 sprints** are needed (reduced from 8 originally estimated):
|
||||
|
||||
### Sprint 1: SPRINT_20251228_001_BE_replay_manifest_ci
|
||||
**Priority:** HIGH | **Effort:** Low
|
||||
- Formalize `replay.json` export schema
|
||||
- Create CI workflow template for SBOM drift detection
|
||||
- Add `--fail-on-drift` CLI flag
|
||||
|
||||
### Sprint 2: SPRINT_20251228_002_BE_oci_attestation_attach
|
||||
**Priority:** HIGH | **Effort:** Low
|
||||
- Implement `IOciAttestationAttacher` service
|
||||
- CLI commands `stella attest attach/verify/list`
|
||||
- Cosign compatibility documentation
|
||||
|
||||
### Sprint 3: SPRINT_20251228_003_FE_evidence_subgraph_ui
|
||||
**Priority:** MEDIUM | **Effort:** High
|
||||
- Evidence subgraph visualization component
|
||||
- Single-action triage cards
|
||||
- "Explain this verdict" summary
|
||||
- Quiet-by-design default filters
|
||||
|
||||
### Sprint 4: SPRINT_20251228_004_AG_ebpf_runtime_signals
|
||||
**Priority:** LOW (optional) | **Effort:** High
|
||||
- eBPF probe implementation
|
||||
- Runtime signal collection
|
||||
- Merge with static reachability
|
||||
|
||||
---
|
||||
|
||||
## Key Implementation Discoveries
|
||||
|
||||
### 1. Determinism Infrastructure
|
||||
|
||||
**Files examined:**
|
||||
- `src/__Libraries/StellaOps.Canonical.Json/CanonJson.cs` - RFC 8785 JCS implementation
|
||||
- `src/__Libraries/StellaOps.Replay.Core/Schemas/replay.schema.json` - Replay schema v1.0.0
|
||||
- `src/__Tests/Integration/StellaOps.Integration.Determinism/` - Comprehensive tests
|
||||
|
||||
**Findings:**
|
||||
- All 25 DET-GAP items documented as DONE
|
||||
- Cross-platform CI (Windows/Linux/macOS) implemented
|
||||
- Roslyn analyzers (STELLA0100/0101/0102) enforce boundaries
|
||||
- Property-based tests with FsCheck
|
||||
|
||||
### 2. Delta Verdict System
|
||||
|
||||
**Files examined:**
|
||||
- `src/Policy/__Libraries/StellaOps.Policy/Deltas/DeltaVerdict.cs`
|
||||
- `src/Scanner/__Libraries/StellaOps.Scanner.SmartDiff/Detection/MaterialRiskChangeDetector.cs`
|
||||
|
||||
**Findings:**
|
||||
- 5-dimension delta analysis (SBOM, Reachability, VEX, Policy, Unknowns)
|
||||
- R1-R4 material change detection rules
|
||||
- Priority scoring with contextual factors
|
||||
- Gate levels G0-G4 with escalation logic
|
||||
|
||||
### 3. Unknowns Management
|
||||
|
||||
**Files examined:**
|
||||
- `src/Policy/__Libraries/StellaOps.Policy/Gates/UnknownsBudgetGate.cs`
|
||||
- `src/Policy/__Libraries/StellaOps.Policy.Unknowns/Services/UnknownRanker.cs`
|
||||
|
||||
**Findings:**
|
||||
- Two-factor ranking (uncertainty + exploit pressure)
|
||||
- Time decay with configurable buckets
|
||||
- Containment reduction from blast radius signals
|
||||
- Band assignment (Hot/Warm/Cold/Resolved)
|
||||
|
||||
### 4. VEX Trust Scoring
|
||||
|
||||
**Files examined:**
|
||||
- `src/VexLens/StellaOps.VexLens/Trust/SourceTrust/SourceTrustScoreCalculator.cs`
|
||||
- `src/VexLens/StellaOps.VexLens/Consensus/VexConsensusEngine.cs`
|
||||
|
||||
**Findings:**
|
||||
- 5-component scoring: Authority (0.20), Accuracy (0.25), Timeliness (0.20), Coverage (0.15), Verification (0.20)
|
||||
- Trust tiers: Critical (>0.75), High, Medium, Low
|
||||
- 4 consensus algorithms implemented
|
||||
- Conflict detection with severity levels
|
||||
|
||||
### 5. Air-Gap Capabilities
|
||||
|
||||
**Files examined:**
|
||||
- `src/AirGap/StellaOps.AirGap.Bundle/Services/BundleExportService.cs`
|
||||
- `src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/Security/MirrorSignatureVerifier.cs`
|
||||
- `devops/compose/docker-compose.airgap.yaml`
|
||||
|
||||
**Findings:**
|
||||
- Production-grade offline kit with 300-500MB bundles
|
||||
- JWS signature verification for mirrors
|
||||
- Delta updates (<30MB daily)
|
||||
- Egress policy enforcement with sealed mode
|
||||
- Local observability stack (Prometheus/Grafana/Loki)
|
||||
|
||||
### 6. Attestation Infrastructure
|
||||
|
||||
**Files examined:**
|
||||
- `src/Attestor/StellaOps.Attestor.Envelope/DsseEnvelope.cs`
|
||||
- `src/__Libraries/StellaOps.AuditPack/Services/AuditPackBuilder.cs`
|
||||
- `src/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.Core/AttestationBundle/AttestationBundleBuilder.cs`
|
||||
|
||||
**Findings:**
|
||||
- DSSE/in-toto v1 fully implemented
|
||||
- Multiple signing modes: keyless (Fulcio), KMS, HSM, FIDO2
|
||||
- Merkle tree integrity verification
|
||||
- Rekor transparency log integration
|
||||
- Multiple export formats: ZIP, JSON, DSSE envelope
|
||||
- Shell verification scripts for offline validation
|
||||
|
||||
---
|
||||
|
||||
## Validation Against StellaOps Vision
|
||||
|
||||
| Advisory Principle | CLAUDE.md Alignment |
|
||||
|-------------------|---------------------|
|
||||
| Determinism | "Outputs must be reproducible - stable ordering, UTC ISO-8601 timestamps" |
|
||||
| Offline-first | "Remote host allowlist, strict schema validation" |
|
||||
| VEX-first decisioning | "Exploitability modeled in OpenVEX with lattice logic for stable outcomes" |
|
||||
| Plugin architecture | "Concelier connectors, Authority plugins, Scanner analyzers are all plugin-based" |
|
||||
| Evidence as artifacts | EvidenceLocker, AuditPack, Attestor modules exist |
|
||||
|
||||
**Conclusion:** The advisory is a **natural evolution** of StellaOps' existing architecture, not a pivot.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Q1 2026 (Immediate)
|
||||
- Sprint 1: replay.json formalization
|
||||
- Sprint 2: OCI attestation attachment
|
||||
|
||||
### Q2 2026
|
||||
- Sprint 3: Evidence subgraph UI
|
||||
|
||||
### Q3 2026 (Optional)
|
||||
- Sprint 4: eBPF runtime signals
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
### Sprint Files Created
|
||||
- `docs/implplan/SPRINT_20251228_001_BE_replay_manifest_ci.md`
|
||||
- `docs/implplan/SPRINT_20251228_002_BE_oci_attestation_attach.md`
|
||||
- `docs/implplan/SPRINT_20251228_003_FE_evidence_subgraph_ui.md`
|
||||
- `docs/implplan/SPRINT_20251228_004_AG_ebpf_runtime_signals.md`
|
||||
|
||||
### Key Source Files Analyzed
|
||||
- `src/Replay/` - Verdict replay infrastructure
|
||||
- `src/Policy/__Libraries/StellaOps.Policy/Deltas/` - Delta verdict system
|
||||
- `src/Scanner/__Libraries/StellaOps.Scanner.SmartDiff/` - Material change detection
|
||||
- `src/VexLens/` - VEX consensus and trust scoring
|
||||
- `src/AirGap/` - Offline bundle management
|
||||
- `src/Attestor/` - DSSE/in-toto attestations
|
||||
- `src/__Libraries/StellaOps.AuditPack/` - Audit pack export
|
||||
|
||||
### Documentation Referenced
|
||||
- `docs/contributing/canonicalization-determinism.md`
|
||||
- `docs/24_OFFLINE_KIT.md`
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
- Module AGENTS.md files
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Original Advisory
|
||||
|
||||
<details>
|
||||
<summary>Click to expand original advisory text</summary>
|
||||
|
||||
### Why this matters (quick context)
|
||||
|
||||
Most scanners stop at "found X vulns." That creates noise, rework, and weak audit trails. The next leap is **deterministic, replayable verdicts** with **explainable evidence**—so engineers, auditors, and vendors see *why* something is (not) a risk and can reproduce the result exactly.
|
||||
|
||||
### Gaps to close (what typical tools miss)
|
||||
|
||||
* **Deterministic SBOMs + reproducibility proofs**
|
||||
* **Patch/backport awareness (distro-verified)**
|
||||
* **Delta verdicts as first-class artifacts**
|
||||
* **Offline/air-gap mode with self-hosted feeds**
|
||||
* **Explainable UI (evidence subgraphs, not lists)**
|
||||
|
||||
### Stella Ops "fill" (what to build in)
|
||||
|
||||
* **Binaries DB + call-stack capture**
|
||||
* **Policy-driven "unknowns budgets"**
|
||||
* **VEX authoring/ingestion with trust scoring**
|
||||
* **DSSE/in-toto attestations**
|
||||
* **Auditor-ready export packs**
|
||||
|
||||
### Minimal product slices (shippable now)
|
||||
|
||||
* **MVP-A: Deterministic SBOM kit**
|
||||
* **MVP-B: Backport-aware triage**
|
||||
* **MVP-C: Delta verdicts**
|
||||
|
||||
</details>
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# Building a Deterministic Verdict Engine
|
||||
|
||||
> **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 auditor‑grade trails and signed "delta verdicts."
|
||||
|
||||
## Why this matters (quick)
|
||||
|
||||
* **Reproducibility:** auditors can replay any scan and get identical results.
|
||||
* **Trust & scale:** cross‑agent consensus via content‑addressed inputs and signed outputs.
|
||||
* **Operational clarity:** diffs between builds become crisp, machine‑verifiable artifacts.
|
||||
|
||||
## Core principles
|
||||
|
||||
* **Determinism-first:** no wall‑clock time, no random iteration order, no network during evaluation.
|
||||
* **Content‑addressing:** 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)`.
|
||||
|
||||
## 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, feature‑flags, env‑guards
|
||||
* **explanations:** stable, template‑driven 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), time‑stamped, and linkable to both verdicts
|
||||
|
||||
## 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**
|
||||
* Declarative rules (e.g., lattice/merge semantics), compiled to a **canonical IR** (e.g., OPA‑Rego → sorted DNF).
|
||||
* Merge precedence is explicit (e.g., `vendor > distro > internal` can be replaced by a lattice‑merge table).
|
||||
* Unknowns policy baked in: e.g., `fail_if_unknowns > N in prod`.
|
||||
|
||||
3. **Evaluation**
|
||||
* Build a **finding set**: `(component, vuln, context)` tuples with deterministic IDs.
|
||||
* Apply **lattice‑based VEX merge** (proof‑carrying): each suppression must carry an evidence pointer (feature flag off, code path unreachable, patched‑backport proof).
|
||||
* Compute final `status` and `risk_score` using fixed‑precision math; round rules are part of the bundle.
|
||||
|
||||
4. **Emit**
|
||||
* Canonicalize verdict JSON; attach **evidence map** (content IDs only).
|
||||
* Sign verdict; attach as **OCI attestation** to image/digest.
|
||||
|
||||
## APIs (minimal but complete)
|
||||
|
||||
* `POST /evaluate` → returns `verdict.json` + attestation
|
||||
* `POST /delta` with `{base_verdict, head_verdict}` → `delta.json` (signed)
|
||||
* `GET /replay?manifest_sha=` → re‑executes using cached snapshot bundles, returns the same `verdict_sha`
|
||||
* `GET /evidence/:cid` → fetches immutable evidence blobs (offline‑ready)
|
||||
|
||||
## Storage & indexing
|
||||
|
||||
* **CAS (content‑addressable store):** `/evidence/<sha256>` for SBOM/VEX/graphs/feeds/policies.
|
||||
* **Verdict registry:** keyed by `(image_digest, manifest_sha, engine_version)`.
|
||||
* **Delta ledger:** append‑only, signed; supports cross‑agent consensus (multiple engines can co‑sign identical deltas).
|
||||
|
||||
## 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 drill‑down to proofs.
|
||||
* **"Replay" button:** shows the exact manifest & engine version; one‑click re‑evaluation (offline possible).
|
||||
* **Audit export:** zip of `manifest.jsonc`, `verdict.json`, `delta.json` (if any), attestation, and referenced evidence.
|
||||
|
||||
## Testing & QA (must‑have)
|
||||
|
||||
* **Golden tests:** fixtures of manifests → frozen verdict JSONs (byte‑for‑byte).
|
||||
* **Chaos determinism tests:** vary thread counts, env vars, map iteration seeds; assert identical verdicts.
|
||||
* **Cross‑engine round‑trips:** two independent builds of the engine produce the same verdict for the same manifest.
|
||||
* **Time‑travel tests:** replay older feed snapshots to ensure stability.
|
||||
|
||||
## Rollout plan
|
||||
|
||||
1. **Phase 1:** Introduce Manifest + canonical verdict format alongside existing policy engine (shadow mode).
|
||||
2. **Phase 2:** Make verdicts the **first‑class artifact** (OCI‑attached); ship UI "Verdict/Diff".
|
||||
3. **Phase 3:** Enforce **delta‑gates** 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
|
||||
|
||||
* **scanner.webservice:** keep lattice algorithms here (per your standing rule). Concelier/Excitors "preserve‑prune 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 BSD‑only profile).
|
||||
@@ -0,0 +1,190 @@
|
||||
# Enforcing Canonical JSON for Stable Verdicts
|
||||
|
||||
> **Status:** SUPERSEDED — Already implemented in codebase
|
||||
> **Date:** 2025-12-25
|
||||
> **Updated:** 2025-12-26
|
||||
> **Superseded By:** Existing implementation in `StellaOps.Canonical.Json`
|
||||
> **Related:** Merged into `25-Dec-2025 - Building a Deterministic Verdict Engine.md`
|
||||
|
||||
---
|
||||
|
||||
## Implementation Status
|
||||
|
||||
This advisory's recommendations are **already implemented** in the codebase:
|
||||
|
||||
| Recommendation | Implementation | Location |
|
||||
|----------------|----------------|----------|
|
||||
| RFC 8785 JCS canonicalization | `Rfc8785JsonCanonicalizer` | `src/Attestor/__Libraries/StellaOps.Attestor.ProofChain/Json/` |
|
||||
| Unicode NFC normalization | `NfcStringNormalizer` | `src/__Libraries/StellaOps.Resolver/` |
|
||||
| Canonical JSON library | `CanonJson` | `src/__Libraries/StellaOps.Canonical.Json/` |
|
||||
| Version markers | `CanonVersion` (stella:canon:v1) | `src/__Libraries/StellaOps.Canonical.Json/` |
|
||||
| Content-addressed IDs | `ContentAddressedIdGenerator` | `src/Attestor/__Libraries/StellaOps.Attestor.ProofChain/Identifiers/` |
|
||||
| Determinism testing | `DeterminismGate` | `src/__Libraries/StellaOps.TestKit/Determinism/` |
|
||||
|
||||
### Remaining Gap Tasks
|
||||
|
||||
Minor enforcement gaps are tracked in [`SPRINT_20251226_007_BE_determinism_gaps.md`](../implplan/SPRINT_20251226_007_BE_determinism_gaps.md):
|
||||
|
||||
- DET-GAP-17: Optional NFC pass in canonicalizers
|
||||
- DET-GAP-18: Roslyn analyzer for resolver boundary enforcement
|
||||
- DET-GAP-19: Pre-canonical hash debug logging
|
||||
- DET-GAP-20: Document resolver boundary pattern in CONTRIBUTING.md
|
||||
|
||||
---
|
||||
|
||||
## Original Advisory Content
|
||||
|
||||
Here's a small but high‑impact practice to make your hashes/signatures and "same inputs → same verdict" truly stable across services: **pick one canonicalization and enforce it at the resolver boundary.**
|
||||
|
||||
---
|
||||
|
||||
### Why this matters (in plain words)
|
||||
|
||||
Two JSONs that *look* the same can serialize differently (key order, spacing, Unicode forms). If one producer emits slightly different bytes, your REG/verdict hash changes—even though the meaning didn't—breaking dedup, cache hits, attestations, and audits.
|
||||
|
||||
---
|
||||
|
||||
### The rule
|
||||
|
||||
**Adopt one canonicalization spec and apply it everywhere at ingress/egress of your resolver:**
|
||||
|
||||
* **Strings:** normalize to **UTF‑8, Unicode NFC** (Normalization Form C).
|
||||
* **JSON:** canonicalize with a deterministic scheme (e.g., **RFC 8785 JCS**: sorted keys, no insignificant whitespace, exact number formatting, escape rules).
|
||||
* **Binary for hashing/signing:** always hash **the canonical bytes**, never ad‑hoc serializer output.
|
||||
|
||||
---
|
||||
|
||||
### Minimal contract (put this in your CONTRIBUTING/AGREEMENTS.md)
|
||||
|
||||
1. Inputs may arrive in any well‑formed JSON.
|
||||
2. Resolver **normalizes strings (NFC)** and **re‑emits JSON in JCS**.
|
||||
3. **REG hash** is computed from **JCS‑canonical UTF‑8 bytes** only.
|
||||
4. Any signature/attestation (DSSE/OCI) MUST cover those same bytes.
|
||||
5. Any module that can't speak JCS must pass raw data to the resolver; only the resolver serializes.
|
||||
|
||||
---
|
||||
|
||||
### Existing Implementation (supersedes sample code)
|
||||
|
||||
The codebase already provides a more robust implementation:
|
||||
|
||||
```csharp
|
||||
// Canonical JSON with version markers
|
||||
using StellaOps.Canonical.Json;
|
||||
|
||||
var canonical = CanonJson.Canonicalize(myObject);
|
||||
var hash = CanonJson.Hash(myObject);
|
||||
var versionedHash = CanonJson.HashVersioned(myObject, CanonVersion.V1);
|
||||
|
||||
// NFC normalization
|
||||
using StellaOps.Resolver;
|
||||
|
||||
var normalizer = NfcStringNormalizer.Instance;
|
||||
var nfcString = normalizer.Normalize(input);
|
||||
|
||||
// RFC 8785 JCS for raw JSON bytes
|
||||
using StellaOps.Attestor.ProofChain.Json;
|
||||
|
||||
var canonicalizer = new Rfc8785JsonCanonicalizer();
|
||||
var canonicalBytes = canonicalizer.Canonicalize(utf8JsonBytes);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Drop‑in checklist (pin on your wall)
|
||||
|
||||
- [x] One canonicalization policy: **UTF‑8 + NFC + JCS**. *(Implemented: `CanonJson`, `NfcStringNormalizer`)*
|
||||
- [x] Resolver owns canonicalization (single choke‑point). *(Pattern documented)*
|
||||
- [x] **REG hash/signatures always over canonical bytes.** *(Implemented: `ContentAddressedIdGenerator`)*
|
||||
- [ ] CI gate: reject outputs that aren't JCS; fuzz keys/order/whitespace in tests. *(In progress: SPRINT_20251226_007)*
|
||||
- [ ] Log both the pre‑canonical and canonical SHA‑256 for audits. *(Planned: DET-GAP-19)*
|
||||
- [x] Backward‑compat path: migrate legacy verdicts by re‑canonicalizing once, store "old_hash → new_hash" map. *(Implemented: `CanonVersion` with version markers)*
|
||||
|
||||
---
|
||||
|
||||
### Original Sample Code (archived for reference)
|
||||
|
||||
<details>
|
||||
<summary>Click to expand original sample code</summary>
|
||||
|
||||
```csharp
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Globalization;
|
||||
|
||||
public static class Canon
|
||||
{
|
||||
// 1) Unicode → NFC
|
||||
public static string ToNfc(string s) => s is null ? null : s.Normalize(NormalizationForm.FormC);
|
||||
|
||||
// 2) Walk JSON tree, NFC all strings
|
||||
public static JsonElement NormalizeStrings(JsonElement node, JsonSerializerOptions opts = null!)
|
||||
{
|
||||
switch (node.ValueKind)
|
||||
{
|
||||
case JsonValueKind.String:
|
||||
var nfc = ToNfc(node.GetString()!);
|
||||
return JsonDocument.Parse(JsonSerializer.Serialize(nfc)).RootElement;
|
||||
|
||||
case JsonValueKind.Object:
|
||||
{
|
||||
using var doc = JsonDocument.Parse("{}");
|
||||
var dict = new SortedDictionary<string, JsonElement>(StringComparer.Ordinal); // sorted keys (JCS)
|
||||
foreach (var p in node.EnumerateObject())
|
||||
{
|
||||
var key = ToNfc(p.Name);
|
||||
dict[key] = NormalizeStrings(p.Value, opts);
|
||||
}
|
||||
// re‑emit in sorted order
|
||||
using var buf = new MemoryStream();
|
||||
using (var w = new Utf8JsonWriter(buf, new JsonWriterOptions { SkipValidation = false, Indented = false }))
|
||||
{
|
||||
w.WriteStartObject();
|
||||
foreach (var kv in dict)
|
||||
WriteCanonical(kv.Key, kv.Value, w);
|
||||
w.WriteEndObject();
|
||||
}
|
||||
return JsonDocument.Parse(buf.ToArray()).RootElement;
|
||||
}
|
||||
|
||||
case JsonValueKind.Array:
|
||||
{
|
||||
var items = new List<JsonElement>();
|
||||
foreach (var v in node.EnumerateArray())
|
||||
items.Add(NormalizeStrings(v, opts));
|
||||
using var buf = new MemoryStream();
|
||||
using (var w = new Utf8JsonWriter(buf, new JsonWriterOptions { SkipValidation = false, Indented = false }))
|
||||
{
|
||||
w.WriteStartArray();
|
||||
foreach (var v in items) v.WriteTo(w);
|
||||
w.WriteEndArray();
|
||||
}
|
||||
return JsonDocument.Parse(buf.ToArray()).RootElement;
|
||||
}
|
||||
|
||||
default:
|
||||
return node; // numbers/bools/null unchanged (JCS rules avoid extra zeros, no NaN/Inf)
|
||||
}
|
||||
}
|
||||
|
||||
// helper to write a property with an already-built JsonElement
|
||||
static void WriteCanonical(string name, JsonElement value, Utf8JsonWriter w)
|
||||
{
|
||||
w.WritePropertyName(name); // JCS: name is exact UTF‑8, no extra spaces
|
||||
value.WriteTo(w); // keep canonicalized subtree
|
||||
}
|
||||
|
||||
// 3) Canonical bytes for hashing/signing
|
||||
public static byte[] CanonicalizeUtf8(ReadOnlySpan<byte> utf8Json)
|
||||
{
|
||||
using var doc = JsonDocument.Parse(utf8Json);
|
||||
var normalized = NormalizeStrings(doc.RootElement);
|
||||
using var buf = new MemoryStream();
|
||||
using (var w = new Utf8JsonWriter(buf, new JsonWriterOptions { Indented = false })) // no whitespace
|
||||
normalized.WriteTo(w);
|
||||
return buf.ToArray(); // feed into SHA‑256/DSSE
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,108 @@
|
||||
Here’s a practical blueprint for linking what you *build* to what actually *runs*, and turning that into proof-grade security decisions.
|
||||
|
||||
# Static → Binary braid (build-time proof of “what functions are inside”)
|
||||
|
||||
**Goal:** Prove exactly which functions/offsets shipped in an artifact—without exposing full source.
|
||||
|
||||
* **What to store (per artifact):**
|
||||
|
||||
* Minimal call‑stack “entry→sink” traces for relevant code paths (e.g., public handlers → sensitive sinks).
|
||||
* Symbol map concordance: `{ function, file, address-range, Build‑ID, debug-id }`.
|
||||
* Hashes per function-range (e.g., rolling BLAKE3 over `.text` subranges), plus overall `.text`/`.rodata` digests.
|
||||
* **How to generate:**
|
||||
|
||||
* During build, emit:
|
||||
|
||||
* ELF/PE/Mach‑O: capture Build‑ID, section ranges, and DWARF/CodeView ↔ symbol table mapping.
|
||||
* Function-range hashing: disassemble to find prolog/epilog (fallback to symbol boundaries), hash byte ranges.
|
||||
* Entry→sink traces: from static CFG or unit/integration tests with instrumentation; serialize as compact spans (start fn, end fn, edge list hash).
|
||||
* **Proof object (tiny & replayable):**
|
||||
|
||||
* `{ build_id, section_hashes, [ {func: name, addr: start..end, func_hash}, … ], [trace_hashes] }`
|
||||
* Sign with DSSE (in‑toto envelope). Auditors can replay using the published Build‑ID + debug symbols to verify function boundaries without your source.
|
||||
* **Attach & ship:**
|
||||
|
||||
* Publish as an OCI referrers artifact alongside the image (e.g., `application/vnd.stellaops.funcproof+json`), referenced from SBOM (CycloneDX `evidence` or SPDX `verificationCode` extension).
|
||||
* **Why it matters:**
|
||||
|
||||
* When a CVE names a *symbol* (not just a package version), you can prove whether that symbol (and exact byte-range) is present in your binary.
|
||||
|
||||
# Runtime → Build braid (production proof of “what code ran”)
|
||||
|
||||
**Goal:** Observe live stacks (cheaply), canonicalize to symbols, and correlate to SBOM components. If a vulnerable symbol appears *in hot paths*, automatically downgrade VEX posture.
|
||||
|
||||
* **Collection (Linux):**
|
||||
|
||||
* eBPF sampling for targeted processes/containers; use `bpf_get_stackid` to capture stack traces (user & kernel) into a perf map with low overhead.
|
||||
* Collapse stacks (“frameA;frameB;… count”) à la flamegraph format; include PID, container image digest, Build‑ID tuples.
|
||||
* **Canonicalization:**
|
||||
|
||||
* Resolve PCs → (Build‑ID, function, offset) via `perf-map-agent`/`eu-stack`, or your own resolver using `.note.gnu.build-id` + symbol table (prefer `debuginfod` in lab; ship a slim symbol cache in prod).
|
||||
* Normalize language runtimes: Java/.NET/Python frames mapped to package+symbol via runtime metadata; native frames via ELF.
|
||||
* **Correlate to SBOM:**
|
||||
|
||||
* For each frame: map `(image-digest, Build‑ID, function)` → SBOM component (pkg + version) and to your **Static→Binary proof** entry.
|
||||
* **VEX policy reaction:**
|
||||
|
||||
* If a CVE’s vulnerable symbol appears in observed stacks **and** matches your static proof:
|
||||
|
||||
* Auto‑emit a **VEX downgrade** (e.g., from `not_affected` to `affected`) with DSSE signatures, including runtime evidence:
|
||||
|
||||
* Top stacks where the symbol was hot (counts/percentile),
|
||||
* Build‑ID(s) observed,
|
||||
* Timestamp window and container IDs.
|
||||
* If symbol is present in build but never observed (and policy allows), maintain or upgrade to `not_affected(conditions: not_reachable_at_runtime)`—with time‑boxed confidence.
|
||||
* **Controls & SLOs:**
|
||||
|
||||
* Sampling budget per workload (e.g., 49 Hz for N minutes per hour), P99 overhead <1%.
|
||||
* Privacy guardrails: hash short-lived arguments; only persist canonical frames + counts.
|
||||
|
||||
# How this lands in Stella Ops (concrete modules & evidence flow)
|
||||
|
||||
* **Sbomer**: add `funcproof` generator at build (ELF range hashing + entry→sink traces). Emit CycloneDX `components.evidence` link to funcproof artifact.
|
||||
* **Attestor**: wrap funcproof in DSSE, push as OCI referrer; record in Proof‑of‑Integrity Graph.
|
||||
* **Signals/Excititor**: eBPF sampler daemonset; push collapsed frames with `(image-digest, Build‑ID)` to pipeline.
|
||||
* **Concelier**: resolver service mapping frames → SBOM components + funcproof presence; maintain hot‑symbol index.
|
||||
* **Vexer/Policy Engine**: when hot vulnerable symbol is confirmed, produce signed VEX downgrade; route to **Authority** for policy‑gated actions (quarantine, canary freeze, diff-aware release gate).
|
||||
* **Timeline/Notify**: human‑readable evidence pack: “CVE‑2025‑XXXX observed in `libfoo::parse_hdr` (Build‑ID abc…), 17.3% of CPU in api‑gw@prod between 12:00–14:00 UTC; VEX → affected.”
|
||||
|
||||
# Data shapes (keep them tiny)
|
||||
|
||||
* **FuncProof JSON (per binary):**
|
||||
|
||||
```json
|
||||
{
|
||||
"buildId": "ab12…",
|
||||
"sections": {".text": "hash", ".rodata": "hash"},
|
||||
"functions": [
|
||||
{"sym": "foo::bar", "start": "0x401120", "end": "0x4013af", "hash": "…"}
|
||||
],
|
||||
"traces": ["hash(edge-list-1)", "hash(edge-list-2)"],
|
||||
"meta": {"compiler": "clang-18", "flags": "-O2 -fno-plt"}
|
||||
}
|
||||
```
|
||||
* **Runtime frame sample (collapsed):**
|
||||
|
||||
```
|
||||
api-gw@sha256:…;buildid=ab12…;foo::bar+0x3a;net/http::Serve;… 97
|
||||
```
|
||||
|
||||
# Rollout plan (short and sweet)
|
||||
|
||||
1. **Phase 1 — Build plumbing:** implement function-range hashing + DSSE attestation; publish as OCI referrer; link from SBOM.
|
||||
2. **Phase 2 — Runtime sampler:** ship eBPF agent with stack collapse + Build‑ID resolution; store only canonical frames.
|
||||
3. **Phase 3 — Correlation & VEX:** map frames ↔ SBOM ↔ funcproof; auto‑downgrade VEX on hot vulnerable symbols; wire policy actions.
|
||||
4. **Phase 4 — Auditor replay:** `stella verify --image X` downloads funcproof + symbols and replays hashes and traces to prove presence/absence without source.
|
||||
|
||||
# Why this is a moat
|
||||
|
||||
* **Symbol‑level truth**, not just package versions.
|
||||
* **Runtime‑aware VEX** that flips based on evidence, not assumptions.
|
||||
* **Tiny proof objects** make audits fast and air‑gap‑friendly.
|
||||
* **Deterministic replay**: “same inputs → same verdict,” signed.
|
||||
|
||||
If you want, I can draft:
|
||||
|
||||
* the DSSE schemas,
|
||||
* the eBPF sampler config for Alpine/Debian/RHEL/SLES/Astra,
|
||||
* and the exact CycloneDX/SPDX extensions to carry `funcproof` links.
|
||||
@@ -0,0 +1,100 @@
|
||||
# Planning Keyless Signing for Verdicts
|
||||
|
||||
> **Status:** PLANNED — Implementation sprints created
|
||||
> **Date:** 2025-12-25
|
||||
> **Updated:** 2025-12-26
|
||||
> **Related:** Extends `25-Dec-2025 - Building a Deterministic Verdict Engine.md`
|
||||
|
||||
---
|
||||
|
||||
## Implementation Sprints
|
||||
|
||||
This advisory has been decomposed into the following implementation sprints:
|
||||
|
||||
| Sprint | Module | Topic | Priority |
|
||||
|--------|--------|-------|----------|
|
||||
| [`SPRINT_20251226_001_SIGNER_fulcio_keyless_client.md`](../implplan/SPRINT_20251226_001_SIGNER_fulcio_keyless_client.md) | Signer | Fulcio keyless signing client | P0 |
|
||||
| [`SPRINT_20251226_002_ATTESTOR_bundle_rotation.md`](../implplan/SPRINT_20251226_002_ATTESTOR_bundle_rotation.md) | Attestor | Monthly bundle rotation | P1 |
|
||||
| [`SPRINT_20251226_003_ATTESTOR_offline_verification.md`](../implplan/SPRINT_20251226_003_ATTESTOR_offline_verification.md) | Attestor | Offline/air-gap verification | P2 |
|
||||
| [`SPRINT_20251226_004_BE_cicd_signing_templates.md`](../implplan/SPRINT_20251226_004_BE_cicd_signing_templates.md) | Backend | CI/CD integration templates | P2 |
|
||||
|
||||
**Total Tasks:** 98 across 4 sprints
|
||||
|
||||
---
|
||||
|
||||
## Documentation Created
|
||||
|
||||
- [`docs/modules/signer/guides/keyless-signing.md`](../modules/signer/guides/keyless-signing.md) — Keyless signing guide
|
||||
- [`src/Signer/AGENTS.md`](../../src/Signer/AGENTS.md) — Signer module charter
|
||||
|
||||
---
|
||||
|
||||
## Advisory Content
|
||||
|
||||
Here's a practical, low‑friction way to modernize how you sign and verify build "verdicts" in CI/CD using Sigstore—no long‑lived keys, offline‑friendly, and easy to audit.
|
||||
|
||||
---
|
||||
|
||||
### 1) Use **keyless** signing in CI
|
||||
|
||||
* In your pipeline, obtain an OIDC token (from your CI runner) and let **Fulcio** issue a short‑lived X.509 code‑signing cert (~10 minutes). You sign with the ephemeral key; cert + signature are logged to Rekor. ([Sigstore Blog][1])
|
||||
|
||||
**Why:** no key escrow in CI, nothing persistent to steal, and every signature is time‑bound + transparency‑logged.
|
||||
|
||||
---
|
||||
|
||||
### 2) Keep one **hardware‑backed org key** only for special cases
|
||||
|
||||
* Reserve a physical HSM/YubiKey (or KMS) key for:
|
||||
a) re‑signing monthly bundles (see §4), and
|
||||
b) offline/air‑gapped verification workflows where a trust anchor is needed.
|
||||
Cosign supports disconnected/offline verification patterns and mirroring the proof data. ([Sigstore][2])
|
||||
|
||||
---
|
||||
|
||||
### 3) Make “verdicts” first‑class OCI attestations
|
||||
|
||||
* Emit DSSE/attestations (SBOM deltas, reachability graphs, policy results) as OCI‑attached artifacts and sign them with keyless in CI. (Cosign is designed to sign/verify arbitrary OCI artifacts alongside images.) ([Artifact Hub][3])
|
||||
|
||||
---
|
||||
|
||||
### 4) Publish a **rotation & refresh policy**
|
||||
|
||||
* Every month, collect older attestations and **re‑sign into a long‑lived “bundle”** (plus timestamps) using the org key. This keeps proofs verifiable over years—even if the 10‑minute certs expire—because the bundle contains the cert chain, Rekor inclusion proof, and timestamps suitable for **offline** verification. ([Trustification][4])
|
||||
|
||||
**Suggested SLOs**
|
||||
|
||||
* CI keyless cert TTL: 10 minutes (Fulcio default). ([Sigstore][5])
|
||||
* Bundle cadence: monthly (or per release); retain N=24 months.
|
||||
|
||||
---
|
||||
|
||||
### 5) Offline / air‑gapped verification
|
||||
|
||||
* Mirror the image + attestation + Rekor proof (or bundle) into the disconnected registry. Verify with `cosign verify` using the mirrored materials—no internet needed. (Multiple guides show fully disconnected OpenShift/air‑gapped flows.) ([Red Hat Developer][6])
|
||||
|
||||
---
|
||||
|
||||
### 6) Address common concerns (“myths”)
|
||||
|
||||
* “Short‑lived certs will break verification later.” → They don’t: you verify against the Rekor proof/bundle, not live cert validity. ([Trustification][4])
|
||||
* “Keyless means less security.” → The opposite: no static secrets in CI; certs expire in ~10 minutes; identity bound via OIDC and logged. ([Chainguard][7])
|
||||
|
||||
---
|
||||
|
||||
### Minimal rollout checklist
|
||||
|
||||
* [ ] Enable OIDC on your CI runners; test `cosign sign --identity-token ...`
|
||||
* [ ] Enforce identity/issuer in policy: `--certificate-identity` + `--certificate-oidc-issuer` at verify time. ([Sigstore][2])
|
||||
* [ ] Set up a monthly job to build **Sigstore bundles** from past attestations and re‑sign with the org key. ([Trustification][4])
|
||||
* [ ] For offline sites: mirror images + attestations + bundles; verify with `cosign verify` entirely offline. ([Red Hat Developer][6])
|
||||
|
||||
Want me to draft the exact cosign commands and a GitLab/GitHub Actions snippet for your Stella Ops pipelines (keyless sign, verify gates, monthly bundling, and an offline verification playbook)?
|
||||
|
||||
[1]: https://blog.sigstore.dev/trusted-time/?utm_source=chatgpt.com "Trusted Time in Sigstore"
|
||||
[2]: https://docs.sigstore.dev/cosign/verifying/verify/?utm_source=chatgpt.com "Verifying Signatures - Cosign"
|
||||
[3]: https://artifacthub.io/packages/container/cosign/cosign/latest?utm_source=chatgpt.com "cosign latest · sigstore/cosign"
|
||||
[4]: https://trustification.io/blog/?utm_source=chatgpt.com "Blog"
|
||||
[5]: https://docs.sigstore.dev/certificate_authority/overview/?utm_source=chatgpt.com "Fulcio"
|
||||
[6]: https://developers.redhat.com/articles/2025/08/27/how-verify-container-signatures-disconnected-openshift?utm_source=chatgpt.com "How to verify container signatures in disconnected OpenShift"
|
||||
[7]: https://www.chainguard.dev/unchained/life-of-a-sigstore-signature?utm_source=chatgpt.com "Life of a Sigstore signature"
|
||||
@@ -0,0 +1,178 @@
|
||||
# AI Assistant as Proof-Carrying Evidence Engine
|
||||
|
||||
**Status:** ANALYZED - Sprints Created
|
||||
**Date:** 2025-12-26
|
||||
**Type:** Strategic AI Feature Advisory
|
||||
**Implementation Sprints:** SPRINT_20251226_015 through 019
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory proposes building Stella Ops AI as a **proof-carrying assistant** that:
|
||||
- Copies best UX outcomes from competitors (Snyk: speed to fix; JFrog/Docker: context reduces noise)
|
||||
- Keeps authority in deterministic, replayable engines and signed evidence packs
|
||||
- Extends into Stella Ops' moats: lattice merge semantics, deterministic replay, sovereign/offline cryptography
|
||||
|
||||
## Advisory Content
|
||||
|
||||
### 1) What to Copy: Competitor AI Patterns
|
||||
|
||||
#### A. Snyk-style: "developer-time" intelligence
|
||||
- Fast, developer-local explanation of "why this is a problem" and "what change fixes it"
|
||||
- Reachability-informed prioritization (not just CVSS)
|
||||
- Autofix PRs where safe
|
||||
|
||||
**Stella Ops takeaway:** Make "time-to-understanding" and "time-to-first-fix" first-class KPIs.
|
||||
|
||||
#### B. JFrog-style: contextual exploitability filtering
|
||||
- "Is this vulnerability exploitable in *this* app?" filtering
|
||||
- Runtime loaded-code validation to reduce noise
|
||||
|
||||
**Stella Ops takeaway:** Treat exploitability as an evidence question; label uncertainty explicitly.
|
||||
|
||||
#### C. Aqua-style: AI-guided remediation
|
||||
- Prescriptive remediation steps in human language (and as patches/PRs)
|
||||
- Integrate into CI/CD and ticketing
|
||||
|
||||
**Stella Ops takeaway:** The assistant must be operational: PR creation, change plans, risk acceptance packages.
|
||||
|
||||
#### D. Docker Scout-style: operational context
|
||||
- Use runtime telemetry to prioritize vulnerabilities that can actually bite
|
||||
|
||||
**Stella Ops takeaway:** Runtime evidence as attestable evidence beats "black box AI prioritization."
|
||||
|
||||
#### E. Grype/Trivy reality: deterministic scanners win trust
|
||||
- Strong data hygiene, VEX ingestion, deterministic outputs
|
||||
|
||||
**Stella Ops takeaway:** AI layer must never undermine deterministic trust; must be additive, signed, replayable.
|
||||
|
||||
### 2) Where Competitors Are Weak (Stella Ops Openings)
|
||||
|
||||
1. **Audit-grade reproducibility:** AI explanations often non-replayable
|
||||
2. **Offline/sovereign operations:** Air-gapped + local inference rare
|
||||
3. **Proof-carrying verdicts:** Most risk scores are opaque
|
||||
4. **Merge semantics for VEX:** Few ship policy-controlled lattice merge
|
||||
5. **Time-travel replay + delta verdicts:** Rare as first-class artifacts
|
||||
6. **Network effects for proofs:** Proof-market ledger concepts largely absent
|
||||
|
||||
### 3) Core Principle: "AI is an assistant; evidence is the authority"
|
||||
|
||||
**Every AI output must be either:**
|
||||
- **Pure suggestion** (non-authoritative), or
|
||||
- **Evidence-backed** (authoritative only when evidence pack suffices)
|
||||
|
||||
### 4) Proposed Features
|
||||
|
||||
#### Feature 1: Zastava Companion
|
||||
Evidence-grounded explainability answering: What is it? Why it matters? What evidence supports?
|
||||
- Output anchored to evidence nodes
|
||||
- OCI-attached "Explanation Attestation" with hashes + model digest
|
||||
|
||||
#### Feature 2: Exploitability Confidence Engine
|
||||
- Deterministic classification: Confirmed/Likely/Unknown/Likely Not/Not exploitable
|
||||
- AI proposes "cheapest additional evidence" to reduce Unknown
|
||||
|
||||
#### Feature 3: Remedy Autopilot
|
||||
- AI generates remediation plans
|
||||
- Automated PRs with reproducible build, tests, SBOM delta, signed delta verdict
|
||||
- Fallback to "suggestion-only" if build/tests fail
|
||||
|
||||
#### Feature 4: Auto-VEX Drafting
|
||||
- Generate VEX drafts from evidence
|
||||
- Lattice-aware merge preview
|
||||
|
||||
#### Feature 5: Advisory Ingestion Copilot
|
||||
- Convert unstructured advisories to structured records
|
||||
- Cross-check multiple sources, require corroboration for "trusted" status
|
||||
|
||||
#### Feature 6: Policy Studio Copilot
|
||||
- NL → lattice rules
|
||||
- Test case generation
|
||||
- Compile to deterministic policy with signed snapshots
|
||||
|
||||
### 5) Architecture
|
||||
|
||||
- **scanner.webservice:** lattice merges, deterministic verdict engine (authoritative)
|
||||
- **zastava.webservice (new):** LLM inference + RAG; non-authoritative suggestions
|
||||
- **Feedser/Vexer:** immutable feed snapshots for replay
|
||||
- **Postgres:** system of record
|
||||
- **Valkey:** ephemeral caching (never authoritative)
|
||||
- **Offline profile:** Postgres-only + local inference bundle
|
||||
|
||||
### 6) Deterministic, Replayable AI
|
||||
|
||||
Record and hash:
|
||||
- Prompt template version
|
||||
- Retrieved evidence node IDs + content hashes
|
||||
- Model identifier + weights digest
|
||||
- Decoding parameters (temperature=0, fixed seed)
|
||||
|
||||
Emit as OCI-attached attestation: AIExplanation, RemediationPlan, VEXDraft, PolicyDraft.
|
||||
|
||||
### 7) Roadmap
|
||||
|
||||
- **Phase 1:** Deterministic confidence states + Zastava "Explain with evidence"
|
||||
- **Phase 2:** Remedy Autopilot + Auto-VEX drafting
|
||||
- **Phase 3:** Sovereign/offline AI bundle
|
||||
- **Phase 4:** Proof-market + trust economics
|
||||
|
||||
### 8) KPIs
|
||||
|
||||
- Mean time to triage (MTTT)
|
||||
- Mean time to remediate (MTTR)
|
||||
- Noise rate (% findings that end up "not exploitable")
|
||||
- "Unknown" reduction speed
|
||||
- Reproducibility (% AI artifacts replayed to identical output)
|
||||
- Audit extraction time
|
||||
|
||||
### 9) Risks and Mitigations
|
||||
|
||||
1. Hallucinations → enforce evidence citation
|
||||
2. Prompt injection → sanitize, isolate untrusted text
|
||||
3. Data exfiltration → offline profile, strict egress
|
||||
4. Bad patches → require build+tests+policy gates
|
||||
5. Model drift → pin model digests, snapshot outputs
|
||||
|
||||
---
|
||||
|
||||
## Implementation Assessment
|
||||
|
||||
### Existing Infrastructure (Substantial)
|
||||
|
||||
| Component | Coverage | Location |
|
||||
|-----------|----------|----------|
|
||||
| AdvisoryAI Pipeline | 90% | `src/AdvisoryAI/` |
|
||||
| Guardrail Pipeline | 100% | `AdvisoryAI/Guardrails/` |
|
||||
| Evidence Retrieval | 80% | SBOM context, vector/structured retrieval |
|
||||
| TrustLatticeEngine | 100% | `Policy/TrustLattice/` |
|
||||
| SourceTrustScoreCalculator | 100% | `VexLens/Trust/` |
|
||||
| Remediation Hints | 30% | `Policy.Unknowns/Services/` |
|
||||
| ProofChain/Attestations | 100% | `Attestor/ProofChain/` |
|
||||
| DeltaVerdict | 100% | `StellaOps.DeltaVerdict/` |
|
||||
| Offline/Airgap | 80% | Various modules |
|
||||
|
||||
### Gaps Requiring New Development
|
||||
|
||||
1. **LLM-generated explanations** - Feature 1
|
||||
2. **Remedy Autopilot with PRs** - Feature 3
|
||||
3. **Policy NL→Rules** - Feature 6
|
||||
4. **AI artifact attestation types** - All features
|
||||
5. **Sovereign/offline LLM** - Phase 3
|
||||
|
||||
### Created Sprints
|
||||
|
||||
| Sprint | Topic | Tasks |
|
||||
|--------|-------|-------|
|
||||
| SPRINT_20251226_015_AI_zastava_companion | Explanation generation | 21 tasks |
|
||||
| SPRINT_20251226_016_AI_remedy_autopilot | Automated remediation PRs | 26 tasks |
|
||||
| SPRINT_20251226_017_AI_policy_copilot | NL→lattice rules | 26 tasks |
|
||||
| SPRINT_20251226_018_AI_attestations | AI artifact attestation types | 23 tasks |
|
||||
| SPRINT_20251226_019_AI_offline_inference | Sovereign/offline AI | 26 tasks |
|
||||
|
||||
**Total:** 5 sprints, 122 tasks
|
||||
|
||||
### Archived Advisory
|
||||
|
||||
- "Weighted Confidence for VEX Sources" → moved to `archived/2025-12-26-vex-scoring/`
|
||||
(Substantially implemented in VexLens SourceTrustScoreCalculator)
|
||||
@@ -0,0 +1,117 @@
|
||||
# AI Surfacing UX Patterns Advisory
|
||||
|
||||
**Status:** ANALYZED - Sprint Created
|
||||
**Date:** 2025-12-26
|
||||
**Type:** UX/Design Advisory
|
||||
**Implementation Sprint:** SPRINT_20251226_020_FE_ai_ux_patterns
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory defines how AI results should surface in Stella Ops without becoming obtrusive. The core principle: **AI must behave like a high-quality staff officer—present when needed, silent when not, and always subordinate to evidence and policy.**
|
||||
|
||||
## Core Design Principles
|
||||
|
||||
### 1. Deterministic Verdict First, AI Second
|
||||
|
||||
**Non-negotiable UI ordering:**
|
||||
1. Deterministic verdict (authoritative): severity, policy state, exploitability, SLA, delta
|
||||
2. Evidence summary (authoritative): minimal proof set that drove the verdict
|
||||
3. AI assist (non-authoritative unless evidence-backed): explanation, remediation, suggestions
|
||||
|
||||
### 2. Progressive Disclosure UX
|
||||
|
||||
AI should not add new screens or workflows. It appears as small, optional expansions:
|
||||
- **AI Chips**: Short (3-5 words), action-oriented, clickable
|
||||
- **"Explain" drawer**: Opens on click, not by default
|
||||
|
||||
Chip examples:
|
||||
- "Likely Not Exploitable"
|
||||
- "Reachable Path Found"
|
||||
- "Fix Available: 1-step"
|
||||
- "Needs Evidence: runtime"
|
||||
- "VEX candidate"
|
||||
|
||||
### 3. The "3-Line Doctrine"
|
||||
|
||||
AI output constrained to 3 lines by default:
|
||||
- Line 1: What changed / why you're seeing this now
|
||||
- Line 2: Why it matters in this service
|
||||
- Line 3: Next best action (single step)
|
||||
|
||||
Everything else behind "Show details" / "Show evidence" / "Show alternative fixes"
|
||||
|
||||
### 4. Surface-by-Surface Guidance
|
||||
|
||||
| Surface | AI Behavior |
|
||||
|---------|-------------|
|
||||
| Findings list | 1-2 AI chips max per row; no paragraphs in list view |
|
||||
| Finding detail | 3-panel layout: Verdict → Evidence → AI (subordinate) |
|
||||
| CI/CD output | Opt-in only (`--ai-summary`); max 1 paragraph |
|
||||
| PR comments | Only on state change + actionable fix; no repeated comments |
|
||||
| Notifications | Only on state changes; never "still the same" |
|
||||
| Executive dashboards | No generative narrative; "Top 3 drivers" with evidence links |
|
||||
|
||||
### 5. Contextual Command Bar ("Ask Stella")
|
||||
|
||||
Not a persistent chatbot; a scoped command bar:
|
||||
- Auto-scoped to current context (finding/build/service/release)
|
||||
- Suggested prompts as buttons: "Explain why exploitable", "How to fix?"
|
||||
- Freeform input as secondary option
|
||||
|
||||
### 6. Clear Authority Labels
|
||||
|
||||
Every AI output labeled:
|
||||
- **Evidence-backed**: Links to evidence nodes, citations valid
|
||||
- **Suggestion**: No evidence; user decision required
|
||||
|
||||
### 7. User Controls
|
||||
|
||||
- AI verbosity: Minimal / Standard / Detailed
|
||||
- AI surfaces: Toggle per surface (PR comments, CI logs, UI)
|
||||
- Notifications: Default off; per-team opt-in
|
||||
|
||||
## Implementation Status
|
||||
|
||||
### Created Sprint
|
||||
|
||||
**SPRINT_20251226_020_FE_ai_ux_patterns** (44 tasks):
|
||||
- Phase 1: Core AI Chip Components (7 tasks)
|
||||
- Phase 2: 3-Line AI Summary Component (5 tasks)
|
||||
- Phase 3: AI Panel in Finding Detail (6 tasks)
|
||||
- Phase 4: Contextual Command Bar (6 tasks)
|
||||
- Phase 5: Findings List AI Integration (5 tasks)
|
||||
- Phase 6: User Controls & Preferences (5 tasks)
|
||||
- Phase 7: Dashboard AI Integration (4 tasks)
|
||||
- Phase 8: Testing & Documentation (6 tasks)
|
||||
|
||||
### Dependency Updates
|
||||
|
||||
This sprint is a dependency for:
|
||||
- **SPRINT_20251226_015_AI_zastava_companion**: ZASTAVA-15/16/17/18 (FE tasks)
|
||||
- **SPRINT_20251226_013_FE_triage_canvas**: TRIAGE-14/15/16/17 (AI panel tasks)
|
||||
- **SPRINT_20251226_016_AI_remedy_autopilot**: REMEDY-22/23/24 (FE tasks)
|
||||
|
||||
### Existing Components to Extend
|
||||
|
||||
| Component | Pattern Alignment | Extension Needed |
|
||||
|-----------|-------------------|------------------|
|
||||
| `ReachabilityChipComponent` | ✓ Compact chip | None |
|
||||
| `VexStatusChipComponent` | ✓ Compact chip | None |
|
||||
| `EvidenceDrawerComponent` | ✓ Progressive disclosure | Add AI tab |
|
||||
| `FindingsListComponent` | Partial | Add AI chip slots |
|
||||
| `ConfidenceTierBadgeComponent` | ✓ Authority indicator | Extend for AI |
|
||||
|
||||
## Key Constraints
|
||||
|
||||
1. **No AI text on list views** - chips only
|
||||
2. **3-line default AI** - expandable for more
|
||||
3. **No AI in CI logs unless opt-in** - `--ai-summary` flag
|
||||
4. **PR comments only on state change + actionable fix**
|
||||
5. **AI always subordinate to evidence + deterministic policy**
|
||||
6. **AI must never auto-change enforcement** - no silent downgrades, waivers, or overrides
|
||||
|
||||
## Advisory Content
|
||||
|
||||
[Full advisory content preserved in sprint documentation]
|
||||
@@ -0,0 +1,233 @@
|
||||
# Stella Ops vNext - SBOM Spine and Deterministic Evidence
|
||||
|
||||
> **Status:** IMPLEMENTED — Architecture overview document
|
||||
> **Date:** 2025-12-26
|
||||
> **Updated:** 2025-12-26
|
||||
> **Type:** Vision Document / Architecture Summary
|
||||
> **Implementation:** 100% complete in existing infrastructure
|
||||
|
||||
---
|
||||
|
||||
## Implementation Status
|
||||
|
||||
This advisory describes the **existing StellaOps architecture**. All proposed features are implemented:
|
||||
|
||||
### Core Infrastructure
|
||||
|
||||
| Component | Implementation | Location |
|
||||
|-----------|----------------|----------|
|
||||
| SBOM-first canonical graph | `SbomService` module | `src/SbomService/` |
|
||||
| CycloneDX 1.6 + SPDX 3.x | `SbomNormalizationService` | `src/SbomService/.../Services/` |
|
||||
| in-toto + DSSE attestations | `Attestor` module (6+ predicate types) | `src/Attestor/` |
|
||||
| OCI referrers | `OciReferrerPushClient`, `OciReferrerDiscovery` | `src/ExportCenter/.../Oci/` |
|
||||
| Cosign/Sigstore signatures | `Signer` module | `src/Signer/` |
|
||||
| Regional crypto (eIDAS/FIPS/GOST/SM) | `Cryptography` module | `src/Cryptography/` |
|
||||
| Evidence graph | `ProofChain` library | `src/Attestor/__Libraries/StellaOps.Attestor.ProofChain/` |
|
||||
| Smart-diff algorithm | `DeltaComputationEngine` | `src/__Libraries/StellaOps.DeltaVerdict/` |
|
||||
| Signed delta verdicts | `DeltaSigningService` | `src/__Libraries/StellaOps.DeltaVerdict/Signing/` |
|
||||
| Content-addressed IDs | `IContentAddressedIdGenerator` | `src/Attestor/__Libraries/.../Identifiers/` |
|
||||
| Decision digest (Provcache) | `DecisionDigest`, `VeriKey` | `src/__Libraries/StellaOps.Provcache/` |
|
||||
| Merkle proof verification | `MerkleProofVerifier` | `src/Attestor/__Libraries/.../Merkle/` |
|
||||
| Deterministic replay | `ReplaySeed`, `FrozenEpoch` | `src/__Libraries/StellaOps.Provcache/` |
|
||||
| PostgreSQL + Valkey | All modules | Per-module storage |
|
||||
|
||||
### Predicate Types (All Implemented)
|
||||
|
||||
| Predicate | Type URI | Implementation |
|
||||
|-----------|----------|----------------|
|
||||
| Build Provenance | `StellaOps.BuildProvenance@1` | `Attestor.ProofChain` |
|
||||
| SBOM Attestation | `StellaOps.SBOMAttestation@1` | `Attestor.ProofChain` |
|
||||
| Scan Results | `StellaOps.ScanResults@1` | `Attestor.ProofChain` |
|
||||
| Policy Evaluation | `StellaOps.PolicyEvaluation@1` | `Attestor.ProofChain` |
|
||||
| VEX Attestation | `StellaOps.VEXAttestation@1` | `Attestor.ProofChain` |
|
||||
| Risk Profile Evidence | `StellaOps.RiskProfileEvidence@1` | `Attestor.ProofChain` |
|
||||
|
||||
### Implementation Checklist (All Complete)
|
||||
|
||||
**Pipelines:**
|
||||
- [x] Build: emit SBOM (CDX + SPDX), SLSA provenance (in-toto/DSSE), sign all
|
||||
- [x] Scan: OS + language + config; one attestation per tool
|
||||
- [x] Policy: evaluate rules -> signed verdict attestation; include unknowns count
|
||||
- [x] Publish: push all as OCI referrers; enable verification gate
|
||||
|
||||
**Schema & IDs:**
|
||||
- [x] Normalize component IDs (PURL/CPE) + strong hashes
|
||||
- [x] Evidence graph store: Postgres (authoritative) + cache (Valkey)
|
||||
- [x] Index by image digest; maintain as-of snapshots
|
||||
|
||||
**Determinism:**
|
||||
- [x] Lock feeds, rule versions, tool versions; record all input digests
|
||||
- [x] Provide replay manifest capturing inputs -> expected verdict hash
|
||||
|
||||
**Security & Sovereignty:**
|
||||
- [x] Pluggable crypto: eIDAS/FIPS/GOST/SM; offline bundle export/import
|
||||
- [x] Air-gapped profile: Postgres-only with documented trade-offs
|
||||
|
||||
---
|
||||
|
||||
## Documentation References
|
||||
|
||||
- **SbomService Architecture:** `docs/modules/sbomservice/architecture.md`
|
||||
- **Attestor Architecture:** `docs/modules/attestor/architecture.md`
|
||||
- **Signer Keyless Guide:** `docs/modules/signer/guides/keyless-signing.md`
|
||||
- **ProofChain Specification:** `docs/modules/attestor/proof-chain-specification.md`
|
||||
- **Determinism Gates:** `docs/testing/determinism-gates.md`
|
||||
|
||||
---
|
||||
|
||||
## Advisory Content (Original Vision)
|
||||
|
||||
Here's a simple, practical way to think about a **SBOM-first, VEX-ready supply-chain spine** and the **evidence graph + smart-diff** you can build on top of it—starting from zero and ending with reproducible, signed decisions.
|
||||
|
||||
### SBOM-first spine (VEX-ready)
|
||||
|
||||
**Goal:** make the SBOM the canonical graph of "what's inside," then layer signed evidence (build, scans, policy) so every verdict is portable, replayable, and auditable across registries.
|
||||
|
||||
**Core choices:**
|
||||
|
||||
* **Canonical graph:** treat **CycloneDX 1.6** and **SPDX 3.x** as first-class. Keep both in sync; normalize component IDs (PURL/CPE), hashes, licenses, and relationships.
|
||||
* **Attestations:** use **in-toto + DSSE** for all lifecycle facts:
|
||||
* build (SLSA provenance),
|
||||
* scan results (vuln, secrets, IaC, reachability),
|
||||
* policy evaluation (allow/deny, risk budgets, exceptions).
|
||||
* **Storage/transport:** publish everything as **OCI-attached artifacts** via **OCI Referrers**:
|
||||
* `image:tag` -> SBOM (spdx/cdx), VEX, SARIF, provenance, policy verdicts, exception notes—each a referrer with media type + signature.
|
||||
* **Signatures:** cosign/sigstore (or your regional crypto: eIDAS/FIPS/GOST/SM) for **content-addressed** blobs.
|
||||
|
||||
**Minimum viable workflow:**
|
||||
|
||||
1. **Build step**
|
||||
* Produce identical SBOMs in CycloneDX and SPDX.
|
||||
* Emit SLSA-style provenance attestation.
|
||||
|
||||
2. **Scan step(s)**
|
||||
* OS + language deps + container layers; add **reachability proofs** where possible.
|
||||
* Emit one **scan attestation per tool** (don't conflate).
|
||||
|
||||
3. **Policy step**
|
||||
* Evaluate policies (e.g., OPA/Rego or your lattice rules) **against the SBOM graph + scan evidence**.
|
||||
* Emit a **signed policy verdict attestation** (pass/fail + reasons + unknowns count).
|
||||
|
||||
4. **Publish**
|
||||
* Push image, then push SBOMs, VEX, scan attestations, policy verdicts as **OCI referrers**.
|
||||
|
||||
5. **Verify / consume**
|
||||
* Pull the image's **referrer set**; verify signatures; reconstruct graph locally; **replay** the policy evaluation deterministically.
|
||||
|
||||
**Data model tips:**
|
||||
|
||||
* Stable identifiers: PURLs for packages, digests for layers, Build-ID for binaries.
|
||||
* Edges: `component->dependsOn`, `component->vulnerability`, `component->evidence(attestation)`, `component->policyClaim`.
|
||||
* Keep **time (as-of)** and **source** on every node/edge for replay.
|
||||
|
||||
### Evidence graph + smart-diff
|
||||
|
||||
**Goal:** persist an **explainability graph** (findings <-> components <-> provenance <-> policies) and compute **signed delta-verdicts** on diffs to drive precise impact analysis and quiet noise.
|
||||
|
||||
**What to store:**
|
||||
|
||||
* **Provenance:** who built it, from what, when (commit, builder, materials).
|
||||
* **Findings:** CVEs, misconfigs, secrets, license flags, each with source tool, version, rule, confidence, timestamp.
|
||||
* **Policies & verdicts:** rule set version, inputs' hashes, outcome, rationale.
|
||||
* **Reachability subgraphs:** the minimal path proving exploitability (e.g., symbol -> function -> package -> process start).
|
||||
|
||||
**Smart-diff algorithm (high level):**
|
||||
|
||||
* Compare two images (or SBOM graphs) **by component identity + version + hash**.
|
||||
* For each change class:
|
||||
* **Added/removed/changed component**
|
||||
* **New/cleared/changed finding**
|
||||
* **Changed reachability path**
|
||||
* **Changed policy version/inputs**
|
||||
* Re-evaluate only affected subgraph; produce a **Delta Verdict**:
|
||||
* `status`: safer / risk-equal / risk-higher
|
||||
* `why`: list of net-new reachable vulns, removed reachable vulns, policy/exception impacts
|
||||
* `evidenceRefs`: hashes of attestations used
|
||||
* **Sign the delta verdict (DSSE)** and publish it as an **OCI referrer** too.
|
||||
|
||||
**UX essentials:**
|
||||
|
||||
* Artifact page shows: **"Evidence Stack"** (SBOM, scans, VEX, policy, provenance) with green checks for signatures.
|
||||
* **Smart-diff view:** left vs right image -> "net-new reachable CVEs (+3)", "downgraded risk (-1)" with drill-downs to the exact path/evidence.
|
||||
* **Explain button:** expands to show **why** a CVE is (not) applicable (feature flag off, code path unreachable, kernel mitigation present, etc.).
|
||||
* **Replay badge:** "Deterministic ✓" (inputs' hashes match; verdict reproducible).
|
||||
|
||||
### APIs & types (implemented media types)
|
||||
|
||||
* `application/vnd.cyclonedx+json`
|
||||
* `application/spdx+json`
|
||||
* `application/vnd.in-toto+json; statement=provenance|scan|policy`
|
||||
* `application/vnd.stella.verdict+json` (signed verdict/delta)
|
||||
|
||||
### Existing Implementation Examples
|
||||
|
||||
**Scan Attestation (StellaOps.ScanResults@1):**
|
||||
```json
|
||||
{
|
||||
"type": "https://in-toto.io/Statement/v1",
|
||||
"predicateType": "https://stella.dev/scan/v1",
|
||||
"subject": [{"name": "registry/app@sha256:...", "digest": {"sha256": "..."}}],
|
||||
"predicate": {
|
||||
"tool": {"name": "scannerX", "version": "1.4.2"},
|
||||
"inputs": {"sbom": "sha256:...", "db": "sha256:..."},
|
||||
"findings": [{"id": "CVE-2025-1234", "component": "pkg:pypi/xyz@1.2.3", "severity": "HIGH"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Policy Verdict (StellaOps.PolicyEvaluation@1):**
|
||||
```json
|
||||
{
|
||||
"type": "https://in-toto.io/Statement/v1",
|
||||
"predicateType": "https://stella.dev/verdict/v1",
|
||||
"subject": [{"name": "registry/app@sha256:..."}],
|
||||
"predicate": {
|
||||
"policy": {"id": "prod.v1.7", "hash": "sha256:..."},
|
||||
"inputs": {"sbom": "sha256:...", "scans": ["sha256:...","sha256:..."]},
|
||||
"unknowns": 2,
|
||||
"decision": "allow",
|
||||
"reasons": [
|
||||
"CVE-2025-1234 not reachable (path pruned)",
|
||||
"License policy ok"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Delta Verdict (stella.dev/delta-verdict/v1):**
|
||||
```json
|
||||
{
|
||||
"predicateType": "https://stella.dev/delta-verdict/v1",
|
||||
"predicate": {
|
||||
"from": "sha256:old", "to": "sha256:new",
|
||||
"impact": "risk-higher",
|
||||
"changes": {
|
||||
"componentsAdded": ["pkg:apk/openssl@3.2.1-r1"],
|
||||
"reachableVulnsAdded": ["CVE-2025-2222"]
|
||||
},
|
||||
"evidenceRefs": ["sha256:scanA", "sha256:policyV1"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Operating rules (adopted)
|
||||
|
||||
* **Everything is evidence.** If it influenced a decision, it's an attestation you can sign and attach.
|
||||
* **Same inputs -> same verdict.** If not, treat it as a bug.
|
||||
* **Unknowns budgeted by policy.** E.g., "fail prod if unknowns > 0; warn in dev."
|
||||
* **Diffs decide deployments.** Gate on the **delta verdict**, not raw CVE counts.
|
||||
* **Portable by default.** If you move registries, your decisions move with the image via referrers.
|
||||
|
||||
---
|
||||
|
||||
## Related Implementation
|
||||
|
||||
| Module | Purpose | Documentation |
|
||||
|--------|---------|---------------|
|
||||
| `SbomService` | SBOM ledger, lineage, versioning | `docs/modules/sbomservice/` |
|
||||
| `Attestor` | DSSE, in-toto, Rekor integration | `docs/modules/attestor/` |
|
||||
| `Signer` | Keyless + KMS signing | `docs/modules/signer/` |
|
||||
| `Provcache` | Decision digest, VeriKey | `src/__Libraries/StellaOps.Provcache/` |
|
||||
| `DeltaVerdict` | Smart-diff engine | `src/__Libraries/StellaOps.DeltaVerdict/` |
|
||||
| `ExportCenter` | OCI referrer publishing | `src/ExportCenter/` |
|
||||
| `Policy` | Lattice evaluation, verdicts | `src/Policy/` |
|
||||
@@ -0,0 +1,175 @@
|
||||
Here’s a simple, practical way to think about a **SBOM‑first, VEX‑ready supply‑chain spine** and the **evidence graph + smart‑diff** you can build on top of it—starting from zero and ending with reproducible, signed decisions.
|
||||
|
||||
# SBOM‑first spine (VEX‑ready)
|
||||
|
||||
**Goal:** make the SBOM the canonical graph of “what’s inside,” then layer signed evidence (build, scans, policy) so every verdict is portable, replayable, and auditable across registries.
|
||||
|
||||
**Core choices**
|
||||
|
||||
* **Canonical graph:** treat **CycloneDX 1.6** and **SPDX 3.x** as first‑class. Keep both in sync; normalize component IDs (PURL/CPE), hashes, licenses, and relationships.
|
||||
* **Attestations:** use **in‑toto + DSSE** for all lifecycle facts:
|
||||
|
||||
* build (SLSA provenance),
|
||||
* scan results (vuln, secrets, IaC, reachability),
|
||||
* policy evaluation (allow/deny, risk budgets, exceptions).
|
||||
* **Storage/transport:** publish everything as **OCI‑attached artifacts** via **OCI Referrers**:
|
||||
|
||||
* `image:tag` → SBOM (spdx/cdx), VEX, SARIF, provenance, policy verdicts, exception notes—each a referrer with media type + signature.
|
||||
* **Signatures:** cosign/sigstore (or your regional crypto: eIDAS/FIPS/GOST/SM) for **content‑addressed** blobs.
|
||||
|
||||
**Minimum viable workflow**
|
||||
|
||||
1. **Build step**
|
||||
|
||||
* Produce identical SBOMs in CycloneDX and SPDX.
|
||||
* Emit SLSA‑style provenance attestation.
|
||||
2. **Scan step(s)**
|
||||
|
||||
* OS + language deps + container layers; add **reachability proofs** where possible.
|
||||
* Emit one **scan attestation per tool** (don’t conflate).
|
||||
3. **Policy step**
|
||||
|
||||
* Evaluate policies (e.g., OPA/Rego or your lattice rules) **against the SBOM graph + scan evidence**.
|
||||
* Emit a **signed policy verdict attestation** (pass/fail + reasons + unknowns count).
|
||||
4. **Publish**
|
||||
|
||||
* Push image, then push SBOMs, VEX, scan attestations, policy verdicts as **OCI referrers**.
|
||||
5. **Verify / consume**
|
||||
|
||||
* Pull the image’s **referrer set**; verify signatures; reconstruct graph locally; **replay** the policy evaluation deterministically.
|
||||
|
||||
**Data model tips**
|
||||
|
||||
* Stable identifiers: PURLs for packages, digests for layers, Build‑ID for binaries.
|
||||
* Edges: `component→dependsOn`, `component→vulnerability`, `component→evidence(attestation)`, `component→policyClaim`.
|
||||
* Keep **time (as‑of)** and **source** on every node/edge for replay.
|
||||
|
||||
# Evidence graph + smart‑diff
|
||||
|
||||
**Goal:** persist an **explainability graph** (findings ↔ components ↔ provenance ↔ policies) and compute **signed delta‑verdicts** on diffs to drive precise impact analysis and quiet noise.
|
||||
|
||||
**What to store**
|
||||
|
||||
* **Provenance:** who built it, from what, when (commit, builder, materials).
|
||||
* **Findings:** CVEs, misconfigs, secrets, license flags, each with source tool, version, rule, confidence, timestamp.
|
||||
* **Policies & verdicts:** rule set version, inputs’ hashes, outcome, rationale.
|
||||
* **Reachability subgraphs:** the minimal path proving exploitability (e.g., symbol → function → package → process start).
|
||||
|
||||
**Smart‑diff algorithm (high level)**
|
||||
|
||||
* Compare two images (or SBOM graphs) **by component identity + version + hash**.
|
||||
* For each change class:
|
||||
|
||||
* **Added/removed/changed component**
|
||||
* **New/cleared/changed finding**
|
||||
* **Changed reachability path**
|
||||
* **Changed policy version/inputs**
|
||||
* Re‑evaluate only affected subgraph; produce a **Delta Verdict**:
|
||||
|
||||
* `status`: safer / risk‑equal / risk‑higher
|
||||
* `why`: list of net‑new reachable vulns, removed reachable vulns, policy/exception impacts
|
||||
* `evidenceRefs`: hashes of attestations used
|
||||
* **Sign the delta verdict (DSSE)** and publish it as an **OCI referrer** too.
|
||||
|
||||
**UX essentials**
|
||||
|
||||
* Artifact page shows: **“Evidence Stack”** (SBOM, scans, VEX, policy, provenance) with green checks for signatures.
|
||||
* **Smart‑diff view:** left vs right image → “net‑new reachable CVEs (+3)”, “downgraded risk (‑1)” with drill‑downs to the exact path/evidence.
|
||||
* **Explain button:** expands to show **why** a CVE is (not) applicable (feature flag off, code path unreachable, kernel mitigation present, etc.).
|
||||
* **Replay badge:** “Deterministic ✅” (inputs’ hashes match; verdict reproducible).
|
||||
|
||||
# Implementation checklist (team‑ready)
|
||||
|
||||
**Pipelines**
|
||||
|
||||
* [ ] Build: emit SBOM (CDX + SPDX), SLSA provenance (in‑toto/DSSE), sign all.
|
||||
* [ ] Scan: OS + language + config + (optional) eBPF/runtime; one attestation per tool.
|
||||
* [ ] Policy: evaluate rules → signed verdict attestation; include **unknowns count**.
|
||||
* [ ] Publish: push all as OCI referrers; enable verification gate on pull/deploy.
|
||||
|
||||
**Schema & IDs**
|
||||
|
||||
* [ ] Normalize component IDs (PURL/CPE) + strong hashes; map binaries (Build‑ID → package).
|
||||
* [ ] Evidence graph store: Postgres (authoritative) + cache (Valkey) for queries.
|
||||
* [ ] Index by image digest; maintain **as‑of** snapshots for time‑travel.
|
||||
|
||||
**Determinism**
|
||||
|
||||
* [ ] Lock feeds, rule versions, tool versions; record all **input digests**.
|
||||
* [ ] Provide a `replay.yaml` manifest capturing inputs → expected verdict hash.
|
||||
|
||||
**Security & sovereignty**
|
||||
|
||||
* [ ] Pluggable crypto: eIDAS/FIPS/GOST/SM; offline bundle export/import.
|
||||
* [ ] Air‑gapped profile: Postgres‑only with documented trade‑offs.
|
||||
|
||||
**APIs & types (suggested media types)**
|
||||
|
||||
* `application/vnd.cyclonedx+json`
|
||||
* `application/spdx+json`
|
||||
* `application/vnd.in-toto+json; statement=provenance|scan|policy`
|
||||
* `application/vnd.stella.verdict+json` (your signed verdict/delta)
|
||||
|
||||
**Minimal object examples (sketches)**
|
||||
|
||||
*Attestation (scan)*
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "https://in-toto.io/Statement/v1",
|
||||
"predicateType": "https://stella.dev/scan/v1",
|
||||
"subject": [{"name": "registry/app@sha256:…", "digest": {"sha256": "..."} }],
|
||||
"predicate": {
|
||||
"tool": {"name": "scannerX", "version": "1.4.2"},
|
||||
"inputs": {"sbom": "sha256:…", "db": "sha256:…"},
|
||||
"findings": [{"id": "CVE-2025-1234", "component": "pkg:pypi/xyz@1.2.3", "severity": "HIGH"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Policy verdict (replayable)*
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "https://in-toto.io/Statement/v1",
|
||||
"predicateType": "https://stella.dev/verdict/v1",
|
||||
"subject": [{"name": "registry/app@sha256:…"}],
|
||||
"predicate": {
|
||||
"policy": {"id": "prod.v1.7", "hash": "sha256:…"},
|
||||
"inputs": {"sbom": "sha256:…", "scans": ["sha256:…","sha256:…"]},
|
||||
"unknowns": 2,
|
||||
"decision": "allow",
|
||||
"reasons": [
|
||||
"CVE-2025-1234 not reachable (path pruned)",
|
||||
"License policy ok"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Delta verdict (smart‑diff)*
|
||||
|
||||
```json
|
||||
{
|
||||
"predicateType": "https://stella.dev/delta-verdict/v1",
|
||||
"predicate": {
|
||||
"from": "sha256:old", "to": "sha256:new",
|
||||
"impact": "risk-higher",
|
||||
"changes": {
|
||||
"componentsAdded": ["pkg:apk/openssl@3.2.1-r1"],
|
||||
"reachableVulnsAdded": ["CVE-2025-2222"]
|
||||
},
|
||||
"evidenceRefs": ["sha256:scanA", "sha256:policyV1"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Operating rules you can adopt today
|
||||
|
||||
* **Everything is evidence.** If it influenced a decision, it’s an attestation you can sign and attach.
|
||||
* **Same inputs → same verdict.** If not, treat it as a bug.
|
||||
* **Unknowns budgeted by policy.** E.g., “fail prod if unknowns > 0; warn in dev.”
|
||||
* **Diffs decide deployments.** Gate on the **delta verdict**, not raw CVE counts.
|
||||
* **Portable by default.** If you move registries, your decisions move with the image via referrers.
|
||||
|
||||
If you want, I can turn this into starter repos (SBOM/attestation schemas, OCI‑referrer publish/verify CLI, and a smart‑diff service stub in .NET 10) so your team can plug it into your current pipelines without a big rewrite.
|
||||
@@ -0,0 +1,175 @@
|
||||
# Mapping a Binary Intelligence Graph
|
||||
|
||||
> **Status:** SUPERSEDED
|
||||
> **Date:** 2026-12-26
|
||||
> **Updated:** 2025-12-26
|
||||
> **Superseded By:** BinaryIndex Module Architecture
|
||||
> **Related Sprints:** [`SPRINT_20251226_011_BINIDX_known_build_catalog.md`](../implplan/SPRINT_20251226_011_BINIDX_known_build_catalog.md), [`SPRINT_20251226_012_BINIDX_backport_handling.md`](../implplan/SPRINT_20251226_012_BINIDX_backport_handling.md), [`SPRINT_20251226_013_BINIDX_fingerprint_factory.md`](../implplan/SPRINT_20251226_013_BINIDX_fingerprint_factory.md), [`SPRINT_20251226_014_BINIDX_scanner_integration.md`](../implplan/SPRINT_20251226_014_BINIDX_scanner_integration.md)
|
||||
|
||||
---
|
||||
|
||||
## Supersession Notice
|
||||
|
||||
This advisory has been **superseded** by the comprehensive BinaryIndex module architecture. All proposals in this advisory are covered by the existing design:
|
||||
|
||||
| Advisory Proposal | Implementation | Location |
|
||||
|-------------------|----------------|----------|
|
||||
| artifacts table | `binaries.binary_identity` | `docs/modules/binaryindex/architecture.md` |
|
||||
| symbols table | `BinaryFeatures` in `IBinaryFeatureExtractor` | `src/BinaryIndex/__Libraries/.../Services/` |
|
||||
| vuln_segments (byte_sig/patch_sig) | `VulnFingerprint` model | `src/BinaryIndex/__Libraries/.../Fingerprints/` |
|
||||
| matches table | `FingerprintMatch` model | `src/BinaryIndex/__Libraries/.../Fingerprints/` |
|
||||
| reachability_hints | `ReachabilityStatus` enum | `src/BinaryIndex/__Libraries/.../Models/` |
|
||||
| Build-ID/PE indexer | `ElfFeatureExtractor`, `IBinaryFeatureExtractor` | `src/BinaryIndex/__Libraries/.../Services/` |
|
||||
| Patch-aware handling | `FixEvidence`, changelog/patch parsers | `src/BinaryIndex/__Libraries/.../FixIndex/` |
|
||||
| Corpus connectors | `DebianCorpusConnector`, `IBinaryCorpusConnector` | `src/BinaryIndex/__Libraries/.../Corpus/` |
|
||||
|
||||
### Related Archived Advisories
|
||||
|
||||
- `18-Dec-2025 - Building Better Binary Mapping and Call‑Stack Reachability.md`
|
||||
- `23-Dec-2026 - Binary Mapping as Attestable Proof.md`
|
||||
|
||||
### Related Active Advisories
|
||||
|
||||
- `25-Dec-2025 - Evolving Evidence Models for Reachability.md` - Runtime → build braid, eBPF sampling
|
||||
|
||||
---
|
||||
|
||||
## Original Advisory Content
|
||||
|
||||
Here's a compact blueprint for a **binary‑level knowledge base** that maps ELF Build‑IDs / PE signatures to vulnerable functions, patch lineage, and reachability hints—so your scanner can act like a provenance‑aware "binary oracle," not just a CVE lookup.
|
||||
|
||||
---
|
||||
|
||||
# Why this matters (in plain terms)
|
||||
|
||||
* **Same version ≠ same risk.** Distros (and vendors) frequently **backport** fixes without bumping versions. Only the **binary** tells the truth.
|
||||
* **Function‑level matching** turns noisy "package has CVE" into precise "this exact function range is vulnerable in your binary."
|
||||
* **Reachability hints** cut triage noise by ranking vulns the code path can actually hit at runtime.
|
||||
|
||||
---
|
||||
|
||||
# Minimal starter schema (MVP)
|
||||
|
||||
Keep it tiny so it grows with real evidence:
|
||||
|
||||
**artifacts**
|
||||
|
||||
* `id (pk)`
|
||||
* `platform` (linux, windows)
|
||||
* `format` (ELF, PE)
|
||||
* `build_id` (ELF `.note.gnu.build-id`), `pdb_guid` / `pe_imphash` (Windows)
|
||||
* `sha256` (whole‑file)
|
||||
* `compiler_fingerprint` (e.g., `gcc-13.2`, `msvc-19.39`)
|
||||
* `source_hint` (optional: pname/version if known)
|
||||
|
||||
**symbols**
|
||||
|
||||
* `artifact_id (fk)`
|
||||
* `symbol_name`
|
||||
* `addr_start`, `addr_end` (or RVA for PE)
|
||||
* `section`, `file_offset` (optional)
|
||||
|
||||
**vuln_segments**
|
||||
|
||||
* `id (pk)`
|
||||
* `cve_id` (CVE‑YYYY‑NNNN)
|
||||
* `function_signature` (normalized name + arity)
|
||||
* `byte_sig` (short stable pattern around the vulnerable hunk)
|
||||
* `patch_sig` (pattern from fixed hunk)
|
||||
* `evidence_ref` (link to patch diff, commit, or NVD note)
|
||||
* `backport_flag` (bool)
|
||||
* `introduced_in`, `fixed_in` (semver-ish text; note "backport" when used)
|
||||
|
||||
**matches**
|
||||
|
||||
* `artifact_id (fk)`, `vuln_segment_id (fk)`
|
||||
* `match_type` (`byte`, `range`, `symbol`)
|
||||
* `confidence` (0–1)
|
||||
* `explain` (why we think this matches)
|
||||
|
||||
**reachability_hints**
|
||||
|
||||
* `artifact_id (fk)`, `symbol_name`
|
||||
* `hint_type` (`imported`, `exported`, `hot`, `ebpf_seen`, `graph_core`)
|
||||
* `weight` (0–100)
|
||||
|
||||
---
|
||||
|
||||
# How the oracle answers "Am I affected?"
|
||||
|
||||
1. **Identify**: Look up by Build‑ID / PE signature; fall back to file hash.
|
||||
2. **Locate**: Map symbols → address ranges; scan for `byte_sig`/`patch_sig`.
|
||||
3. **Decide**:
|
||||
|
||||
* if `patch_sig` present ⇒ **Not affected (backported)**.
|
||||
* if `byte_sig` present and reachable (weighted) ⇒ **Affected (prioritized)**.
|
||||
* if only `byte_sig` present, unreachable ⇒ **Affected (low priority)**.
|
||||
* if neither ⇒ **Unknown**.
|
||||
4. **Explain**: Attach `evidence_ref`, the exact offsets, and the reason (match_type + reachability).
|
||||
|
||||
---
|
||||
|
||||
# Ingestion pipeline (no humans in the loop)
|
||||
|
||||
* **Fingerprinting**: extract Build‑ID / PE GUID; compute `sha256`.
|
||||
* **Symbol map**: parse DWARF/PDB if present; else fall back to heuristics (ELF `symtab`, PE exports).
|
||||
* **Patch intelligence**: auto‑diff upstream commits (plus major distros) → synthesize short **byte signatures** around changed hunks (stable across relocations).
|
||||
* **Evidence links**: store URLs/commit IDs for cross‑audit.
|
||||
* **Noise control**: only accept a vuln signature if it hits N≥3 independent binaries across distros (tunable).
|
||||
|
||||
---
|
||||
|
||||
# Deterministic verdicts (fit to Stella Ops)
|
||||
|
||||
* **Inputs**: `(artifact fingerprint, vuln_segments@version, reachability@policy)`
|
||||
* **Output**: **Signed OCI attestation** "verdict.json" (same inputs → same verdict).
|
||||
* **Replay**: keep rule bundle & feed hashes for audit.
|
||||
* **Backport precedence**: `patch_sig` beats package version claims every time.
|
||||
|
||||
---
|
||||
|
||||
# Fast path to MVP (2 sprints)
|
||||
|
||||
* Add a **Build‑ID/PE indexer** to Scanner.
|
||||
* Teach Feedser/Vexer to ingest `vuln_segments` (with `byte_sig`/`patch_sig`).
|
||||
* Implement matching + verdict attestation; surface **"Backported & Safe"** vs **"Affected & Reachable"** badges in UI.
|
||||
* Seed DB with 10 high‑impact CVEs (OpenSSL, zlib, xz, glibc, libxml2, curl, musl, busybox, OpenSSH, sudo).
|
||||
|
||||
---
|
||||
|
||||
# Example: SQL skeleton (Postgres)
|
||||
|
||||
```sql
|
||||
create table artifacts(
|
||||
id bigserial primary key,
|
||||
platform text, format text,
|
||||
build_id text, pdb_guid text, pe_imphash text,
|
||||
sha256 bytea not null unique,
|
||||
compiler_fingerprint text, source_hint text
|
||||
);
|
||||
|
||||
create table symbols(
|
||||
artifact_id bigint references artifacts(id),
|
||||
symbol_name text, addr_start bigint, addr_end bigint,
|
||||
section text, file_offset bigint
|
||||
);
|
||||
|
||||
create table vuln_segments(
|
||||
id bigserial primary key,
|
||||
cve_id text, function_signature text,
|
||||
byte_sig bytea, patch_sig bytea,
|
||||
evidence_ref text, backport_flag boolean,
|
||||
introduced_in text, fixed_in text
|
||||
);
|
||||
|
||||
create table matches(
|
||||
artifact_id bigint references artifacts(id),
|
||||
vuln_segment_id bigint references vuln_segments(id),
|
||||
match_type text, confidence real, explain text
|
||||
);
|
||||
|
||||
create table reachability_hints(
|
||||
artifact_id bigint references artifacts(id),
|
||||
symbol_name text, hint_type text, weight int
|
||||
);
|
||||
```
|
||||
@@ -0,0 +1,524 @@
|
||||
# Advisory Lens - Gap Analysis and Implementation Plan
|
||||
|
||||
**Date:** 2025-12-27
|
||||
**Status:** Under Review
|
||||
**Related Advisory:** Advisory Lens Vision Document
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The "Advisory Lens" vision proposes a contextual copilot that learns from organizational data (SBOM changes, reachability graphs, triage outcomes, policy decisions) to surface explainable suggestions. After comprehensive analysis against the StellaOps codebase, this advisory represents a **high-value, strategically aligned enhancement** that leverages substantial existing infrastructure while filling critical gaps.
|
||||
|
||||
### Strategic Fit Score: 9/10
|
||||
|
||||
**Why this matters for StellaOps:**
|
||||
- Directly amplifies the platform's core differentiator: **explainable, evidence-backed decisioning**
|
||||
- Builds on existing investments in reachability, attestations, and policy infrastructure
|
||||
- Creates defensible moat through institutional memory and deterministic replay
|
||||
- Aligns with offline-first, determinism-first architectural principles
|
||||
|
||||
---
|
||||
|
||||
## Gap Analysis: What Exists vs. What's Needed
|
||||
|
||||
### 1. Signals & Learning Sources
|
||||
|
||||
| Advisory Requirement | Existing Capability | Gap Level |
|
||||
|---------------------|---------------------|-----------|
|
||||
| **Reachability graphs** | Scanner: SmartDiff, ReachabilityDrift, 3-bit ReachabilityGate, CallGraph extractors (5 languages) | **LOW** - Already rich |
|
||||
| **SBOM deltas** | Scanner: diff-aware rescans, SmartDiffPredicate; SbomService: lineage ledger, LNM schema | **LOW** - Needs delta extraction API |
|
||||
| **VEX & triage history** | Excititor: VexCandidateEmitter, emission triggers; Findings Ledger: immutable audit trail | **MEDIUM** - Need outcome correlation |
|
||||
| **Runtime hints** | Signals: 5-factor Unknowns scoring, HOT/WARM/COLD bands; Scanner: eBPF/ETW runtime traces (Sprint 3840) | **MEDIUM** - Feature flag detection missing |
|
||||
| **Policy outcomes** | Policy: K4 lattice logic, 7-status PolicyVerdict, PolicyExplanation, SuppressionRuleEvaluator | **LOW** - Outcomes tracked |
|
||||
|
||||
### 2. Core Loop Components
|
||||
|
||||
| Advisory Requirement | Existing Capability | Gap Level |
|
||||
|---------------------|---------------------|-----------|
|
||||
| **Ingest & normalize** | CycloneDX/SPDX fully supported; VEX ingestion; reachability edges via CallGraph | **LOW** |
|
||||
| **Match similar situations** | **BinaryIndex.Fingerprints** exists for binary matching; **NO semantic case matching** | **HIGH** - Core gap |
|
||||
| **Rank next actions** | Signals: Unknowns scoring with decay; Policy: risk scoring | **MEDIUM** - Need action ranking |
|
||||
| **Explain with evidence** | Attestor: ProofBundle, ReasoningPredicate, ProofSpine; StellaVerdict consolidation underway | **LOW** - Strong foundation |
|
||||
| **Capture feedback** | Findings Ledger: immutable audit; VEX approval workflow | **MEDIUM** - Need feedback loop |
|
||||
|
||||
### 3. Data Model & Storage
|
||||
|
||||
| Advisory Requirement | Existing Capability | Gap Level |
|
||||
|---------------------|---------------------|-----------|
|
||||
| **EvidenceCase** | Attestor: EvidencePredicate, content-addressed IDs (RFC 8785) | **MEDIUM** - Need advisory-specific schema |
|
||||
| **Outcome** | PolicyVerdict, VexCandidate with proof_refs | **MEDIUM** - Need outcome consolidation |
|
||||
| **Pattern** (graph-embedding + rules) | Graph module: in-memory, needs persistent backing; BinaryIndex: fingerprints | **HIGH** - Core gap |
|
||||
| **Signed & replayable** | Attestor: DSSE, Rekor, offline verification; Replay module exists | **LOW** |
|
||||
|
||||
### 4. Attestation Infrastructure
|
||||
|
||||
| Advisory Requirement | Existing Capability | Gap Level |
|
||||
|---------------------|---------------------|-----------|
|
||||
| **advisory.attestation type** | Attestor supports 6+ predicate types; adding new types is documented pattern | **LOW** - Add new predicate |
|
||||
| **OCI-attached attestation** | Scanner Sprint 3850: OCI artifact storage for slices | **LOW** - Reuse pattern |
|
||||
|
||||
### 5. UI Components
|
||||
|
||||
| Advisory Requirement | Existing Capability | Gap Level |
|
||||
|---------------------|---------------------|-----------|
|
||||
| **Lens panel** | Angular 17 frontend exists; no "Lens" component yet | **MEDIUM** - New component |
|
||||
| **Inline hints** | VEX emission surfaces candidates in triage UI | **MEDIUM** - Extend pattern |
|
||||
| **Playbooks drawer** | Policy templates exist; no dry-run UI | **HIGH** - New feature |
|
||||
| **Evidence chips** | Attestor proof chain visualization exists | **LOW** - Reuse |
|
||||
|
||||
---
|
||||
|
||||
## Detailed Gap Assessment
|
||||
|
||||
### GAP-1: Semantic Case Matching (HIGH)
|
||||
|
||||
**What's missing:** The ability to fingerprint a situation (vuln + reachability path + context) and find similar historical cases.
|
||||
|
||||
**What exists:**
|
||||
- `BinaryIndex.Fingerprints` for binary identity extraction
|
||||
- `Scheduler.FailureSignatureIndexer` for failure pattern indexing
|
||||
- Graph module with diff/overlay capabilities
|
||||
|
||||
**Required:**
|
||||
- Graph embedding/fingerprint library for vulnerability situations
|
||||
- Similarity index (top-k nearest neighbor search)
|
||||
- Pattern storage with policy/outcome linkage
|
||||
|
||||
### GAP-2: Action Ranking Engine (MEDIUM)
|
||||
|
||||
**What's missing:** Greedy risk-per-change ranking algorithm.
|
||||
|
||||
**What exists:**
|
||||
- Signals: Unknowns 5-factor scoring with configurable weights
|
||||
- Policy: Risk scoring via `StellaOps.Policy.Scoring`
|
||||
- SmartDiff: reachability-weighted findings
|
||||
|
||||
**Required:**
|
||||
- Upgrade ranking algorithm (actions that remove most reachable CVEs per change)
|
||||
- Integration with SBOM delta to compute "change units"
|
||||
|
||||
### GAP-3: Feedback Loop Integration (MEDIUM)
|
||||
|
||||
**What's missing:** Capturing accept/modify/ignore actions to train suggestions.
|
||||
|
||||
**What exists:**
|
||||
- Findings Ledger: immutable audit trail
|
||||
- VEX approval workflow in Excititor
|
||||
|
||||
**Required:**
|
||||
- Feedback event schema
|
||||
- Outcome correlation service
|
||||
- Precision@k tracking
|
||||
|
||||
### GAP-4: Playbook/Dry-Run Infrastructure (HIGH)
|
||||
|
||||
**What's missing:** One-click policy application with preview.
|
||||
|
||||
**What exists:**
|
||||
- Policy simulation (Scheduler: `PolicyBatchSimulationWorker`)
|
||||
- Suppression rules with override providers
|
||||
|
||||
**Required:**
|
||||
- Dry-run API with diff preview
|
||||
- Rollback plan generation
|
||||
- Playbook templating system
|
||||
|
||||
### GAP-5: Advisory Service (NEW MODULE)
|
||||
|
||||
**What's missing:** Central service to compute and surface suggestions.
|
||||
|
||||
**What exists:**
|
||||
- AdvisoryAI module (AI-assisted analysis with LLM guardrails) - can be extended
|
||||
- Scanner.WebService adjacent pattern
|
||||
|
||||
**Required:**
|
||||
- Advisory suggestion computation service
|
||||
- REST API for suggestions
|
||||
- Background worker for proactive analysis
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| Similarity matching produces poor results | Medium | High | Start with simple heuristics; add ML gradually |
|
||||
| Performance overhead on suggestion computation | Medium | Medium | Background computation; aggressive caching |
|
||||
| User distrust of "AI suggestions" | Low | High | Always show evidence; never hide reasoning |
|
||||
| Scope creep into full ML platform | High | Medium | Phase boundaries; v1 heuristics-only |
|
||||
| Integration complexity across modules | Medium | Medium | Consolidate into single AdvisoryLens module |
|
||||
|
||||
---
|
||||
|
||||
## Recommendation: PROCEED with Phased Implementation
|
||||
|
||||
### Why Proceed:
|
||||
1. **Strategic Moat:** Institutional memory is defensible
|
||||
2. **Leverage Existing:** 70%+ infrastructure already built
|
||||
3. **User Delight:** Reduces triage time measurably
|
||||
4. **Determinism Aligned:** Replay-safe suggestions fit StellaOps philosophy
|
||||
|
||||
### Critical Success Factors:
|
||||
1. Every suggestion MUST cite prior evidence
|
||||
2. Deterministic replay of suggestion computation
|
||||
3. No opaque ML - start with interpretable heuristics
|
||||
4. Offline-first: works in air-gapped deployments
|
||||
|
||||
---
|
||||
|
||||
## Sprint/Task Breakdown
|
||||
|
||||
### Phase 1: Foundation (Sprints 4000-4020)
|
||||
|
||||
#### SPRINT_4000_0001_0001_LB_advisory_lens_core
|
||||
|
||||
**Objective:** Create core AdvisoryLens library with data models and interfaces.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 1.1 | TODO | Define `AdvisoryCase` model (sbom_hash_from/to, vuln_id, reachable_path_hash, context_keys) |
|
||||
| 1.2 | TODO | Define `AdvisoryOutcome` model (action, reason_code, proof_refs, feedback_status) |
|
||||
| 1.3 | TODO | Define `AdvisoryPattern` model (fingerprint, rules_digest, linked_outcomes) |
|
||||
| 1.4 | TODO | Define `AdvisorySuggestion` model (action, confidence, evidence_refs, explanation) |
|
||||
| 1.5 | TODO | Create `IAdvisoryLensService` interface |
|
||||
| 1.6 | TODO | Add canonical JSON serialization with RFC 8785 |
|
||||
| 1.7 | TODO | Add content-addressed ID generation |
|
||||
|
||||
**Files:**
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Models/AdvisoryCase.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Models/AdvisoryOutcome.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Models/AdvisoryPattern.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Models/AdvisorySuggestion.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Services/IAdvisoryLensService.cs`
|
||||
|
||||
#### SPRINT_4000_0001_0002_LB_graph_fingerprint
|
||||
|
||||
**Objective:** Deterministic graph fingerprinting for reachability subgraphs.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 2.1 | TODO | Design fingerprint schema (vuln + entrypoint + path + context) |
|
||||
| 2.2 | TODO | Implement `ReachabilityFingerprintBuilder` with deterministic hashing |
|
||||
| 2.3 | TODO | Add context extraction (feature flags, env vars, policy bindings) |
|
||||
| 2.4 | TODO | Create `IGraphFingerprintService` interface |
|
||||
| 2.5 | TODO | Add serialization to BLAKE3 content-addressed ID |
|
||||
| 2.6 | TODO | Write determinism tests (same inputs = same fingerprint) |
|
||||
|
||||
**Files:**
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Fingerprinting/ReachabilityFingerprintBuilder.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Fingerprinting/IGraphFingerprintService.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Fingerprinting/ContextExtractor.cs`
|
||||
|
||||
#### SPRINT_4000_0001_0003_BE_similarity_index
|
||||
|
||||
**Objective:** Pattern similarity index with top-k retrieval.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 3.1 | TODO | Design PostgreSQL schema for patterns with GIN indexes |
|
||||
| 3.2 | TODO | Implement `PatternRepository` with similarity search |
|
||||
| 3.3 | TODO | Add Valkey cache layer for hot patterns |
|
||||
| 3.4 | TODO | Create `ISimilarityIndexService` interface |
|
||||
| 3.5 | TODO | Implement simple Jaccard similarity for v1 |
|
||||
| 3.6 | TODO | Add threshold-based noise gating |
|
||||
| 3.7 | TODO | Write integration tests with Testcontainers |
|
||||
|
||||
**Files:**
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens.Persistence/Postgres/PatternRepository.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Services/SimilarityIndexService.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Services/ISimilarityIndexService.cs`
|
||||
|
||||
#### SPRINT_4000_0002_0001_BE_sbom_delta_service
|
||||
|
||||
**Objective:** Extract and expose SBOM deltas for suggestion computation.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 4.1 | TODO | Create `SbomDeltaExtractor` using existing SmartDiff infrastructure |
|
||||
| 4.2 | TODO | Define delta schema (added, removed, upgraded, downgraded packages) |
|
||||
| 4.3 | TODO | Add transitive dependency tracking |
|
||||
| 4.4 | TODO | Expose `GET /api/v1/sbom/{id}/delta?to={id}` endpoint |
|
||||
| 4.5 | TODO | Add deterministic ordering to delta output |
|
||||
|
||||
**Files:**
|
||||
- `src/SbomService/StellaOps.SbomService/Services/SbomDeltaExtractor.cs`
|
||||
- `src/SbomService/StellaOps.SbomService/Endpoints/SbomDeltaEndpoints.cs`
|
||||
|
||||
### Phase 1 Continued: Heuristics Engine (Sprints 4010-4020)
|
||||
|
||||
#### SPRINT_4010_0001_0001_BE_suggestion_engine
|
||||
|
||||
**Objective:** Core suggestion computation with initial heuristics.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 5.1 | TODO | Implement `GreedyRiskPerChangeRanker` |
|
||||
| 5.2 | TODO | Implement `SubgraphSimilarityMatcher` |
|
||||
| 5.3 | TODO | Implement `NoiseGateFilter` (weak evidence threshold) |
|
||||
| 5.4 | TODO | Create `SuggestionEngine` orchestrator |
|
||||
| 5.5 | TODO | Add explanation generator with evidence links |
|
||||
| 5.6 | TODO | Configure heuristic weights via IOptions |
|
||||
|
||||
**Files:**
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Heuristics/GreedyRiskPerChangeRanker.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Heuristics/SubgraphSimilarityMatcher.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Heuristics/NoiseGateFilter.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Services/SuggestionEngine.cs`
|
||||
|
||||
#### SPRINT_4010_0001_0002_BE_outcome_tracker
|
||||
|
||||
**Objective:** Capture and correlate outcomes from policy decisions.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 6.1 | TODO | Create `OutcomeCorrelationService` |
|
||||
| 6.2 | TODO | Integrate with Findings Ledger events |
|
||||
| 6.3 | TODO | Integrate with VEX approval workflow |
|
||||
| 6.4 | TODO | Add feedback event schema |
|
||||
| 6.5 | TODO | Store outcomes with pattern linkage |
|
||||
| 6.6 | TODO | Implement precision@k tracking |
|
||||
|
||||
**Files:**
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Services/OutcomeCorrelationService.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Events/FeedbackEvent.cs`
|
||||
|
||||
#### SPRINT_4010_0002_0001_BE_advisory_attestation
|
||||
|
||||
**Objective:** New attestation type for advisory suggestions.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 7.1 | TODO | Define `AdvisoryPredicate` following existing patterns |
|
||||
| 7.2 | TODO | Add predicate type: `application/vnd.stellaops.advisory+json` |
|
||||
| 7.3 | TODO | Implement `AdvisoryAttestationBuilder` |
|
||||
| 7.4 | TODO | Add DSSE signing integration |
|
||||
| 7.5 | TODO | Create schema: `docs/schemas/stellaops-advisory.v1.schema.json` |
|
||||
| 7.6 | TODO | Add to Attestor predicate registry |
|
||||
|
||||
**Files:**
|
||||
- `src/Attestor/__Libraries/StellaOps.Attestor.ProofChain/Predicates/AdvisoryPredicate.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Attestation/AdvisoryAttestationBuilder.cs`
|
||||
- `docs/schemas/stellaops-advisory.v1.schema.json`
|
||||
|
||||
### Phase 1: API & Integration (Sprint 4020)
|
||||
|
||||
#### SPRINT_4020_0001_0001_BE_advisory_api
|
||||
|
||||
**Objective:** REST API for advisory suggestions.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 8.1 | TODO | Create `AdvisoryLensController` |
|
||||
| 8.2 | TODO | Implement `GET /api/v1/advisory/suggestions?artifact={id}` |
|
||||
| 8.3 | TODO | Implement `GET /api/v1/advisory/suggestions/{id}/evidence` |
|
||||
| 8.4 | TODO | Implement `POST /api/v1/advisory/feedback` |
|
||||
| 8.5 | TODO | Add tenant isolation via RLS |
|
||||
| 8.6 | TODO | Add rate limiting and caching |
|
||||
|
||||
**Files:**
|
||||
- `src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/Controllers/AdvisoryLensController.cs`
|
||||
|
||||
#### SPRINT_4020_0001_0002_BE_background_worker
|
||||
|
||||
**Objective:** Background suggestion computation on SBOM/VEX changes.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 9.1 | TODO | Create `AdvisorySuggestionWorker` |
|
||||
| 9.2 | TODO | Subscribe to SBOM ingestion events |
|
||||
| 9.3 | TODO | Subscribe to VEX change events |
|
||||
| 9.4 | TODO | Implement batch suggestion computation |
|
||||
| 9.5 | TODO | Add metrics: suggestion latency, cache hit ratio |
|
||||
|
||||
**Files:**
|
||||
- `src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/Workers/AdvisorySuggestionWorker.cs`
|
||||
|
||||
### Phase 2: UI Integration (Sprints 4030-4040)
|
||||
|
||||
#### SPRINT_4030_0001_0001_FE_lens_panel
|
||||
|
||||
**Objective:** "Top 3 Suggestions Today" panel for Timeline/Projects.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 10.1 | TODO | Create `LensPanelComponent` |
|
||||
| 10.2 | TODO | Design suggestion card with evidence chips |
|
||||
| 10.3 | TODO | Add "Apply as dry-run" button |
|
||||
| 10.4 | TODO | Integrate with Timeline view |
|
||||
| 10.5 | TODO | Add loading/empty states |
|
||||
|
||||
**Files:**
|
||||
- `src/Web/StellaOps.Web/src/app/components/lens-panel/`
|
||||
|
||||
#### SPRINT_4030_0001_0002_FE_inline_hints
|
||||
|
||||
**Objective:** Inline hints on detail pages.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 11.1 | TODO | Create `InlineHintComponent` |
|
||||
| 11.2 | TODO | Add to vulnerability detail pages |
|
||||
| 11.3 | TODO | Add to SBOM component pages |
|
||||
| 11.4 | TODO | Style with non-obtrusive design |
|
||||
|
||||
**Files:**
|
||||
- `src/Web/StellaOps.Web/src/app/components/inline-hint/`
|
||||
|
||||
#### SPRINT_4040_0001_0001_FE_playbooks_drawer
|
||||
|
||||
**Objective:** Playbook application with dry-run preview.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 12.1 | TODO | Create `PlaybookDrawerComponent` |
|
||||
| 12.2 | TODO | Implement dry-run diff view |
|
||||
| 12.3 | TODO | Add rollback plan display |
|
||||
| 12.4 | TODO | Integrate with policy application |
|
||||
| 12.5 | TODO | Add confirmation flow |
|
||||
|
||||
**Files:**
|
||||
- `src/Web/StellaOps.Web/src/app/components/playbook-drawer/`
|
||||
|
||||
#### SPRINT_4040_0001_0002_BE_dry_run_api
|
||||
|
||||
**Objective:** Backend support for dry-run policy application.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 13.1 | TODO | Extend `PolicyBatchSimulationWorker` for dry-run |
|
||||
| 13.2 | TODO | Implement `POST /api/v1/advisory/apply?dryRun=true` |
|
||||
| 13.3 | TODO | Generate signed delta-verdict |
|
||||
| 13.4 | TODO | Generate rollback plan |
|
||||
| 13.5 | TODO | Add to attestation chain |
|
||||
|
||||
**Files:**
|
||||
- `src/Policy/StellaOps.Policy.Engine/Services/DryRunService.cs`
|
||||
|
||||
### Phase 2 Continued: Counterfactuals & Templates (Sprint 4050)
|
||||
|
||||
#### SPRINT_4050_0001_0001_BE_counterfactuals
|
||||
|
||||
**Objective:** "Had you done X, Y wouldn't have happened" analysis.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 14.1 | TODO | Design counterfactual computation model |
|
||||
| 14.2 | TODO | Implement `CounterfactualAnalyzer` |
|
||||
| 14.3 | TODO | Integrate with historical findings |
|
||||
| 14.4 | TODO | Add to suggestion explanations |
|
||||
|
||||
**Files:**
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Analysis/CounterfactualAnalyzer.cs`
|
||||
|
||||
#### SPRINT_4050_0001_0002_BE_playbook_templates
|
||||
|
||||
**Objective:** Turn accepted advisories into reusable playbooks.
|
||||
|
||||
| Task | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| 15.1 | TODO | Design playbook template schema |
|
||||
| 15.2 | TODO | Implement `PlaybookTemplateService` |
|
||||
| 15.3 | TODO | Add parameterization support |
|
||||
| 15.4 | TODO | Create template storage |
|
||||
| 15.5 | TODO | Add sharing/team-scope controls |
|
||||
|
||||
**Files:**
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Playbooks/PlaybookTemplate.cs`
|
||||
- `src/__Libraries/StellaOps.AdvisoryLens/Playbooks/PlaybookTemplateService.cs`
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria for v1
|
||||
|
||||
| Metric | Target |
|
||||
|--------|--------|
|
||||
| Suggestions with prior case evidence | >= 70% |
|
||||
| Acceptance rate (accepted or edited) | >= 50% in pilot |
|
||||
| Mean triage time reduction | >= 30% on reachable CVE bursts |
|
||||
| Determinism | Same inputs = identical suggestions |
|
||||
| Offline support | Full functionality in air-gapped mode |
|
||||
|
||||
---
|
||||
|
||||
## Architecture Decision Records
|
||||
|
||||
### ADR-1: Module Placement
|
||||
|
||||
**Decision:** Create `StellaOps.AdvisoryLens` as new library under `src/__Libraries/`, extend `AdvisoryAI` module for hosting.
|
||||
|
||||
**Rationale:**
|
||||
- AdvisoryAI already exists with AI guardrails
|
||||
- Keep core logic in reusable library
|
||||
- WebService/Worker pattern matches existing modules
|
||||
|
||||
### ADR-2: Heuristics Before ML
|
||||
|
||||
**Decision:** Phase 1 uses deterministic heuristics only; ML deferred to Phase 3+.
|
||||
|
||||
**Rationale:**
|
||||
- Determinism is core StellaOps principle
|
||||
- Explainability requires interpretable rules
|
||||
- ML adds complexity without proven value
|
||||
- Easy to add ML later via strategy pattern
|
||||
|
||||
### ADR-3: Pattern Storage
|
||||
|
||||
**Decision:** PostgreSQL with GIN indexes + Valkey cache.
|
||||
|
||||
**Rationale:**
|
||||
- Consistent with platform data strategy
|
||||
- Supports offline operation
|
||||
- GIN indexes efficient for similarity search
|
||||
- Valkey provides hot pattern caching
|
||||
|
||||
### ADR-4: Attestation Type
|
||||
|
||||
**Decision:** New predicate `application/vnd.stellaops.advisory+json`.
|
||||
|
||||
**Rationale:**
|
||||
- Follows established Attestor predicate pattern
|
||||
- Enables signed, replayable suggestions
|
||||
- OCI attachment for portability
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Prerequisites
|
||||
|
||||
| Dependency | Status | Notes |
|
||||
|------------|--------|-------|
|
||||
| StellaVerdict consolidation | In Progress | Sprint 1227.0014.0001 |
|
||||
| Scanner SmartDiff | Complete | Provides reachability basis |
|
||||
| Findings Ledger | Complete | Outcome tracking |
|
||||
| Attestor ProofChain | Complete | Evidence linking |
|
||||
| Angular 17 frontend | Complete | UI foundation |
|
||||
|
||||
---
|
||||
|
||||
## Related Documents
|
||||
|
||||
- `docs/modules/advisory-ai/architecture.md` (to be created)
|
||||
- `docs/modules/scanner/reachability-drift.md`
|
||||
- `docs/modules/attestor/architecture.md`
|
||||
- `docs/modules/policy/architecture.md`
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Module Inventory Leveraged
|
||||
|
||||
| Module | Capabilities Used |
|
||||
|--------|------------------|
|
||||
| Scanner | SmartDiff, ReachabilityDrift, CallGraph, ReachabilityGate, VulnSurfaces |
|
||||
| Policy | K4 lattice, PolicyVerdict, SuppressionRules, RiskScoring |
|
||||
| Signals | Unknowns scoring, HOT/WARM/COLD bands, decay |
|
||||
| Attestor | DSSE, ProofChain, EvidencePredicate, ReasoningPredicate |
|
||||
| VexLens | VEX consensus |
|
||||
| Excititor | VexCandidateEmitter, emission triggers |
|
||||
| SbomService | Lineage ledger, LNM schema |
|
||||
| Graph | Query/diff/overlay APIs |
|
||||
| Findings Ledger | Immutable audit trail |
|
||||
| BinaryIndex | Fingerprinting patterns |
|
||||
|
||||
---
|
||||
|
||||
*This advisory was generated based on comprehensive codebase analysis. All sprint estimates are scope-based, not time-based.*
|
||||
636
docs/product-advisories/archived/ADVISORY_SBOM_LINEAGE_GRAPH.md
Normal file
636
docs/product-advisories/archived/ADVISORY_SBOM_LINEAGE_GRAPH.md
Normal file
@@ -0,0 +1,636 @@
|
||||
# Product Advisory Analysis: SBOM Lineage Graph
|
||||
|
||||
**Advisory Date:** 2025-12-28
|
||||
**Status:** Analysis Complete
|
||||
**Recommendation:** **APPROVED - High Strategic Value**
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The SBOM Lineage Graph advisory proposes a Git-like visualization of container image lineage with hover-to-proof micro-interactions, enabling auditors and developers to explore SBOM/VEX deltas across artifact versions. This feature aligns strongly with StellaOps' core mission of **reproducible vulnerability scanning with VEX-first decisioning** and leverages significant existing infrastructure.
|
||||
|
||||
### Strategic Alignment Score: **9/10**
|
||||
|
||||
| Criterion | Score | Rationale |
|
||||
|-----------|-------|-----------|
|
||||
| Vision Alignment | 10/10 | Core to "proof" differentiator - turns evidence into explorable UX |
|
||||
| Existing Infrastructure | 8/10 | 70%+ backend exists; frontend needs new components |
|
||||
| Customer Value | 9/10 | Direct auditor/compliance value; differentiator vs competitors |
|
||||
| Implementation Risk | Low | Builds on proven patterns (ledger, attestation, diff engine) |
|
||||
| Air-Gap Compatibility | 10/10 | Designed offline-first with replay hashes |
|
||||
|
||||
---
|
||||
|
||||
## Module-by-Module Gap Analysis
|
||||
|
||||
### 1. SbomService (`src/SbomService/`)
|
||||
|
||||
**Existing Capabilities:**
|
||||
- Immutable append-only versioning (`SbomLedgerVersion`)
|
||||
- Parent-child lineage via `ParentVersionId` and `ParentDigest` fields
|
||||
- Build relationship linking (same CI `BuildId`)
|
||||
- Component-level diff engine (`SbomLedgerService.DiffAsync`)
|
||||
- Lineage graph generation with nodes and edges (`GetLineageAsync`)
|
||||
- Content-addressed by SHA256 digest
|
||||
- Deterministic ordering (seq DESC, ordinal comparisons)
|
||||
- Version timeline queries with cursor pagination
|
||||
|
||||
**Gaps Identified:**
|
||||
|
||||
| Gap | Severity | Description |
|
||||
|-----|----------|-------------|
|
||||
| **OCI Ancestry Ingestion** | High | Scanner doesn't populate `ParentVersionId`/`ParentDigest` from OCI manifest |
|
||||
| **Lineage Edge Persistence** | Medium | Edges reconstructed on-read, not persisted for query efficiency |
|
||||
| **Replay Hash per Node** | Medium | No addressable replay token per lineage node for determinism verification |
|
||||
| **Upload API Extension** | Low | `SbomUploadRequestDto` missing `parentImageDigest` field |
|
||||
|
||||
**Recommended Changes:**
|
||||
```csharp
|
||||
// Extend SbomUploadRequestDto
|
||||
public sealed record SbomUploadRequestDto(
|
||||
string ArtifactRef,
|
||||
string Sbom,
|
||||
string? Format,
|
||||
string? Source,
|
||||
string? ParentArtifactDigest, // NEW: OCI parent image digest
|
||||
string? BaseImageRef // NEW: Base image reference
|
||||
);
|
||||
|
||||
// New table: sbom_lineage_edges (persistent)
|
||||
CREATE TABLE sbom_lineage_edges (
|
||||
parent_digest TEXT NOT NULL,
|
||||
child_digest TEXT NOT NULL,
|
||||
relationship TEXT NOT NULL, -- 'parent' | 'build' | 'base'
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (parent_digest, child_digest)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. VEX Modules (`src/Excititor/`, `src/VexLens/`, `src/VexHub/`)
|
||||
|
||||
**Existing Capabilities:**
|
||||
- Merge trace recording with trust weights (`MergeTrace`)
|
||||
- Lattice-based conflict resolution (K4 logic in `OpenVexStatementMerger`)
|
||||
- VexLens consensus projections with `StatusChanged` flag
|
||||
- History tracking via `PreviousProjectionId`
|
||||
- Confidence scoring (0-1 scale)
|
||||
- Conflict detection with severity levels
|
||||
|
||||
**Gaps Identified:**
|
||||
|
||||
| Gap | Severity | Description |
|
||||
|-----|----------|-------------|
|
||||
| **VEX Delta Table** | High | No persistent `vex_delta` table for A→B status changes with rationale |
|
||||
| **Consensus Persistence** | High | VexLens uses in-memory store; no PostgreSQL backend |
|
||||
| **SBOM-Verdict Join** | High | No linking table for (sbom_version_id, cve, verdict) |
|
||||
| **Delta Verdict Attestation** | Medium | No signed delta predicate type for VEX changes |
|
||||
| **Merge Trace Persistence** | Medium | Traces captured but not persisted; lost on restart |
|
||||
|
||||
**Recommended Schema:**
|
||||
```sql
|
||||
-- VEX verdict deltas (A→B)
|
||||
CREATE TABLE vex_delta (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
from_artifact_digest TEXT NOT NULL,
|
||||
to_artifact_digest TEXT NOT NULL,
|
||||
cve TEXT NOT NULL,
|
||||
from_status TEXT NOT NULL,
|
||||
to_status TEXT NOT NULL,
|
||||
rationale JSONB,
|
||||
replay_hash TEXT NOT NULL,
|
||||
signature BYTEA,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (from_artifact_digest, to_artifact_digest, cve)
|
||||
);
|
||||
|
||||
-- SBOM-Verdict linking
|
||||
CREATE TABLE sbom_verdict_link (
|
||||
sbom_version_id UUID NOT NULL REFERENCES sbom_snapshots(id),
|
||||
cve TEXT NOT NULL,
|
||||
consensus_projection_id UUID NOT NULL,
|
||||
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (sbom_version_id, cve)
|
||||
);
|
||||
|
||||
-- Migrate VexLens consensus to Postgres
|
||||
CREATE TABLE vex_consensus_projections (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
vulnerability_id TEXT NOT NULL,
|
||||
product_key TEXT NOT NULL,
|
||||
tenant_id UUID NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
confidence_score DECIMAL(5,4) NOT NULL,
|
||||
outcome TEXT NOT NULL,
|
||||
statement_count INT NOT NULL,
|
||||
conflict_count INT NOT NULL,
|
||||
computed_at TIMESTAMPTZ NOT NULL,
|
||||
stored_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
previous_projection_id UUID REFERENCES vex_consensus_projections(id),
|
||||
status_changed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
UNIQUE (tenant_id, vulnerability_id, product_key, computed_at)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Reachability/Graph (`src/Graph/`, `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/`)
|
||||
|
||||
**Existing Capabilities:**
|
||||
- RichGraph-v1 with nodes, edges, roots
|
||||
- Surface-aware reachability analysis
|
||||
- PathWitness attestations (DSSE-signable)
|
||||
- Gate detection (auth, feature flags, admin-only)
|
||||
- PostgreSQL reachability cache
|
||||
- Deterministic ordering and BLAKE3/SHA256 digests
|
||||
- Confidence tiers (Confirmed, Likely, Present, Unreachable, Unknown)
|
||||
|
||||
**Gaps Identified:**
|
||||
|
||||
| Gap | Severity | Description |
|
||||
|-----|----------|-------------|
|
||||
| **Reachability Delta** | Medium | No diff between two reachability graphs for same CVE across versions |
|
||||
| **Node-level Replay Hash** | Low | Graph has digest but not per-CVE replay hash |
|
||||
|
||||
**Recommended Addition:**
|
||||
```csharp
|
||||
// New service: IReachabilityDeltaService
|
||||
public interface IReachabilityDeltaService
|
||||
{
|
||||
ValueTask<ReachabilityDelta> ComputeDeltaAsync(
|
||||
string fromArtifactDigest,
|
||||
string toArtifactDigest,
|
||||
string cve,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record ReachabilityDelta(
|
||||
string Cve,
|
||||
ReachabilityStatus FromStatus,
|
||||
ReachabilityStatus ToStatus,
|
||||
IReadOnlyList<PathDelta> AddedPaths,
|
||||
IReadOnlyList<PathDelta> RemovedPaths,
|
||||
IReadOnlyList<GateDelta> GateChanges,
|
||||
string ReplayHash);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Policy/Evidence (`src/Policy/`, `src/EvidenceLocker/`, `src/__Libraries/StellaOps.AuditPack/`)
|
||||
|
||||
**Existing Capabilities:**
|
||||
- Evidence-weighted scoring (6D formula with guardrails)
|
||||
- Audit Pack with Merkle root and optional DSSE signing
|
||||
- Replay mechanism with drift detection
|
||||
- Time anchors for deterministic replay
|
||||
- in-toto/DSSE attestations for replays
|
||||
- Export Center with delta computation (added/changed/removed)
|
||||
- Evidence bundle with RFC 3161 timestamps
|
||||
|
||||
**Gaps Identified:**
|
||||
|
||||
| Gap | Severity | Description |
|
||||
|-----|----------|-------------|
|
||||
| **Delta Verdict Export** | Medium | Export Center computes deltas but not as signed attestations |
|
||||
| **Node-Addressable Audit Pack** | Low | Audit packs are per-scan, not per-lineage-node |
|
||||
|
||||
**Recommended Addition:**
|
||||
```csharp
|
||||
// New predicate type for delta verdicts
|
||||
public static class DeltaPredicateTypes
|
||||
{
|
||||
public const string VexDelta = "stella.ops/vex-delta@v1";
|
||||
public const string SbomDelta = "stella.ops/sbom-delta@v1";
|
||||
public const string VerdictDelta = "stella.ops/verdict-delta@v1";
|
||||
}
|
||||
|
||||
// Lineage node evidence pack
|
||||
public sealed record LineageNodeEvidencePack(
|
||||
string ArtifactDigest,
|
||||
string SbomDigest,
|
||||
IReadOnlyList<string> VexVerdictDigests,
|
||||
string? PolicyVerdictDigest,
|
||||
string ReplayHash,
|
||||
DateTimeOffset GeneratedAt);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Attestor/Signer (`src/Attestor/`, `src/Signer/`)
|
||||
|
||||
**Existing Capabilities:**
|
||||
- DSSE signing (keyless/Fulcio, KMS, HSM)
|
||||
- Rekor transparency logging (primary + mirror)
|
||||
- 18+ predicate types (SBOM, VEX, verdict, reachability, PoE)
|
||||
- Proof chain predicates (ProofSpine, Evidence, VEX)
|
||||
- Offline bundle support with inclusion proofs
|
||||
- Trust verdict evaluation
|
||||
|
||||
**Gaps Identified:**
|
||||
|
||||
| Gap | Severity | Description |
|
||||
|-----|----------|-------------|
|
||||
| **Delta Predicate Types** | Medium | Missing `stella.ops/vex-delta@v1`, `stella.ops/sbom-delta@v1` |
|
||||
| **Lineage Attestation** | Low | No predicate for full lineage graph attestation |
|
||||
|
||||
**Recommended Additions to PredicateTypes.cs:**
|
||||
```csharp
|
||||
// Delta predicates
|
||||
public const string VexDelta = "stella.ops/vex-delta@v1";
|
||||
public const string SbomDelta = "stella.ops/sbom-delta@v1";
|
||||
public const string VerdictDelta = "stella.ops/verdict-delta@v1";
|
||||
public const string LineageGraph = "stella.ops/lineage-graph@v1";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Scanner (`src/Scanner/`)
|
||||
|
||||
**Existing Capabilities:**
|
||||
- SBOM generation (CycloneDX 1.4-1.7, SPDX 2.3-3.0.1)
|
||||
- OCI image digest handling via `OciImageReference`
|
||||
- Layer-based component tracking with `LayerDigest`
|
||||
- `SbomCompositionResult` with content hash and recipe
|
||||
|
||||
**Gaps Identified:**
|
||||
|
||||
| Gap | Severity | Description |
|
||||
|-----|----------|-------------|
|
||||
| **OCI Manifest Parsing** | High | No extraction of parent/base image from OCI manifest `config.history` |
|
||||
| **Parent Propagation** | High | `ParentVersionId` infrastructure exists but Scanner doesn't populate it |
|
||||
| **Multi-stage Build Tracking** | Medium | No linking of multi-stage build stages |
|
||||
|
||||
**Recommended Implementation:**
|
||||
```csharp
|
||||
// New service: IOciAncestryExtractor
|
||||
public interface IOciAncestryExtractor
|
||||
{
|
||||
ValueTask<OciAncestry> ExtractAncestryAsync(
|
||||
string imageReference,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record OciAncestry(
|
||||
string ImageDigest,
|
||||
string? BaseImageDigest, // FROM instruction base
|
||||
string? BaseImageRef, // e.g., "alpine:3.19"
|
||||
IReadOnlyList<string> LayerDigests,
|
||||
IReadOnlyList<OciHistoryEntry> History);
|
||||
|
||||
// Extend scan job to include ancestry
|
||||
public sealed record ScanJobResult(
|
||||
/* existing fields */,
|
||||
OciAncestry? Ancestry);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. Frontend (`src/Web/StellaOps.Web/`)
|
||||
|
||||
**Existing Capabilities:**
|
||||
- Graph-diff component with SVG/pan/zoom (0.1x-3x)
|
||||
- Evidence graph (D3 force-directed, lazy-loaded)
|
||||
- Proof tree/spine visualization
|
||||
- Lattice diagram for VEX merge outcomes
|
||||
- Function diff (side-by-side, unified, summary)
|
||||
- Accessibility (ARIA, keyboard, high-contrast, reduced motion)
|
||||
- Dark mode support
|
||||
|
||||
**Gaps Identified:**
|
||||
|
||||
| Gap | Severity | Description |
|
||||
|-----|----------|-------------|
|
||||
| **Lineage Lane View** | High | No Git-like horizontal graph with lanes/branches |
|
||||
| **Hover-to-Proof Card** | High | No micro-panel showing component diff + VEX delta on hover |
|
||||
| **Compare Mode** | High | No arbitrary A⇄B node selection for comparison |
|
||||
| **Timeline Slider** | Medium | No scrubbing through releases by time |
|
||||
| **Evidence Pack Export** | Medium | No one-click ZIP from lineage node |
|
||||
| **"Why Safe?" Button** | Medium | No human paragraph explanation from evidence |
|
||||
|
||||
**Recommended Components:**
|
||||
|
||||
```
|
||||
src/app/features/lineage/
|
||||
├── components/
|
||||
│ ├── lineage-graph/ # Git-like lane visualization
|
||||
│ │ ├── lineage-graph.component.ts
|
||||
│ │ ├── lineage-node.component.ts
|
||||
│ │ └── lineage-edge.component.ts
|
||||
│ ├── lineage-hover-card/ # Micro-panel on hover
|
||||
│ │ ├── component-diff-card.component.ts
|
||||
│ │ └── vex-delta-card.component.ts
|
||||
│ ├── lineage-compare/ # A⇄B comparison view
|
||||
│ │ ├── compare-selector.component.ts
|
||||
│ │ └── compare-panel.component.ts
|
||||
│ ├── lineage-timeline/ # Time slider
|
||||
│ └── why-safe-panel/ # Human explanation
|
||||
├── services/
|
||||
│ ├── lineage-graph.service.ts
|
||||
│ ├── lineage-diff.service.ts
|
||||
│ └── lineage-export.service.ts
|
||||
├── models/
|
||||
│ ├── lineage-node.model.ts
|
||||
│ └── lineage-diff.model.ts
|
||||
└── lineage.routes.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Consolidated Gap Summary
|
||||
|
||||
### Critical (Must Have for MVP)
|
||||
|
||||
| # | Gap | Module | Effort |
|
||||
|---|-----|--------|--------|
|
||||
| 1 | OCI ancestry extraction from manifest | Scanner | 3 days |
|
||||
| 2 | Persist lineage edges to Postgres | SbomService | 2 days |
|
||||
| 3 | VEX delta table with rationale | Excititor/VexLens | 3 days |
|
||||
| 4 | SBOM-verdict linking table | SbomService + VexLens | 2 days |
|
||||
| 5 | Lineage lane view (Git-like UI) | Web Frontend | 5 days |
|
||||
| 6 | Hover-to-proof card (component + VEX diff) | Web Frontend | 3 days |
|
||||
|
||||
### High (Sprint 2)
|
||||
|
||||
| # | Gap | Module | Effort |
|
||||
|---|-----|--------|--------|
|
||||
| 7 | Migrate VexLens consensus to Postgres | VexLens | 3 days |
|
||||
| 8 | A⇄B compare mode with reachability | Web Frontend | 4 days |
|
||||
| 9 | Delta verdict attestation (signed) | Attestor | 2 days |
|
||||
| 10 | Replay hash per lineage node | SbomService | 2 days |
|
||||
| 11 | Evidence pack export (ZIP) | ExportCenter | 2 days |
|
||||
|
||||
### Medium (Future)
|
||||
|
||||
| # | Gap | Module | Effort |
|
||||
|---|-----|--------|--------|
|
||||
| 12 | Timeline slider UI | Web Frontend | 2 days |
|
||||
| 13 | "Why Safe?" human explanation | AdvisoryAI + Web | 3 days |
|
||||
| 14 | Reachability delta service | Graph | 3 days |
|
||||
| 15 | Merge trace persistence | Excititor | 2 days |
|
||||
| 16 | Lineage graph attestation | Attestor | 1 day |
|
||||
|
||||
---
|
||||
|
||||
## Recommended Sprint Plan
|
||||
|
||||
### Sprint 1: Graph + Hover (2 weeks)
|
||||
|
||||
**Objective:** Render lineage graph with hover cards showing SBOM/VEX deltas
|
||||
|
||||
**Working Directories:**
|
||||
- `src/Scanner/` (OCI ancestry)
|
||||
- `src/SbomService/` (lineage edges, SBOM-verdict linking)
|
||||
- `src/Excititor/` (VEX delta table)
|
||||
- `src/Web/StellaOps.Web/` (lineage UI)
|
||||
|
||||
**Tasks:**
|
||||
|
||||
| # | Task | Module | Est. | Depends |
|
||||
|---|------|--------|------|---------|
|
||||
| 1.1 | Implement `IOciAncestryExtractor` to parse OCI manifest history | Scanner | 2d | - |
|
||||
| 1.2 | Add `parentImageDigest`, `baseImageRef` to `SbomUploadRequestDto` | Scanner | 0.5d | - |
|
||||
| 1.3 | Propagate ancestry to SbomService on upload | Scanner + SbomService | 1d | 1.1, 1.2 |
|
||||
| 1.4 | Create `sbom_lineage_edges` table and repository | SbomService | 1d | - |
|
||||
| 1.5 | Persist edges on version creation | SbomService | 0.5d | 1.4 |
|
||||
| 1.6 | Create `vex_delta` table and repository | Excititor | 1.5d | - |
|
||||
| 1.7 | Compute and store VEX deltas on consensus change | VexLens | 1.5d | 1.6 |
|
||||
| 1.8 | Create `sbom_verdict_link` table | SbomService | 1d | - |
|
||||
| 1.9 | Link verdicts to SBOM versions on evaluation | Policy | 1d | 1.8 |
|
||||
| 1.10 | Create `LineageGraphComponent` (lane view) | Web | 3d | - |
|
||||
| 1.11 | Create `LineageNodeComponent` with badges | Web | 1d | 1.10 |
|
||||
| 1.12 | Create `LineageHoverCardComponent` (diff + VEX) | Web | 2d | 1.10 |
|
||||
| 1.13 | Wire hover card to SBOM diff API | Web | 1d | 1.12 |
|
||||
| 1.14 | Wire hover card to VEX delta API | Web | 1d | 1.7, 1.12 |
|
||||
| 1.15 | Add lineage API endpoint `/api/v1/lineage/{artifact}` | SbomService | 1d | 1.5 |
|
||||
|
||||
**Sprint 1 Deliverables:**
|
||||
- Lineage lane view rendering from OCI ancestry
|
||||
- Hover card showing component diff (added/removed/changed)
|
||||
- Hover card showing VEX status deltas with reason
|
||||
- Evidence links in hover card
|
||||
|
||||
---
|
||||
|
||||
### Sprint 2: Compare + Replay (2 weeks)
|
||||
|
||||
**Objective:** Enable A⇄B comparison with replay verification and export
|
||||
|
||||
**Working Directories:**
|
||||
- `src/SbomService/` (replay hash)
|
||||
- `src/VexLens/` (Postgres migration)
|
||||
- `src/Attestor/` (delta attestations)
|
||||
- `src/ExportCenter/` (evidence pack)
|
||||
- `src/Web/StellaOps.Web/` (compare UI)
|
||||
|
||||
**Tasks:**
|
||||
|
||||
| # | Task | Module | Est. | Depends |
|
||||
|---|------|--------|------|---------|
|
||||
| 2.1 | Migrate `InMemoryConsensusProjectionStore` to Postgres | VexLens | 2d | - |
|
||||
| 2.2 | Add `vex_consensus_projections` table | VexLens | 1d | - |
|
||||
| 2.3 | Compute replay hash per lineage node | SbomService | 1.5d | - |
|
||||
| 2.4 | Add delta predicate types to `PredicateTypes.cs` | Attestor | 0.5d | - |
|
||||
| 2.5 | Create `IDeltaVerdictAttestationService` | Attestor | 1.5d | 2.4 |
|
||||
| 2.6 | Sign delta verdicts on VEX change | Attestor + VexLens | 1d | 2.5 |
|
||||
| 2.7 | Create `LineageCompareComponent` (A⇄B selector) | Web | 2d | - |
|
||||
| 2.8 | Create `ComparePanelComponent` (side-by-side) | Web | 2d | 2.7 |
|
||||
| 2.9 | Wire compare to SBOM diff with reachability | Web | 1d | 2.8 |
|
||||
| 2.10 | Add reachability notes to compare view | Web | 1d | 2.9 |
|
||||
| 2.11 | Create `LineageNodeEvidencePack` model | ExportCenter | 0.5d | - |
|
||||
| 2.12 | Implement evidence pack export (ZIP) | ExportCenter | 1.5d | 2.11 |
|
||||
| 2.13 | Add "Export Audit Pack" button to compare view | Web | 0.5d | 2.12 |
|
||||
| 2.14 | Add replay hash display in node detail | Web | 0.5d | 2.3 |
|
||||
| 2.15 | Create replay verification endpoint | SbomService | 1d | 2.3 |
|
||||
|
||||
**Sprint 2 Deliverables:**
|
||||
- Select any two nodes for comparison
|
||||
- Side-by-side SBOM/VEX diff with reachability notes
|
||||
- Signed delta verdict attestations
|
||||
- Exportable Audit Pack (ZIP) with SBOMs, VEX, policy, signatures
|
||||
- Replay hash for local verification
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
### Sprint 1 (Demoable)
|
||||
|
||||
- [ ] Lineage graph renders with lanes (base → derived)
|
||||
- [ ] Hover on node shows component diff in <150ms (cached)
|
||||
- [ ] Hover shows VEX status deltas (e.g., `CVE-2024-1234: not_affected → affected`)
|
||||
- [ ] Evidence links navigate to source documents
|
||||
- [ ] Badges show "N new vulns", "K resolved", "signature ✓/✗"
|
||||
|
||||
### Sprint 2 (Demoable)
|
||||
|
||||
- [ ] Click any two nodes to compare
|
||||
- [ ] Compare view shows reachability delta (paths added/removed)
|
||||
- [ ] "Why?" click shows policy rule + evidence that produced verdict
|
||||
- [ ] Export produces signed Delta Verdict with stable replay hash
|
||||
- [ ] Auditors can replay locally and get identical results
|
||||
|
||||
---
|
||||
|
||||
## Technical Specifications
|
||||
|
||||
### API Contracts
|
||||
|
||||
```yaml
|
||||
# GET /api/v1/lineage/{artifactDigest}
|
||||
LineageGraphResponse:
|
||||
artifact: string
|
||||
nodes:
|
||||
- id: string (version_id)
|
||||
digest: string
|
||||
createdAt: string (ISO-8601)
|
||||
sequenceNumber: int
|
||||
source: string
|
||||
badges:
|
||||
newVulns: int
|
||||
resolvedVulns: int
|
||||
signatureStatus: "valid" | "invalid" | "unsigned"
|
||||
edges:
|
||||
- from: string
|
||||
to: string
|
||||
relationship: "parent" | "build" | "base"
|
||||
|
||||
# GET /api/v1/lineage/diff?from={digest}&to={digest}
|
||||
LineageDiffResponse:
|
||||
sbomDiff:
|
||||
added: ComponentDiff[]
|
||||
removed: ComponentDiff[]
|
||||
versionChanged: VersionChange[]
|
||||
vexDiff:
|
||||
- cve: string
|
||||
fromStatus: string
|
||||
toStatus: string
|
||||
reason: string
|
||||
evidenceLink: string
|
||||
reachabilityDiff:
|
||||
- cve: string
|
||||
fromStatus: string
|
||||
toStatus: string
|
||||
addedPaths: int
|
||||
removedPaths: int
|
||||
replayHash: string
|
||||
|
||||
# POST /api/v1/lineage/export
|
||||
LineageExportRequest:
|
||||
artifactDigest: string
|
||||
includeAttestations: bool
|
||||
sign: bool
|
||||
|
||||
LineageExportResponse:
|
||||
downloadUrl: string
|
||||
bundleDigest: string
|
||||
expiresAt: string
|
||||
```
|
||||
|
||||
### Database Schema
|
||||
|
||||
```sql
|
||||
-- New tables for SBOM Lineage Graph
|
||||
|
||||
-- Persistent lineage edges
|
||||
CREATE TABLE sbom_lineage_edges (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
parent_digest TEXT NOT NULL,
|
||||
child_digest TEXT NOT NULL,
|
||||
relationship TEXT NOT NULL CHECK (relationship IN ('parent', 'build', 'base')),
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (parent_digest, child_digest, tenant_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_lineage_edges_parent ON sbom_lineage_edges(parent_digest, tenant_id);
|
||||
CREATE INDEX idx_lineage_edges_child ON sbom_lineage_edges(child_digest, tenant_id);
|
||||
|
||||
-- VEX delta tracking
|
||||
CREATE TABLE vex_deltas (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
from_artifact_digest TEXT NOT NULL,
|
||||
to_artifact_digest TEXT NOT NULL,
|
||||
cve TEXT NOT NULL,
|
||||
from_status TEXT NOT NULL,
|
||||
to_status TEXT NOT NULL,
|
||||
rationale JSONB,
|
||||
replay_hash TEXT NOT NULL,
|
||||
attestation_digest TEXT,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (from_artifact_digest, to_artifact_digest, cve, tenant_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_vex_deltas_artifact ON vex_deltas(to_artifact_digest, tenant_id);
|
||||
CREATE INDEX idx_vex_deltas_cve ON vex_deltas(cve, tenant_id);
|
||||
|
||||
-- SBOM-verdict linking
|
||||
CREATE TABLE sbom_verdict_links (
|
||||
sbom_version_id UUID NOT NULL,
|
||||
cve TEXT NOT NULL,
|
||||
consensus_projection_id UUID NOT NULL,
|
||||
verdict_status TEXT NOT NULL,
|
||||
confidence_score DECIMAL(5,4) NOT NULL,
|
||||
tenant_id UUID NOT NULL,
|
||||
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (sbom_version_id, cve, tenant_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_verdict_links_cve ON sbom_verdict_links(cve, tenant_id);
|
||||
|
||||
-- Migrate VexLens consensus to Postgres
|
||||
CREATE TABLE vex_consensus_projections (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
vulnerability_id TEXT NOT NULL,
|
||||
product_key TEXT NOT NULL,
|
||||
tenant_id UUID NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
confidence_score DECIMAL(5,4) NOT NULL,
|
||||
outcome TEXT NOT NULL,
|
||||
statement_count INT NOT NULL,
|
||||
conflict_count INT NOT NULL,
|
||||
computed_at TIMESTAMPTZ NOT NULL,
|
||||
stored_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
previous_projection_id UUID REFERENCES vex_consensus_projections(id),
|
||||
status_changed BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_consensus_unique ON vex_consensus_projections(
|
||||
tenant_id, vulnerability_id, product_key, computed_at
|
||||
);
|
||||
CREATE INDEX idx_consensus_status_changed ON vex_consensus_projections(
|
||||
tenant_id, status_changed, computed_at DESC
|
||||
) WHERE status_changed = TRUE;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| OCI manifest parsing complexity | Medium | Medium | Use existing `go-containerregistry` patterns; fallback to layer heuristics |
|
||||
| VexLens migration data loss | Low | High | Dual-write during transition; feature flag rollout |
|
||||
| Frontend performance (large graphs) | Medium | Medium | Virtual scrolling, canvas rendering, pagination (max 100 nodes) |
|
||||
| Replay hash collision | Very Low | High | Use SHA-256 with all inputs; include timestamp in hash |
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The SBOM Lineage Graph advisory is **strongly recommended** for implementation. It:
|
||||
|
||||
1. **Leverages existing infrastructure** - SbomService ledger, VexLens consensus, Attestor signing, Evidence export
|
||||
2. **Fills a clear market gap** - No competitor offers hover-to-proof UX with signed deltas
|
||||
3. **Aligns with core differentiators** - Reproducibility, VEX-first, offline-capable
|
||||
4. **Provides immediate auditor value** - "Show me the proof" becomes explorable
|
||||
|
||||
The 2-sprint plan (4 weeks) delivers a demoable MVP with the core value proposition:
|
||||
- **Sprint 1:** Lineage graph + hover cards (foundation)
|
||||
- **Sprint 2:** Compare mode + signed exports (audit value)
|
||||
|
||||
---
|
||||
|
||||
*Generated by Claude Code analysis on 2025-12-28*
|
||||
@@ -0,0 +1,567 @@
|
||||
# Consolidated Advisory: Deterministic Evidence and Verdict Architecture
|
||||
|
||||
> **Status:** PLANNED — Implementation ~85% complete
|
||||
> **Created:** 2025-12-26
|
||||
> **Consolidated From:**
|
||||
> - `25-Dec-2025 - Building a Deterministic Verdict Engine.md` (original)
|
||||
> - `25-Dec-2025 - Enforcing Canonical JSON for Stable Verdicts.md` (superseded)
|
||||
> - `25-Dec-2025 - Planning Keyless Signing for Verdicts.md` (original)
|
||||
> - `26-Dec-2026 - Smart‑Diff as a Core Evidence Primitive.md` (archived)
|
||||
> - `26-Dec-2026 - Reachability as Cryptographic Proof.md` (archived)
|
||||
> **Technical Specification:** [`docs/technical/architecture/determinism-specification.md`](../technical/architecture/determinism-specification.md)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document consolidates StellaOps guidance on **deterministic verdict computation**, **canonical serialization**, **keyless signing**, and **proof-carrying reachability** into a single authoritative reference. The core proposition:
|
||||
|
||||
**Same SBOM + VEX + reachability subgraph ⇒ exact same, replayable verdict every time—with auditor-grade trails and signed evidence.**
|
||||
|
||||
### Key Capabilities
|
||||
|
||||
1. **Deterministic Evaluation**: Pure functions with no wall-clock, RNG, or network during evaluation
|
||||
2. **Canonical Serialization**: RFC 8785 JCS + Unicode NFC for stable hashes
|
||||
3. **Content-Addressed Storage**: Every input identified by cryptographic hash
|
||||
4. **Keyless Signing**: Sigstore/Fulcio for short-lived certificates with Rekor transparency
|
||||
5. **Proof-Carrying Reachability**: Minimal, reproducible chains showing why vulns can/cannot hit runtime
|
||||
6. **Delta Verdicts**: Signed diffs between evaluation states for CI/CD gates
|
||||
|
||||
### 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 |
|
||||
| Keyless Signing (Fulcio) | 🔄 TODO | SPRINT_20251226_001 |
|
||||
| Cross-Platform Testing | 🔄 TODO | SPRINT_20251226_007 |
|
||||
|
||||
**Overall Progress:** ~85% complete
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Why Determinism Matters](#1-why-determinism-matters)
|
||||
2. [Core Principles](#2-core-principles)
|
||||
3. [Canonical Serialization](#3-canonical-serialization)
|
||||
4. [Data Artifacts](#4-data-artifacts)
|
||||
5. [Signing & Attestation](#5-signing--attestation)
|
||||
6. [Proof-Carrying Reachability](#6-proof-carrying-reachability)
|
||||
7. [Delta Verdicts](#7-delta-verdicts)
|
||||
8. [Engine Architecture](#8-engine-architecture)
|
||||
9. [Testing Strategy](#9-testing-strategy)
|
||||
10. [APIs & Integration](#10-apis--integration)
|
||||
11. [Implementation Status Matrix](#11-implementation-status-matrix)
|
||||
|
||||
---
|
||||
|
||||
## 1. Why Determinism Matters
|
||||
|
||||
### Reproducibility for Auditors
|
||||
Auditors can replay any scan and get identical results. No "it worked on my machine" scenarios—verdicts are cryptographically verifiable.
|
||||
|
||||
### Content-Addressed Caching
|
||||
Hash-based storage enables:
|
||||
- Deduplication across scans
|
||||
- Cache hits on unchanged inputs
|
||||
- Efficient delta computation
|
||||
|
||||
### Cross-Agent Consensus
|
||||
Multiple evaluation engines can independently produce the same verdict for the same manifest, enabling:
|
||||
- Distributed verification
|
||||
- Multi-party attestations
|
||||
- Trust without centralization
|
||||
|
||||
### Operational Clarity
|
||||
Diffs between builds become crisp, machine-verifiable artifacts. When a verdict changes, you know exactly why.
|
||||
|
||||
---
|
||||
|
||||
## 2. Core Principles
|
||||
|
||||
### 2.1 No Wall-Clock Time
|
||||
Evaluation functions never read current time. All timestamps come from input manifests.
|
||||
|
||||
### 2.2 No Random Iteration
|
||||
All collections use deterministic ordering:
|
||||
- Objects: keys sorted lexicographically (Ordinal)
|
||||
- Arrays: preserve input order or sort by stable key
|
||||
- Sets: sort by content hash
|
||||
|
||||
### 2.3 No Network During Evaluation
|
||||
All external data is pre-fetched and pinned by hash before evaluation begins.
|
||||
|
||||
### 2.4 Content-Addressing All Inputs
|
||||
Every input is identified by its cryptographic hash:
|
||||
- `sbom_sha256` - SBOM graph hash
|
||||
- `vex_set_sha256[]` - VEX document hashes
|
||||
- `reach_subgraph_sha256` - Reachability graph hash
|
||||
- `feeds_snapshot_sha256` - Feed snapshot hash
|
||||
- `policy_bundle_sha256` - Policy/rules hash
|
||||
|
||||
### 2.5 Pure Evaluation Functions
|
||||
The verdict function is referentially transparent:
|
||||
```
|
||||
Verdict = f(Manifest)
|
||||
```
|
||||
Given the same manifest, the function always returns the same verdict.
|
||||
|
||||
---
|
||||
|
||||
## 3. Canonical Serialization
|
||||
|
||||
### 3.1 The Rule
|
||||
**Adopt one canonicalization spec and apply it everywhere at ingress/egress of your resolver:**
|
||||
|
||||
- **Strings:** normalize to **UTF-8, Unicode NFC** (Normalization Form C)
|
||||
- **JSON:** canonicalize with **RFC 8785 JCS**: sorted keys, no insignificant whitespace, exact number formatting
|
||||
- **Binary for hashing/signing:** always hash **the canonical bytes**, never ad-hoc serializer output
|
||||
|
||||
### 3.2 Implementation
|
||||
|
||||
```csharp
|
||||
// Canonical JSON with version markers
|
||||
using StellaOps.Canonical.Json;
|
||||
|
||||
var canonical = CanonJson.Canonicalize(myObject);
|
||||
var hash = CanonJson.Hash(myObject);
|
||||
var versionedHash = CanonJson.HashVersioned(myObject, CanonVersion.V1);
|
||||
|
||||
// NFC normalization
|
||||
using StellaOps.Resolver;
|
||||
|
||||
var normalizer = NfcStringNormalizer.Instance;
|
||||
var nfcString = normalizer.Normalize(input);
|
||||
|
||||
// RFC 8785 JCS for raw JSON bytes
|
||||
using StellaOps.Attestor.ProofChain.Json;
|
||||
|
||||
var canonicalizer = new Rfc8785JsonCanonicalizer();
|
||||
var canonicalBytes = canonicalizer.Canonicalize(utf8JsonBytes);
|
||||
```
|
||||
|
||||
### 3.3 Canonicalization Rules
|
||||
|
||||
1. **Object keys** sorted lexicographically (Ordinal comparison)
|
||||
2. **No whitespace** or formatting variations
|
||||
3. **UTF-8 encoding** without BOM
|
||||
4. **IEEE 754 number formatting** (no trailing zeros, no exponent for small integers)
|
||||
5. **Version markers** for migration safety: `_canonVersion: "stella:canon:v1"`
|
||||
|
||||
### 3.4 Contract
|
||||
|
||||
1. Inputs may arrive in any well-formed JSON
|
||||
2. Resolver **normalizes strings (NFC)** and **re-emits JSON in JCS**
|
||||
3. **Content hash** is computed from **JCS-canonical UTF-8 bytes** only
|
||||
4. Any signature/attestation (DSSE/OCI) MUST cover those same bytes
|
||||
5. Any module that can't speak JCS must pass raw data to the resolver
|
||||
|
||||
---
|
||||
|
||||
## 4. Data Artifacts
|
||||
|
||||
### 4.1 Scan Manifest
|
||||
|
||||
The manifest lists all input hashes plus engine version:
|
||||
|
||||
```json
|
||||
{
|
||||
"sbom_sha256": "sha256:a1b2c3...",
|
||||
"vex_set_sha256": ["sha256:d4e5f6...", "sha256:g7h8i9..."],
|
||||
"reach_subgraph_sha256": "sha256:j0k1l2...",
|
||||
"feeds_snapshot_sha256": "sha256:m3n4o5...",
|
||||
"policy_bundle_sha256": "sha256:p6q7r8...",
|
||||
"engine_version": "1.0.0",
|
||||
"policy_semver": "2025.12",
|
||||
"options_hash": "sha256:s9t0u1..."
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Verdict
|
||||
|
||||
Canonical JSON with stable key order:
|
||||
|
||||
```json
|
||||
{
|
||||
"risk_score": 42,
|
||||
"status": "warn",
|
||||
"unknowns_count": 3,
|
||||
"evidence_refs": [
|
||||
"sha256:...",
|
||||
"sha256:..."
|
||||
],
|
||||
"explanations": [
|
||||
{
|
||||
"template": "CVE-{cve} suppressed by VEX claim from {source}",
|
||||
"params": {"cve": "2025-1234", "source": "vendor"},
|
||||
"machine_reason": "VEX_NOT_AFFECTED"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Delta Verdict
|
||||
|
||||
Computed between two manifests/verdicts:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_manifest_sha": "sha256:...",
|
||||
"head_manifest_sha": "sha256:...",
|
||||
"added_findings": [...],
|
||||
"removed_findings": [...],
|
||||
"severity_shift": [...],
|
||||
"unknowns_delta": -2,
|
||||
"policy_effects": [...],
|
||||
"timestamp": "2025-12-26T00:00:00Z",
|
||||
"signature": "..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Signing & Attestation
|
||||
|
||||
### 5.1 Keyless Signing with Sigstore
|
||||
|
||||
Use **keyless** signing in CI pipelines:
|
||||
- Obtain an OIDC token from your CI runner
|
||||
- **Fulcio** issues a short-lived X.509 cert (~10 minutes)
|
||||
- Sign with the ephemeral key
|
||||
- Cert + signature logged to **Rekor**
|
||||
|
||||
**Why:** No key escrow in CI, nothing persistent to steal, every signature is time-bound + transparency-logged.
|
||||
|
||||
### 5.2 Hardware-Backed Org Key
|
||||
|
||||
Reserve a physical HSM/YubiKey (or KMS) key for:
|
||||
- Re-signing monthly bundles
|
||||
- Offline/air-gapped verification workflows
|
||||
|
||||
### 5.3 OCI Attestations
|
||||
|
||||
Emit DSSE/attestations as OCI-attached artifacts:
|
||||
- SBOM deltas
|
||||
- Reachability graphs
|
||||
- Policy results
|
||||
- Verdicts
|
||||
|
||||
### 5.4 Bundle Rotation Policy
|
||||
|
||||
Every month:
|
||||
1. Collect older attestations
|
||||
2. Re-sign into a long-lived "bundle" (plus timestamps) using the org key
|
||||
3. Bundle contains: cert chain, Rekor inclusion proof, timestamps
|
||||
|
||||
**Suggested SLOs:**
|
||||
- CI keyless cert TTL: 10 minutes (Fulcio default)
|
||||
- Bundle cadence: monthly (or per release)
|
||||
- Retention: N=24 months
|
||||
|
||||
### 5.5 Offline Verification
|
||||
|
||||
Mirror the image + attestation + Rekor proof (or bundle) into the disconnected registry. Verify with `cosign verify` using mirrored materials—no internet needed.
|
||||
|
||||
### 5.6 Implementation Sprints
|
||||
|
||||
| Sprint | Module | Topic |
|
||||
|--------|--------|-------|
|
||||
| SPRINT_20251226_001 | Signer | Fulcio keyless signing client |
|
||||
| SPRINT_20251226_002 | Attestor | Monthly bundle rotation |
|
||||
| SPRINT_20251226_003 | Attestor | Offline/air-gap verification |
|
||||
| SPRINT_20251226_004 | Backend | CI/CD integration templates |
|
||||
|
||||
---
|
||||
|
||||
## 6. Proof-Carrying Reachability
|
||||
|
||||
### 6.1 The Concept
|
||||
|
||||
**Reachability** asks: "Could data flow from an attacker to the vulnerable code path during real execution?"
|
||||
|
||||
**Proof-carrying reachability** says: "Don't just say yes/no—hand me a *proof chain* I can re-run."
|
||||
|
||||
### 6.2 Proof Structure
|
||||
|
||||
1. **Scope hash**: content digests for artifact(s) (image layers, SBOM nodes, commit IDs, compiler flags)
|
||||
2. **Policy hash**: the decision rules used
|
||||
3. **Graph snippet**: the *minimal subgraph* connecting entrypoints → sources → validators → sinks
|
||||
4. **Conditions**: feature flags, env vars, platform guards, version ranges, eBPF-observed edges
|
||||
5. **Verdict** (signed): A → {Affected | Not Affected | Under-Constrained} with reason codes
|
||||
6. **Replay manifest**: the inputs needed to recompute the same verdict
|
||||
|
||||
### 6.3 Example Proof
|
||||
|
||||
```
|
||||
Artifact: svc.payments:1.4.7 (image digest sha256:...)
|
||||
CVE: CVE-2024-XYZ in libyaml 0.2.5
|
||||
Entry: POST /import, body → YamlDeserializer.Parse
|
||||
Guards: none (no schema/whitelist prior to parse)
|
||||
Edge chain: HttpBody → Parse(bytes) → LoadNode() → vulnerable_path()
|
||||
Condition: feature flag BULK_IMPORT=true
|
||||
Verdict: AFFECTED
|
||||
Signed: DSSE envelope over {scope hash, policy hash, graph snippet, conditions, verdict}
|
||||
```
|
||||
|
||||
### 6.4 Operating Modes
|
||||
|
||||
| Mode | Unknowns Policy | Proofs |
|
||||
|------|-----------------|--------|
|
||||
| **Strict** (prod) | Fail-closed | Required for Not Affected |
|
||||
| **Lenient** (dev) | Tolerated | Optional but encouraged |
|
||||
|
||||
### 6.5 What to Measure
|
||||
|
||||
- Proof generation rate
|
||||
- Median proof size (KB)
|
||||
- Replay success %
|
||||
- Proof dedup ratio
|
||||
- "Unknowns" burn-down
|
||||
|
||||
---
|
||||
|
||||
## 7. Delta Verdicts
|
||||
|
||||
### 7.1 Evidence Model
|
||||
|
||||
A **semantic delta** captures meaningful differences between two states:
|
||||
|
||||
```json
|
||||
{
|
||||
"subject": {"ociDigest": "sha256:..."},
|
||||
"inputs": {
|
||||
"feeds": [{"type":"cve","digest":"sha256:..."}],
|
||||
"tools": {"sbomer":"1.6.3","reach":"0.9.0","policy":"lattice-2025.12"},
|
||||
"baseline": {"sbomG":"sha256:...","vexSet":"sha256:..."}
|
||||
},
|
||||
"delta": {
|
||||
"components": {"added":[...],"removed":[...],"updated":[...]},
|
||||
"reachability": {"edgesAdded":[...],"edgesRemoved":[...]},
|
||||
"settings": {"changed":[...]},
|
||||
"vex": [{"cve":"CVE-2025-1234","from":"affected","to":"not_affected",
|
||||
"reason":"config_flag_off","evidenceRef":"att#cfg-42"}],
|
||||
"attestations": {"changed":[...]}
|
||||
},
|
||||
"verdict": {
|
||||
"decision": "allow",
|
||||
"riskBudgetUsed": 2,
|
||||
"policyId": "lattice-2025.12",
|
||||
"explanationRefs": ["vex[0]","reachability.edgesRemoved[3]"]
|
||||
},
|
||||
"signing": {"dsse":"...","signer":"stella-authority"}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 Merge Semantics
|
||||
|
||||
Define a policy-controlled lattice for claims:
|
||||
- **Orderings:** `exploit_observed > affected > under_investigation > fixed > not_affected`
|
||||
- **Source weights:** vendor, distro, internal SCA, runtime sensor, pentest
|
||||
- **Conflict rules:** tie-breaks, quorum, freshness windows, required evidence hooks
|
||||
|
||||
### 7.3 OCI Attachment
|
||||
|
||||
Publish delta verdicts as OCI-attached attestations:
|
||||
- Media type: `application/vnd.stella.delta-verdict+json`
|
||||
- Attached alongside SBOM + VEX
|
||||
|
||||
---
|
||||
|
||||
## 8. Engine Architecture
|
||||
|
||||
### 8.1 Evaluation Pipeline
|
||||
|
||||
1. **Normalize inputs**
|
||||
- SBOM: sort by `packageUrl`/`name@version`; resolve aliases
|
||||
- VEX: normalize provider → `vex_id`, `product_ref`, `status`
|
||||
- Reachability: adjacency lists sorted by node ID; hash after topological ordering
|
||||
- Feeds: lock to snapshot (timestamp + commit/hash); no live calls
|
||||
|
||||
2. **Policy bundle**
|
||||
- Declarative rules compiled to canonical IR
|
||||
- Explicit merge precedence (lattice-merge table)
|
||||
- Unknowns policy baked in
|
||||
|
||||
3. **Evaluation**
|
||||
- Build finding set: `(component, vuln, context)` tuples with deterministic IDs
|
||||
- Apply lattice-based VEX merge with evidence pointers
|
||||
- Compute `status` and `risk_score` using fixed-precision math
|
||||
|
||||
4. **Emit**
|
||||
- Canonicalize verdict JSON (RFC 8785 JCS)
|
||||
- Sign verdict (DSSE/COSE/JWS)
|
||||
- Attach as OCI attestation
|
||||
|
||||
### 8.2 Storage & Indexing
|
||||
|
||||
- **CAS (content-addressable store):** `/evidence/<sha256>` for SBOM/VEX/graphs/feeds/policies
|
||||
- **Verdict registry:** keyed by `(image_digest, manifest_sha, engine_version)`
|
||||
- **Delta ledger:** append-only, signed; supports cross-agent consensus
|
||||
|
||||
---
|
||||
|
||||
## 9. Testing Strategy
|
||||
|
||||
### 9.1 Golden Tests
|
||||
|
||||
Fixtures of manifests → frozen verdict JSONs (byte-for-byte comparison).
|
||||
|
||||
```csharp
|
||||
[Theory]
|
||||
[MemberData(nameof(GoldenTestCases))]
|
||||
public async Task Verdict_MatchesGoldenOutput(string manifestPath, string expectedVerdictPath)
|
||||
{
|
||||
var manifest = await LoadManifest(manifestPath);
|
||||
var actual = await _engine.Evaluate(manifest);
|
||||
var expected = await File.ReadAllBytesAsync(expectedVerdictPath);
|
||||
|
||||
Assert.Equal(expected, CanonJson.Canonicalize(actual));
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 Chaos Determinism Tests
|
||||
|
||||
Vary thread counts, env vars, map iteration seeds; assert identical verdicts.
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public async Task Verdict_IsDeterministic_AcrossThreadCounts()
|
||||
{
|
||||
var manifest = CreateTestManifest();
|
||||
var verdicts = new List<byte[]>();
|
||||
|
||||
for (int threads = 1; threads <= 16; threads++)
|
||||
{
|
||||
var verdict = await EvaluateWithThreads(manifest, threads);
|
||||
verdicts.Add(CanonJson.Canonicalize(verdict));
|
||||
}
|
||||
|
||||
Assert.All(verdicts, v => Assert.Equal(verdicts[0], v));
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 Cross-Engine Round-Trips
|
||||
|
||||
Two independent builds of the engine produce the same verdict for the same manifest.
|
||||
|
||||
### 9.4 Time-Travel Tests
|
||||
|
||||
Replay older feed snapshots to ensure stability.
|
||||
|
||||
---
|
||||
|
||||
## 10. APIs & Integration
|
||||
|
||||
### 10.1 API Endpoints
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|----------|---------|
|
||||
| `POST /evaluate` | Returns `verdict.json` + attestation |
|
||||
| `POST /delta` | Returns `delta.json` (signed) |
|
||||
| `GET /replay?manifest_sha=` | Re-executes with cached snapshots |
|
||||
| `GET /evidence/:cid` | Fetches immutable evidence blobs |
|
||||
|
||||
### 10.2 CLI Commands
|
||||
|
||||
```bash
|
||||
# Evaluate an image
|
||||
stella evaluate --subject sha256:... --policy prod.json
|
||||
|
||||
# Verify delta between versions
|
||||
stella verify delta --from abc123 --to def456 --print-proofs
|
||||
|
||||
# Replay a verdict
|
||||
stella replay --manifest-sha sha256:... --assert-identical
|
||||
```
|
||||
|
||||
### 10.3 UI Integration
|
||||
|
||||
- **Run details → "Verdict" tab:** status, risk score, unknowns, top evidence links
|
||||
- **"Diff" tab:** render Delta Verdict (added/removed/changed) with drill-down to proofs
|
||||
- **"Replay" button:** shows exact manifest & engine version; one-click re-evaluation
|
||||
- **Audit export:** zip of manifest, verdict, delta (if any), attestation, referenced evidence
|
||||
|
||||
---
|
||||
|
||||
## 11. Implementation Status Matrix
|
||||
|
||||
### 11.1 Complete (✅)
|
||||
|
||||
| Component | Location | Notes |
|
||||
|-----------|----------|-------|
|
||||
| Canonical JSON (JCS) | `StellaOps.Canonical.Json` | RFC 8785 compliant |
|
||||
| NFC Normalization | `StellaOps.Resolver.NfcStringNormalizer` | Unicode NFC |
|
||||
| Content-Addressed IDs | `Attestor.ProofChain/Identifiers/` | VerdictId, EvidenceId, GraphRevisionId |
|
||||
| DSSE Signing | `Signer/`, `Attestor/` | Multiple algorithm support |
|
||||
| Delta Verdict | `Policy/Deltas/DeltaVerdict.cs` | Full delta computation |
|
||||
| Merkle Trees | `ProofChain/Merkle/` | Evidence chain verification |
|
||||
| Determinism Guards | `Policy.Engine/DeterminismGuard/` | Runtime enforcement |
|
||||
| Replay Manifest | `StellaOps.Replay.Core` | Full manifest serialization |
|
||||
|
||||
### 11.2 In Progress (🔄)
|
||||
|
||||
| Component | Sprint | Priority |
|
||||
|-----------|--------|----------|
|
||||
| Feed Snapshot Coordinator | SPRINT_20251226_007 (DET-GAP-01..04) | P0 |
|
||||
| Keyless Signing (Fulcio) | SPRINT_20251226_001 | P0 |
|
||||
| Monthly Bundle Rotation | SPRINT_20251226_002 | P1 |
|
||||
| Offline Verification | SPRINT_20251226_003 | P2 |
|
||||
| Cross-Platform Testing | SPRINT_20251226_007 (DET-GAP-11..13) | P1 |
|
||||
|
||||
### 11.3 Planned (📋)
|
||||
|
||||
| Component | Target | Notes |
|
||||
|-----------|--------|-------|
|
||||
| Roslyn Analyzer for Resolver Boundary | Q1 2026 | Compile-time enforcement |
|
||||
| Pre-canonical Hash Debug Logging | Q1 2026 | Audit trail |
|
||||
| Consensus Mode | Q2 2026 | Multi-agent verification |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Rollout Plan
|
||||
|
||||
### Phase 1: Shadow Mode
|
||||
Introduce Manifest + canonical verdict format alongside existing policy engine.
|
||||
|
||||
### Phase 2: First-Class Verdicts
|
||||
Make verdicts the first-class artifact (OCI-attached); ship UI "Verdict/Diff".
|
||||
|
||||
### Phase 3: Delta Gates
|
||||
Enforce delta-gates in CI/CD (risk budgets + exception packs referenceable by content ID).
|
||||
|
||||
### Phase 4: Consensus Mode
|
||||
Accept externally signed identical delta verdicts to strengthen trust.
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Archive References
|
||||
|
||||
The following advisories were consolidated into this document:
|
||||
|
||||
| Original File | Archive Location |
|
||||
|--------------|------------------|
|
||||
| `25-Dec-2025 - Building a Deterministic Verdict Engine.md` | (kept in place - primary reference) |
|
||||
| `25-Dec-2025 - Enforcing Canonical JSON for Stable Verdicts.md` | (kept in place - marked superseded) |
|
||||
| `25-Dec-2025 - Planning Keyless Signing for Verdicts.md` | (kept in place - primary reference) |
|
||||
| `26-Dec-2026 - Smart‑Diff as a Core Evidence Primitive.md` | `archived/2025-12-26-superseded/` |
|
||||
| `26-Dec-2026 - Reachability as Cryptographic Proof.md` | `archived/2025-12-26-superseded/` |
|
||||
|
||||
---
|
||||
|
||||
## Appendix C: Related Documents
|
||||
|
||||
| Document | Relationship |
|
||||
|----------|--------------|
|
||||
| [`docs/modules/policy/architecture.md`](../modules/policy/architecture.md) | Policy Engine implementation |
|
||||
| [`docs/modules/policy/design/deterministic-evaluator.md`](../modules/policy/design/deterministic-evaluator.md) | Evaluator design |
|
||||
| [`docs/modules/policy/design/policy-determinism-tests.md`](../modules/policy/design/policy-determinism-tests.md) | Test strategy |
|
||||
| [`docs/modules/scanner/deterministic-execution.md`](../modules/scanner/deterministic-execution.md) | Scanner determinism |
|
||||
| [`docs/technical/architecture/determinism-specification.md`](../technical/architecture/determinism-specification.md) | Technical specification |
|
||||
@@ -0,0 +1,737 @@
|
||||
# Consolidated Advisory: Diff-Aware Release Gates and Risk Budgets
|
||||
|
||||
> **Status:** PLANNED — Consolidated reference document
|
||||
> **Created:** 2025-12-26
|
||||
> **Consolidated From:**
|
||||
> - `25-Dec-2025 - Building a Deterministic Verdict Engine.md` (original)
|
||||
> - `26-Dec-2026 - Diff‑Aware Releases and Auditable Exceptions.md` (archived)
|
||||
> - `26-Dec-2026 - Smart‑Diff as a Core Evidence Primitive.md` (archived)
|
||||
> - `25-Dec-2025 - Visual Diffs for Explainable Triage.md` (archived)
|
||||
> - `26-Dec-2026 - Visualizing the Risk Budget.md` (archived)
|
||||
> - `26-Dec-2026 - Weighted Confidence for VEX Sources.md` (archived)
|
||||
> **Technical References:**
|
||||
> - `archived/2025-12-21-moat-gap-closure/14-Dec-2025 - Smart-Diff Technical Reference.md`
|
||||
> - `archived/2025-12-21-moat-phase2/20-Dec-2025 - Moat Explanation - Risk Budgets and Diff-Aware Release Gates.md`
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document consolidates StellaOps guidance on **diff-aware release gates**, **risk budgets**, **delta verdicts**, and **VEX trust scoring** into a single authoritative reference. The core proposition:
|
||||
|
||||
**Ship fast on low-risk diffs, slow down only when the change warrants it—with deterministic, auditable, replayable evidence at every step.**
|
||||
|
||||
### Key Capabilities
|
||||
|
||||
1. **Risk Budgets**: Quantitative "capacity to take risk" per service tier, preventing reliability degradation
|
||||
2. **Diff-Aware Gates**: Release strictness scales with *what changed*, not generic process
|
||||
3. **Delta Verdicts**: Signed, replayable verdicts comparing before/after states
|
||||
4. **VEX Trust Scoring**: Lattice-based merge of conflicting vulnerability evidence
|
||||
5. **Exception Workflow**: Auditable, evidence-backed, auto-expiring exceptions
|
||||
6. **Visual Diffs**: Explainable triage UI showing exactly what changed and why
|
||||
|
||||
### Implementation Status
|
||||
|
||||
| Component | Status | Location |
|
||||
|-----------|--------|----------|
|
||||
| Canonical JSON (JCS) | COMPLETE | `StellaOps.Canonical.Json` |
|
||||
| Delta Verdict Engine | COMPLETE | `StellaOps.DeltaVerdict.Engine` |
|
||||
| Smart-Diff UI | COMPLETE | `TriageWorkspaceComponent` |
|
||||
| Proof Tree Visualization | COMPLETE | `ProofTreeComponent` |
|
||||
| VEX Merge with Trust Scoring | COMPLETE | `Policy.Engine/VexMerge/` |
|
||||
| Exception Entity Model | COMPLETE | `Policy.Engine/Exceptions/` |
|
||||
| Risk Budget Dashboard | TODO | Sprint 2025Q1 |
|
||||
| Feed Snapshot Coordinator | TODO | SPRINT_20251226_007 |
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Core Concepts](#1-core-concepts)
|
||||
2. [Risk Budget Model](#2-risk-budget-model)
|
||||
3. [Release Gate Levels](#3-release-gate-levels)
|
||||
4. [Delta Verdict Engine](#4-delta-verdict-engine)
|
||||
5. [Smart-Diff Algorithm](#5-smart-diff-algorithm)
|
||||
6. [Exception Workflow](#6-exception-workflow)
|
||||
7. [VEX Trust Scoring](#7-vex-trust-scoring)
|
||||
8. [UI/UX Patterns](#8-uiux-patterns)
|
||||
9. [CI/CD Integration](#9-cicd-integration)
|
||||
10. [Data Models](#10-data-models)
|
||||
|
||||
---
|
||||
|
||||
## 1. Core Concepts
|
||||
|
||||
### 1.1 SBOM, VEX, and Reachability
|
||||
|
||||
- **SBOM (Software Bill of Materials)**: Complete inventory of components (CycloneDX 1.6 / SPDX 3.0.1)
|
||||
- **VEX (Vulnerability Exploitability eXchange)**: Claims about whether vulnerabilities affect a specific product
|
||||
- **Reachability**: Analysis of whether vulnerable code paths are actually exercised at runtime
|
||||
|
||||
### 1.2 Semantic Delta
|
||||
|
||||
A **semantic delta** captures *meaningful* differences between two states:
|
||||
|
||||
- Components added/removed/updated
|
||||
- Reachability edges added/removed
|
||||
- VEX claim transitions (affected → not_affected)
|
||||
- Configuration/feature flag changes
|
||||
- Attestation/provenance changes
|
||||
|
||||
### 1.3 Determinism-First Principles
|
||||
|
||||
All verdict computations must be:
|
||||
|
||||
- **Reproducible**: Same inputs → identical outputs, always
|
||||
- **Content-addressed**: Every input identified by cryptographic hash
|
||||
- **Declarative**: Compact manifest lists all input hashes + engine version
|
||||
- **Pure**: No wall-clock time, no random iteration, no network during evaluation
|
||||
|
||||
---
|
||||
|
||||
## 2. Risk Budget Model
|
||||
|
||||
### 2.1 Service Tiers
|
||||
|
||||
Each service/product component must be assigned a **Criticality Tier**:
|
||||
|
||||
| Tier | Description | Monthly Budget (RP) |
|
||||
|------|-------------|---------------------|
|
||||
| **Tier 0** | Internal only, low business impact | 300 |
|
||||
| **Tier 1** | Customer-facing non-critical | 200 |
|
||||
| **Tier 2** | Customer-facing critical | 120 |
|
||||
| **Tier 3** | Safety/financial/data-critical | 80 |
|
||||
|
||||
### 2.2 Risk Point Scoring
|
||||
|
||||
**Release Risk Score (RRS) = Base + Diff Risk + Operational Context − Mitigations**
|
||||
|
||||
**Base (by criticality):**
|
||||
- Tier 0: +1
|
||||
- Tier 1: +3
|
||||
- Tier 2: +6
|
||||
- Tier 3: +10
|
||||
|
||||
**Diff Risk (additive):**
|
||||
| Change Type | Points |
|
||||
|-------------|--------|
|
||||
| Docs, comments, non-executed code | +1 |
|
||||
| UI changes, refactors with high coverage | +3 |
|
||||
| API contract changes, dependency upgrades | +6 |
|
||||
| Database schema migrations, auth logic | +10 |
|
||||
| Infra/networking, encryption, payment flows | +15 |
|
||||
|
||||
**Operational Context (additive):**
|
||||
| Condition | Points |
|
||||
|-----------|--------|
|
||||
| Active incident or recent Sev1/Sev2 | +5 |
|
||||
| Error budget < 50% remaining | +3 |
|
||||
| High on-call load | +2 |
|
||||
| Release during freeze window | +5 |
|
||||
|
||||
**Mitigations (subtract):**
|
||||
| Control | Points |
|
||||
|---------|--------|
|
||||
| Feature flag with staged rollout + kill switch | −3 |
|
||||
| Canary + automated health gates + tested rollback | −3 |
|
||||
| High-confidence integration coverage | −2 |
|
||||
| Backward-compatible migration with proven rollback | −2 |
|
||||
| Change isolated behind permission boundary | −2 |
|
||||
|
||||
### 2.3 Budget Thresholds
|
||||
|
||||
| Status | Remaining | Action |
|
||||
|--------|-----------|--------|
|
||||
| **Green** | ≥60% | Normal operation |
|
||||
| **Yellow** | 30–59% | Gates tighten by 1 level for medium/high-risk diffs |
|
||||
| **Red** | <30% | Freeze high-risk diffs; allow only low-risk or reliability work |
|
||||
| **Exhausted** | ≤0% | Incident/security fixes only with explicit sign-off |
|
||||
|
||||
### 2.4 Risk Budget Visualization
|
||||
|
||||
The **Risk Budget Burn-Up Chart** is the key PM dashboard:
|
||||
|
||||
- **X-axis**: Calendar dates up to code freeze
|
||||
- **Y-axis**: Risk points
|
||||
- **Budget line**: Allowable risk over time (flat or stepped)
|
||||
- **Actual Risk line**: Cumulative unknowns + knowns − mitigations
|
||||
- **Shaded area**: Headroom (green) or Overrun (red)
|
||||
- **Vertical markers**: Feature freeze, pen-test start, dependency bumps
|
||||
- **Burn targets**: Dotted lines showing required pace
|
||||
|
||||
**Dashboard KPIs:**
|
||||
- "Headroom: 28 pts (green)"
|
||||
- "Unknowns↑ +6 (24h)", "Risk retired −18 (7d)"
|
||||
- "Exceptions expiring: 3"
|
||||
- "At current burn, overrun in 5 days"
|
||||
|
||||
---
|
||||
|
||||
## 3. Release Gate Levels
|
||||
|
||||
### 3.1 Gate Definitions
|
||||
|
||||
#### G0 — No-risk / Administrative
|
||||
**Use for:** docs-only, comments-only, non-functional metadata
|
||||
|
||||
**Requirements:**
|
||||
- Lint/format checks
|
||||
- Basic CI pass (build)
|
||||
|
||||
#### G1 — Low Risk
|
||||
**Use for:** small localized changes with strong unit coverage, non-core UI, telemetry additions
|
||||
|
||||
**Requirements:**
|
||||
- All automated unit tests
|
||||
- Static analysis/linting
|
||||
- 1 peer review
|
||||
- Automated deploy to staging
|
||||
- Post-deploy smoke checks
|
||||
|
||||
#### G2 — Moderate Risk
|
||||
**Use for:** moderate logic changes in customer-facing paths, dependency upgrades, backward-compatible API changes
|
||||
|
||||
**Requirements:**
|
||||
- G1 +
|
||||
- Integration tests for impacted modules
|
||||
- Code owner review
|
||||
- Feature flag required if customer impact possible
|
||||
- Staged rollout: canary or small cohort
|
||||
- Rollback plan documented in PR
|
||||
|
||||
#### G3 — High Risk
|
||||
**Use for:** schema migrations, auth/permission changes, core business logic, infra changes
|
||||
|
||||
**Requirements:**
|
||||
- G2 +
|
||||
- Security scan + dependency audit
|
||||
- Migration plan (forward + rollback) reviewed
|
||||
- Load/performance checks if in hot path
|
||||
- New/updated dashboards/alerts
|
||||
- Release captain sign-off
|
||||
- Progressive delivery with automatic health gates
|
||||
|
||||
#### G4 — Very High Risk / Safety-Critical
|
||||
**Use for:** Tier 3 systems with low budget, freeze window exceptions, broad blast radius, post-incident remediation
|
||||
|
||||
**Requirements:**
|
||||
- G3 +
|
||||
- Formal risk review (PM+DM+Security/SRE) in writing
|
||||
- Explicit rollback rehearsal
|
||||
- Extended canary with success/abort criteria
|
||||
- Customer comms plan if impact plausible
|
||||
- Post-release verification checklist executed
|
||||
|
||||
### 3.2 Gate Selection Logic
|
||||
|
||||
1. Compute **RRS** from diff + context
|
||||
2. Map RRS to default gate:
|
||||
- 1–5 RP → G1
|
||||
- 6–12 RP → G2
|
||||
- 13–20 RP → G3
|
||||
- 21+ RP → G4
|
||||
3. Apply modifiers:
|
||||
- Budget Yellow → escalate one gate for ≥G2
|
||||
- Budget Red → escalate one gate for ≥G1, block high-risk unless exception
|
||||
- Active incident → block non-fix releases by default
|
||||
|
||||
---
|
||||
|
||||
## 4. Delta Verdict Engine
|
||||
|
||||
### 4.1 Core Architecture
|
||||
|
||||
The delta verdict engine computes **deterministic, signed verdicts** comparing two states:
|
||||
|
||||
```
|
||||
Verdict = f(Manifest)
|
||||
```
|
||||
|
||||
Where `Manifest` contains:
|
||||
- `sbom_sha256` - SBOM graph hash
|
||||
- `vex_set_sha256[]` - VEX document hashes
|
||||
- `reach_subgraph_sha256` - Reachability graph hash
|
||||
- `feeds_snapshot_sha256` - Feed snapshot hash
|
||||
- `policy_bundle_sha256` - Policy/rules hash
|
||||
- `engine_version` - Engine version for reproducibility
|
||||
|
||||
### 4.2 Evaluation Pipeline
|
||||
|
||||
1. **Normalize inputs**
|
||||
- SBOM: sort by `packageUrl`/`name@version`; resolve aliases
|
||||
- VEX: normalize provider → `vex_id`, `product_ref`, `status`
|
||||
- Reachability: adjacency lists sorted by node ID; hash after topological ordering
|
||||
- Feeds: lock to snapshot (timestamp + commit/hash); no live calls
|
||||
|
||||
2. **Policy bundle**
|
||||
- Declarative rules compiled to canonical IR
|
||||
- Explicit merge precedence (lattice-merge table)
|
||||
- Unknowns policy baked in: e.g., `fail_if_unknowns > N in prod`
|
||||
|
||||
3. **Evaluation**
|
||||
- Build finding set: `(component, vuln, context)` tuples with deterministic IDs
|
||||
- Apply lattice-based VEX merge with evidence pointers
|
||||
- Compute `status` and `risk_score` using fixed-precision math
|
||||
|
||||
4. **Emit**
|
||||
- Canonicalize verdict JSON (RFC 8785 JCS)
|
||||
- Sign verdict (DSSE/COSE/JWS)
|
||||
- Attach as OCI attestation to image/digest
|
||||
|
||||
### 4.3 Delta Verdict Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"subject": {"ociDigest": "sha256:..."},
|
||||
"inputs": {
|
||||
"feeds": [{"type":"cve","digest":"sha256:..."}],
|
||||
"tools": {"sbomer":"1.6.3","reach":"0.9.0","policy":"lattice-2025.12"},
|
||||
"baseline": {"sbomG":"sha256:...","vexSet":"sha256:..."}
|
||||
},
|
||||
"delta": {
|
||||
"components": {"added":[...],"removed":[...],"updated":[...]},
|
||||
"reachability": {"edgesAdded":[...],"edgesRemoved":[...]},
|
||||
"settings": {"changed":[...]},
|
||||
"vex": [{"cve":"CVE-2025-1234","from":"affected","to":"not_affected",
|
||||
"reason":"config_flag_off","evidenceRef":"att#cfg-42"}],
|
||||
"attestations": {"changed":[...]}
|
||||
},
|
||||
"verdict": {
|
||||
"decision": "allow",
|
||||
"riskBudgetUsed": 2,
|
||||
"policyId": "lattice-2025.12",
|
||||
"explanationRefs": ["vex[0]","reachability.edgesRemoved[3]"]
|
||||
},
|
||||
"signing": {"dsse":"...","signer":"stella-authority"}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 Replay Contract
|
||||
|
||||
For deterministic replay, pin and record:
|
||||
- Feed snapshots + hashes
|
||||
- Scanner versions + rule packs + lattice/policy version
|
||||
- SBOM generator version + mode
|
||||
- Reachability engine settings
|
||||
- Merge semantics ID
|
||||
|
||||
**Replayer re-hydrates exact inputs and must reproduce the same verdict bit-for-bit.**
|
||||
|
||||
---
|
||||
|
||||
## 5. Smart-Diff Algorithm
|
||||
|
||||
### 5.1 Material Risk Change Detection
|
||||
|
||||
**FindingKey:** `(component_purl, component_version, cve_id)`
|
||||
|
||||
**RiskState Fields:**
|
||||
- `reachable: bool | unknown`
|
||||
- `vex_status: enum` (AFFECTED | NOT_AFFECTED | FIXED | UNDER_INVESTIGATION | UNKNOWN)
|
||||
- `in_affected_range: bool | unknown`
|
||||
- `kev: bool`
|
||||
- `epss_score: float | null`
|
||||
- `policy_flags: set<string>`
|
||||
- `evidence_links: list<EvidenceLink>`
|
||||
|
||||
### 5.2 Change Detection Rules
|
||||
|
||||
**Rule R1: Reachability Flip**
|
||||
- `reachable` changes: `false → true` (risk ↑) or `true → false` (risk ↓)
|
||||
|
||||
**Rule R2: VEX Status Flip**
|
||||
- Meaningful changes: `AFFECTED ↔ NOT_AFFECTED`, `UNDER_INVESTIGATION → NOT_AFFECTED`
|
||||
|
||||
**Rule R3: Affected Range Boundary**
|
||||
- `in_affected_range` flips: `false → true` or `true → false`
|
||||
|
||||
**Rule R4: Intelligence/Policy Flip**
|
||||
- `kev` changes `false → true`
|
||||
- `epss_score` crosses configured threshold
|
||||
- `policy_flag` changes severity (warn → block)
|
||||
|
||||
### 5.3 Suppression Rules
|
||||
|
||||
**All must apply for suppression:**
|
||||
1. `reachable == false`
|
||||
2. `vex_status == NOT_AFFECTED`
|
||||
3. `kev == false`
|
||||
4. No policy override
|
||||
|
||||
**Patch Churn Suppression:**
|
||||
- If version changes AND `in_affected_range` remains false in both AND no KEV/policy flip → suppress
|
||||
|
||||
### 5.4 Priority Score Formula
|
||||
|
||||
```
|
||||
score =
|
||||
+ 1000 if new.kev
|
||||
+ 500 if new.reachable
|
||||
+ 200 if reason includes RANGE_FLIP to affected
|
||||
+ 150 if VEX_FLIP to AFFECTED
|
||||
+ 0..100 based on EPSS (epss * 100)
|
||||
+ policy weight: +300 if decision BLOCK, +100 if WARN
|
||||
```
|
||||
|
||||
### 5.5 Reachability Gate (3-Bit Severity)
|
||||
|
||||
```csharp
|
||||
public sealed record ReachabilityGate(
|
||||
bool? Reachable, // true / false / null for unknown
|
||||
bool? ConfigActivated,
|
||||
bool? RunningUser,
|
||||
int Class, // 0..7 derived from the bits when all known
|
||||
string Rationale
|
||||
);
|
||||
```
|
||||
|
||||
**Class Computation:** 0-7 based on 3 binary gates (reachable, config-activated, running user)
|
||||
|
||||
**Unknown Handling:** Never silently treat `null` as `false` or `true`. If any bit is `null`, set `Class = -1` or compute from known bits only.
|
||||
|
||||
---
|
||||
|
||||
## 6. Exception Workflow
|
||||
|
||||
### 6.1 Exception Entity Model
|
||||
|
||||
```csharp
|
||||
public record Exception(
|
||||
string Id,
|
||||
string Scope, // image:repo/app:tag, component:pkg@ver
|
||||
string Subject, // CVE-2025-1234, package name
|
||||
string Reason, // Human-readable justification
|
||||
List<string> EvidenceRefs, // att:sha256:..., vex:sha256:...
|
||||
string CreatedBy,
|
||||
DateTime CreatedAt,
|
||||
DateTime? ExpiresAt,
|
||||
string PolicyBinding,
|
||||
string Signature
|
||||
);
|
||||
```
|
||||
|
||||
### 6.2 Exception Requirements
|
||||
|
||||
- **Signed rationale + evidence**: Justification with linked proofs (attestation IDs, VEX note, reachability subgraph slice)
|
||||
- **Auto-expiry & revalidation**: Scheduler re-tests on expiry or when feeds mark "fix available / EPSS ↑ / reachability ↑"
|
||||
- **Audit view**: Timeline of exception lifecycle (who/why, evidence, re-checks)
|
||||
- **Policy hooks**: "allow only if: reason ∧ evidence present ∧ max TTL ≤ X ∧ owner = team-Y"
|
||||
- **Inheritance**: repo→image→env scoping with explicit shadowing
|
||||
|
||||
### 6.3 Exception CLI
|
||||
|
||||
```bash
|
||||
stella exception create \
|
||||
--cve CVE-2025-1234 \
|
||||
--scope image:repo/app:tag \
|
||||
--reason "Feature disabled" \
|
||||
--evidence att:sha256:... \
|
||||
--ttl 30d
|
||||
```
|
||||
|
||||
### 6.4 Break-Glass Policy
|
||||
|
||||
Exceptions permitted only for:
|
||||
- Incident mitigation or customer harm prevention
|
||||
- Urgent security fix (actively exploited or high severity)
|
||||
- Legal/compliance deadline
|
||||
|
||||
**Requirements:**
|
||||
- Recorded rationale in PR/release ticket
|
||||
- Named approvers: DM + on-call owner; PM for customer-impacting risk
|
||||
- Mandatory follow-up within 5 business days
|
||||
- **Budget penalty:** +50% of change's RRS
|
||||
|
||||
---
|
||||
|
||||
## 7. VEX Trust Scoring
|
||||
|
||||
### 7.1 Evidence Atoms
|
||||
|
||||
For every VEX statement, extract:
|
||||
- **scope**: package@version, image@digest, file hash
|
||||
- **claim**: affected, not_affected, under_investigation, fixed
|
||||
- **reason**: reachable?, feature flag off, vulnerable code not present
|
||||
- **provenance**: who said it, how it's signed
|
||||
- **when**: issued_at, observed_at, expires_at
|
||||
- **supporting artifacts**: SBOM ref, in-toto link, CVE IDs
|
||||
|
||||
### 7.2 Confidence Score (C: 0–1)
|
||||
|
||||
Multiply factors, cap at 1:
|
||||
|
||||
| Factor | Weight |
|
||||
|--------|--------|
|
||||
| DSSE + Sigstore/Rekor inclusion | 0.35 |
|
||||
| Hardware-backed key or org OIDC | 0.15 |
|
||||
| NVD source | 0.20 |
|
||||
| Major distro PSIRT | 0.20 |
|
||||
| Upstream vendor | 0.20 |
|
||||
| Reputable CERT | 0.15 |
|
||||
| Small vendor | 0.10 |
|
||||
| Reachability proof/test | 0.25 |
|
||||
| Code diff linking | 0.20 |
|
||||
| Deterministic build link | 0.15 |
|
||||
| "Reason" present | 0.10 |
|
||||
| ≥2 independent concurring sources | +0.10 |
|
||||
|
||||
### 7.3 Freshness Score (F: 0–1)
|
||||
|
||||
```
|
||||
F = exp(−Δdays / τ)
|
||||
```
|
||||
|
||||
**τ values by source class:**
|
||||
- Vendor VEX: τ = 30
|
||||
- NVD: τ = 90
|
||||
- Exploit-active feeds: τ = 14
|
||||
|
||||
**Update reset:** New attestation with same subject resets Δdays.
|
||||
**Expiry clamp:** If `now > expires_at`, set F = 0.
|
||||
|
||||
### 7.4 Claim Strength (S_claim)
|
||||
|
||||
| Claim | Base Weight |
|
||||
|-------|-------------|
|
||||
| not_affected | 0.9 |
|
||||
| fixed | 0.8 |
|
||||
| affected | 0.7 |
|
||||
| under_investigation | 0.4 |
|
||||
|
||||
**Reason multipliers:**
|
||||
- reachable? → +0.15 to "affected"
|
||||
- "feature flag off" → +0.10 to "not_affected"
|
||||
- platform mismatch → +0.10
|
||||
- backport patch note (with commit hash) → +0.10
|
||||
|
||||
### 7.5 Lattice Merge
|
||||
|
||||
Per evidence `e`:
|
||||
```
|
||||
Score(e) = C(e) × F(e) × S_claim(e)
|
||||
```
|
||||
|
||||
Merge in distributive lattice ordered by:
|
||||
1. **Claim precedence**: not_affected > fixed > affected > under_investigation
|
||||
2. Break ties by **Score(e)**
|
||||
3. If competing top claims within ε (0.05), **escalate to "disputed"** and surface both with proofs
|
||||
|
||||
### 7.6 Worked Example
|
||||
|
||||
**Small vendor Sigstore VEX (signed, reason: code path unreachable, issued 7 days ago):**
|
||||
- C ≈ 0.35 + 0.10 + 0.10 + 0.25 = 0.70
|
||||
- F = exp(−7/30) ≈ 0.79
|
||||
- S_claim = 0.9 + 0.10 = 1.0 (capped)
|
||||
- **Score ≈ 0.70 × 0.79 × 1.0 = 0.55**
|
||||
|
||||
**NVD entry (affected, no reasoning, 180 days old):**
|
||||
- C ≈ 0.20
|
||||
- F = exp(−180/90) ≈ 0.14
|
||||
- S_claim = 0.7
|
||||
- **Score ≈ 0.20 × 0.14 × 0.7 = 0.02**
|
||||
|
||||
**Outcome:** Vendor VEX wins → **not_affected** with linked proofs.
|
||||
|
||||
---
|
||||
|
||||
## 8. UI/UX Patterns
|
||||
|
||||
### 8.1 Three-Pane Layout
|
||||
|
||||
1. **Categories Pane**: Filterable list of change categories
|
||||
2. **Items Pane**: Delta items within selected category
|
||||
3. **Proof Pane**: Evidence details for selected item
|
||||
|
||||
### 8.2 Visual Diff Components
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `DeltaSummaryStripComponent` | Risk delta header: "Risk ↓ Medium → Low" |
|
||||
| `ProofPaneComponent` | Evidence rail with witness paths |
|
||||
| `VexMergeExplanationComponent` | Trust algebra visualization |
|
||||
| `CompareViewComponent` | Side-by-side before/after |
|
||||
| `TriageShortcutsService` | Keyboard navigation |
|
||||
|
||||
### 8.3 Micro-interactions
|
||||
|
||||
- **Hover changed node** → inline badge explaining *why it changed*
|
||||
- **Click rule change** → spotlight the exact subgraph it affected
|
||||
- **"Explain like I'm new" toggle** → expand jargon into plain language
|
||||
- **"Copy audit bundle"** → export delta + evidence as attachment
|
||||
|
||||
### 8.4 Hotkeys
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `1` | Focus changes only |
|
||||
| `2` | Show full graph |
|
||||
| `E` | Expand evidence |
|
||||
| `A` | Export audit |
|
||||
|
||||
### 8.5 Empty States
|
||||
|
||||
- **Incomplete evidence**: Yellow "Unknowns present" ribbon with count and collection button
|
||||
- **Huge graphs**: Default to "changed neighborhood only" with mini-map
|
||||
|
||||
---
|
||||
|
||||
## 9. CI/CD Integration
|
||||
|
||||
### 9.1 API Endpoints
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|----------|---------|
|
||||
| `POST /evaluate` | Returns `verdict.json` + attestation |
|
||||
| `POST /delta` | Returns `delta.json` (signed) |
|
||||
| `GET /replay?manifest_sha=` | Re-executes with cached snapshots |
|
||||
| `GET /evidence/:cid` | Fetches immutable evidence blobs |
|
||||
|
||||
### 9.2 CLI Commands
|
||||
|
||||
```bash
|
||||
# Verify delta between two versions
|
||||
stella verify delta \
|
||||
--from abc123 \
|
||||
--to def456 \
|
||||
--policy prod.json \
|
||||
--print-proofs
|
||||
|
||||
# Create exception
|
||||
stella exception create \
|
||||
--cve CVE-2025-1234 \
|
||||
--scope image:repo/app:tag \
|
||||
--reason "Feature disabled" \
|
||||
--evidence att:sha256:... \
|
||||
--ttl 30d
|
||||
|
||||
# Replay a verdict
|
||||
stella replay \
|
||||
--manifest-sha sha256:... \
|
||||
--assert-identical
|
||||
```
|
||||
|
||||
### 9.3 Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| 0 | PASS - Release allowed |
|
||||
| 1 | FAIL - Gate blocked |
|
||||
| 2 | WARN - Proceed with caution |
|
||||
| 3 | ERROR - Evaluation failed |
|
||||
|
||||
### 9.4 Pipeline Recipe
|
||||
|
||||
```yaml
|
||||
release-gate:
|
||||
script:
|
||||
- stella evaluate --subject $IMAGE_DIGEST --policy $GATE_POLICY
|
||||
- |
|
||||
if [ $? -eq 1 ]; then
|
||||
echo "Gate blocked - risk budget exceeded or policy violation"
|
||||
exit 1
|
||||
fi
|
||||
- stella delta --from $BASELINE --to $IMAGE_DIGEST --export audit-bundle.zip
|
||||
artifacts:
|
||||
paths:
|
||||
- audit-bundle.zip
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Data Models
|
||||
|
||||
### 10.1 Scan Manifest
|
||||
|
||||
```json
|
||||
{
|
||||
"sbom_sha256": "sha256:...",
|
||||
"vex_set_sha256": ["sha256:..."],
|
||||
"reach_subgraph_sha256": "sha256:...",
|
||||
"feeds_snapshot_sha256": "sha256:...",
|
||||
"policy_bundle_sha256": "sha256:...",
|
||||
"engine_version": "1.0.0",
|
||||
"policy_semver": "2025.12",
|
||||
"options_hash": "sha256:..."
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 Verdict
|
||||
|
||||
```json
|
||||
{
|
||||
"risk_score": 42,
|
||||
"status": "pass|warn|fail",
|
||||
"unknowns_count": 3,
|
||||
"evidence_refs": ["sha256:...", "sha256:..."],
|
||||
"explanations": [
|
||||
{"template": "CVE-{cve} suppressed by VEX claim from {source}",
|
||||
"params": {"cve": "2025-1234", "source": "vendor"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 Smart-Diff Predicate
|
||||
|
||||
```json
|
||||
{
|
||||
"predicateType": "stellaops.dev/predicates/smart-diff@v1",
|
||||
"predicate": {
|
||||
"baseImage": {"name":"...", "digest":"sha256:..."},
|
||||
"targetImage": {"name":"...", "digest":"sha256:..."},
|
||||
"diff": {
|
||||
"filesAdded": [...],
|
||||
"filesRemoved": [...],
|
||||
"filesChanged": [{"path":"...", "hunks":[...]}],
|
||||
"packagesChanged": [{"name":"openssl","from":"1.1.1u","to":"3.0.14"}]
|
||||
},
|
||||
"context": {
|
||||
"entrypoint":["/app/start"],
|
||||
"env":{"FEATURE_X":"true"},
|
||||
"user":{"uid":1001,"caps":["NET_BIND_SERVICE"]}
|
||||
},
|
||||
"reachabilityGate": {"reachable":true,"configActivated":true,"runningUser":false,"class":6}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Success Metrics
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| **Mean Time to Explain (MTTE)** | Time from "why did this change?" to "Understood" |
|
||||
| **Change Failure Rate** | % of releases causing incidents |
|
||||
| **MTTR** | Mean time to recovery |
|
||||
| **Gate Compliance Rate** | % of releases following required gates |
|
||||
| **Budget Utilization** | Actual RP consumed vs. allocated |
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Related Documents
|
||||
|
||||
| Document | Relationship |
|
||||
|----------|--------------|
|
||||
| [`docs/modules/policy/architecture.md`](../modules/policy/architecture.md) | Policy Engine implementation |
|
||||
| [`docs/modules/scanner/architecture.md`](../modules/scanner/architecture.md) | Scanner/Reachability implementation |
|
||||
| [`docs/modules/web/smart-diff-ui-architecture.md`](../modules/web/smart-diff-ui-architecture.md) | UI component specifications |
|
||||
| [`SPRINT_20251226_007_BE_determinism_gaps.md`](../implplan/SPRINT_20251226_007_BE_determinism_gaps.md) | Determinism implementation sprint |
|
||||
|
||||
---
|
||||
|
||||
## Appendix C: Archive References
|
||||
|
||||
The following advisories were consolidated into this document:
|
||||
|
||||
| Original File | Archive Location |
|
||||
|--------------|------------------|
|
||||
| `25-Dec-2025 - Building a Deterministic Verdict Engine.md` | (kept in place - primary reference) |
|
||||
| `26-Dec-2026 - Diff‑Aware Releases and Auditable Exceptions.md` | `archived/2025-12-26-superseded/` |
|
||||
| `26-Dec-2026 - Smart‑Diff as a Core Evidence Primitive.md` | `archived/2025-12-26-superseded/` |
|
||||
| `25-Dec-2025 - Visual Diffs for Explainable Triage.md` | `archived/2025-12-26-triage-advisories/` |
|
||||
| `26-Dec-2026 - Visualizing the Risk Budget.md` | `archived/2025-12-26-triage-advisories/` |
|
||||
| `26-Dec-2026 - Weighted Confidence for VEX Sources.md` | `archived/2025-12-26-vex-scoring/` |
|
||||
|
||||
**Technical References (not moved):**
|
||||
- `archived/2025-12-21-moat-gap-closure/14-Dec-2025 - Smart-Diff Technical Reference.md`
|
||||
- `archived/2025-12-21-moat-phase2/20-Dec-2025 - Moat Explanation - Risk Budgets and Diff-Aware Release Gates.md`
|
||||
Reference in New Issue
Block a user