feat: Implement distro-native version comparison for RPM, Debian, and Alpine packages

- Add RpmVersionComparer for RPM version comparison with epoch, version, and release handling.
- Introduce DebianVersion for parsing Debian EVR (Epoch:Version-Release) strings.
- Create ApkVersion for parsing Alpine APK version strings with suffix support.
- Define IVersionComparator interface for version comparison with proof-line generation.
- Implement VersionComparisonResult struct to encapsulate comparison results and proof lines.
- Add tests for Debian and RPM version comparers to ensure correct functionality and edge case handling.
- Create project files for the version comparison library and its tests.
This commit is contained in:
StellaOps Bot
2025-12-22 09:49:38 +02:00
parent aff0ceb2fe
commit 634233dfed
112 changed files with 31925 additions and 1813 deletions

View File

@@ -0,0 +1,408 @@
# Reachability Drift Detection
**Date**: 2025-12-17
**Status**: ARCHIVED - Implementation Complete (Sprints 3600.2-3600.3)
**Archived**: 2025-12-22
**Related Advisories**:
- 14-Dec-2025 - Smart-Diff Technical Reference
- 14-Dec-2025 - Reachability Analysis Technical Reference
**Implementation Documentation**:
- Architecture: `docs/modules/scanner/reachability-drift.md`
- API Reference: `docs/api/scanner-drift-api.md`
- Operations Guide: `docs/operations/reachability-drift-guide.md`
**Follow-up Sprints**:
- `SPRINT_3600_0004_0001` - Node.js Babel Integration (TODO)
- `SPRINT_3600_0005_0001` - Policy CI Gate Integration (TODO)
- `SPRINT_3600_0006_0001` - Documentation Finalization (TODO)
---
## 1. EXECUTIVE SUMMARY
This advisory proposes extending StellaOps' Smart-Diff capabilities to detect **reachability drift** - changes in whether vulnerable code paths are reachable from application entry points between container image versions.
**Core Insight**: Raw diffs don't equal risk. Most changed lines don't matter for exploitability. Reachability drift detection fuses **call-stack reachability graphs** with **Smart-Diff metadata** to flag only paths that went from **unreachable to reachable** (or vice-versa), tied to **SBOM components** and **VEX statements**.
---
## 2. GAP ANALYSIS vs EXISTING INFRASTRUCTURE
### 2.1 What Already Exists (Leverage Points)
| Component | Location | Status |
|-----------|----------|--------|
| `MaterialRiskChangeDetector` | `Scanner.SmartDiff.Detection` | DONE - R1-R4 rules |
| `VexCandidateEmitter` | `Scanner.SmartDiff.Detection` | DONE - Absent API detection |
| `ReachabilityGateBridge` | `Scanner.SmartDiff.Detection` | DONE - Lattice to 3-bit |
| `ReachabilitySignal` | `Signals.Contracts` | DONE - Call path model |
| `ReachabilityLatticeState` | `Signals.Contracts.Evidence` | DONE - 5-state enum |
| `CallPath`, `CallPathNode` | `Signals.Contracts.Evidence` | DONE - Path representation |
| `ReachabilityEvidenceChain` | `Signals.Contracts.Evidence` | DONE - Proof chain |
| `vex.graph_nodes/edges` | DB Schema | DONE - Graph storage |
| `scanner.risk_state_snapshots` | DB Schema | DONE - State storage |
| `scanner.material_risk_changes` | DB Schema | DONE - Change storage |
| `FnDriftCalculator` | `Scanner.Core.Drift` | DONE - Classification drift |
| `SarifOutputGenerator` | `Scanner.SmartDiff.Output` | DONE - CI output |
| Reachability Benchmark | `bench/reachability-benchmark/` | DONE - Ground truth cases |
| Language Analyzers | `Scanner.Analyzers.Lang.*` | PARTIAL - Package detection, limited call graph |
### 2.2 What's Missing (New Implementation Required)
| Component | Advisory Ref | Gap Description | **Post-Implementation Status** |
|-----------|-------------|-----------------|-------------------------------|
| **Call Graph Extractor (.NET)** | §7 C# Roslyn | No MSBuildWorkspace/Roslyn analysis exists | **DONE** - `DotNetCallGraphExtractor` |
| **Call Graph Extractor (Go)** | §7 Go SSA | No golang.org/x/tools/go/ssa integration | **DONE** - `GoCallGraphExtractor` |
| **Call Graph Extractor (Java)** | §7 | No Soot/WALA integration | **DONE** - `JavaCallGraphExtractor` (ASM) |
| **Call Graph Extractor (Node)** | §7 | No @babel/traverse integration | **PARTIAL** - Skeleton exists |
| **`scanner.code_changes` table** | §4 Smart-Diff | AST-level diff facts not persisted | **DONE** - Migration 010 |
| **Drift Cause Explainer** | §6 Timeline | No causal attribution on path nodes | **DONE** - `DriftCauseExplainer` |
| **Path Viewer UI** | §UX | No Angular component for call path visualization | **DONE** - `path-viewer.component.ts` |
| **Cross-scan Function-level Drift** | §6 | State drift exists, function-level doesn't | **DONE** - `ReachabilityDriftDetector` |
| **Entrypoint Discovery (per-framework)** | §3 | Limited beyond package.json/manifest parsing | **DONE** - 9 entrypoint types |
### 2.3 Terminology Mapping
| Advisory Term | StellaOps Equivalent | Notes |
|--------------|---------------------|-------|
| `commit_sha` | `scan_id` | StellaOps is image-centric, not commit-centric |
| `call_node` | `vex.graph_nodes` | Existing schema, extend don't duplicate |
| `call_edge` | `vex.graph_edges` | Existing schema |
| `reachability_drift` | `scanner.material_risk_changes` | Add `cause`, `path_nodes` columns |
| Risk Drift | Material Risk Change | Existing term is more precise |
| Router, Signals | Signals module only | Router module is not implemented |
---
## 3. RECOMMENDED IMPLEMENTATION PATH
### 3.1 What to Ship (Delta from Current State)
```
NEW TABLES:
├── scanner.code_changes # AST-level diff facts
└── scanner.call_graph_snapshots # Per-scan call graph cache
NEW COLUMNS:
├── scanner.material_risk_changes.cause # TEXT - "guard_removed", "new_route", etc.
├── scanner.material_risk_changes.path_nodes # JSONB - Compressed path representation
└── scanner.material_risk_changes.base_scan_id # UUID - For cross-scan comparison
NEW SERVICES:
├── CallGraphExtractor.DotNet # Roslyn-based for .NET projects
├── CallGraphExtractor.Node # AST-based for Node.js
├── DriftCauseExplainer # Attribute causes to code changes
└── PathCompressor # Compress paths for storage/UI
NEW UI:
└── PathViewerComponent # Angular component for call path visualization
```
### 3.2 What NOT to Ship (Avoid Duplication)
- **Don't create `call_node`/`call_edge` tables** - Use existing `vex.graph_nodes`/`vex.graph_edges`
- **Don't add `commit_sha` columns** - Use `scan_id` consistently
- **Don't build React components** - Angular v17 is the stack
### 3.3 Use Valkey for Graph Caching
Valkey is already integrated in `Router.Gateway.RateLimit`. Use it for:
- **Call graph snapshot caching** - Fast cross-instance lookups
- **Reachability result caching** - Avoid recomputation
- **Key pattern**: `stella:callgraph:{scan_id}:{lang}:{digest}`
```yaml
# Configuration pattern (align with existing Router rate limiting)
reachability:
valkey_connection: "localhost:6379"
valkey_bucket: "stella-reachability"
cache_ttl_hours: 24
circuit_breaker:
failure_threshold: 5
timeout_seconds: 30
```
---
## 4. TECHNICAL DESIGN
### 4.1 Call Graph Extraction Model
```csharp
/// <summary>
/// Per-scan call graph snapshot for drift comparison.
/// </summary>
public sealed record CallGraphSnapshot
{
public required string ScanId { get; init; }
public required string GraphDigest { get; init; } // Content hash
public required string Language { get; init; }
public required DateTimeOffset ExtractedAt { get; init; }
public required ImmutableArray<CallGraphNode> Nodes { get; init; }
public required ImmutableArray<CallGraphEdge> Edges { get; init; }
public required ImmutableArray<string> EntrypointIds { get; init; }
}
public sealed record CallGraphNode
{
public required string NodeId { get; init; } // Stable identifier
public required string Symbol { get; init; } // Fully qualified name
public required string File { get; init; }
public required int Line { get; init; }
public required string Package { get; init; }
public required string Visibility { get; init; } // public/internal/private
public required bool IsEntrypoint { get; init; }
public required bool IsSink { get; init; }
public string? SinkCategory { get; init; } // CMD_EXEC, SQL_RAW, etc.
}
public sealed record CallGraphEdge
{
public required string SourceId { get; init; }
public required string TargetId { get; init; }
public required string CallKind { get; init; } // direct/virtual/delegate
}
```
### 4.2 Code Change Facts Model
```csharp
/// <summary>
/// AST-level code change facts from Smart-Diff.
/// </summary>
public sealed record CodeChangeFact
{
public required string ScanId { get; init; }
public required string File { get; init; }
public required string Symbol { get; init; }
public required CodeChangeKind Kind { get; init; }
public required JsonDocument Details { get; init; }
}
public enum CodeChangeKind
{
Added,
Removed,
SignatureChanged,
GuardChanged, // Boolean condition around call modified
DependencyChanged, // Callee package/version changed
VisibilityChanged // public<->internal<->private
}
```
### 4.3 Drift Cause Attribution
```csharp
/// <summary>
/// Explains why a reachability flip occurred.
/// </summary>
public sealed class DriftCauseExplainer
{
public DriftCause Explain(
CallGraphSnapshot baseGraph,
CallGraphSnapshot headGraph,
string sinkSymbol,
IReadOnlyList<CodeChangeFact> codeChanges)
{
// Find shortest path to sink in head graph
var path = ShortestPath(headGraph.EntrypointIds, sinkSymbol, headGraph);
if (path is null)
return DriftCause.Unknown;
// Check each node on path for code changes
foreach (var nodeId in path.NodeIds)
{
var node = headGraph.Nodes.First(n => n.NodeId == nodeId);
var change = codeChanges.FirstOrDefault(c => c.Symbol == node.Symbol);
if (change is not null)
{
return change.Kind switch
{
CodeChangeKind.GuardChanged => DriftCause.GuardRemoved(node.Symbol, node.File, node.Line),
CodeChangeKind.Added => DriftCause.NewPublicRoute(node.Symbol),
CodeChangeKind.VisibilityChanged => DriftCause.VisibilityEscalated(node.Symbol),
CodeChangeKind.DependencyChanged => DriftCause.DepUpgraded(change.Details),
_ => DriftCause.CodeModified(node.Symbol)
};
}
}
return DriftCause.Unknown;
}
}
```
### 4.4 Database Schema Extensions
```sql
-- New table: Code change facts from AST-level Smart-Diff
CREATE TABLE scanner.code_changes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
scan_id TEXT NOT NULL,
file TEXT NOT NULL,
symbol TEXT NOT NULL,
change_kind TEXT NOT NULL, -- added|removed|signature|guard|dep|visibility
details JSONB,
detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT code_changes_unique UNIQUE (tenant_id, scan_id, file, symbol)
);
CREATE INDEX idx_code_changes_scan ON scanner.code_changes(scan_id);
CREATE INDEX idx_code_changes_symbol ON scanner.code_changes(symbol);
-- New table: Per-scan call graph snapshots (compressed)
CREATE TABLE scanner.call_graph_snapshots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
scan_id TEXT NOT NULL,
language TEXT NOT NULL,
graph_digest TEXT NOT NULL, -- Content hash for dedup
node_count INT NOT NULL,
edge_count INT NOT NULL,
entrypoint_count INT NOT NULL,
extracted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
cas_uri TEXT NOT NULL, -- Reference to CAS for full graph
CONSTRAINT call_graph_snapshots_unique UNIQUE (tenant_id, scan_id, language)
);
CREATE INDEX idx_call_graph_snapshots_digest ON scanner.call_graph_snapshots(graph_digest);
-- Extend existing material_risk_changes table
ALTER TABLE scanner.material_risk_changes
ADD COLUMN IF NOT EXISTS cause TEXT,
ADD COLUMN IF NOT EXISTS path_nodes JSONB,
ADD COLUMN IF NOT EXISTS base_scan_id TEXT;
CREATE INDEX IF NOT EXISTS idx_material_risk_changes_cause
ON scanner.material_risk_changes(cause) WHERE cause IS NOT NULL;
```
---
## 5. UI DESIGN
### 5.1 Risk Drift Card (PR/Commit View)
```
┌─────────────────────────────────────────────────────────────────────┐
│ RISK DRIFT ▼ │
├─────────────────────────────────────────────────────────────────────┤
│ +3 new reachable paths -2 mitigated paths │
│ │
│ ┌─ NEW REACHABLE ──────────────────────────────────────────────┐ │
│ │ POST /payments → PaymentsController.Capture → ... → │ │
│ │ crypto.Verify(legacy) │ │
│ │ │ │
│ │ [pkg:payments@1.8.2] [CVE-2024-1234] [EPSS 0.72] [VEX:affected]│ │
│ │ │ │
│ │ Cause: guard removed in AuthFilter.cs:42 │ │
│ │ │ │
│ │ [View Path] [Quarantine Route] [Pin Version] [Add Exception] │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ MITIGATED ──────────────────────────────────────────────────┐ │
│ │ GET /admin → AdminController.Execute → ... → cmd.Run │ │
│ │ │ │
│ │ [pkg:admin@2.0.0] [CVE-2024-5678] [VEX:not_affected] │ │
│ │ │ │
│ │ Reason: Vulnerable API removed in upgrade │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
### 5.2 Path Viewer Component
```
┌─────────────────────────────────────────────────────────────────────┐
│ CALL PATH: POST /payments → crypto.Verify(legacy) [Collapse] │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ○ POST /payments [ENTRYPOINT] │
│ │ PaymentsController.cs:45 │
│ │ │
│ ├──○ PaymentsController.Capture() │
│ │ │ PaymentsController.cs:89 │
│ │ │ │
│ │ ├──○ PaymentService.ProcessPayment() │
│ │ │ │ PaymentService.cs:156 │
│ │ │ │ │
│ │ │ ├──● CryptoHelper.Verify() ← GUARD REMOVED │
│ │ │ │ │ CryptoHelper.cs:42 [Changed: AuthFilter removed] │
│ │ │ │ │ │
│ │ │ │ └──◆ crypto.Verify(legacy) [VULNERABLE SINK] │
│ │ │ │ pkg:crypto@1.2.3 │
│ │ │ │ CVE-2024-1234 (CVSS 9.8) │
│ │
│ Legend: ○ Node ● Changed ◆ Sink ─ Call │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 6. POLICY INTEGRATION
### 6.1 CI Gate Behavior
```yaml
# Policy wiring for drift detection
smart_diff:
gates:
# Fail PR when new reachable paths to affected sinks
- condition: "delta_reachable > 0 AND vex_status IN ['affected', 'under_investigation']"
action: block
message: "New reachable paths to vulnerable sinks detected"
# Warn when new paths to any sink
- condition: "delta_reachable > 0"
action: warn
message: "New reachable paths detected - review recommended"
# Auto-mitigate when VEX confirms not_affected
- condition: "vex_status == 'not_affected' AND vex_justification IN ['component_not_present', 'fix_applied']"
action: allow
auto_mitigate: true
```
### 6.2 Exit Codes
| Code | Meaning |
|------|---------|
| 0 | Success, no material drift |
| 1 | Success, material drift found (info) |
| 2 | Success, hardening regression detected |
| 3 | Success, new KEV reachable |
| 10+ | Errors |
---
## 7. SPRINT STRUCTURE
### 7.1 Master Sprint: SPRINT_3600_0001_0001
**Topic**: Reachability Drift Detection
**Dependencies**: SPRINT_3500 (Smart-Diff) - COMPLETE
### 7.2 Sub-Sprints
| ID | Topic | Priority | Effort | Dependencies | **Status** |
|----|-------|----------|--------|--------------|------------|
| SPRINT_3600_0002_0001 | Call Graph Infrastructure | P0 | Large | Master | **DONE** |
| SPRINT_3600_0003_0001 | Drift Detection Engine | P0 | Medium | 3600.2 | **DONE** |
| SPRINT_3600_0004_0001 | Node.js Babel Integration | P1 | Medium | 3600.3 | TODO |
| SPRINT_3600_0005_0001 | Policy CI Gate Integration | P1 | Small | 3600.3 | TODO |
| SPRINT_3600_0006_0001 | Documentation Finalization | P0 | Medium | 3600.3 | TODO |
---
## 8. REFERENCES
- `docs/product-advisories/14-Dec-2025 - Smart-Diff Technical Reference.md`
- `docs/product-advisories/14-Dec-2025 - Reachability Analysis Technical Reference.md`
- `docs/implplan/SPRINT_3500_0001_0001_smart_diff_master.md`
- `docs/reachability/lattice.md`
- `bench/reachability-benchmark/README.md`

