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:
@@ -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`
|
||||
@@ -0,0 +1,104 @@
|
||||
Below is a **feature → moat strength** map for Stella Ops, explicitly benchmarked against the tools we’ve been discussing (Trivy/Aqua, Grype/Syft, Anchore Enterprise, Snyk, Prisma Cloud). I’m 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 isn’t) 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 it’s “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]) | Low–Medium | **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 won’t 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 | Aqua’s VEX Hub is explicitly a centralized repository designed for discover/fetch/consume flows with Trivy ([Aqua][5]) | Medium | **3–4** | 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 | **1–2** | 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 head‑on 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.
|
||||
|
||||
### Snyk’s 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 won’t beat Snyk; **proof-carrying, artifact-tied, replayable reachability** can.
|
||||
|
||||
### Prisma Cloud’s 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.
|
||||
|
||||
### Anchore’s 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.
|
||||
|
||||
### Aqua’s 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, don’t 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 don’t 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"
|
||||
@@ -0,0 +1,124 @@
|
||||
Here’s a practical, from‑scratch blueprint for a **two‑stage reachability map** that turns low‑level 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 **binary‑level 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/Mach‑O), 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 points‑to 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), content‑addressed 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 Build‑ID) → 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, Build‑IDs, 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 isn’t) 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 + in‑toto 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:** fail‑safe 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 re‑hydrates inputs and re‑computes the slice; byte‑for‑byte match required.
|
||||
|
||||
# Where this plugs into Stella Ops (suggested modules)
|
||||
|
||||
* **Sbomer**: component/file mapping & SBOM import.
|
||||
* **Scanner.webservice**: binary parse & call‑graph extraction (keep lattice/policy elsewhere per your rule).
|
||||
* **Vexer/Policy Engine**: consume slices as evidence for “affected/not‑affected” claims.
|
||||
* **Attestor/Authority**: sign DSSE/in‑toto statements; push to OCI.
|
||||
* **Timeline/Notify**: surface verdict deltas over time, link to slices.
|
||||
|
||||
# Guardrails & fallbacks
|
||||
|
||||
* If stripped binaries: prefer Build‑ID + external symbol servers; else conservative over‑approx (mark unknown).
|
||||
* For JIT/dynamic plugins: capture runtime traces (eBPF/ETW) and merge as **observed edges** with timestamps.
|
||||
* Mixed‑lang stacks: unify by file hash + symbol name mangling rules per toolchain.
|
||||
|
||||
# Quick implementation plan (6 sprints)
|
||||
|
||||
1. **Binary ingest**: ELF/PE/Mach‑O parsing, Build‑ID hashing, symbol tables, PLT/IAT resolution.
|
||||
2. **Call‑edge recovery**: direct calls, basic indirect resolution, slice extractor by entrypoint.
|
||||
3. **SBOM mapping**: file→component map, layer digests, purl normalization.
|
||||
4. **Evidence format**: DSSE/in‑toto schema, deterministic manifests, OCI storage.
|
||||
5. **Queries & policies**: “is‑reachable?” API, unknowns budget, feature‑flag conditions, VEX plumbing.
|
||||
6. **Runtime merge**: optional eBPF/ETW traces → annotate edges, produce “observed‑path” 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)
|
||||
|
||||
* `CVE‑XXXX‑YYYY` → advisory lists function `foo_decrypt` in `libfoo.so`
|
||||
* We resolve `libfoo.so` Build‑ID in image, find symbols that match demangled name, build call paths from service entrypoints; if path exists, slice is “reachable” with 3–7 hop chain; otherwise “unreachable” with reasons (no import, stripped at link‑time, dead code eliminated, or gated by `FEATURE_X=false`).
|
||||
|
||||
# Costs (rough, for planning inside Stella Ops)
|
||||
|
||||
* **Core parsing & graph**: 3–4 engineer‑weeks
|
||||
* **Indirect calls & heuristics**: +3–5 weeks
|
||||
* **SBOM mapping & layers**: 2 weeks
|
||||
* **Attestations & OCI storage**: 1–2 weeks
|
||||
* **Policy/VEX integration & UI surfacing**: 2–3 weeks
|
||||
* **Runtime trace merge (optional)**: 2–4 weeks
|
||||
*(Parallelizable; add 25–40% for hardening/tests.)*
|
||||
|
||||
If you want, I can turn this into:
|
||||
|
||||
* a concrete **.NET 10 service skeleton** (endpoints + data contracts),
|
||||
* a **DSSE/in‑toto schema** for the slice, and
|
||||
* a **dev checklist** for deterministic builds and replay harness.
|
||||
@@ -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"
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
### 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.
|
||||
@@ -0,0 +1,469 @@
|
||||
I’m sharing a **competitive security‑tool matrix** that you can immediately plug into Stella Ops 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 Stella Ops can exploit.
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Competitive Security Tool Matrix (CSV)
|
||||
|
||||
**Columns:**
|
||||
`Tool,SBOM Fidelity,VEX Handling,Explainability,Smart‑Diff,Call‑Stack Reachability,Deterministic Scoring,Unknowns State,Ecosystem Integrations,Policy Engine,Offline/Air‑Gapped,Provenance/Attestations,Public Evidence`
|
||||
|
||||
```
|
||||
Tool,SBOM Fidelity,VEX Handling,Explainability,Smart‑Diff,Call‑Stack Reachability,Deterministic Scoring,Unknowns State,Ecosystem Integrations,Policy Engine,Offline/Air‑Gapped,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 smart‑diff,No,Risk prioritization,Supports multi‑cloud 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 smart‑diff,No,Risk prioritization,Comprehensive integrations (cloud/CI/CD/SIEM),Enterprise policy supports compliance,Air‑gapped 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),Policy‑as‑code,Air‑gapped deploy supported,SBOM provenance & signing via Syft/in‑toto,:contentReference[oaicite:5]{index=5}
|
||||
Stella Ops,High fidelity SBOM (CycloneDX/SPDX) planned,Native VEX ingestion + decisioning,Explainability + proof extracts,Smart‑diff tech planned,Call‑stack reachability analysis,Deterministic scoring with proofs,Explicit unknowns state,Integrations with CI/CD/SIGSTORE,Declarative multimodal policy engine,Full offline/air‑gapped support,Provenance/attestations via DSSE/in‑toto,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 long‑term 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:** First‑class VEX ingestion with evaluation rules + automated scoring.
|
||||
|
||||
### **Explainability**
|
||||
|
||||
* Commercial tools (Prisma/Snyk) offer UI report context and dev‑oriented 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.
|
||||
|
||||
### **Smart‑Diff & 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.
|
||||
|
||||
### **Call‑Stack Reachability**
|
||||
|
||||
* None of these tools publicly document call‑stack based exploit reachability analysis out‑of‑the‑box.
|
||||
* **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 aren’t 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/Air‑Gapped**
|
||||
|
||||
* Anchore supports air‑gapped deployment in enterprise contexts. ([Anchore][8])
|
||||
* Support across all open tools is ad‑hoc at best.
|
||||
* **Opportunity:** Built‑in deterministic offline modes with offline SBOM stores and VEX ingestion.
|
||||
|
||||
### **Provenance/Attestations**
|
||||
|
||||
* Syft supports SBOM output in various formats; also *in‑toto* for attestations. ([Ox Security][1])
|
||||
* Most competitors don’t prominently advertise *attestation pipelines*.
|
||||
* **Opportunity:** End‑to‑end DSSE/in‑toto 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, dev‑first 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 you’d 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 isn’t 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.
|
||||
@@ -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 top‑level 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 aren’t 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 user’s 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) Don’t 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.
|
||||
@@ -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 3‑component trust vector scored in [0..1]:
|
||||
|
||||
* **Provenance (P):** cryptographic & process integrity
|
||||
|
||||
* 1.00 = DSSE‑signed, timestamped, Rekor/Git tag anchored, org DKIM/Sigstore OIDC, key in allow‑list, rotation policy OK
|
||||
* 0.75 = DSSE‑signed + 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 = product‑level only; maps via CPE/PURL family
|
||||
* 0.25 = family‑level heuristics; no version proof
|
||||
* **Replayability (R):** can we deterministically re‑derive the claim?
|
||||
|
||||
* 1.00 = all inputs pinned (feeds, SBOM hash, ruleset hash, lattice version); replays byte‑identical
|
||||
* 0.60 = inputs mostly pinned; non‑deterministic 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*‑affected‑because‑{reason}
|
||||
|
||||
* Exploitability analysis + reachability proof subgraph provided → 1.00
|
||||
* Config/feature‑flag reason with evidence → 0.80
|
||||
* Vendor blanket statement → 0.60
|
||||
* "Under investigation" → 0.40
|
||||
* **Freshness (F):** time‑decay curve; default half‑life 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 down‑weight 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 (Σ(1–ClaimScore) over unknowns > T).
|
||||
* **Source quotas:** cap influence from any single vendor at 60% unless a second independent source supports within Δ=0.1.
|
||||
* **Reason allow‑list:** 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 (read‑only 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 (build‑aware), R=0.6
|
||||
* **Internal:** P=0.85 (org‑signed), C=0.95 (exact SBOM+reach), R=0.9
|
||||
|
||||
Tune per issuer using rolling calibration: compare past ClaimScores vs. post‑mortem 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:** trust‑vector 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.
|
||||
Reference in New Issue
Block a user