docs consoliation work
This commit is contained in:
@@ -0,0 +1,413 @@
|
||||
# Product Advisory: Deterministic Resolver Architecture
|
||||
|
||||
**Date:** 2025-12-24
|
||||
**Author:** Architecture Guild
|
||||
**Status:** Approved for Implementation
|
||||
**Sprint Epoch:** 9100
|
||||
**Priority:** P0 (Critical Path for Audit Compliance)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory defines the architecture for a **unified deterministic resolver** that evaluates vulnerability graphs using only declared evidence edges and produces cryptographically verifiable, reproducible verdicts. The resolver guarantees: **same inputs → same traversal order → same per-node inputs → same verdicts → same output digest**.
|
||||
|
||||
This is a foundational capability for:
|
||||
- Auditor-friendly verification ("same inputs → same verdicts")
|
||||
- Offline replay by vendors/distributors
|
||||
- Single-digest comparison across runs
|
||||
- CI/CD gate assertions
|
||||
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Current implementation has strong building blocks but lacks unified orchestration:
|
||||
|
||||
| Component | Location | Gap |
|
||||
|-----------|----------|-----|
|
||||
| Topological Sort | `DeterministicGraphOrderer` | Traversal only; no verdict output |
|
||||
| Lattice Evaluation | `TrustLatticeEngine`, `PolicyEvaluator` | No traversal sequence in output |
|
||||
| Content Addressing | `ContentAddressedIdGenerator` | Node IDs only; no edge IDs |
|
||||
| Purity Analysis | `ProhibitedPatternAnalyzer` | Static only; no runtime enforcement |
|
||||
| Digest Computation | Various | No unified "run digest" |
|
||||
|
||||
**Result:** Cannot produce a single `ResolutionResult` that captures:
|
||||
1. The exact traversal sequence used
|
||||
2. Per-node verdicts with individual digests
|
||||
3. A composite `FinalDigest` for verification
|
||||
|
||||
---
|
||||
|
||||
## Solution Architecture
|
||||
|
||||
### Core Data Model
|
||||
|
||||
```csharp
|
||||
// New: src/__Libraries/StellaOps.Resolver/
|
||||
|
||||
/// <summary>
|
||||
/// Content-addressed node identifier.
|
||||
/// Format: sha256(kind || ":" || normalized-key)
|
||||
/// </summary>
|
||||
public sealed record NodeId(string Value) : IComparable<NodeId>
|
||||
{
|
||||
public int CompareTo(NodeId? other) =>
|
||||
string.Compare(Value, other?.Value, StringComparison.Ordinal);
|
||||
|
||||
public static NodeId From(string kind, string normalizedKey) =>
|
||||
new(HexSha256($"{kind}:{normalizedKey}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Content-addressed edge identifier.
|
||||
/// Format: sha256(srcID || "->" || edgeKind || "->" || dstID)
|
||||
/// </summary>
|
||||
public sealed record EdgeId(string Value) : IComparable<EdgeId>
|
||||
{
|
||||
public static EdgeId From(NodeId src, string kind, NodeId dst) =>
|
||||
new(HexSha256($"{src.Value}->{kind}->{dst.Value}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Graph edge with content-addressed identity and optional cycle-cut marker.
|
||||
/// </summary>
|
||||
public sealed record Edge(
|
||||
EdgeId Id,
|
||||
NodeId Src,
|
||||
string Kind,
|
||||
NodeId Dst,
|
||||
JsonElement Attrs,
|
||||
bool IsCycleCut = false);
|
||||
|
||||
/// <summary>
|
||||
/// Graph node with content-addressed identity.
|
||||
/// </summary>
|
||||
public sealed record Node(
|
||||
NodeId Id,
|
||||
string Kind,
|
||||
JsonElement Attrs);
|
||||
|
||||
/// <summary>
|
||||
/// Immutable policy with content-addressed identity.
|
||||
/// </summary>
|
||||
public sealed record Policy(
|
||||
string Version,
|
||||
JsonElement Rules,
|
||||
string ConstantsDigest);
|
||||
|
||||
/// <summary>
|
||||
/// Individual verdict with its own content-addressed digest.
|
||||
/// </summary>
|
||||
public sealed record Verdict(
|
||||
NodeId Node,
|
||||
string Status,
|
||||
JsonElement Evidence,
|
||||
string VerdictDigest);
|
||||
|
||||
/// <summary>
|
||||
/// Complete resolution result with all digests for verification.
|
||||
/// </summary>
|
||||
public sealed record ResolutionResult(
|
||||
ImmutableArray<NodeId> TraversalSequence,
|
||||
ImmutableArray<Verdict> Verdicts,
|
||||
string GraphDigest,
|
||||
string PolicyDigest,
|
||||
string FinalDigest);
|
||||
```
|
||||
|
||||
### Resolver Algorithm
|
||||
|
||||
```text
|
||||
normalize(graph); // IDs, fields, ordering
|
||||
validate(graph); // No implicit data, cycles declared
|
||||
assert no-ambient-inputs(); // Runtime purity check
|
||||
|
||||
order = topoSortWithLexTieBreak(graph); // Uses only evidence edges
|
||||
verdicts = []
|
||||
|
||||
for node in order:
|
||||
inbound = gatherInboundEvidence(node) // Pure, no IO
|
||||
verdict = Evaluate(node, inbound, policy) // Pure, no IO
|
||||
verdicts.append(canonicalize(verdict) with VerdictDigest)
|
||||
|
||||
result = {
|
||||
traversal: order,
|
||||
verdicts: verdicts,
|
||||
graphDigest: digest(canonical(graph)),
|
||||
policyDigest: digest(canonical(policy))
|
||||
}
|
||||
result.finalDigest = digest(canonical(result))
|
||||
return result
|
||||
```
|
||||
|
||||
### Key Invariants
|
||||
|
||||
1. **Canonical IDs**: `NodeId = sha256(kind:normalized-key)`, `EdgeId = sha256(src->kind->dst)`
|
||||
2. **Canonical Serialization**: Alphabetical keys, sorted arrays, fixed numerics, ISO-8601 Z timestamps
|
||||
3. **Fixed Traversal**: Kahn's algorithm with lexicographic tie-breaker (`SortedSet<NodeId>`)
|
||||
4. **Explicit Cycles**: Cycles require `IsCycleCut = true` edge; unmarked cycles → invalid graph
|
||||
5. **Evidence-Only Evaluation**: Node verdict = f(node, inbound_edges, policy) — no ambient IO
|
||||
6. **Stable Outputs**: `(TraversalSequence, Verdicts[], GraphDigest, PolicyDigest, FinalDigest)`
|
||||
|
||||
---
|
||||
|
||||
## Gap Analysis vs Current Implementation
|
||||
|
||||
### Already Implemented (No Work Needed)
|
||||
|
||||
| Requirement | Implementation | Location |
|
||||
|-------------|---------------|----------|
|
||||
| Canonical JSON | RFC 8785 compliant | `CanonicalJsonSerializer`, `Rfc8785JsonCanonicalizer` |
|
||||
| Alphabetical key sorting | `StableDictionaryConverter` | `StellaOps.Canonicalization` |
|
||||
| ISO-8601 UTC timestamps | `Iso8601DateTimeConverter` | `StellaOps.Canonicalization` |
|
||||
| Topological sort with lex tie-break | `SortedSet<string>` | `DeterministicGraphOrderer:134` |
|
||||
| K4 Four-Valued Lattice | Complete | `K4Lattice.cs` |
|
||||
| VEX Lattice Merging | With traces | `OpenVexStatementMerger` |
|
||||
| Merkle Tree Proofs | SHA256-based | `DeterministicMerkleTreeBuilder` |
|
||||
| Static Purity Analysis | 16+ patterns | `ProhibitedPatternAnalyzer` |
|
||||
| Injected Timestamp | Context-based | `PolicyEvaluationContext.Now` |
|
||||
| Graph Content Hash | SHA256 | `DeterministicGraphOrderer.ComputeCanonicalHash()` |
|
||||
|
||||
### Gaps to Implement
|
||||
|
||||
| Gap | Priority | Sprint | Description |
|
||||
|-----|----------|--------|-------------|
|
||||
| Unified Resolver | P0 | 9100.0001.0001 | Single entry point producing `ResolutionResult` |
|
||||
| Cycle-Cut Edges | P1 | 9100.0001.0002 | `IsCycleCut` edge property; validation |
|
||||
| EdgeId | P2 | 9100.0001.0003 | Content-addressed edge identifiers |
|
||||
| FinalDigest | P1 | 9100.0002.0001 | Composite run-level digest |
|
||||
| Per-Node VerdictDigest | P2 | 9100.0002.0002 | Individual verdict digests |
|
||||
| Runtime Purity | P1 | 9100.0003.0001 | Runtime enforcement beyond static analysis |
|
||||
| Graph Validation + NFC | P3 | 9100.0003.0002 | Pre-traversal validation; NFC normalization |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Core Resolver Package (Sprint 9100.0001.*)
|
||||
|
||||
**Goal:** Create `StellaOps.Resolver` library with unified resolver pattern.
|
||||
|
||||
1. **Sprint 9100.0001.0001** — Core Resolver
|
||||
- `DeterministicResolver` class
|
||||
- `ResolutionResult` record
|
||||
- Integration with existing `DeterministicGraphOrderer`
|
||||
- Integration with existing `TrustLatticeEngine`
|
||||
|
||||
2. **Sprint 9100.0001.0002** — Cycle-Cut Edges
|
||||
- `IsCycleCut` property on edges
|
||||
- Cycle validation (unmarked cycles → error)
|
||||
- Graph validation before traversal
|
||||
|
||||
3. **Sprint 9100.0001.0003** — Content-Addressed EdgeId
|
||||
- `EdgeId` record
|
||||
- Edge content addressing
|
||||
- Merkle tree inclusion of edges
|
||||
|
||||
### Phase 2: Digest Infrastructure (Sprint 9100.0002.*)
|
||||
|
||||
**Goal:** Implement comprehensive digest chain for verification.
|
||||
|
||||
1. **Sprint 9100.0002.0001** — FinalDigest
|
||||
- Composite digest computation
|
||||
- `sha256(canonical({graphDigest, policyDigest, verdicts[]}))`
|
||||
- Integration with attestation system
|
||||
|
||||
2. **Sprint 9100.0002.0002** — Per-Node VerdictDigest
|
||||
- Verdict-level content addressing
|
||||
- Drill-down debugging ("which node changed?")
|
||||
- Delta detection between runs
|
||||
|
||||
### Phase 3: Purity & Validation (Sprint 9100.0003.*)
|
||||
|
||||
**Goal:** Harden determinism guarantees with runtime enforcement.
|
||||
|
||||
1. **Sprint 9100.0003.0001** — Runtime Purity Enforcement
|
||||
- Runtime guards beyond static analysis
|
||||
- Dependency injection shims for ambient services
|
||||
- Test harness for purity verification
|
||||
|
||||
2. **Sprint 9100.0003.0002** — Graph Validation & NFC
|
||||
- Pre-traversal validation ("no implicit data")
|
||||
- Unicode NFC normalization for string fields
|
||||
- Evidence completeness assertions
|
||||
|
||||
---
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Mandatory Test Types
|
||||
|
||||
1. **Replay Test**: Same input twice → identical `FinalDigest`
|
||||
2. **Permutation Test**: Shuffle input nodes/edges → identical outputs
|
||||
3. **Cycle Test**: Introduce cycle → fail unless `IsCycleCut` edge present
|
||||
4. **Ambience Test**: Forbid calls to time/env/network during evaluation
|
||||
5. **Serialization Test**: Canonicalization changes → predictable digest changes
|
||||
|
||||
### Property-Based Tests
|
||||
|
||||
```csharp
|
||||
// Idempotency
|
||||
[Property]
|
||||
public void Resolver_SameInput_SameOutput(Graph graph)
|
||||
{
|
||||
var result1 = resolver.Run(graph);
|
||||
var result2 = resolver.Run(graph);
|
||||
Assert.Equal(result1.FinalDigest, result2.FinalDigest);
|
||||
}
|
||||
|
||||
// Order Independence
|
||||
[Property]
|
||||
public void Resolver_ShuffledInputs_SameOutput(Graph graph, int seed)
|
||||
{
|
||||
var shuffled = ShuffleNodesAndEdges(graph, seed);
|
||||
var result1 = resolver.Run(graph);
|
||||
var result2 = resolver.Run(shuffled);
|
||||
Assert.Equal(result1.FinalDigest, result2.FinalDigest);
|
||||
}
|
||||
|
||||
// Cycle Detection
|
||||
[Property]
|
||||
public void Resolver_UnmarkedCycle_Throws(Graph graphWithCycle)
|
||||
{
|
||||
Assume.That(!graphWithCycle.Edges.Any(e => e.IsCycleCut));
|
||||
Assert.Throws<InvalidGraphException>(() => resolver.Run(graphWithCycle));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
### Existing Components to Integrate
|
||||
|
||||
| Component | Integration |
|
||||
|-----------|-------------|
|
||||
| `DeterministicGraphOrderer` | Use for traversal order computation |
|
||||
| `TrustLatticeEngine` | Use for K4 lattice evaluation |
|
||||
| `CanonicalJsonSerializer` | Use for all serialization |
|
||||
| `ContentAddressedIdGenerator` | Extend for EdgeId and VerdictId |
|
||||
| `ProhibitedPatternAnalyzer` | Combine with runtime guards |
|
||||
| `PolicyEvaluationContext` | Use for injected inputs |
|
||||
|
||||
### New Modules
|
||||
|
||||
| Module | Purpose |
|
||||
|--------|---------|
|
||||
| `StellaOps.Resolver` | Core resolver library |
|
||||
| `StellaOps.Resolver.Testing` | Test fixtures and harnesses |
|
||||
| `StellaOps.Resolver.Attestation` | Integration with proof chain |
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. **Unified API**: Single `resolver.Run(graph)` produces complete `ResolutionResult`
|
||||
2. **Deterministic**: 100% pass rate on replay/permutation/cycle tests
|
||||
3. **Auditable**: `FinalDigest` enables single-value verification
|
||||
4. **Performant**: No more than 10% overhead vs current fragmented approach
|
||||
5. **Documented**: Full API documentation and integration guide
|
||||
|
||||
---
|
||||
|
||||
## Appendix: C# Reference Implementation
|
||||
|
||||
```csharp
|
||||
public sealed class DeterministicResolver
|
||||
{
|
||||
private readonly Policy _policy;
|
||||
private readonly string _policyDigest;
|
||||
private readonly DeterministicGraphOrderer _orderer;
|
||||
private readonly TrustLatticeEngine _lattice;
|
||||
private readonly CanonicalJsonSerializer _serializer;
|
||||
|
||||
public DeterministicResolver(
|
||||
Policy policy,
|
||||
DeterministicGraphOrderer orderer,
|
||||
TrustLatticeEngine lattice,
|
||||
CanonicalJsonSerializer serializer)
|
||||
{
|
||||
_policy = Canon(policy);
|
||||
_policyDigest = HexSha256(serializer.SerializeToBytes(_policy));
|
||||
_orderer = orderer;
|
||||
_lattice = lattice;
|
||||
_serializer = serializer;
|
||||
}
|
||||
|
||||
public ResolutionResult Run(EvidenceGraph graph)
|
||||
{
|
||||
// 1. Canonicalize and validate
|
||||
var canonical = _orderer.Canonicalize(graph);
|
||||
ValidateCycles(canonical);
|
||||
EnsureNoAmbientInputs();
|
||||
|
||||
// 2. Traverse in deterministic order
|
||||
var traversal = canonical.Nodes
|
||||
.Select(n => NodeId.Parse(n.Id))
|
||||
.ToImmutableArray();
|
||||
|
||||
// 3. Evaluate each node purely
|
||||
var verdicts = new List<Verdict>(traversal.Length);
|
||||
foreach (var nodeId in traversal)
|
||||
{
|
||||
var node = GetNode(canonical, nodeId);
|
||||
var inbound = GatherInboundEvidence(canonical, nodeId);
|
||||
var verdict = EvaluatePure(node, inbound, _policy);
|
||||
var verdictBytes = _serializer.SerializeToBytes(verdict);
|
||||
verdicts.Add(verdict with { VerdictDigest = HexSha256(verdictBytes) });
|
||||
}
|
||||
|
||||
// 4. Compute digests
|
||||
var verdictsArray = verdicts.ToImmutableArray();
|
||||
var resultWithoutFinal = new ResolutionResult(
|
||||
traversal,
|
||||
verdictsArray,
|
||||
canonical.ContentHash,
|
||||
_policyDigest,
|
||||
"");
|
||||
|
||||
var finalBytes = _serializer.SerializeToBytes(resultWithoutFinal);
|
||||
return resultWithoutFinal with { FinalDigest = HexSha256(finalBytes) };
|
||||
}
|
||||
|
||||
private void ValidateCycles(CanonicalGraph graph)
|
||||
{
|
||||
// Detect cycles; require IsCycleCut for each
|
||||
var cycles = DetectCycles(graph);
|
||||
var unmarked = cycles.Where(c => !c.CutEdge.IsCycleCut).ToList();
|
||||
if (unmarked.Any())
|
||||
{
|
||||
throw new InvalidGraphException(
|
||||
$"Graph contains {unmarked.Count} unmarked cycle(s). " +
|
||||
"Add IsCycleCut=true to break cycles.");
|
||||
}
|
||||
}
|
||||
|
||||
private Verdict EvaluatePure(Node node, InboundEvidence evidence, Policy policy)
|
||||
{
|
||||
// Pure evaluation: no IO, no ambient state
|
||||
return _lattice.Evaluate(node, evidence, policy);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documents
|
||||
|
||||
- `docs/modules/attestor/proof-chain-specification.md`
|
||||
- `docs/modules/policy/architecture.md`
|
||||
- `docs/testing/testing-strategy-models.md`
|
||||
- `src/Policy/__Libraries/StellaOps.Policy/TrustLattice/K4Lattice.cs`
|
||||
- `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/Ordering/DeterministicGraphOrderer.cs`
|
||||
|
||||
---
|
||||
|
||||
## Approval
|
||||
|
||||
| Role | Name | Date | Decision |
|
||||
|------|------|------|----------|
|
||||
| Architecture Lead | — | 2025-12-24 | Approved |
|
||||
| Policy Guild Lead | — | 2025-12-24 | Approved |
|
||||
| Scanner Guild Lead | — | 2025-12-24 | Approved |
|
||||
@@ -0,0 +1,271 @@
|
||||
# Product Advisory: Evidence-Weighted Score Model
|
||||
|
||||
**Date:** 24-Dec-2025
|
||||
**Status:** Approved for Implementation
|
||||
**Sprint Batch:** 8200.0012
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory introduces a **unified evidence-weighted scoring model** that combines six evidence dimensions into a single 0-100 score per vulnerability finding. The score enables rapid triage by surfacing the most "real" risks with transparent, decomposable evidence.
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- **Prioritize quickly:** Sort by score to fix the most "real" risks first
|
||||
- **Reduce noise:** Thin, speculative findings sink to the bottom
|
||||
- **Stay explainable:** Score decomposes into evidence "slices" on hover
|
||||
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The current platform has **four independent scoring systems** that don't interoperate:
|
||||
|
||||
1. `ConfidenceCalculator` (Policy module) — 5 factors, outputs 0-1
|
||||
2. `ScorePolicy` (Policy module) — 4 factors in basis points
|
||||
3. `EvidenceDensityScorer` (Scanner module) — 8 factors, outputs 0-1
|
||||
4. `TrustVector` (Excititor module) — 3 factors, outputs 0-1
|
||||
|
||||
Users cannot sort findings by a single "risk strength" metric. They must mentally combine multiple signals, leading to:
|
||||
|
||||
- Inconsistent prioritization across teams
|
||||
- Missed high-risk findings buried in noise
|
||||
- Difficulty explaining triage decisions to stakeholders
|
||||
|
||||
---
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
### Scoring Model
|
||||
|
||||
```
|
||||
Score = clamp01(
|
||||
W_rch*RCH + W_rts*RTS + W_bkp*BKP + W_xpl*XPL + W_src*SRC - W_mit*MIT
|
||||
) * 100
|
||||
```
|
||||
|
||||
### Evidence Dimensions
|
||||
|
||||
| Symbol | Name | Description | Weight |
|
||||
|--------|------|-------------|--------|
|
||||
| **RCH** | Reachability | Static/dynamic reachability confidence | 0.30 |
|
||||
| **RTS** | Runtime | Runtime signal strength (eBPF, hits, recency) | 0.25 |
|
||||
| **BKP** | Backport | Backport/patch evidence strength | 0.15 |
|
||||
| **XPL** | Exploit | Exploit likelihood (EPSS + KEV) | 0.15 |
|
||||
| **SRC** | Source Trust | Source trust (vendor > distro > community) | 0.10 |
|
||||
| **MIT** | Mitigations | Active mitigations (subtractive) | 0.10 |
|
||||
|
||||
### Guardrails
|
||||
|
||||
| Condition | Action | Rationale |
|
||||
|-----------|--------|-----------|
|
||||
| BKP=1 + not_affected + RTS<0.6 | Cap at 15 | Vendor confirms not affected |
|
||||
| RTS >= 0.8 | Floor at 60 | Strong live signal |
|
||||
| RCH=0 + RTS=0 | Cap at 45 | Speculative finding |
|
||||
|
||||
### Score Buckets
|
||||
|
||||
| Bucket | Range | Action |
|
||||
|--------|-------|--------|
|
||||
| **ActNow** | 90-100 | Immediate action required |
|
||||
| **ScheduleNext** | 70-89 | Schedule for next sprint |
|
||||
| **Investigate** | 40-69 | Investigate when touching component |
|
||||
| **Watchlist** | 0-39 | Low priority, monitor |
|
||||
|
||||
---
|
||||
|
||||
## Gap Analysis
|
||||
|
||||
### Existing Infrastructure (Reusable)
|
||||
|
||||
| Component | Location | Reuse |
|
||||
|-----------|----------|-------|
|
||||
| Reachability states | `Policy/ConfidenceCalculator.cs` | Direct mapping to RCH |
|
||||
| Runtime evidence | `Policy/RuntimeEvidence` | Direct mapping to RTS |
|
||||
| Backport detection | `Concelier/BackportProofService.cs` | Needs normalization |
|
||||
| EPSS bands | `Scanner/EpssPriorityCalculator.cs` | Needs normalization |
|
||||
| KEV status | `Concelier/VendorRiskSignalExtractor.cs` | Direct flag |
|
||||
| Trust vector | `Excititor/TrustVector.cs` | Direct mapping to SRC |
|
||||
| Gate multipliers | `Policy/GateMultipliersBps` | Needs inversion to MIT |
|
||||
|
||||
### New Components Required
|
||||
|
||||
1. **Unified Score Calculator** — Core formula implementation
|
||||
2. **Normalizers** — Convert raw evidence to 0-1 inputs
|
||||
3. **Guardrails Engine** — Apply caps/floors
|
||||
4. **API Endpoints** — Expose scores via REST
|
||||
5. **UI Components** — Score pill, breakdown popover, badges
|
||||
|
||||
---
|
||||
|
||||
## Implementation Sprints
|
||||
|
||||
| Sprint | Focus | Key Deliverables |
|
||||
|--------|-------|------------------|
|
||||
| **8200.0012.0001** | Core Library | Calculator, input models, weight policy, guardrails |
|
||||
| **8200.0012.0002** | Normalizers | BKP, XPL, MIT, RCH, RTS, SRC normalizers |
|
||||
| **8200.0012.0003** | Policy Integration | Score-based rules, verdict enrichment, attestation |
|
||||
| **8200.0012.0004** | API Endpoints | Single/batch score, history, webhooks |
|
||||
| **8200.0012.0005** | Frontend UI | Score pill, breakdown, badges, history chart |
|
||||
|
||||
**Total Tasks:** 268 across 5 sprints
|
||||
**Estimated Duration:** 6 months
|
||||
|
||||
---
|
||||
|
||||
## API Shape
|
||||
|
||||
### Score Response
|
||||
|
||||
```json
|
||||
{
|
||||
"findingId": "CVE-2024-1234@pkg:deb/debian/curl@7.64.0-4",
|
||||
"score": 78,
|
||||
"bucket": "ScheduleNext",
|
||||
"inputs": {
|
||||
"rch": 0.85, "rts": 0.40, "bkp": 0.00,
|
||||
"xpl": 0.70, "src": 0.80, "mit": 0.10
|
||||
},
|
||||
"weights": {
|
||||
"rch": 0.30, "rts": 0.25, "bkp": 0.15,
|
||||
"xpl": 0.15, "src": 0.10, "mit": 0.10
|
||||
},
|
||||
"flags": ["live-signal", "proven-path"],
|
||||
"explanations": [
|
||||
"Static path to vulnerable sink (confidence: 85%)",
|
||||
"EPSS: 0.8% probability (High band)",
|
||||
"Distro VEX signed (trust: 80%)"
|
||||
],
|
||||
"caps": {
|
||||
"speculativeCap": false,
|
||||
"notAffectedCap": false,
|
||||
"runtimeFloor": false
|
||||
},
|
||||
"policyDigest": "sha256:abc123...",
|
||||
"calculatedAt": "2026-01-15T14:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UI Design
|
||||
|
||||
### Score Pill
|
||||
|
||||
```
|
||||
┌───────┐
|
||||
│ 78 │ ← White text on amber background (ScheduleNext bucket)
|
||||
└───────┘
|
||||
```
|
||||
|
||||
### Breakdown Popover
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Evidence Score: 78/100 │
|
||||
│ Bucket: Schedule Next Sprint │
|
||||
├─────────────────────────────────────────┤
|
||||
│ Reachability ████████▒▒ 0.85 │
|
||||
│ Runtime ████▒▒▒▒▒▒ 0.40 │
|
||||
│ Backport ▒▒▒▒▒▒▒▒▒▒ 0.00 │
|
||||
│ Exploit ███████▒▒▒ 0.70 │
|
||||
│ Source Trust ████████▒▒ 0.80 │
|
||||
│ Mitigations -█▒▒▒▒▒▒▒▒▒ 0.10 │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 🟢 Live signal detected │
|
||||
│ ✓ Proven reachability path │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Governance & Tuning
|
||||
|
||||
1. **Policy-Driven Weights:** Different environments (prod/staging/dev) can have different weight profiles
|
||||
2. **Versioned Policies:** Policy changes are versioned and signed; same inputs + policy = same score
|
||||
3. **Audit Trail:** All score calculations logged with inputs, policy digest, and result
|
||||
4. **Threshold Tuning:** Bucket thresholds configurable per tenant
|
||||
|
||||
---
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1: Dual Emit (3 months)
|
||||
- Both Confidence and EWS emitted in verdicts
|
||||
- Feature flag controls which is primary
|
||||
- Telemetry compares rankings
|
||||
|
||||
### Phase 2: EWS Primary (3 months)
|
||||
- EWS is default; Confidence available for compatibility
|
||||
- Deprecation warnings for Confidence consumers
|
||||
|
||||
### Phase 3: Confidence Removal (v3.0)
|
||||
- Confidence scoring removed
|
||||
- EWS is sole scoring system
|
||||
|
||||
---
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
|------|--------|------------|
|
||||
| Weight tuning requires iteration | Suboptimal prioritization | Conservative defaults; telemetry |
|
||||
| Migration confusion | User errors | Clear docs; gradual rollout |
|
||||
| Performance regression | Slower evaluation | Caching; benchmarks (<50ms) |
|
||||
| Attestation size increase | Storage cost | Compact proof format |
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. **Adoption:** 80% of findings viewed with EWS score within 6 months
|
||||
2. **Triage Time:** 30% reduction in mean-time-to-triage
|
||||
3. **False Positive Rate:** <5% of "ActNow" findings downgraded after review
|
||||
4. **Determinism:** 100% of scores reproducible given same inputs + policy
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Normalization Details
|
||||
|
||||
### BKP (Backport) Normalization
|
||||
|
||||
| Evidence Tier | BKP Range |
|
||||
|---------------|-----------|
|
||||
| No evidence | 0.00 |
|
||||
| Distro advisory text | 0.40-0.55 |
|
||||
| Changelog mention | 0.45-0.60 |
|
||||
| Patch header match | 0.70-0.85 |
|
||||
| HunkSig match | 0.80-0.92 |
|
||||
| Binary fingerprint | 0.90-1.00 |
|
||||
|
||||
### XPL (Exploit) Normalization
|
||||
|
||||
| Signal | XPL Value |
|
||||
|--------|-----------|
|
||||
| KEV present | floor 0.40 |
|
||||
| EPSS top 1% | 0.90-1.00 |
|
||||
| EPSS top 5% | 0.70-0.89 |
|
||||
| EPSS top 25% | 0.40-0.69 |
|
||||
| EPSS below 25% | 0.20-0.39 |
|
||||
| No EPSS data | 0.30 |
|
||||
|
||||
### MIT (Mitigation) Normalization
|
||||
|
||||
| Mitigation | MIT Value |
|
||||
|------------|-----------|
|
||||
| Feature flag disabled | 0.20-0.40 |
|
||||
| Auth required | 0.10-0.20 |
|
||||
| Admin only | 0.15-0.25 |
|
||||
| Seccomp profile | 0.10-0.25 |
|
||||
| Network isolation | 0.05-0.15 |
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Sprint Files: `docs/implplan/SPRINT_8200_0012_*.md`
|
||||
- Module Docs: `docs/modules/signals/architecture.md` (to be created)
|
||||
- Existing Confidence: `docs/modules/policy/confidence-scoring.md`
|
||||
- EPSS Enrichment: `docs/modules/scanner/epss-enrichment.md`
|
||||
- Backport Detection: `docs/modules/concelier/backport-detection.md`
|
||||
Reference in New Issue
Block a user