partly or unimplemented features - now implemented
This commit is contained in:
@@ -69,7 +69,37 @@ src/Policy/__Libraries/StellaOps.Policy.Determinization/
|
||||
│ ├── IDecayedConfidenceCalculator.cs
|
||||
│ ├── DecayedConfidenceCalculator.cs # Half-life decay application
|
||||
│ ├── SignalWeights.cs # Configurable signal weights
|
||||
│ └── PriorDistribution.cs # Default priors for missing signals
|
||||
│ ├── PriorDistribution.cs # Default priors for missing signals
|
||||
│ ├── EvidenceWeightedScoring/ # 6-dimension EWS model
|
||||
│ │ ├── EwsDimension.cs # RCH/RTS/BKP/XPL/SRC/MIT enum
|
||||
│ │ ├── IEwsDimensionNormalizer.cs # Pluggable normalizer interface
|
||||
│ │ ├── EwsSignalInput.cs # Raw signal inputs
|
||||
│ │ ├── EwsModels.cs # Scores, weights, guardrails
|
||||
│ │ ├── IGuardrailsEngine.cs # Guardrails enforcement interface
|
||||
│ │ ├── GuardrailsEngine.cs # Caps/floors (KEV, backport, etc.)
|
||||
│ │ ├── IEwsCalculator.cs # Unified calculator interface
|
||||
│ │ ├── EwsCalculator.cs # Orchestrates normalizers + guardrails
|
||||
│ │ └── Normalizers/
|
||||
│ │ ├── ReachabilityNormalizer.cs
|
||||
│ │ ├── RuntimeSignalsNormalizer.cs
|
||||
│ │ ├── BackportEvidenceNormalizer.cs
|
||||
│ │ ├── ExploitabilityNormalizer.cs
|
||||
│ │ ├── SourceConfidenceNormalizer.cs
|
||||
│ │ └── MitigationStatusNormalizer.cs
|
||||
│ └── Triage/ # Decay-based triage queue (Sprint 050)
|
||||
│ ├── TriageModels.cs # TriagePriority, TriageItem, TriageQueueSnapshot, TriageQueueOptions
|
||||
│ ├── ITriageQueueEvaluator.cs # Batch + single evaluation interface
|
||||
│ ├── TriageQueueEvaluator.cs # Priority classification, days-until-stale, OTel metrics
|
||||
│ ├── ITriageObservationSource.cs # Source for observation candidates
|
||||
│ ├── ITriageReanalysisSink.cs # Sink interface for re-analysis queue
|
||||
│ ├── InMemoryTriageReanalysisSink.cs # ConcurrentQueue-based default sink
|
||||
│ └── UnknownTriageQueueService.cs # Fetch→evaluate→enqueue cycle orchestrator
|
||||
│ └── WeightManifest/ # Versioned weight manifests (Sprint 051)
|
||||
│ ├── WeightManifestModels.cs # WeightManifestDocument, weights, guardrails, buckets, diff models
|
||||
│ ├── WeightManifestHashComputer.cs # Deterministic SHA-256 with canonical JSON (excludes contentHash)
|
||||
│ ├── IWeightManifestLoader.cs # Interface: list, load, select, validate, diff
|
||||
│ ├── WeightManifestLoader.cs # File-based discovery, effectiveFrom selection, OTel metrics
|
||||
│ └── WeightManifestCommands.cs # CLI backing: list, validate, diff, activate, hash
|
||||
├── Policies/
|
||||
│ ├── IDeterminizationPolicy.cs
|
||||
│ ├── DeterminizationPolicy.cs # Allow/quarantine/escalate rules
|
||||
@@ -913,6 +943,158 @@ public static class DeterminizationMetrics
|
||||
}
|
||||
```
|
||||
|
||||
## Evidence-Weighted Score (EWS) Model
|
||||
|
||||
The EWS model extends the Determinization subsystem with a **6-dimension scoring
|
||||
pipeline** that replaces ad-hoc signal weighting with a unified, pluggable, and
|
||||
guardrail-enforced composite score.
|
||||
|
||||
### Dimensions
|
||||
|
||||
Each dimension maps a family of raw signals to a **normalised risk score 0–100**
|
||||
(higher = riskier) and a **confidence 0.0–1.0**:
|
||||
|
||||
| Code | Dimension | Key signals | Score semantics |
|
||||
|------|-----------|-------------|-----------------|
|
||||
| RCH | Reachability | Call-graph tier R0–R4, runtime trace | Higher = more reachable |
|
||||
| RTS | RuntimeSignals | Instrumentation coverage, invocation count, APM | Higher = more actively exercised |
|
||||
| BKP | BackportEvidence | Vendor confirmation, binary-analysis confidence | Higher = no backport / low confidence |
|
||||
| XPL | Exploitability | EPSS, KEV, exploit-kit availability, PoC age, CVSS | Higher = more exploitable |
|
||||
| SRC | SourceConfidence | SBOM completeness, signatures, attestation count | **Inverted**: high confidence = low risk |
|
||||
| MIT | MitigationStatus | VEX status, workarounds, network controls | Higher = less mitigated |
|
||||
|
||||
### Default Weights
|
||||
|
||||
```
|
||||
RCH 0.25 XPL 0.20 RTS 0.15
|
||||
BKP 0.15 SRC 0.15 MIT 0.10
|
||||
─── Total: 1.00 ───
|
||||
```
|
||||
|
||||
A **Legacy** preset preserves backward-compatible weights aligned with the
|
||||
original `SignalWeights` record.
|
||||
|
||||
### Guardrails
|
||||
|
||||
After weighted scoring, a `GuardrailsEngine` enforces hard caps and floors:
|
||||
|
||||
| Guardrail | Default | Trigger condition |
|
||||
|-----------|---------|-------------------|
|
||||
| `kev_floor` | 70 | `IsInKev == true` — floor the score |
|
||||
| `backported_cap` | 20 | `BackportDetected && Confidence ≥ 0.8` — cap the score |
|
||||
| `not_affected_cap` | 25 | `VexStatus == not_affected` — cap the score |
|
||||
| `runtime_floor` | 30 | `RuntimeTraceConfirmed == true` — floor the score |
|
||||
| `speculative_cap` | 60 | Overall confidence < `MinConfidenceThreshold` (0.3) — cap |
|
||||
|
||||
Guardrails are applied in priority order (KEV first). The resulting
|
||||
`EwsCompositeScore` records which guardrails fired and whether the score was
|
||||
adjusted up or down.
|
||||
|
||||
### Calculator API
|
||||
|
||||
```csharp
|
||||
// Via DI
|
||||
IEwsCalculator calculator = serviceProvider.GetRequiredService<IEwsCalculator>();
|
||||
|
||||
// Or standalone
|
||||
IEwsCalculator calculator = EwsCalculator.CreateDefault();
|
||||
|
||||
var signal = new EwsSignalInput
|
||||
{
|
||||
CveId = "CVE-2025-1234",
|
||||
ReachabilityTier = 3, // R3
|
||||
EpssProbability = 0.42,
|
||||
IsInKev = false,
|
||||
VexStatus = "under_investigation",
|
||||
SbomCompleteness = 0.85,
|
||||
};
|
||||
|
||||
EwsCompositeScore result = calculator.Calculate(signal);
|
||||
// result.Score → 0-100 composite
|
||||
// result.BasisPoints → 0-10000 (fine-grained)
|
||||
// result.Confidence → weighted confidence
|
||||
// result.RiskTier → Critical/High/Medium/Low/Negligible
|
||||
// result.AppliedGuardrails → list of guardrail names that fired
|
||||
// result.NeedsReview → true when confidence < threshold
|
||||
```
|
||||
|
||||
### Normalizer Interface
|
||||
|
||||
Each dimension is implemented as an `IEwsDimensionNormalizer`:
|
||||
|
||||
```csharp
|
||||
public interface IEwsDimensionNormalizer
|
||||
{
|
||||
EwsDimension Dimension { get; }
|
||||
int Normalize(EwsSignalInput signal); // 0-100
|
||||
double GetConfidence(EwsSignalInput signal); // 0.0-1.0
|
||||
string GetExplanation(EwsSignalInput signal, int score);
|
||||
}
|
||||
```
|
||||
|
||||
Normalizers are registered via DI as `IEnumerable<IEwsDimensionNormalizer>`.
|
||||
Custom normalizers can be added by registering additional implementations.
|
||||
|
||||
### Observability
|
||||
|
||||
The calculator emits two OTel metrics:
|
||||
|
||||
- **`stellaops_ews_score`** (Histogram) — score distribution 0–100
|
||||
- **`stellaops_ews_guardrails_applied`** (Counter) — number of guardrail applications
|
||||
|
||||
## Unknowns Decay Triage Queue
|
||||
|
||||
> **Sprint:** SPRINT_20260208_050_Policy_unknowns_decay_and_triage_queue
|
||||
|
||||
The triage queue automatically identifies unknowns whose evidence has decayed past staleness thresholds and queues them for re-analysis, closing the gap between passive `ObservationDecay.CheckIsStale()` tracking and active re-analysis triggering.
|
||||
|
||||
### Triage Priority Classification
|
||||
|
||||
| Priority | Decay Multiplier Range | Action |
|
||||
|----------|----------------------|--------|
|
||||
| **None** | > 0.70 | No action — fresh |
|
||||
| **Low** | 0.50 – 0.70 | Monitor — approaching staleness |
|
||||
| **Medium** | 0.30 – 0.50 | Schedule re-analysis — stale |
|
||||
| **High** | 0.10 – 0.30 | Re-analyse soon — heavily decayed |
|
||||
| **Critical** | ≤ 0.10 | URGENT — evidence at floor |
|
||||
|
||||
Thresholds are configurable via `TriageQueueOptions` (section: `Determinization:TriageQueue`).
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
UnknownTriageQueueService (orchestrator)
|
||||
├── ITriageObservationSource → fetch candidates
|
||||
├── ITriageQueueEvaluator → classify priority, compute days-until-stale
|
||||
└── ITriageReanalysisSink → enqueue Medium+ items for re-analysis
|
||||
```
|
||||
|
||||
- **`TriageQueueEvaluator`**: Deterministic evaluator. Given the same observations and reference time, produces identical output. Calculates days-until-stale using the formula: `d = -halfLife × ln(threshold) / ln(2) - currentAgeDays`.
|
||||
- **`UnknownTriageQueueService`**: Orchestrates fetch→evaluate→enqueue cycles. Designed for periodic invocation by a background host, timer, or scheduler. Also supports on-demand evaluation (CLI/API) without auto-enqueue.
|
||||
- **`InMemoryTriageReanalysisSink`**: Default `ConcurrentQueue<TriageItem>` implementation for single-node and offline scenarios. Host-level can replace with message bus or database-backed sink.
|
||||
|
||||
### OTel Metrics
|
||||
|
||||
- **`stellaops_triage_items_evaluated_total`** (Counter) — observations evaluated per cycle
|
||||
- **`stellaops_triage_items_queued_total`** (Counter) — items added to triage queue
|
||||
- **`stellaops_triage_decay_multiplier`** (Histogram) — decay multiplier distribution
|
||||
- **`stellaops_triage_cycles_total`** (Counter) — evaluation cycles executed
|
||||
- **`stellaops_triage_reanalysis_enqueued_total`** (Counter) — items sent to re-analysis sink
|
||||
- **`stellaops_triage_cycle_duration_seconds`** (Histogram) — cycle duration
|
||||
|
||||
### Configuration
|
||||
|
||||
```yaml
|
||||
Determinization:
|
||||
TriageQueue:
|
||||
ApproachingThreshold: 0.70 # Multiplier below which Low priority starts
|
||||
HighPriorityThreshold: 0.30 # Below this → High
|
||||
CriticalPriorityThreshold: 0.10 # Below this → Critical
|
||||
MaxSnapshotItems: 500 # Max items per snapshot
|
||||
IncludeApproaching: true # Include Low priority in snapshots
|
||||
MinEvaluationIntervalMinutes: 60
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
| Test Category | Focus Area | Example |
|
||||
@@ -934,6 +1116,90 @@ public static class DeterminizationMetrics
|
||||
4. **Escalation Path:** Runtime evidence always escalates regardless of other signals
|
||||
5. **Tamper Detection:** Signal snapshots hashed for integrity verification
|
||||
|
||||
## Versioned Weight Manifests
|
||||
|
||||
Weight manifests (Sprint 051) provide versioned, content-addressed configuration for
|
||||
all scoring weights, guardrails, buckets, and determinization thresholds. Manifests
|
||||
live in `etc/weights/` as JSON files with a `*.weights.json` extension.
|
||||
|
||||
### Manifest Schema (v1.0.0)
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `schemaVersion` | string | Must be `"1.0.0"` |
|
||||
| `version` | string | Manifest version identifier (e.g. `"v2026-01-22"`) |
|
||||
| `effectiveFrom` | ISO-8601 | UTC date from which this manifest is active |
|
||||
| `profile` | string | Environment profile (`production`, `staging`, etc.) |
|
||||
| `contentHash` | string | `sha256:<hex>` content hash or `sha256:auto` placeholder |
|
||||
| `weights.legacy` | dict | 6-dimension EWS weights (must sum to 1.0) |
|
||||
| `weights.advisory` | dict | Advisory-profile weights |
|
||||
| `guardrails` | object | Guardrail rules (notAffectedCap, runtimeFloor, speculativeCap) |
|
||||
| `buckets` | object | Action tier boundaries (actNowMin, scheduleNextMin, investigateMin) |
|
||||
| `determinizationThresholds` | object | Entropy thresholds for triage |
|
||||
| `signalWeightsForEntropy` | dict | Signal weights for uncertainty calculation (sum to 1.0) |
|
||||
| `metadata` | object | Provenance: createdBy, createdAt, changelog, notes |
|
||||
|
||||
### Content Hash Computation
|
||||
|
||||
The `WeightManifestHashComputer` computes a deterministic SHA-256 hash over
|
||||
canonical JSON (alphabetically sorted properties, `contentHash` field excluded):
|
||||
|
||||
```
|
||||
Input JSON → parse → remove contentHash → sort keys recursively → UTF-8 → SHA-256 → "sha256:<hex>"
|
||||
```
|
||||
|
||||
This enables tamper detection and content-addressed references. The `sha256:auto`
|
||||
placeholder is replaced by `stella weights hash --write-back` or at build time.
|
||||
|
||||
### CLI Commands (backing services)
|
||||
|
||||
| Command | Service Method | Description |
|
||||
| --- | --- | --- |
|
||||
| `stella weights list` | `WeightManifestCommands.ListAsync()` | List all manifests with version, profile, hash status |
|
||||
| `stella weights validate` | `WeightManifestCommands.ValidateAsync()` | Validate schema, weight normalization, hash integrity |
|
||||
| `stella weights diff` | `WeightManifestCommands.DiffAsync()` | Compare two manifests field-by-field |
|
||||
| `stella weights activate` | `WeightManifestCommands.ActivateAsync()` | Select effective manifest for a reference date |
|
||||
| `stella weights hash` | `WeightManifestCommands.HashAsync()` | Compute/verify content hash, optionally write back |
|
||||
|
||||
### EffectiveFrom Selection
|
||||
|
||||
`WeightManifestLoader.SelectEffectiveAsync(referenceDate)` picks the most recent
|
||||
manifest where `effectiveFrom ≤ referenceDate`, enabling time-travel replay:
|
||||
|
||||
```
|
||||
Manifests: v2026-01-01 v2026-01-22 v2026-03-01
|
||||
Reference: 2026-02-15
|
||||
Selected: v2026-01-22 (most recent ≤ reference date)
|
||||
```
|
||||
|
||||
### OTel Metrics
|
||||
|
||||
| Metric | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `stellaops.weight_manifest.loaded_total` | Counter | Manifests loaded from disk |
|
||||
| `stellaops.weight_manifest.validated_total` | Counter | Manifests validated |
|
||||
| `stellaops.weight_manifest.hash_mismatch_total` | Counter | Content hash mismatches |
|
||||
| `stellaops.weight_manifest.validation_error_total` | Counter | Validation errors |
|
||||
|
||||
### DI Registration
|
||||
|
||||
```csharp
|
||||
services.AddDeterminization(); // Registers WeightManifestLoaderOptions,
|
||||
// IWeightManifestLoader → WeightManifestLoader,
|
||||
// WeightManifestCommands
|
||||
```
|
||||
|
||||
### YAML Configuration
|
||||
|
||||
```yaml
|
||||
Determinization:
|
||||
WeightManifest:
|
||||
ManifestDirectory: "etc/weights"
|
||||
FilePattern: "*.weights.json"
|
||||
RequireComputedHash: true # Reject sha256:auto in production
|
||||
StrictHashVerification: true # Fail on hash mismatch
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- Product Advisory: "Unknown CVEs: graceful placeholders, not blockers"
|
||||
|
||||
Reference in New Issue
Block a user