View File

@@ -0,0 +1,104 @@
Below is a **feature → moat strength** map for Stella Ops, explicitly benchmarked against the tools weve been discussing (Trivy/Aqua, Grype/Syft, Anchore Enterprise, Snyk, Prisma Cloud). Im using **“moat”** in the strict sense: *how hard is it for an incumbent to replicate the capability to parity, and how strong are the switching costs once deployed.*
### Moat scale
* **5 = Structural moat** (new primitives, strong defensibility, durable switching cost)
* **4 = Strong moat** (difficult multi-domain engineering; incumbents have only partial analogs)
* **3 = Moderate moat** (others can build; differentiation is execution + packaging)
* **2 = Weak moat** (table-stakes soon; limited defensibility)
* **1 = Commodity** (widely available in OSS / easy to replicate)
---
## 1) Stella Ops candidate features mapped to moat strength
| Stella Ops feature (precisely defined) | Closest competitor analogs (evidence) | Competitive parity today | Moat strength | Why this is (or isnt) defensible | How to harden the moat |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------: | ------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Signed, replayable risk verdicts**: “this artifact is acceptable” decisions produced deterministically, with an evidence bundle + policy snapshot, signed as an attestation | Ecosystem can sign SBOM attestations (e.g., Syft + Sigstore; DSSE/in-toto via cosign), but not “risk verdict” decisions end-to-end ([Anchore][1]) | Low | **5** | This requires a **deterministic evaluation model**, a **proof/evidence schema**, and “knowledge snapshotting” so results are replayable months later. Incumbents mostly stop at exporting scan results or SBOMs, not signing a decision in a reproducible way. | Make the verdict format a **first-class artifact** (OCI-attached attestation), with strict replay semantics (“same inputs → same verdict”), plus auditor-friendly evidence extraction. |
| **VEX decisioning engine (not just ingestion)**: ingest OpenVEX/CycloneDX/CSAF, resolve conflicts with a trust/policy lattice, and produce explainable outcomes | Trivy supports multiple VEX formats (CycloneDX/OpenVEX/CSAF) but notes its “experimental/minimal functionality” ([Trivy][2]). Grype supports OpenVEX ingestion ([Chainguard][3]). Anchore can generate VEX docs from annotations (OpenVEX + CycloneDX) ([Anchore Docs][4]). Aqua runs VEX Hub for distributing VEX statements to Trivy ([Aqua][5]) | Medium (ingestion exists; decision logic is thin) | **4** | Ingestion alone is easy; the moat comes from **formal conflict resolution**, provenance-aware trust weighting, and deterministic outcomes. Most tools treat VEX as suppression/annotation, not a reasoning substrate. | Ship a **policy-controlled merge semantics** (“vendor > distro > internal” is too naive) + required evidence hooks (e.g., “not affected because feature flag off”). |
| **Reachability with proof**, tied to deployable artifacts: produce a defensible chain “entrypoint → call path → vulnerable symbol,” plus configuration gates | Snyk has reachability analysis in GA for certain languages/integrations and uses call-graph style reasoning to determine whether vulnerable code is called ([Snyk User Docs][6]). Some commercial vendors also market reachability (e.g., Endor Labs is listed in CycloneDX Tool Center as analyzing reachability) ([CycloneDX][7]) | Medium (reachability exists, but proof portability varies) | **4** | “Reachability” as a label is no longer unique. The moat is **portable proofs** (usable in audits and in air-gapped environments) + artifact-level mapping (not just source repo analysis) + deterministic replay. | Focus on **proof-carrying reachability**: store the reachability subgraph as evidence; make it reproducible and attestable; support both source and post-build artifacts. |
| **Smart-Diff (semantic risk delta)**: between releases, explain “what materially changed in exploitable surface,” not just “CVE count changed” | Anchore provides SBOM management and policy evaluation (good foundation), but “semantic risk diff” is not a prominent, standardized feature in typical scanners ([Anchore Docs][8]) | LowMedium | **4** | Most incumbents can diff findings lists. Few can diff **reachability graphs, policy outcomes, and VEX state** to produce stable “delta narratives.” Hard to replicate without the underlying evidence model. | Treat diff as first-class: version SBOM graphs + reachability graphs + VEX claims; compute deltas over those graphs and emit a signed “delta verdict.” |
| **Unknowns as first-class state**: represent “unknown-reachable/unknown-unreachable” and force policies to account for uncertainty | Not a standard capability in common scanners/platforms; most systems output findings and (optionally) suppressions | Low | **4** | This is conceptually simple but operationally rare; it requires rethinking UX, scoring, and policy evaluation. It becomes sticky once orgs base governance on uncertainty budgets. | Bake unknowns into policies (“fail if unknowns > N in prod”), reporting, and attestations. Make it the default rather than optional. |
| **Air-gapped epistemic mode**: offline operation where the tool can prove what knowledge it used (feed snapshot + timestamps + trust anchors) | Prisma Cloud Compute Edition supports air-gapped environments and has an offline Intel Stream update mechanism ([Prisma Cloud Docs][9]). (But “prove exact knowledge state used for decisions” is typically not the emphasis.) | Medium | **4** | Air-gapped “runtime” is common; air-gapped **reproducibility** is not. The moat is packaging offline feeds + policies + deterministic scoring into a replayable bundle tied to attestations. | Deliver a “sealed knowledge snapshot” workflow (export/import), and make audits a one-command replay. |
| **SBOM ledger + lineage**: BYOS ingestion plus versioned SBOM storage, grouping, and historical tracking | Anchore explicitly positions centralized SBOM management and “Bring Your Own SBOM” ([Anchore Docs][8]). Snyk can generate SBOMs and expose SBOM via API in CycloneDX/SPDX formats ([Snyk User Docs][10]). Prisma can export CycloneDX SBOMs for scans ([Prisma Cloud Docs][11]) | High | **3** | SBOM generation/storage is quickly becoming table stakes. You can still differentiate on **graph fidelity + lineage semantics**, but “having SBOMs” alone wont be a moat. | Make the ledger valuable via **semantic diff, evidence joins (reachability/VEX), and provenance** rather than storage. |
| **Policy engine with proofs**: policy-as-code that produces a signed explanation (“why pass/fail”) and links to evidence nodes | Anchore has a mature policy model (policy JSON, gates, allowlists, mappings) ([Anchore Docs][12]). Prisma/Aqua have rich policy + runtime guardrails (platform-driven) ([Aqua][13]) | High | **3** | Policy engines are common. The moat is the **proof output** + deterministic replay + integration with attestations. | Keep policy language small but rigorous; always emit evidence pointers; support “policy compilation” to deterministic decision artifacts. |
| **VEX distribution network**: ecosystem layer that aggregates, validates, and serves VEX at scale | Aquas VEX Hub is explicitly a centralized repository designed for discover/fetch/consume flows with Trivy ([Aqua][5]) | Medium | **34** | A network layer can become a moat if it achieves broad adoption. But incumbents can also launch hubs. This becomes defensible only with **network effects + trust frameworks**. | Differentiate with **verification + trust scoring** of VEX sources, plus tight coupling to deterministic decisioning and attestations. |
| **“Integrations everywhere”** (CI/CD, registry, Kubernetes, IDE) | Everyone in this space integrates broadly; reachability and scoring features often ride those integrations (e.g., Snyk reachability depends on repo/integration access) ([Snyk User Docs][6]) | High | **12** | Integrations are necessary, but not defensible—mostly engineering throughput. | Use integrations to *distribute attestations and proofs*, not as the headline differentiator. |
---
## 2) Where competitors already have strong moats (avoid headon fights early)
These are areas where incumbents are structurally advantaged, so Stella Ops should either (a) integrate rather than replace, or (b) compete only if you have a much sharper wedge.
### Snyks moat: developer adoption + reachability-informed prioritization
* Snyk publicly documents **reachability analysis** (GA for certain integrations/languages) ([Snyk User Docs][6])
* Snyk prioritization incorporates reachability and other signals into **Priority Score** ([Snyk User Docs][14])
**Implication:** pure “reachability” claims wont beat Snyk; **proof-carrying, artifact-tied, replayable reachability** can.
### Prisma Clouds moat: CNAPP breadth + graph-based risk prioritization + air-gapped CWPP
* Prisma invests in graph-driven investigation/tracing of vulnerabilities ([Prisma Cloud Docs][15])
* Risk prioritization and risk-score ranked vulnerability views are core platform capabilities ([Prisma Cloud Docs][16])
* Compute Edition supports **air-gapped environments** and has offline update workflows ([Prisma Cloud Docs][9])
**Implication:** competing on “platform breadth” is a losing battle early; compete on **decision integrity** (deterministic, attestable, replayable) and integrate where needed.
### Anchores moat: SBOM operations + policy-as-code maturity
* Anchore is explicitly SBOM-management centric and supports policy gating constructs ([Anchore Docs][8])
**Implication:** Anchore is strong at “SBOM at scale.” Stella Ops should outperform on **semantic diff, VEX reasoning, and proof outputs**, not just SBOM storage.
### Aquas moat: code-to-runtime enforcement plus emerging VEX distribution
* Aqua provides CWPP-style runtime policy enforcement/guardrails ([Aqua][13])
* Aqua backs VEX Hub for VEX distribution and Trivy consumption ([Aqua][5])
**Implication:** if Stella Ops is not a runtime protection platform, dont chase CWPP breadth—use Aqua/Prisma integrations and focus on upstream decision quality.
---
## 3) Practical positioning: which features produce the most durable wedge
If you want the shortest path to a *defensible* position:
1. **Moat anchor (5): Signed, replayable risk verdicts**
* Everything else (VEX, reachability, diff) becomes evidence feeding that verdict.
2. **Moat amplifier (4): VEX decisioning + proof-carrying reachability**
* In 2025, VEX ingestion exists in Trivy/Grype/Anchore ([Trivy][2]), and reachability exists in Snyk ([Snyk User Docs][6]).
* Your differentiation must be: **determinism + portability + auditability**.
3. **Moat compounding (4): Smart-Diff over risk meaning**
* Turns “scan results” into an operational change-control primitive.
---
## 4) A concise “moat thesis” per feature (one-liners you can use internally)
* **Deterministic signed verdicts:** “We dont output findings; we output an attestable decision that can be replayed.”
* **VEX decisioning:** “We treat VEX as a logical claim system, not a suppression file.”
* **Reachability proofs:** “We provide proof of exploitability in *this* artifact, not just a badge.”
* **Smart-Diff:** “We explain what changed in exploitable surface area, not what changed in CVE count.”
* **Unknowns modeling:** “We quantify uncertainty and gate on it.”
---
If you want, I can convert the table into a **2×2 moat map** (Customer Value vs Defensibility) and a **build-order roadmap** that maximizes durable advantage while minimizing overlap with entrenched competitor moats.
[1]: https://anchore.com/sbom/creating-sbom-attestations-using-syft-and-sigstore/?utm_source=chatgpt.com "Creating SBOM Attestations Using Syft and Sigstore"
[2]: https://trivy.dev/docs/v0.50/supply-chain/vex/?utm_source=chatgpt.com "VEX"
[3]: https://www.chainguard.dev/unchained/vexed-then-grype-about-it-chainguard-and-anchore-announce-grype-supports-openvex?utm_source=chatgpt.com "VEXed? Then Grype about it"
[4]: https://docs.anchore.com/current/docs/vulnerability_management/vuln_annotations/?utm_source=chatgpt.com "Vulnerability Annotations and VEX"
[5]: https://www.aquasec.com/blog/introducing-vex-hub-unified-repository-for-vex-statements/?utm_source=chatgpt.com "Trivy VEX Hub:The Solution to Vulnerability Fatigue"
[6]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/reachability-analysis?utm_source=chatgpt.com "Reachability analysis"
[7]: https://cyclonedx.org/tool-center/?utm_source=chatgpt.com "CycloneDX Tool Center"
[8]: https://docs.anchore.com/current/docs/sbom_management/?utm_source=chatgpt.com "SBOM Management"
[9]: https://docs.prismacloud.io/en/compute-edition?utm_source=chatgpt.com "Prisma Cloud Compute Edition"
[10]: https://docs.snyk.io/developer-tools/snyk-cli/commands/sbom?utm_source=chatgpt.com "SBOM | Snyk User Docs"
[11]: https://docs.prismacloud.io/en/compute-edition/32/admin-guide/vulnerability-management/exporting-sboms?utm_source=chatgpt.com "Exporting Software Bill of Materials on CycloneDX"
[12]: https://docs.anchore.com/current/docs/overview/concepts/policy/policies/?utm_source=chatgpt.com "Policies and Evaluation"
[13]: https://www.aquasec.com/products/cwpp-cloud-workload-protection/?utm_source=chatgpt.com "Cloud workload protection in Runtime - Aqua Security"
[14]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing?utm_source=chatgpt.com "Prioritize issues for fixing"
[15]: https://docs.prismacloud.io/en/enterprise-edition/content-collections/search-and-investigate/c2c-tracing-vulnerabilities/investigate-vulnerabilities-tracing?utm_source=chatgpt.com "Use Vulnerabilities Tracing on Investigate"
[16]: https://docs.prismacloud.io/en/enterprise-edition/use-cases/secure-the-infrastructure/risk-prioritization?utm_source=chatgpt.com "Risk Prioritization - Prisma Cloud Documentation"

