tests fixes and sprints work
This commit is contained in:
299
docs/technical/scoring-algebra.md
Normal file
299
docs/technical/scoring-algebra.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# Unified Trust Score Architecture
|
||||
|
||||
> **Ownership:** Policy Guild • Signals Guild
|
||||
> **Services:** `StellaOps.Signals.UnifiedScore` (facade), `StellaOps.Signals.EvidenceWeightedScore` (core), `StellaOps.Policy.Determinization` (entropy)
|
||||
> **Related docs:** [Policy architecture](../modules/policy/architecture.md), [EWS migration](../modules/policy/design/confidence-to-ews-migration.md), [Score Proofs API](../api/scanner-score-proofs-api.md)
|
||||
|
||||
This document describes the **unified trust score facade** that provides a single API for accessing risk scores, uncertainty metrics, and evidence from the underlying EWS and Determinization systems.
|
||||
|
||||
---
|
||||
|
||||
## 1 · Design Principle: Facade Over Rewrite
|
||||
|
||||
Stella Ops has mature, battle-tested scoring systems:
|
||||
|
||||
| System | Purpose | Maturity |
|
||||
|--------|---------|----------|
|
||||
| **EWS** | 6-dimension risk scoring with guardrails | Production (1000+ determinism tests) |
|
||||
| **Determinization** | Entropy, confidence decay, conflict detection | Production |
|
||||
| **RiskEngine** | Signal-specific providers (CVSS/KEV/EPSS) | Production |
|
||||
|
||||
The unified score facade **does not replace these systems**. Instead, it:
|
||||
|
||||
1. **Combines** EWS scores with Determinization entropy in a single result
|
||||
2. **Externalizes** EWS weights to versioned manifest files for auditing
|
||||
3. **Exposes** the unknowns fraction (U) as a first-class metric
|
||||
|
||||
---
|
||||
|
||||
## 2 · Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ IUnifiedScoreService │
|
||||
│ (Facade) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ • ComputeAsync(request) → UnifiedScoreResult │
|
||||
│ • Combines EWS + Determinization + ConflictDetector │
|
||||
│ • Loads weights from versioned manifests │
|
||||
└─────────────┬───────────────────────┬───────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ EvidenceWeightedScore │ │ Determinization │
|
||||
│ Calculator │ │ │
|
||||
├─────────────────────────┤ ├─────────────────────────┤
|
||||
│ • 6-dimension scoring │ │ • Entropy calculation │
|
||||
│ • Guardrails (caps/ │ │ • Confidence decay │
|
||||
│ floors) │ │ • Signal gap tracking │
|
||||
│ • Anchor metadata │ │ • Fingerprinting │
|
||||
│ • Policy digest │ │ • Conflict detection │
|
||||
└─────────────────────────┘ └─────────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ etc/weights/*.json │
|
||||
│ (Versioned Weight Manifests) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3 · What the Facade Provides
|
||||
|
||||
### 3.1 · Unified Score Result
|
||||
|
||||
```csharp
|
||||
public sealed record UnifiedScoreResult
|
||||
{
|
||||
// From EWS
|
||||
public int Score { get; } // 0-100
|
||||
public string Bucket { get; } // ActNow, ScheduleNext, Investigate, Watchlist
|
||||
public IReadOnlyList<DimensionContribution> Breakdown { get; }
|
||||
public AppliedGuardrails Guardrails { get; }
|
||||
public string EwsDigest { get; } // SHA-256 of EWS result
|
||||
|
||||
// From Determinization
|
||||
public double UnknownsFraction { get; } // U metric (0.0 = complete, 1.0 = no data)
|
||||
public UnknownsBand UnknownsBand { get; } // Complete, Adequate, Sparse, Insufficient
|
||||
public IReadOnlyList<SignalGap> Gaps { get; }
|
||||
public IReadOnlyList<SignalConflict> Conflicts { get; }
|
||||
public string DeterminizationFingerprint { get; }
|
||||
|
||||
// Combined
|
||||
public IReadOnlyList<SignalDelta> DeltaIfPresent { get; } // Impact if missing signals arrive
|
||||
public string WeightManifestRef { get; } // version + hash
|
||||
public DateTimeOffset ComputedAt { get; }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 · Unknowns Fraction (U)
|
||||
|
||||
The `UnknownsFraction` directly exposes Determinization's entropy calculation:
|
||||
|
||||
```
|
||||
U = 1 - (weighted_present_signals / total_weight)
|
||||
```
|
||||
|
||||
| U Range | Band | Meaning | Action |
|
||||
|---------|------|---------|--------|
|
||||
| 0.0 – 0.2 | Complete | All signals present | Automated decisions |
|
||||
| 0.2 – 0.4 | Adequate | Sufficient for evaluation | Automated decisions |
|
||||
| 0.4 – 0.6 | Sparse | Signal gaps exist | Manual review recommended |
|
||||
| 0.6 – 1.0 | Insufficient | Critical data missing | Block pending more signals |
|
||||
|
||||
Thresholds align with existing Determinization config:
|
||||
- `RefreshEntropyThreshold: 0.40` → triggers signal refresh
|
||||
- `ManualReviewEntropyThreshold: 0.60` → requires human review
|
||||
|
||||
### 3.3 · Delta-If-Present
|
||||
|
||||
When signals are missing, the facade calculates potential score impact:
|
||||
|
||||
```json
|
||||
{
|
||||
"delta_if_present": [
|
||||
{
|
||||
"signal": "reachability",
|
||||
"min_impact": -15,
|
||||
"max_impact": +8,
|
||||
"description": "If reachability confirmed as not-reachable, score decreases by up to 15"
|
||||
},
|
||||
{
|
||||
"signal": "runtime",
|
||||
"min_impact": 0,
|
||||
"max_impact": +25,
|
||||
"description": "If runtime execution observed, score increases by up to 25"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4 · Weight Manifests
|
||||
|
||||
### 4.1 · Location
|
||||
|
||||
Weight manifests are stored in `etc/weights/` with versioned filenames:
|
||||
|
||||
```
|
||||
etc/weights/
|
||||
├── v2026-01-22.weights.json
|
||||
├── v2026-02-01.weights.json
|
||||
└── ...
|
||||
```
|
||||
|
||||
### 4.2 · Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "v2026-01-22",
|
||||
"effective_from": "2026-01-22T00:00:00Z",
|
||||
"description": "EWS default weights",
|
||||
"weights": {
|
||||
"rch": 0.30,
|
||||
"rts": 0.25,
|
||||
"bkp": 0.15,
|
||||
"xpl": 0.15,
|
||||
"src": 0.10,
|
||||
"mit": 0.10
|
||||
},
|
||||
"hash": "sha256:..."
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 · Versioning Rules
|
||||
|
||||
1. **Immutable once published** – Manifest content never changes after creation
|
||||
2. **Hash verification** – SHA-256 of canonical JSON ensures integrity
|
||||
3. **Policy pinning** – Policies can specify `weights_ref` to lock a version
|
||||
4. **Fallback** – If manifest missing, EWS uses compiled defaults
|
||||
|
||||
---
|
||||
|
||||
## 5 · Existing Systems (Unchanged)
|
||||
|
||||
### 5.1 · EWS Formula (Preserved)
|
||||
|
||||
The EWS formula remains unchanged:
|
||||
|
||||
```
|
||||
rawScore = (RCH × w_rch) + (RTS × w_rts) + (BKP × w_bkp) +
|
||||
(XPL × w_xpl) + (SRC × w_src) - (MIT × w_mit)
|
||||
|
||||
finalScore = clamp(rawScore, 0, 1) × 100
|
||||
```
|
||||
|
||||
With guardrails:
|
||||
- **Speculative cap** (45): RCH=0 and RTS=0
|
||||
- **Not-affected cap** (15): BKP≥1.0, VEX=not_affected, RTS<0.6
|
||||
- **Runtime floor** (60): RTS≥0.8
|
||||
|
||||
### 5.2 · Determinization (Preserved)
|
||||
|
||||
Entropy and decay calculations remain unchanged:
|
||||
|
||||
```
|
||||
entropy = 1 - (present_weight / total_weight)
|
||||
decay = max(floor, exp(-ln(2) × age_days / half_life_days))
|
||||
```
|
||||
|
||||
With conflict detection:
|
||||
- VEX vs Reachability contradiction
|
||||
- Static vs Runtime contradiction
|
||||
- Multiple VEX status conflict
|
||||
- Backport vs Status conflict
|
||||
|
||||
---
|
||||
|
||||
## 6 · Integration Points
|
||||
|
||||
### 6.1 · CLI Commands
|
||||
|
||||
```bash
|
||||
# Existing (enhanced)
|
||||
stella gate score evaluate --finding-id CVE-2024-1234@pkg:npm/lodash \
|
||||
--cvss 7.5 --epss 0.15 --reachability function \
|
||||
--show-unknowns --show-deltas
|
||||
|
||||
# New
|
||||
stella score compute --finding-id CVE-2024-1234@pkg:npm/lodash \
|
||||
--cvss 7.5 --epss 0.15
|
||||
|
||||
stella score explain CVE-2024-1234@pkg:npm/lodash
|
||||
|
||||
stella gate score weights list
|
||||
stella gate score weights show v2026-01-22
|
||||
stella gate score weights diff v2026-01-22 v2026-02-01
|
||||
```
|
||||
|
||||
### 6.2 · API Endpoints
|
||||
|
||||
```
|
||||
POST /api/v1/score/evaluate # Compute unified score
|
||||
GET /api/v1/score/{id}/replay # Fetch signed replay proof
|
||||
GET /api/v1/score/weights # List weight manifests
|
||||
GET /api/v1/score/weights/{v} # Get specific manifest
|
||||
```
|
||||
|
||||
#### Replay Endpoint Response
|
||||
|
||||
The `/score/{id}/replay` endpoint returns a DSSE-signed attestation with payload type `application/vnd.stella.score+json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"signed_replay_log_dsse": "BASE64",
|
||||
"rekor_inclusion": {"logIndex": 12345, "rootHash": "…"},
|
||||
"canonical_inputs": [
|
||||
{"name": "sbom.json", "sha256": "…"},
|
||||
{"name": "vex.json", "sha256": "…"}
|
||||
],
|
||||
"transforms": [
|
||||
{"name": "canonicalize_spdx", "version": "1.1"},
|
||||
{"name": "age_decay", "params": {"lambda": 0.02}}
|
||||
],
|
||||
"algebra_steps": [
|
||||
{"signal": "rch", "w": 0.30, "value": 0.78, "term": 0.234}
|
||||
],
|
||||
"final_score": 85
|
||||
}
|
||||
```
|
||||
|
||||
Replay proofs are stored as OCI referrers ("StellaBundle" pattern) attached to the scored artifact.
|
||||
|
||||
### 6.3 · Console UI
|
||||
|
||||
Finding detail views show:
|
||||
- Score with bucket (existing)
|
||||
- Unknowns fraction (U) with color-coded band
|
||||
- Delta-if-present for missing signals
|
||||
- Weight manifest version
|
||||
|
||||
---
|
||||
|
||||
## 7 · Determinism Guarantees
|
||||
|
||||
The facade inherits determinism from underlying systems:
|
||||
|
||||
| Aspect | Guarantee |
|
||||
|--------|-----------|
|
||||
| EWS score | Identical inputs → identical score (1000+ iteration tests) |
|
||||
| Entropy | Identical signal presence → identical U |
|
||||
| Fingerprint | Content-addressed SHA-256 |
|
||||
| Weight manifest | Immutable after creation |
|
||||
|
||||
The facade adds no additional sources of non-determinism.
|
||||
|
||||
---
|
||||
|
||||
## 8 · What We're NOT Doing
|
||||
|
||||
- ❌ Replacing EWS formula
|
||||
- ❌ Replacing Determinization entropy calculation
|
||||
- ❌ Changing guardrail logic
|
||||
- ❌ Changing conflict detection
|
||||
- ❌ Breaking existing CLI commands
|
||||
- ❌ Breaking existing API contracts
|
||||
|
||||
The facade is **additive** – existing functionality continues to work unchanged.
|
||||
Reference in New Issue
Block a user