partly or unimplemented features - now implemented

This commit is contained in:
master
2026-02-09 08:53:51 +02:00
parent 1bf6bbf395
commit 4bdc298ec1
674 changed files with 90194 additions and 2271 deletions

View File

@@ -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 0100**
(higher = riskier) and a **confidence 0.01.0**:
| Code | Dimension | Key signals | Score semantics |
|------|-----------|-------------|-----------------|
| RCH | Reachability | Call-graph tier R0R4, 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 0100
- **`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"