View File

@@ -0,0 +1,124 @@
Heres a practical, fromscratch blueprint for a **twostage reachability map** that turns lowlevel runtime facts into auditable, reproducible evidence for triage and VEX decisions.
---
# What this is (plain English)
* **Goal:** prove (or rule out) whether a vulnerable function/package could actually run in *your* build and deployment.
* **How:**
1. extract **binarylevel call targets** (what functions your program *could* call),
2. map those targets onto **symbol graphs** (named functions/classes/modules),
3. correlate those symbols with **SBOM components** (which package/image layer they live in),
4. store each “slice” of reachability as a **signed attestation** so anyone can replay and verify it.
---
# Stage A — Binary → Symbol graph
* **Inputs:** built artifacts (ELF/COFF/MachO), debug symbols (when available), stripped bins, and language runtimes.
* **Process (per artifact):**
* Parse binaries (headers, sections, symbol tables, relocations).
* Recover call edges:
* Direct calls: disassemble; record `caller -> callee`.
* Indirect calls: resolve via PLT/IAT/vtables; fall back to conservative pointsto sets.
* Dynamic loading: log `dlopen/LoadLibrary` + exported symbol usage heuristics.
* Normalize to **Symbol Graph**: nodes = `{binary, symbol, addr, hash}`, edges = `CALLS`.
* **Outputs:** `symbol-graph.jsonl` (+ compact binary form), contentaddressed by hash.
# Stage B — Symbol graph ↔ SBOM components
* **Inputs:** CycloneDX/SPDX SBOM for the image/build; file→component mapping (path→pkg).
* **Process:**
* For each symbol: derive file path (or BuildID) → map to SBOM component/version/layer.
* Build **Component Reachability Graph**:
* nodes = `{component@version}`, edges = “component provides symbol X used by Y”.
* annotate with file hashes, BuildIDs, container layer digests.
* **Outputs:** `reachability-slices/COMPONENT@VERSION.slice.json` (per impacted component).
# Attestable “slice” (the evidence object)
Each slice is a minimal proof unit answering: *“This vulnerable symbol is (or isnt) on a feasible path at runtime in build X.”*
* **Contents:**
* Scan manifest (tool versions, ruleset hashes, feed versions).
* Inputs digests (binaries, SBOM, container layers).
* The subgraph (only nodes/edges needed).
* Query + result (e.g., “is `openssl:EVP_PKEY_decrypt` reachable from any exported entrypoint?”).
* **Format:** DSSE + intoto statement, stored as OCI artifact or file; **deterministic** (same inputs → same bytes).
# Triage flow (how it helps today)
* Given CVE → map to symbols/functions → check reachability slice:
* **Reachable path found:** mark “affected (reachable)”, include call chain and components; raise priority.
* **No path / gated by feature flag:** mark “not affected (unreachable/mitigated)”, with proof chain.
* **Unknowns present:** failsafe policy (e.g., “unknowns > N → block prod”) with explicit unknown edges listed.
# Minimal data model (JSON hints)
* `Symbol`: `{ id, name, demangled, addr, file_sha256, build_id }`
* `Edge`: `{ src_symbol_id, dst_symbol_id, kind: "direct"|"plt"|"indirect" }`
* `Mapping`: `{ file_sha256|build_id -> component_purl, layer_digest, path }`
* `Slice`: `{ inputs:{…}, query:{…}, subgraph:{symbols:[…],edges:[…]}, verdict:"reachable"|"unreachable"|"unknown" }`
# Determinism & replay
* Pin **everything**: disassembler version, rules, demangler options, container digests, SBOM doc hash, symbolization flags.
* Emit a **Scan Manifest** with content hashes; store alongside slices.
* Provide a `replay` command that rehydrates inputs and recomputes the slice; byteforbyte match required.
# Where this plugs into StellaOps (suggested modules)
* **Sbomer**: component/file mapping & SBOM import.
* **Scanner.webservice**: binary parse & callgraph extraction (keep lattice/policy elsewhere per your rule).
* **Vexer/Policy Engine**: consume slices as evidence for “affected/notaffected” claims.
* **Attestor/Authority**: sign DSSE/intoto statements; push to OCI.
* **Timeline/Notify**: surface verdict deltas over time, link to slices.
# Guardrails & fallbacks
* If stripped binaries: prefer BuildID + external symbol servers; else conservative overapprox (mark unknown).
* For JIT/dynamic plugins: capture runtime traces (eBPF/ETW) and merge as **observed edges** with timestamps.
* Mixedlang stacks: unify by file hash + symbol name mangling rules per toolchain.
# Quick implementation plan (6 sprints)
1. **Binary ingest**: ELF/PE/MachO parsing, BuildID hashing, symbol tables, PLT/IAT resolution.
2. **Calledge recovery**: direct calls, basic indirect resolution, slice extractor by entrypoint.
3. **SBOM mapping**: file→component map, layer digests, purl normalization.
4. **Evidence format**: DSSE/intoto schema, deterministic manifests, OCI storage.
5. **Queries & policies**: “isreachable?” API, unknowns budget, featureflag conditions, VEX plumbing.
6. **Runtime merge**: optional eBPF/ETW traces → annotate edges, produce “observedpath” slices.
# Lightweight APIs (sketch)
* `POST /reachability/query { cve, symbols[], entrypoints[], policy } -> slice+verdict`
* `GET /slice/{digest}` -> attested slice
* `POST /replay { slice_digest }` -> match | mismatch (with diff)
# Small example (CVE → symbol mapping)
* `CVEXXXXYYYY` → advisory lists function `foo_decrypt` in `libfoo.so`
* We resolve `libfoo.so` BuildID in image, find symbols that match demangled name, build call paths from service entrypoints; if path exists, slice is “reachable” with 37 hop chain; otherwise “unreachable” with reasons (no import, stripped at linktime, dead code eliminated, or gated by `FEATURE_X=false`).
# Costs (rough, for planning inside StellaOps)
* **Core parsing & graph**: 34 engineerweeks
* **Indirect calls & heuristics**: +35 weeks
* **SBOM mapping & layers**: 2 weeks
* **Attestations & OCI storage**: 12 weeks
* **Policy/VEX integration & UI surfacing**: 23 weeks
* **Runtime trace merge (optional)**: 24 weeks
*(Parallelizable; add 2540% for hardening/tests.)*
If you want, I can turn this into:
* a concrete **.NET 10 service skeleton** (endpoints + data contracts),
* a **DSSE/intoto schema** for the slice, and
* a **dev checklist** for deterministic builds and replay harness.

