Complete batch 012 (golden set diff) and 013 (advisory chat), fix build errors
Sprints completed: - SPRINT_20260110_012_* (golden set diff layer - 10 sprints) - SPRINT_20260110_013_* (advisory chat - 4 sprints) Build fixes applied: - Fix namespace conflicts with Microsoft.Extensions.Options.Options.Create - Fix VexDecisionReachabilityIntegrationTests API drift (major rewrite) - Fix VexSchemaValidationTests FluentAssertions method name - Fix FixChainGateIntegrationTests ambiguous type references - Fix AdvisoryAI test files required properties and namespace aliases - Add stub types for CveMappingController (ICveSymbolMappingService) - Fix VerdictBuilderService static context issue Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
296
docs/modules/risk-engine/fix-chain-integration.md
Normal file
296
docs/modules/risk-engine/fix-chain-integration.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Risk Engine FixChain Integration
|
||||
|
||||
> **Sprint:** SPRINT_20260110_012_007_RISK
|
||||
> **Last Updated:** 10-Jan-2026
|
||||
|
||||
## Overview
|
||||
|
||||
The Risk Engine FixChain integration enables automatic risk score adjustment based on verified fix status from FixChain attestations. When a vulnerability has a verified fix, the risk score is reduced proportionally to the verification confidence level.
|
||||
|
||||
## Why This Matters
|
||||
|
||||
| Current State | With FixChain Integration |
|
||||
|---------------|---------------------------|
|
||||
| Risk scores ignore fix verification | Fix confidence reduces risk |
|
||||
| Binary matches = always vulnerable | Verified fixes lower severity |
|
||||
| No credit for patched backports | Backport fixes recognized |
|
||||
| Manual risk exceptions needed | Automatic risk adjustment |
|
||||
|
||||
## Risk Adjustment Model
|
||||
|
||||
### Verdict to Risk Modifier Mapping
|
||||
|
||||
| Verdict | Confidence | Risk Modifier | Rationale |
|
||||
|---------|------------|---------------|-----------|
|
||||
| `fixed` | >= 95% | -80% to -90% | High-confidence verified fix |
|
||||
| `fixed` | 85-95% | -60% to -80% | Verified fix, some uncertainty |
|
||||
| `fixed` | 70-85% | -40% to -60% | Likely fixed, needs confirmation |
|
||||
| `fixed` | 60-70% | -20% to -40% | Possible fix, low confidence |
|
||||
| `fixed` | < 60% | 0% | Below threshold, no adjustment |
|
||||
| `partial` | >= 60% | -25% to -50% | Partial fix applied |
|
||||
| `inconclusive` | any | 0% | Cannot determine, conservative |
|
||||
| `still_vulnerable` | any | 0% | No fix detected |
|
||||
| No attestation | N/A | 0% | No verification performed |
|
||||
|
||||
### Modifier Formula
|
||||
|
||||
```
|
||||
AdjustedRisk = BaseRisk * (1 - (Modifier * ConfidenceWeight))
|
||||
|
||||
Where:
|
||||
Modifier = verdict-based modifier from table above
|
||||
ConfidenceWeight = min(1.0, (Confidence - MinThreshold) / (1.0 - MinThreshold))
|
||||
```
|
||||
|
||||
### Example Calculation
|
||||
|
||||
```
|
||||
CVE-2024-0727 on pkg:deb/debian/openssl@3.0.11-1~deb12u2:
|
||||
BaseRisk = 8.5 (HIGH)
|
||||
FixChain Verdict = "fixed"
|
||||
FixChain Confidence = 0.97
|
||||
|
||||
Modifier = 0.90 (high confidence tier)
|
||||
ConfidenceWeight = (0.97 - 0.60) / (1.0 - 0.60) = 0.925
|
||||
|
||||
AdjustedRisk = 8.5 * (1 - 0.90 * 0.925) = 8.5 * 0.1675 = 1.42 (LOW)
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### IFixChainRiskProvider
|
||||
|
||||
Main interface for FixChain risk integration:
|
||||
|
||||
```csharp
|
||||
public interface IFixChainRiskProvider
|
||||
{
|
||||
Task<FixVerificationStatus?> GetFixStatusAsync(
|
||||
string cveId,
|
||||
string binarySha256,
|
||||
string? componentPurl = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
double ComputeRiskAdjustment(FixVerificationStatus status);
|
||||
|
||||
FixChainRiskFactor CreateRiskFactor(FixVerificationStatus status);
|
||||
}
|
||||
```
|
||||
|
||||
### FixChainRiskProvider
|
||||
|
||||
Implementation that:
|
||||
1. Queries the attestation store for FixChain predicates
|
||||
2. Computes risk adjustment based on verdict and confidence
|
||||
3. Creates structured risk factors for UI display
|
||||
|
||||
### IFixChainAttestationClient
|
||||
|
||||
Client for querying attestations:
|
||||
|
||||
```csharp
|
||||
public interface IFixChainAttestationClient
|
||||
{
|
||||
Task<FixChainAttestationData?> GetFixChainAsync(
|
||||
string cveId,
|
||||
string binarySha256,
|
||||
string? componentPurl = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
Task<ImmutableArray<FixChainAttestationData>> GetForComponentAsync(
|
||||
string componentPurl,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### YAML Configuration
|
||||
|
||||
```yaml
|
||||
RiskEngine:
|
||||
Providers:
|
||||
FixChain:
|
||||
Enabled: true
|
||||
HighConfidenceThreshold: 0.95
|
||||
MediumConfidenceThreshold: 0.85
|
||||
LowConfidenceThreshold: 0.70
|
||||
MinConfidenceThreshold: 0.60
|
||||
FixedReduction: 0.90
|
||||
PartialReduction: 0.50
|
||||
MaxRiskReduction: 0.90
|
||||
CacheMaxAgeHours: 24
|
||||
```
|
||||
|
||||
### Service Registration
|
||||
|
||||
```csharp
|
||||
services.AddOptions<FixChainRiskOptions>()
|
||||
.Bind(config.GetSection("RiskEngine:Providers:FixChain"))
|
||||
.ValidateDataAnnotations()
|
||||
.ValidateOnStart();
|
||||
|
||||
services.AddSingleton<IFixChainRiskProvider, FixChainRiskProvider>();
|
||||
services.AddHttpClient<IFixChainAttestationClient, FixChainAttestationClient>();
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Getting Fix Status
|
||||
|
||||
```csharp
|
||||
var provider = services.GetRequiredService<IFixChainRiskProvider>();
|
||||
|
||||
var status = await provider.GetFixStatusAsync(
|
||||
"CVE-2024-0727",
|
||||
binarySha256,
|
||||
componentPurl);
|
||||
|
||||
if (status is not null)
|
||||
{
|
||||
var adjustment = provider.ComputeRiskAdjustment(status);
|
||||
var adjustedRisk = baseRisk * adjustment;
|
||||
}
|
||||
```
|
||||
|
||||
### Creating Risk Factors
|
||||
|
||||
```csharp
|
||||
var status = await provider.GetFixStatusAsync(cveId, binarySha256);
|
||||
if (status is not null)
|
||||
{
|
||||
var factor = provider.CreateRiskFactor(status);
|
||||
|
||||
// For UI display
|
||||
var display = factor.ToDisplay();
|
||||
var badge = factor.ToBadge();
|
||||
var summary = factor.ToSummary();
|
||||
}
|
||||
```
|
||||
|
||||
### Signal-Based Scoring
|
||||
|
||||
For batch processing via signals:
|
||||
|
||||
```csharp
|
||||
var signals = new Dictionary<string, double>
|
||||
{
|
||||
[FixChainRiskProvider.SignalFixConfidence] = 0.95,
|
||||
[FixChainRiskProvider.SignalFixStatus] = FixChainRiskProvider.EncodeStatus("fixed")
|
||||
};
|
||||
|
||||
var request = new ScoreRequest("fixchain", subject, signals);
|
||||
var adjustment = await provider.ScoreAsync(request, ct);
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
The integration exposes the following OpenTelemetry metrics:
|
||||
|
||||
| Metric | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `risk_fixchain_lookups_total` | Counter | Total attestation lookups |
|
||||
| `risk_fixchain_hits_total` | Counter | Attestations found |
|
||||
| `risk_fixchain_misses_total` | Counter | Lookups with no attestation |
|
||||
| `risk_fixchain_cache_hits_total` | Counter | Lookups served from cache |
|
||||
| `risk_fixchain_lookup_duration_seconds` | Histogram | Lookup duration |
|
||||
| `risk_fixchain_adjustments_total` | Counter | Risk adjustments applied |
|
||||
| `risk_fixchain_reduction_percent` | Histogram | Reduction percentage distribution |
|
||||
| `risk_fixchain_errors_total` | Counter | Lookup errors |
|
||||
|
||||
### Recording Metrics
|
||||
|
||||
```csharp
|
||||
// Automatically recorded by the provider, or manually:
|
||||
FixChainRiskMetrics.RecordLookup(
|
||||
found: true,
|
||||
fromCache: false,
|
||||
durationSeconds: 0.05,
|
||||
verdict: "fixed");
|
||||
|
||||
FixChainRiskMetrics.RecordAdjustment(
|
||||
verdict: FixChainVerdictStatus.Fixed,
|
||||
confidence: 0.95m,
|
||||
reductionPercent: 0.80);
|
||||
```
|
||||
|
||||
## UI Integration
|
||||
|
||||
### Display Model
|
||||
|
||||
```csharp
|
||||
var display = factor.ToDisplay();
|
||||
// display.Label = "Fix Verification"
|
||||
// display.Value = "Fixed (95% confidence)"
|
||||
// display.Impact = -0.80
|
||||
// display.ImpactDirection = "decrease"
|
||||
// display.EvidenceRef = "fixchain://sha256:..."
|
||||
// display.Details = { verdict, confidence, verified_at, ... }
|
||||
```
|
||||
|
||||
### Badge Component
|
||||
|
||||
```csharp
|
||||
var badge = factor.ToBadge();
|
||||
// badge.Status = "Fixed"
|
||||
// badge.Color = "green"
|
||||
// badge.Icon = "check-circle"
|
||||
// badge.Confidence = 0.95m
|
||||
// badge.Tooltip = "Verified fix (95% confidence)"
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public async Task FixedVerdict_HighConfidence_ReturnsLowRisk()
|
||||
{
|
||||
var provider = new FixChainRiskProvider(options);
|
||||
var status = new FixVerificationStatus
|
||||
{
|
||||
Verdict = "fixed",
|
||||
Confidence = 0.97m,
|
||||
VerifiedAt = DateTimeOffset.UtcNow,
|
||||
AttestationDigest = "sha256:test"
|
||||
};
|
||||
|
||||
var adjustment = provider.ComputeRiskAdjustment(status);
|
||||
|
||||
adjustment.Should().BeLessThan(0.3);
|
||||
}
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public async Task FullWorkflow_FixedVerdict_ReducesRisk()
|
||||
{
|
||||
var attestationClient = new InMemoryFixChainAttestationClient();
|
||||
attestationClient.AddAttestation(cveId, binarySha256, attestation);
|
||||
|
||||
var provider = new FixChainRiskProvider(options, attestationClient, logger);
|
||||
var status = await provider.GetFixStatusAsync(cveId, binarySha256);
|
||||
|
||||
status.Should().NotBeNull();
|
||||
status!.Verdict.Should().Be("fixed");
|
||||
}
|
||||
```
|
||||
|
||||
## Decisions and Trade-offs
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| Conservative thresholds | Start high, can lower based on accuracy data |
|
||||
| No automatic upgrade | Inconclusive doesn't increase risk |
|
||||
| Cache TTL 30 minutes | Balances freshness vs. performance |
|
||||
| Attestation required | No reduction without verifiable evidence |
|
||||
| Minimum confidence 60% | Below this, evidence is too weak for adjustment |
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [FixChain Attestation Predicate](../attestor/fix-chain-predicate.md)
|
||||
- [Golden Set Schema](../binary-index/golden-set-schema.md)
|
||||
- [Risk Engine Architecture](./architecture.md)
|
||||
Reference in New Issue
Block a user