View File

@@ -0,0 +1,231 @@
# ARCHIVED
> **Archived:** 2025-12-22
> **Reason:** Gap analysis complete; implementation planned via SPRINT_4300 series
> **Analysis:** `docs/implplan/analysis/4300_explainable_triage_gap_analysis.md`
> **Sprints:** `docs/implplan/SPRINT_4300_*.md`
> **Coverage:** ~85% already implemented via prior sprints (3800, 3801, 4100, 4200)
> **Remaining Gaps:** 6 sprints created to close gaps (CLI verify, Evidence API, Privacy, TTL, Schemas, Metrics)
---
Here's a practical, first-time-friendly blueprint for making your security workflow both **explainable** and **provable**-from triage to approval.
# Explainable triage UX (what & why)
Show every risk score with the minimum evidence a responder needs to trust it:
* **Reachable path:** the concrete call-chain (or network path) proving the vuln is actually hit.
* **Entrypoint boundary:** the external surface (HTTP route, CLI verb, cron, message topic) that leads to that path.
* **VEX status:** the exploitability decision (Affected/Not Affected/Under Investigation/Fixed) with rationale.
* **Last-seen timestamp:** when this evidence was last observed/generated.
## UI pattern (compact, 1-click expand)
* **Row (collapsed):** `Score 72 - CVE-2024-12345 - service: api-gateway - package: x.y.z`
* **Expand panel (evidence):**
* **Path:** `POST /billing/charge -> BillingController.Pay() -> StripeClient.Create()`
* **Boundary:** `Ingress: /billing/charge (JWT: required, scope: payments:write)`
* **VEX:** `Not Affected (runtime guard strips untrusted input before sink)`
* **Last seen:** `2025-12-18T09:22Z` (scan: sbomer#c1a2, policy run: lattice#9f0d)
* **Actions:** "Open proof bundle", "Re-run check", "Create exception (time-boxed)"
## Data contract (what the panel needs)
```json
{
"finding_id": "f-7b3c",
"cve": "CVE-2024-12345",
"component": {"name": "stripe-sdk", "version": "6.1.2"},
"reachable_path": [
"HTTP POST /billing/charge",
"BillingController.Pay",
"StripeClient.Create"
],
"entrypoint": {"type":"http","route":"/billing/charge","auth":"jwt:payments:write"},
"vex": {"status":"not_affected","justification":"runtime_sanitizer_blocks_sink","timestamp":"2025-12-18T09:22:00Z"},
"last_seen":"2025-12-18T09:22:00Z",
"attestation_refs": ["sha256:...sbom", "sha256:...vex", "sha256:...policy"]
}
```
# Evidence-linked approvals (what & why)
Make "Approve to ship" contingent on **verifiable proof**, not screenshots:
* **Chain** must exist and be machine-verifiable: **SBOM -> VEX -> policy decision**.
* Use **in-toto/DSSE** attestations or **SLSA provenance** so each link has a signature, subject digest, and predicate.
* **Gate** merges/deploys only when the chain validates.
## Pipeline gate (simple policy)
* Require:
1. **SBOM attestation** referencing the exact image digest
2. **VEX attestation** covering all listed components (or explicit allow-gaps)
3. **Policy decision attestation** (e.g., "risk <= threshold AND all reachable vulns = Not Affected/Fixed")
### Minimal decision attestation (DSSE envelope -> JSON payload)
```json
{
"predicateType": "stella/policy-decision@v1",
"subject": [{"name":"registry/org/app","digest":{"sha256":"<image-digest>"}}],
"predicate": {
"policy": "risk_threshold<=75 && reachable_vulns.all(v => v.vex in ['not_affected','fixed'])",
"inputs": {
"sbom_ref": "sha256:<sbom>",
"vex_ref": "sha256:<vex>"
},
"result": {"allowed": true, "score": 61, "exemptions":[]},
"evidence_refs": ["sha256:<reachability-proof-bundle>"],
"run_at": "2025-12-18T09:23:11Z"
}
}
```
# How this lands in your product (concrete moves)
* **Backend:** add `/findings/:id/evidence` (returns the contract above) + `/approvals/:artifact/attestations`.
* **Storage:** keep **proof bundles** (graphs, call stacks, logs) as content-addressed blobs; store DSSE envelopes alongside.
* **UI:** one list -> expandable rows; chips for VEX status; "Open proof" shows the call graph and boundary in 1 view.
* **CLI/API:** `stella verify image:<digest> --require sbom,vex,decision` returns a signed summary; pipelines fail on non-zero.
* **Metrics:**
* **% changes with complete attestations** (target >=95%)
* **TTFE (time-to-first-evidence)** from alert -> panel open (target <=30s)
* **Post-deploy reversions** due to missing proof (trend to zero)
# Starter acceptance checklist
* [x] Every risk row expands to path, boundary, VEX, last-seen in <300 ms. *(SPRINT_4200_0001_0001)*
* [x] "Approve" button disabled until SBOM+VEX+Decision attestations validate for the **exact artifact digest**. *(SPRINT_4100_0005_0001)*
* [x] One-click "Show DSSE chain" renders the three envelopes with subject digests and signers. *(SPRINT_4200_0001_0001)*
* [x] Audit log captures who approved, which digests, and which evidence hashes. *(SPRINT_3801_0001_0004)*
---
## Implementation Coverage Summary
| Section | Coverage | Sprint(s) |
|---------|----------|-----------|
| Explainable Triage UX | 85% | 3800.*, 4200.0001.0001 |
| Evidence-Linked Approvals | 100% | 3801.*, 4100.* |
| Backend APIs | 85% | 3800.0003.0001, **4300.0001.0002** |
| CLI/API | 50% | 3500.0004.*, **4300.0001.0001** |
| Invariants | 90% | 4100.0003.*, **4300.0002.0002** |
| Data Model | 100% | Scanner.Triage |
| Evidence Types | 100% | 3800.0002.*, Evidence.Bundle |
| Predicate Types | 80% | 3801.*, **4300.0003.0001** |
| Policy Gate | 100% | Policy.Engine |
| Approve Button | 100% | 4100.0005.0001 |
| Privacy | 0% | **4300.0002.0001** |
| TTL Strategy | 50% | **4300.0002.0002** |
| Metrics | 30% | **4300.0003.0002** |
---
*Original advisory content preserved below for reference.*
---
## 1) Start with the invariants (the rules your system must never violate)
If you implement nothing else, implement these invariants-they're what make the UX trustworthy and the approvals auditable.
### Artifact anchoring invariant
Every finding, every piece of evidence, and every approval must be anchored to an immutable **subject digest** (e.g., container image digest `sha256:...`, binary SHA, or SBOM digest).
* No "latest tag" approvals.
* No "approve commit" without mapping to the built artifact digest.
### Evidence closure invariant
A policy decision is only valid if it references **exactly** the evidence it used:
* `inputs.sbom_ref`
* `inputs.vex_ref`
* `inputs.reachability_ref` (optional but recommended)
* `inputs.scan_ref` (optional)
* and any config/IaC refs used for boundary/exposure.
### Signature chain invariant
Evidence is only admissible if it is:
1. structured (machine readable),
2. signed (DSSE/in-toto),
3. verifiable (trusted identity/keys),
4. retrievable by digest.
### Staleness invariant
Evidence must have:
* `last_seen` and `expires_at` (or TTL),
* a "stale evidence" behavior in policy (deny or degrade score).
---
## 2) Choose the canonical formats and where you'll store "proof"
### Attestation envelope: DSSE + in-toto Statement
Use:
* **in-toto Attestation Framework** "Statement" as the payload model ("subject + predicateType + predicate").
* Wrap it in **DSSE** for signing.
### SBOM format: CycloneDX or SPDX
* SPDX is an ISO/IEC standard and has v3.0 and v2.3 lines in the ecosystem.
* CycloneDX is an ECMA standard (ECMA-424) and widely used for application security contexts.
Pick one as **your canonical** (internally), but ingest both.
### VEX format: OpenVEX (practical) + map to "classic" VEX statuses
VEX's value is triage noise reduction: vendors can assert whether a product is affected, fixed, under investigation, or not affected.
OpenVEX is a minimal, embeddable implementation of VEX intended for interoperability.
### Where to store proof: OCI registry referrers
Use OCI "subject/referrers" so proofs travel with the artifact:
* OCI 1.1 introduces an explicit `subject` field and referrers graph for signatures/attestations/SBOMs.
---
## 3-13) [Additional Sections]
*See original advisory for full content on:*
- System architecture
- Data model
- Explainable triage computation
- UI components
- Proof-linked evidence generation
- Predicate types
- Policy gate
- Approve button behavior
- Implementation details
- MVP path
- Quick checklist
---
## References
[1]: https://github.com/secure-systems-lab/dsse "DSSE: Dead Simple Signing Envelope"
[2]: https://github.com/in-toto/attestation "in-toto Attestation Framework"
[3]: https://docs.sigstore.dev/about/bundle/ "Sigstore Bundle Format"
[4]: https://spdx.dev/use/specifications/ "Specifications"
[5]: https://github.com/CycloneDX/specification "CycloneDX/specification"
[6]: https://www.ntia.gov/sites/default/files/publications/vex_one-page_summary_0.pdf "VEX one-page summary"
[7]: https://github.com/openvex/spec "OpenVEX Specification"
[8]: https://opencontainers.org/posts/blog/2024-03-13-image-and-distribution-1-1/ "OCI Image and Distribution Specs v1.1 Releases"
[9]: https://oras.land/docs/concepts/reftypes/ "Attached Artifacts | OCI Registry As Storage"
[10]: https://learn.microsoft.com/en-us/azure/container-registry/container-registry-manage-artifact "Manage OCI Artifacts and Supply Chain Artifacts with ORAS"
[11]: https://openpolicyagent.org/docs/policy-language "Policy Language"
[12]: https://docs.sigstore.dev/cosign/verifying/attestation/ "In-Toto Attestations"

View File

@@ -0,0 +1,154 @@
Below are operating guidelines for Product and Development Managers to deliver a “vulnerability-first + reachability + multi-analyzer + single built-in attested verdict” capability as a coherent, top-of-market feature set.
## 1) Product north star and non-negotiables
**North star:** Every vulnerability finding must resolve to a **policy-backed, reachability-informed, runtime-corroborated verdict** that is **exportable as one signed attestation attached to the built artifact**.
**Non-negotiables**
* **Vulnerability-first UX:** Users start from a CVE/finding and immediately see applicability, reachability, runtime corroboration, and policy rationale.
* **Single canonical verdict artifact:** One built-in, signed verdict attestation per subject (OCI digest), replayable (“same inputs → same output”).
* **Deterministic evidence:** Evidence objects are content-hashed and versioned (feeds, policies, analyzers, graph snapshots).
* **Unknowns are first-class:** “Unknown reachability/runtime/config” is not hidden; it is budgeted and policy-controlled.
## 2) Scope: what “reachability” means across analyzers
PMs must define reachability per layer and force consistent semantics:
1. **Source reachability**
* Entry points → call graph → vulnerable function/symbol (proof subgraph stored).
2. **Language dependency reachability**
* Resolved dependency graph + vulnerable component mapping + (where feasible) call-path to vulnerable code.
3. **OS dependency applicability**
* Installed package inventory + file ownership + linkage/usage hints (where available).
4. **Binary mapping reachability**
* Build-ID / symbol tables / imports + (optional) DWARF/source map; fallback heuristics are explicitly labeled.
5. **Runtime corroboration (eBPF / runtime sensors)**
* Execution facts: library loads, syscalls, network exposure, process ancestry; mapped to a “supports/contradicts/unknown” posture for the finding.
**Manager rule:** Any analyzer that cannot produce a proof object must emit an explicit “UNKNOWN with reason code,” never a silent “not reachable.”
## 3) The decision model: a strict, explainable merge into one verdict
Adopt a small fixed set of verdicts and require all teams to use them:
* `AFFECTED`, `NOT_AFFECTED`, `MITIGATED`, `NEEDS_REVIEW`
Each verdict must carry:
* **Reason steps** (policy/lattice merge trace)
* **Confidence score** (bounded; explainable inputs)
* **Counterfactuals** (“what would flip this verdict”)
* **Evidence pointers** (hashes to proof objects)
**PM guidance on precedence:** Do not hardcode “vendor > distro > internal.” Require a policy-defined merge (lattice semantics) where evidence quality and freshness influence trust.
## 4) Built-in attestation as the primary deliverable
**Deliverable:** An OCI-attached DSSE/in-toto style attestation called (example) `stella.verdict.v1`.
Minimum contents:
* Subject: image digest(s)
* Inputs: feed snapshot IDs, analyzer versions/digests, policy bundle hash, time window, environment tags
* Per-CVE records: component, installed version, fixed version, verdict, confidence, reason steps
* Proof pointers: reachability subgraph hash, runtime fact hashes, config/exposure facts hash
* Replay manifest: “verify this verdict” command + inputs hash
**Acceptance criterion:** A third party can validate signature and replay deterministically using exported inputs, obtaining byte-identical verdict output.
## 5) UX requirements (vulnerability-first, proof-linked)
PMs must enforce these UX invariants:
* Finding row shows: Verdict chip + confidence + “why” one-liner + proof badges (Reachability / Runtime / Policy / Provenance).
* Click-through yields:
* Policy explanation (human-readable steps)
* Evidence graph (hashes, issuers, timestamps, signature status)
* Reachability mini-map (stored subgraph)
* Runtime corroboration timeline (windowed)
* Export: “Audit pack” (verdict + proofs + inputs)
**Rule:** Any displayed claim must link to a proof node or be explicitly marked “operator note.”
## 6) Engineering execution rules (to keep this shippable)
**Modular contracts**
* Each analyzer outputs into a shared internal schema (typed nodes/edges + content hashes).
* Evidence objects are immutable; updates create new objects (versioned snapshots).
**Performance strategy**
* Vulnerability-first query plan: build “vulnerable element set” per CVE, then run targeted reachability; avoid whole-program graphs unless needed.
* Progressive fidelity: fast heuristic → deeper proof when requested; verdict must reflect confidence accordingly.
**Determinism**
* Pin all feeds/policies/analyzer images by digest.
* Canonical serialization for graphs and verdicts.
* Stable hashing rules documented and tested.
## 7) Release gates and KPIs (what managers track weekly)
**Quality KPIs**
* % findings with non-UNKNOWN reachability
* % findings with runtime corroboration available (where sensor deployed)
* False-positive reduction vs baseline (measured via developer confirmations / triage outcomes)
* “Explainability completeness”: % verdicts with reason steps + at least one proof pointer
* Replay success rate: % attestations replaying deterministically in CI
**Operational KPIs**
* Median time to first verdict per image
* Cache hit rate for graphs/proofs
* Storage growth per scan (evidence size budgets)
**Policy KPIs**
* Unknown budget breaches by environment (prod/dev)
* Override/exception volume and aging
## 8) Roadmap sequencing (recommended)
1. **Phase 1: Single attested verdict + OS/lang SCA applicability**
* Deterministic inputs, verdict schema, signature, OCI attach, basic policy steps.
2. **Phase 2: Source reachability proofs (top languages)**
* Store subgraphs; introduce confidence + counterfactuals.
3. **Phase 3: Binary mapping fallback**
* Build-ID/symbol-based reachability + explicit “heuristic” labeling.
4. **Phase 4: Runtime corroboration (eBPF) integration**
* Evidence facts + time-window model + correlation to findings.
5. **Phase 5: Full lattice merge + Trust Algebra Studio**
* Operator-defined semantics; evidence quality weighting; vendor trust scoring.
## 9) Risk management rules (preempt common failure modes)
* **Overclaiming:** Never present “not affected” without an evidence-backed rationale; otherwise use `NEEDS_REVIEW` with a clear missing-evidence reason.
* **Evidence sprawl:** Enforce evidence budgets (per-scan size caps) and retention tiers; “audit pack export” must remain complete even when the platform prunes caches.
* **Runtime ambiguity:** Runtime corroboration is supportive, not absolute; map to “observed/supports/contradicts/unknown” rather than binary.
* **Policy drift:** Policy bundles are versioned and pinned into attestations; changes must produce new signed verdicts (delta verdicts).
## 10) Definition of done for the feature
A release is “done” only if:
* A build produces an OCI artifact with an attached **signed verdict attestation**.
* Each verdict is **explainable** (reason steps + proof pointers).
* Reachability evidence is **stored as a reproducible subgraph** (or explicitly UNKNOWN with reason).
* Replay verification reproduces the same verdict with pinned inputs.
* UX starts from vulnerabilities and links directly to proofs and audit export.
If you want, I can turn these guidelines into: (1) a manager-ready checklist per sprint, and (2) a concrete “verdict attestation” JSON schema with canonical hashing/serialization rules.

View File

@@ -0,0 +1,93 @@
# ARCHIVED
> **Archived:** 2025-12-22
> **Reason:** Gap analysis complete. Recommendations incorporated into sprints and documentation.
>
> **Implementation Artifacts:**
> - SPRINT_2000_0003_0001: Alpine connector and APK comparator
> - SPRINT_2000_0003_0002: Comprehensive distro version tests (50-100 per distro)
> - SPRINT_4000_0002_0001: Backport UX explainability ("Compared with" badge, "Why Fixed" popover)
> - SPRINT_6000_SUMMARY.md: Updated to reference existing Concelier comparators
> - `src/Concelier/AGENTS.md`: Added distro backport version handling section
>
> **Existing Implementations Validated:**
> - `src/Concelier/__Libraries/StellaOps.Concelier.Merge/Comparers/Nevra.cs` (RPM)
> - `src/Concelier/__Libraries/StellaOps.Concelier.Merge/Comparers/DebianEvr.cs` (Debian/Ubuntu)
> - Distro connectors: Debian, Ubuntu, RedHat, SUSE
---
Here's a quick, practical heads-up on **patch-aware backport handling** so your vulnerability verdicts don't go sideways.
![Package versions concept diagram](attachment\:image)
### Why this matters
Distros often **backport fixes** without bumping the upstream version. If you compare versions with a generic SemVer library, you can mislabel **fixed** builds as **vulnerable** (or the reverse).
### Use distro-native comparators (not SemVer)
* **RPM (RHEL/CentOS/Fedora/openSUSE):** compare using **EVR** (`epoch:version-release`) via `rpmvercmp`. Tilde `~` sorts **before** anything; releases matter (e.g., `1.2-3.el9_2` > `1.2-3`).
* **Debian/Ubuntu:** compare **epoch >> upstream_version >> debian_revision** using `dpkg --compare-versions` rules. Tilde `~` sorts **lower** than empty, so `2.0~rc1` < `2.0`.
* **Alpine (APK):** follows its own comparator; treat `-r` (pkgrel) as part of ordering, similar in spirit to RPM release.
### Practical rules for your scanner (Stella Ops / Feedser -> Vexer)
1. **Normalize the package coordinate**
* RPM: `name:evr.arch` (epoch default 0 if missing).
* DEB: `name:epoch:upstream_version-debian_revision arch`.
* Keep the **distro release**/revision; it encodes backports.
2. **Compare with native engines**
* On Linux hosts/containers, call the system tool when possible:
* RPM: `rpm --qf '%{EPOCH}:%{VERSION}-%{RELEASE}\n' -q <pkg>` then use `rpmdev-vercmp`/`rpmvercmp`.
* DEB/Ubuntu: `dpkg-query -W -f='${Version}\n' <pkg>` and `dpkg --compare-versions`.
* In offline analysis, embed battle-tested comparators (ports of `rpmvercmp` and `dpkg` logic) in your evaluator.
3. **Model advisories with distro ranges**
* Store **per-ecosystem fixed ranges**:
* RPM example: `fixed >= 2:1.4.3-5.el9_3`
* DEB example: `fixed >= 1:1.4.3-5+deb12u2`
* Do **not** rewrite to SemVer; keep native forms.
4. **VEX/decisioning**
* When upstream says "fixed in 1.4.4" but **distro claims fixed in 1.4.3-5~deb12u2**, prefer distro channel **if source is trusted**.
* Record **evidence**: source (DSA/RHSA/USN), comparator used, installed EVR/DEB version, fixed threshold, and result. Attach to the verdict.
5. **Edge cases to test**
* Epoch jumps: `1:1.2-1` > `0:9.9-9`.
* Tilde pre-releases: `2.0~rc1` < `2.0`.
* Release qualifiers: `1.2-3.el9_2` < `1.2-3.el9_3`.
* Rebuilds/backports: `1.2-3ubuntu0.1` vs `1.2-3`.
### Minimal implementation sketch (C#)
* **Strategy pattern**: `IVersionComparator` with implementations `RpmComparator`, `DpkgComparator`, `ApkComparator`.
* **Selector** by package source (`rpmdb`, `dpkg-status`, `apk info`).
* **Evidence struct**:
```csharp
record VersionVerdict(
string Pkg, string Distro, string Installed, string Fixed,
string Comparator, bool IsFixed, string EvidenceSource, string[] ProofLines);
```
* **Fallback**: If native comparator unavailable, use embedded ports of `rpmvercmp` and Debian's algorithm; never SemVer.
### CI tests you should pin
* A table-driven test set with 50-100 cases covering epochs, tildes, and distro revisions.
* Golden files per distro to prevent regressions.
* Cross-check installed values from real images (e.g., `ubi9`, `debian:12`, `ubuntu:22.04`, `alpine:3.20`).
### UX nudge
* In the UI, show **"Compared with: RPM EVR / dpkg rules"** and link the **exact fixed threshold** that matched. Provide a "why fixed" popover showing the string compare steps.
If you like, I can drop in ready-to-use C# comparators (rpmvercmp/dpkg) and a test corpus so you can wire this straight into Feedser/Vexer.

View File

@@ -0,0 +1,469 @@
Im sharing a **competitive securitytool matrix** that you can immediately plug into StellaOps strategy discussions — it maps real, *comparable evidence* from public sources to categories where most current tools fall short. Below the CSV is a short Markdown commentary that highlights gaps & opportunities StellaOps can exploit.
---
## 🧠 Competitive Security Tool Matrix (CSV)
**Columns:**
`Tool,SBOM Fidelity,VEX Handling,Explainability,SmartDiff,CallStack Reachability,Deterministic Scoring,Unknowns State,Ecosystem Integrations,Policy Engine,Offline/AirGapped,Provenance/Attestations,Public Evidence`
```
Tool,SBOM Fidelity,VEX Handling,Explainability,SmartDiff,CallStack Reachability,Deterministic Scoring,Unknowns State,Ecosystem Integrations,Policy Engine,Offline/AirGapped,Provenance/Attestations,Public Evidence
Trivy (open),CycloneDX/SPDX support (basic),Partial* (SBOM ext refs),Low,No,No,Moderate,No,Strong CI/CD/K8s,Minimal,Unknown,SBOM only evidence; VEX support request exists but unmerged⟨*⟩,:contentReference[oaicite:0]{index=0}
Grype/Syft,Strong CycloneDX/SPDX (generator + scanner),None documented,Low,No,No,Moderate,No,Strong CI/CD/K8s,Policy minimal,Unknown,Syft can create signed SBOMs but not full attestations,:contentReference[oaicite:1]{index=1}
Snyk,SBOM export likely (platform),Unknown/limited,Vuln context explainability (reports),No,No,Proprietary risk scoring,Partial integrations,Strong Black/White list policies in UI,Unknown,Unknown (not focused on attestations),:contentReference[oaicite:2]{index=2}
Prisma Cloud,Enterprise SBOM + vuln scanning,Runtime exploitability contexts?*,Enterprise dashboards,No formal smartdiff,No,Risk prioritization,Supports multicloud integrations,Rich policy engines (CNAPP),Supports offline deployment?,Unknown attestations capabilities,:contentReference[oaicite:3]{index=3}
Aqua (enterprise),SBOM via Trivy,Unknown commercial VEX support,Some explainability in reports,No documented smartdiff,No,Risk prioritization,Comprehensive integrations (cloud/CI/CD/SIEM),Enterprise policy supports compliance,Airgapped options in enterprise,Focus on compliance attestations?,:contentReference[oaicite:4]{index=4}
Anchore Enterprise,Strong SBOM mgmt + format support,Policy engine can ingest SBOM + vulnerability sources,Moderate (reports & SBOM insights),Potential policy diff,No explicit reachability analysis,Moderate policy scoring,Partial,Rich integrations (CI/CD/registry),Policyascode,Airgapped deploy supported,SBOM provenance & signing via Syft/intoto,:contentReference[oaicite:5]{index=5}
StellaOps,High fidelity SBOM (CycloneDX/SPDX) planned,Native VEX ingestion + decisioning,Explainability + proof extracts,Smartdiff tech planned,Callstack reachability analysis,Deterministic scoring with proofs,Explicit unknowns state,Integrations with CI/CD/SIGSTORE,Declarative multimodal policy engine,Full offline/airgapped support,Provenance/attestations via DSSE/intoto,StellaOps internal vision
```
---
## 📌 Key Notes, Gaps & Opportunities (Markdown)
### **SBOM Fidelity**
* **Open tools (Trivy, Syft)** already support CycloneDX/SPDX output, but mostly as flat SBOM artifacts without longterm repositories or versioned diffing. ([Ox Security][1])
* **Opportunity:** Provide *repository + lineage + merge semantics* with proofs — not just generation.
### **VEX Handling**
* Trivy has an open feature request for dynamic VEX ingestion. ([GitHub][2])
* Most competitors either lack VEX support or have no *decisioning logic* based on exploitability.
* **Opportunity:** Firstclass VEX ingestion with evaluation rules + automated scoring.
### **Explainability**
* Commercial tools (Prisma/Snyk) offer UI report context and devoriented remediation guidance. ([Snyk][3])
* OSS tools provide flat scan outputs with minimal causal trace.
* **Opportunity:** Link vulnerability flags back to *proven code paths*, enriched with SBOM + call reachability.
### **SmartDiff & Unknowns State**
* No major tool advertising *smart diffing* between SBOMs for incremental risk deltas across releases.
* **Opportunity:** Automate risk deltas between SBOMs with uncertainty margins.
### **CallStack Reachability**
* None of these tools publicly document callstack based exploit reachability analysis outofthebox.
* **Opportunity:** Integrate dynamic/static reachability evidence that elevates scanning from surface report → *impact map*.
### **Deterministic Scoring**
* Snyk & Prisma offer proprietary scoring that blends severity + context. ([TrustRadius][4])
* But these arent reproducible with *signed verdicts*.
* **Opportunity:** Provide *deterministic, attestable scoring proofs*.
### **Ecosystem Integrations**
* Trivy/Grype excel at lightweight CI/CD and Kubernetes. ([Echo][5])
* Enterprise products integrate deeply into cloud/registry. ([Palo Alto Networks][6])
* **Opportunity:** Expand *sigstore/notation* based pipelines and automated attestation flows.
### **Policy Engine**
* Prisma & Aqua have mature enterprise policies. ([Aqua][7])
* OSS tools have limited simple allow/deny.
* **Opportunity:** Provide *lattice/constraint policies* with proof outputs.
### **Offline/AirGapped**
* Anchore supports airgapped deployment in enterprise contexts. ([Anchore][8])
* Support across all open tools is adhoc at best.
* **Opportunity:** Builtin deterministic offline modes with offline SBOM stores and VEX ingestion.
### **Provenance/Attestations**
* Syft supports SBOM output in various formats; also *intoto* for attestations. ([Ox Security][1])
* Most competitors dont prominently advertise *attestation pipelines*.
* **Opportunity:** Endtoend DSSE/intoto provenance with immutable proofs.
---
### 📌 Public Evidence Links
* **Trivy / Syft / Grype SBOM support & formats:** CycloneDX/SPDX; Syft as generator + Grype scanner. ([Ox Security][1])
* **Trivy VEX feature request:** exists but not mainstream yet. ([GitHub][2])
* **Snyk platform capability:** scans containers, IaC, devfirst prioritization. ([Snyk][3])
* **Prisma Cloud container security:** lifecycle scanning + policy. ([Palo Alto Networks][6])
* **Anchore Enterprise SBOM mgmt & policy:** central imports + CI/CD ecosystem. ([Anchore Docs][9])
---
If youd like this in **Excel, JSON, or Mermaid graph form** next, I can generate it — just tell me the format you prefer.
[1]: https://www.ox.security/blog/sbom-tools/?utm_source=chatgpt.com "Top 5 SBOM Tools for Securing the Software Supply Chain"
[2]: https://github.com/aquasecurity/trivy/discussions/8047?utm_source=chatgpt.com "Support for dynamic VEX retrieval from \"External ..."
[3]: https://snyk.io/?utm_source=chatgpt.com "Snyk AI-powered Developer Security Platform | AI-powered ..."
[4]: https://www.trustradius.com/compare-products/palo-alto-networks-prisma-cloud-vs-snyk?utm_source=chatgpt.com "Palo Alto Networks Prisma Cloud vs Snyk"
[5]: https://www.echohq.com/blog/best-container-scanning-tools?utm_source=chatgpt.com "10 Best Container Scanning Tools for 2025"
[6]: https://www.paloaltonetworks.com/prisma/cloud/container-security?utm_source=chatgpt.com "Container Security Solutions"
[7]: https://www.aquasec.com/integrations/?utm_source=chatgpt.com "Integrations - Aqua Security"
[8]: https://anchore.com/blog/effortless-sbom-analysis-how-anchore-enterprise-simplifies-integration/?utm_source=chatgpt.com "Effortless SBOM Analysis with Anchore Enterprise"
[9]: https://docs.anchore.com/current/docs/sbom_management/?utm_source=chatgpt.com "SBOM Management"
Below is a **deep, implementation-grade elaboration** that turns the comparison matrix into **actionable architecture and product differentiation** for Stella Ops. I will deliberately avoid marketing language and focus on **mechanics, evidence, and execution gaps**.
---
## 1. Why existing tools plateau (root cause analysis)
Across Trivy, Syft/Grype, Snyk, Prisma, Aqua, Anchore, there is a **structural ceiling** they all hit — regardless of OSS vs enterprise.
### Shared structural limitations
1. **SBOM is treated as a static artifact**
* Generated → stored → scanned.
* No concept of *evolving truth*, lineage, or replayability.
2. **Vulnerability scoring is probabilistic, not provable**
* CVSS + vendor heuristics.
* Cannot answer: *“Show me why this CVE is exploitable here.”*
3. **Exploitability ≠ reachability**
* “Runtime context” ≠ call-path proof.
4. **Diffing is file-level, not semantic**
* Image hash change ≠ security delta understanding.
5. **Offline support is operational, not epistemic**
* You can run it offline, but you cannot **prove** what knowledge state was used.
These are not accidental omissions. They arise from **tooling lineage**:
* Trivy/Syft grew from *package scanners*
* Snyk grew from *developer remediation UX*
* Prisma/Aqua grew from *policy & compliance platforms*
None were designed around **forensic reproducibility or trust algebra**.
---
## 2. SBOM fidelity: what “high fidelity” actually means
Most tools claim CycloneDX/SPDX support. That is **necessary but insufficient**.
### Current reality
| Dimension | Industry tools |
| ----------------------- | ---------------------- |
| Component identity | Package name + version |
| Binary provenance | Weak or absent |
| Build determinism | None |
| Dependency graph | Flat or shallow |
| Layer attribution | Partial |
| Rebuild reproducibility | Not supported |
### What Stella Ops must do differently
**SBOM must become a *stateful ledger*, not a document.**
Concrete requirements:
* **Component identity = (source + digest + build recipe hash)**
* **Binary → source mapping**
* ELF Build-ID / Mach-O UUID / PE timestamp+hash
* **Layer-aware dependency graphs**
* Not “package depends on X”
* But “binary symbol A resolves to shared object B via loader rule C”
* **Replay manifest**
* Exact feeds
* Exact policies
* Exact scoring rules
* Exact timestamps
* Hash of everything
This is the foundation for *deterministic replayable scans* — something none of the competitors even attempt.
---
## 3. VEX handling: ingestion vs decisioning
Most vendors misunderstand VEX.
### What competitors do
* Accept VEX as:
* Metadata
* Annotation
* Suppression rule
* No **formal reasoning** over VEX statements.
### What Stella Ops must do
VEX is not a comment — it is a **logical claim**.
Each VEX statement:
```
IF
product == X
AND component == Y
AND version in range Z
THEN
status ∈ {not_affected, affected, fixed, under_investigation}
BECAUSE
justification J
WITH
evidence E
```
Stella Ops advantage:
* VEX statements become **inputs to a lattice merge**
* Conflicting VEX from:
* Vendor
* Distro
* Internal analysis
* Runtime evidence
* Are resolved **deterministically** via policy, not precedence hacks.
This unlocks:
* Vendor-supplied proofs
* Customer-supplied overrides
* Jurisdiction-specific trust rules
---
## 4. Explainability: reports vs proofs
### Industry “explainability”
* “This vulnerability is high because…”
* Screenshots, UI hints, remediation text.
### Required explainability
Security explainability must answer **four non-negotiable questions**:
1. **What exact evidence triggered this finding?**
2. **What code or binary path makes it reachable?**
3. **What assumptions are being made?**
4. **What would falsify this conclusion?**
No existing scanner answers #4.
### Stella Ops model
Each finding emits:
* Evidence bundle:
* SBOM nodes
* Call-graph edges
* Loader resolution
* Runtime symbol presence
* Assumption set:
* Compiler flags
* Runtime configuration
* Feature gates
* Confidence score **derived from evidence density**, not CVSS
This is explainability suitable for:
* Auditors
* Regulators
* Courts
* Defense procurement
---
## 5. Smart-Diff: the missing primitive
All tools compare:
* Image A vs Image B
* Result: *“+3 CVEs, 1 CVE”*
This is **noise-centric diffing**.
### What Smart-Diff must mean
Diff not *artifacts*, but **security meaning**.
Examples:
* Same CVE remains, but:
* Call path removed → risk collapses
* New binary added, but:
* Dead code → no reachable risk
* Dependency upgraded, but:
* ABI unchanged → no exposure delta
Implementation direction:
* Diff **reachability graphs**
* Diff **policy outcomes**
* Diff **trust weights**
* Diff **unknowns**
Output:
> “This release reduces exploitability surface by 41%, despite +2 CVEs.”
No competitor does this.
---
## 6. Call-stack reachability: why runtime context isnt enough
### Current vendor claim
“Runtime exploitability analysis.”
Reality:
* Usually:
* Process exists
* Library loaded
* Port open
This is **coarse correlation**, not proof.
### Stella Ops reachability model
Reachability requires **three layers**:
1. **Static call graph**
* From entrypoints to vulnerable symbols
2. **Binary resolution**
* Dynamic loader rules
* Symbol versioning
3. **Runtime gating**
* Feature flags
* Configuration
* Environment
Only when **all three align** does exploitability exist.
This makes false positives *structurally impossible*, not heuristically reduced.
---
## 7. Deterministic scoring: replacing trust with math
Every competitor uses:
* CVSS
* EPSS
* Proprietary weighting
Problem:
* Scores are **non-reproducible**
* Cannot be attested
* Cannot be audited
### Stella Ops scoring
Score = deterministic function of:
* Evidence count
* Evidence strength
* Assumption penalties
* Trust source weights
* Policy constraints
Same inputs → same outputs → forever.
This enables:
* Signed risk decisions
* Cross-org verification
* Legal defensibility
---
## 8. Unknowns as a first-class state
Industry tools suppress uncertainty.
Stella Ops must **surface it**.
States:
* Known-safe
* Known-vulnerable
* **Unknown-reachable**
* **Unknown-unreachable**
Unknowns are **risk**, but different from vulnerabilities.
This is critical for:
* Air-gapped environments
* Novel exploits
* Zero-day windows
No competitor models this explicitly.
---
## 9. Offline / air-gapped: epistemic completeness
Competitors:
* “Yes, we can run offline.”
But cannot answer:
> “Exactly what knowledge did you have when you made this decision?”
Stella Ops must:
* Snapshot feeds
* Snapshot policies
* Snapshot scoring rules
* Snapshot trust anchors
Then bind them cryptographically to scan results.
This creates **forensic replayability**, not just offline execution.
---
## 10. Strategic conclusion (no hype)
### Where Stella Ops can **objectively dominate**
1. Deterministic replayable scans
2. Formal VEX reasoning
3. Reachability-backed exploitability
4. Semantic smart-diff
5. Evidence-first explainability
6. Unknowns modeling
7. Jurisdiction-ready offline trust
### Where competitors remain stronger (for now)
* Mass-market UX polish
* SaaS onboarding friction
* Marketplace integrations
These are solvable later. The **hard parts** above are not.
---
If you want, next I can:
* Convert this into **developer implementation milestones**
* Map features → **moat strength vs competitors**
* Design the **Trust Algebra / Lattice Engine spec**
* Produce a **regulator-grade threat & evidence model**
Tell me which vector you want next.

View File

@@ -0,0 +1,247 @@
Yes — you need **dedicated UI affordances** for “Verdict + DeltaVerdict + Evidence” because the interaction model is fundamentally different from a classic “vulnerability list” UI.
But you do **not** necessarily need a whole new toplevel product area on day one.
The right approach is usually:
1. **Embed the experience where decisions happen** (build/release/PR gates).
2. Add **one dedicated “Compare / Delta” screen** (a focused view) reachable from those contexts.
3. Introduce a **top-level “Assurance/Audit” workspace only if you have compliance-heavy users** who need cross-project oversight.
Below is a concrete way to implement both options and when to choose each.
---
## When a dedicated UI is warranted
A dedicated UI is justified if at least **two** of these are true:
* You have **multiple repos/services** and security/compliance need to see **fleet-wide deltas**, not just per build.
* You need **approval workflows** (exceptions, risk acceptance, “ship with waiver”).
* You need **auditor-grade artifact browsing**: signatures, provenance, replay, evidence packs.
* Developers complain about “scan noise” and need **diff-first triage** to be fast.
* You have separate personas: **Dev**, **Security**, **Compliance/Audit** — each needs different default views.
If those arent true, keep it embedded and light.
---
## Recommended approach (most teams): Dedicated “Compare view” + embedded panels
### Where it belongs in the existing UI
Assuming your current navigation is something like:
**Projects → Repos → Builds/Releases → Findings/Vulnerabilities**
Then “DeltaVerdict” belongs primarily in **Build/Release details**, not in the global vulnerability list.
**Add two key entry points:**
1. A **status + delta summary** on every Build/Release page (above the fold).
2. A **Compare** action that opens a dedicated comparison screen (or tab).
### Information architecture (practical, minimal)
On the **Build/Release details page**, add a header section:
* **Verdict chip**: Allowed / Blocked / Warn
* **Delta chip**: “+2 new exploitable highs”, “Reachability flip: yes/no”, “Unknowns: +3”
* **Baseline**: “Compared to: v1.4.2 (last green in prod)”
* **Actions**:
* **Compare** (opens dedicated delta view)
* **Download Evidence Pack**
* **Verify Signatures**
* **Replay** (copy command / show determinism hash)
Then add a tab set:
* **Delta (default)**
* Components (SBOM)
* Vulnerabilities
* Reachability
* VEX / Claims
* Attestations (hashes, signatures, provenance)
#### Why “Delta” should be the default tab
The users first question in a release is: *What changed that affects risk?*
If you make them start in a full vuln list, you rebuild the noise problem.
---
## How the dedicated “Compare / Delta” view should work
Think of it as a “git diff”, but for risk and provenance.
### 1) Baseline selection (must be explicit and explainable)
Top of the Compare view:
* **Base** selector (default chosen by system):
* “Last green verdict in same environment”
* “Previous release tag”
* “Parent commit / merge-base”
* **Head** selector:
* Current build/release
* Show **why** the baseline was chosen (small text):
“Selected last prod release with Allowed verdict under policy P123.”
This matters because auditors will ask “why did you compare against *that*?”
### 2) Delta summary strip (fast triage)
A horizontal strip with only the key deltas:
* **New exploitable vulns:** N (by severity)
* **Reachability flips:** N (new reachable / newly unreachable)
* **Component changes:** +A / R / ~C
* **VEX claim flips:** N
* **Policy/feed drift:** policy changed? feed snapshot changed? stale?
### 3) Three-pane layout (best for speed)
Left: **Delta categories** (counts)
* New exploitable vulns
* Newly reachable
* Component adds/removes
* Changed versions
* Claim changes
* Unknowns / missing data
Middle: **List of changed items** (sorted by risk)
* Each item shows: component, version, CVE (if applicable), exploitability, reachability, current disposition (VEX), gating rule triggered
Right: **Proof / explanation panel**
* “Why is it blocked?”
* Shows:
* the **policy rule** that fired (with rule ID)
* the **witness path** for reachability (minimal path)
* the **claim sources** for VEX (vendor/distro/internal) and merge explanation
* links to the exact **envelope hashes** involved
This is where “proof-carrying” becomes usable.
### 4) Actionables output (make it operational)
At the top of the item list include a “What to do next” section:
* Upgrade component X → version Y
* Patch CVE Z
* Add/confirm VEX claim with evidence
* Reduce reachability (feature flag, build config)
* Resolve unknowns (SBOM missing for module A)
This prevents the compare screen from becoming yet another “informational dashboard.”
---
## If you do NOT create any new dedicated view
If you strongly want zero new screens, the minimum acceptable integration is:
* Add a **Delta toggle** on the existing Vulnerabilities page:
* “All findings” vs “Changes since baseline”
* Add a **baseline selector** on that page.
* Add an **Attestations panel** on the Build/Release page for evidence pack + signature verification.
This can work, but it tends to fail as the system grows because:
* Vulnerability list UIs are optimized for volume browsing, not causal proof
* Reachability and VEX explanation become buried
* Auditors still need a coherent “verdict story”
If you go this route, at least add a **“Compare drawer”** (modal) that shows the delta summary and links into filtered views.
---
## When you SHOULD add a top-level dedicated UI (“Assurance” workspace)
Create a dedicated left-nav item only when you have these needs:
1. **Cross-project oversight**: “show me all new exploitable highs introduced this week across org.”
2. **Audit operations**: evidence pack management, replay logs, signature verification at scale.
3. **Policy governance**: browse policy versions, rollout status, exceptions, owners.
4. **Release approvals**: security sign-off steps, waivers, expiry dates.
### What that workspace would contain
* **Overview dashboard**
* blocked releases (by reason)
* new risk deltas by team/repo
* unknowns trend
* stale feed snapshot alerts
* **Comparisons**
* search by repo/build/tag and compare any two artifacts
* **Attestations & Evidence**
* list of verdicts/delta verdicts with verification status
* evidence pack download and replay
* **Policies & Exceptions**
* policy versions, diffs, who changed what
* exceptions with expiry and justification
This becomes the home for Security/Compliance, while Devs stay in the build/release context.
---
## Implementation details that make the UI “work” (avoid common failures)
### 1) Idempotent “Compute delta” behavior
When user opens Compare view:
* UI requests DeltaVerdict by `{base_verdict_hash, head_verdict_hash, policy_hash}`.
* If not present, backend computes it.
* UI shows deterministic progress (“pending”), not “scanning…”.
### 2) Determinism and trust indicators
Every compare screen should surface:
* Determinism hash
* Policy version/hash
* Feed snapshot timestamp/age
* Signature verification status
If verification fails, the UI must degrade clearly (red banner, disable “Approved” actions).
### 3) Baseline rules must be visible
Auditors hate “magic.”
Show baseline selection logic and allow override.
### 4) Dont show full graphs by default
Default to:
* minimal witness path(s)
* minimal changed subgraph
* expand-on-demand for deep investigation
### 5) Role-based access
* Developers: see deltas, actionables, witness paths
* Security: see claims sources, merge rationale, policy reasoning
* Audit: see signatures, replay, evidence pack
---
## Decision recommendation (most likely correct)
* Build **embedded panels** + a **dedicated Compare/Delta view** reachable from Build/Release and PR checks.
* Delay a top-level “Assurance” workspace until you see real demand from security/compliance for cross-project oversight and approvals.
This gives you the usability benefits of “diff-first” without fragmenting navigation or building a parallel UI too early.
If you share (even roughly) your existing nav structure (what pages exist today), I can map the exact placements and propose a concrete IA tree and page wireframe outline aligned to your current UI.

View File

@@ -0,0 +1,184 @@
Here's a compact, practical way to make VEX trust decisions explainable and replayable across vendor, distro, and internal sources—without adding human-in-the-loop friction.
---
# VEX Trust Lattice (compact)
**Goal:** turn messy/contradictory VEX claims into a single, signed, reproducible verdict with a numeric confidence and an audit trail.
## 1) Trust vector per source
Each VEX source S gets a 3component trust vector scored in [0..1]:
* **Provenance (P):** cryptographic & process integrity
* 1.00 = DSSEsigned, timestamped, Rekor/Git tag anchored, org DKIM/Sigstore OIDC, key in allowlist, rotation policy OK
* 0.75 = DSSEsigned + public key known, but no transparency log
* 0.40 = unsigned but retrieved via authenticated, immutable artifact repo
* 0.10 = opaque/CSV/email/manual import
* **Coverage (C):** how well the statement's scope maps to your asset
* 1.00 = exact package + version/build digest + feature/flag context matched
* 0.75 = exact pkg + version range matched; partial feature context
* 0.50 = productlevel only; maps via CPE/PURL family
* 0.25 = familylevel heuristics; no version proof
* **Replayability (R):** can we deterministically rederive the claim?
* 1.00 = all inputs pinned (feeds, SBOM hash, ruleset hash, lattice version); replays byteidentical
* 0.60 = inputs mostly pinned; nondeterministic ordering tolerated but stable outcome
* 0.20 = ephemeral APIs; no snapshot
**BaseTrust(S) = wP·P + wC·C + wR·R** (defaults: wP=0.45, wC=0.35, wR=0.20; tunable per policy).
## 2) Claim strength & freshness
Every individual VEX claim `S asserts X` carries multipliers:
* **Strength (M):** *not*affectedbecause{reason}
* Exploitability analysis + reachability proof subgraph provided → 1.00
* Config/featureflag reason with evidence → 0.80
* Vendor blanket statement → 0.60
* "Under investigation" → 0.40
* **Freshness (F):** timedecay curve; default halflife 90 days
* `F = exp(- ln(2) · age_days / 90)`; floor at 0.35 unless revoked
**ClaimScore = BaseTrust(S) · M · F.**
## 3) Lattice ordering & merge
Define a **partial order** on claims by (scope specificity, ClaimScore). More specific scope wins ties.
For a given CVE×Asset, gather all claims `{Ci}`:
* If any **revocation/contradiction** exists, keep both and trigger **conflict mode**: require replay proof; otherwise downweight older/weaker by Δ=0.25.
* Final **verdict** chosen by **argmax(ClaimScore)** after conflict adjustments.
Return tuple:
```
Verdict = {
status: {affected|not_affected|under_investigation|fixed},
confidence: ClaimScore*,
expl: list of (source, reason, P/C/R, M, F),
evidence_refs: [attestations, SBOM hash, reachability subgraph id],
policy_hash, lattice_version
}
```
## 4) Policy hooks (explainable gates)
* **Minimum confidence by environment:** e.g., prod requires ≥0.75 to accept "not_affected".
* **Unknowns budget:** fail if (#unknown deps > N) OR (Σ(1ClaimScore) over unknowns > T).
* **Source quotas:** cap influence from any single vendor at 60% unless a second independent source supports within Δ=0.1.
* **Reason allowlist:** forbid blanket vendor claims for criticals unless reachability proof exists.
## 5) Deterministic replay
To guarantee "same inputs → same verdict":
* Pin: SBOM digest(s), vuln feed snapshot ids, VEX document digests, reachability graph ids, policy file, lattice version, clock cutoff.
* Sort: stable topological order on inputs (by `(issuer_did, statement_digest)`).
* Serialize verdict + inputs into a **Verdict Manifest** (JSON/CBOR) and sign (DSSE).
* Store in **Authority** with index: `(asset_digest, CVE, policy_hash, lattice_version)`.
## 6) Minimal data model (for Vexer/Policy Engine)
```json
{
"source": {
"id": "did:web:vendor.example",
"provenance": {"sig_type":"dsse","rekor_log_id":"...","key_alias":"vendor_k1"},
"provenance_score": 0.90,
"coverage_score": 0.75,
"replay_score": 0.60,
"weights": {"wP":0.45,"wC":0.35,"wR":0.20}
},
"claim": {
"scope": {"purl":"pkg:rpm/openssl@3.0.12-5","digest":"sha256:...","features":{"fips":true}},
"cve": "CVE-2025-12345",
"status": "not_affected",
"reason": "feature_flag_off",
"strength": 0.80,
"issued_at": "2025-11-28T10:12:00Z",
"evidence": {"reach_subgraph_id":"reg:subg/abcd","attestation":"sha256:..."}
},
"policy": {"min_confidence_prod":0.75,"unknown_budget":5,"require_reachability_for_criticals":true},
"lattice_version": "1.2.0"
}
```
## 7) Deterministic evaluation (C# sketch)
```csharp
public record TrustWeights(double wP=0.45, double wC=0.35, double wR=0.20);
double BaseTrust(double P, double C, double R, TrustWeights W)
=> W.wP*P + W.wC*C + W.wR*R;
double Freshness(DateTime issuedAt, DateTime cutoff, double halfLifeDays=90, double floor=0.35)
{
var age = (cutoff - issuedAt).TotalDays;
var f = Math.Exp(-Math.Log(2) * age / halfLifeDays);
return Math.Max(f, floor);
}
double ClaimScore(Source s, Claim c, TrustWeights W, DateTime cutoffUtc)
{
var baseTrust = BaseTrust(s.P, s.C, s.R, W);
var freshness = Freshness(c.IssuedAt, cutoffUtc);
return baseTrust * c.Strength * freshness;
}
// Merge: pick best score; apply conflict penalty if contradictory present
Verdict Merge(IEnumerable<(Source S, Claim C)> claims, Policy policy, DateTime cutoffUtc)
{
var scored = claims.Select(t => new {
t.S, t.C, Score = ClaimScore(t.S, t.C, t.S.Weights, cutoffUtc)
}).ToList();
bool contradictory = scored.Select(x=>x.C.Status).Distinct().Count() > 1;
if (contradictory) {
scored = scored.Select(x => new {
x.S, x.C, Score = x.Score * 0.75 // conflict penalty
}).ToList();
}
var winner = scored.OrderByDescending(x => (x.C.Scope.Specificity, x.Score)).First();
if (policy.RequireReachForCriticals && winner.C.IsCritical && !winner.C.HasReachabilityProof)
return Verdict.FailGate("No reachability proof for critical");
if (policy.MinConfidenceProd.HasValue && winner.Score < policy.MinConfidenceProd)
return Verdict.FailGate("Below minimum confidence");
return Verdict.Accept(winner.C.Status, winner.Score, AuditTrail.From(scored));
}
```
## 8) UI: "Trust Algebra" panel (1 screen, no new page)
* **Header:** CVE × Asset digest → final status + confidence meter.
* **Stacked bars:** P/C/R contributions for the winning claim.
* **Claim table:** source, status, reason, P/C/R, strength, freshness, ClaimScore; toggle "show conflicts".
* **Policy chips:** which gates applied; click to open policy YAML/JSON (readonly if in replay).
* **Replay button:** "Reproduce verdict" → emits a signed **Verdict Manifest** and logs proof ids.
## 9) Defaults for source classes
* **Vendor:** P=0.9, C=0.7 (often coarse), R=0.6
* **Distro:** P=0.8, C=0.85 (buildaware), R=0.6
* **Internal:** P=0.85 (orgsigned), C=0.95 (exact SBOM+reach), R=0.9
Tune per issuer using rolling calibration: compare past ClaimScores vs. postmortem truth; adjust via small learning rate (±0.02/epoch) under a signed **calibration manifest** (also replayable).
---
If you want, I can drop this into your Stella Ops modules today as:
* **Vexer:** trustvector store + claim normalizer
* **Policy Engine:** lattice evaluator + gates
* **Authority:** verdict manifest signer/indexer
* **UI:** single "Trust Algebra" panel wired to evidence ids
Say the word and I'll generate the concrete JSON schemas, C# interfaces, and a seed policy file.