release orchestrator v1 draft and build fixes

This commit is contained in:
master
2026-01-12 12:24:17 +02:00
parent f3de858c59
commit 9873f80830
1598 changed files with 240385 additions and 5944 deletions

View File

@@ -432,7 +432,7 @@ public sealed class OpenVexNormalizer : IVexNormalizer
return null;
}
var fallbackGuid = guidProvider?.NewGuid() ?? Guid.NewGuid();
var fallbackGuid = (guidProvider ?? SystemGuidProvider.Instance).NewGuid();
return new NormalizedProduct(
Key: key ?? purl ?? cpe ?? $"unknown-{fallbackGuid:N}",
Name: name,

View File

@@ -144,7 +144,31 @@ public enum TrustFactor
/// <summary>
/// Product authority match.
/// </summary>
ProductAuthority
ProductAuthority,
// --- Patch Verification Factors ---
/// <summary>
/// Function-level binary fingerprint match confirms patch.
/// Highest confidence patch verification method.
/// </summary>
PatchFunctionMatch,
/// <summary>
/// Section-level binary hash match confirms patch.
/// Medium confidence patch verification method.
/// </summary>
PatchSectionMatch,
/// <summary>
/// Patch attestation signed by trusted issuer (DSSE).
/// </summary>
PatchIssuerAuthority,
/// <summary>
/// Runtime symbols confirm patched version loaded.
/// </summary>
PatchRuntimeConfirm
}
/// <summary>
@@ -202,7 +226,12 @@ public sealed record TrustConfiguration
[TrustFactor.Freshness] = 0.15,
[TrustFactor.StatusQuality] = 0.10,
[TrustFactor.SourceMatch] = 0.05,
[TrustFactor.ProductAuthority] = 0.05
[TrustFactor.ProductAuthority] = 0.05,
// Patch verification factors (optional, applied when evidence present)
[TrustFactor.PatchFunctionMatch] = 0.20,
[TrustFactor.PatchSectionMatch] = 0.15,
[TrustFactor.PatchIssuerAuthority] = 0.15,
[TrustFactor.PatchRuntimeConfirm] = 0.10
}
};
}

View File

@@ -0,0 +1,308 @@
namespace StellaOps.VexLens.Core.Trust;
/// <summary>
/// Provides trust weight computation for patch verification evidence.
/// Converts binary fingerprint match results into trust factors for VEX consensus.
/// </summary>
public sealed class PatchVerificationTrustProvider
{
private readonly TrustConfiguration _configuration;
/// <summary>
/// Initializes a new instance with the specified configuration.
/// </summary>
public PatchVerificationTrustProvider(TrustConfiguration? configuration = null)
{
_configuration = configuration ?? TrustConfiguration.Default;
}
/// <summary>
/// Computes trust weight contribution from patch verification evidence.
/// </summary>
/// <param name="evidence">Patch verification evidence.</param>
/// <returns>Trust weight with factor breakdown.</returns>
public TrustWeight ComputeWeight(PatchVerificationTrustContext evidence)
{
ArgumentNullException.ThrowIfNull(evidence);
var factors = new Dictionary<TrustFactor, double>();
var warnings = new List<string>();
var totalWeight = 0.0;
// Function-level match (highest confidence)
if (evidence.HasFunctionLevelMatch)
{
var factorWeight = GetFactorWeight(TrustFactor.PatchFunctionMatch);
var contribution = factorWeight * evidence.FunctionMatchConfidence;
factors[TrustFactor.PatchFunctionMatch] = contribution;
totalWeight += contribution;
}
// Section-level match
if (evidence.HasSectionLevelMatch)
{
var factorWeight = GetFactorWeight(TrustFactor.PatchSectionMatch);
var contribution = factorWeight * evidence.SectionMatchConfidence;
factors[TrustFactor.PatchSectionMatch] = contribution;
totalWeight += contribution;
}
// Issuer authority (DSSE attestation)
if (evidence.HasSignedAttestation)
{
var factorWeight = GetFactorWeight(TrustFactor.PatchIssuerAuthority);
var contribution = factorWeight * evidence.IssuerAuthorityScore;
factors[TrustFactor.PatchIssuerAuthority] = contribution;
totalWeight += contribution;
}
else if (evidence.Status == PatchVerificationTrustStatus.Verified)
{
// Warn about missing attestation for verified patches
warnings.Add("Patch verified but no DSSE attestation present");
}
// Runtime confirmation (if available)
if (evidence.HasRuntimeConfirmation)
{
var factorWeight = GetFactorWeight(TrustFactor.PatchRuntimeConfirm);
var contribution = factorWeight * evidence.RuntimeConfirmationConfidence;
factors[TrustFactor.PatchRuntimeConfirm] = contribution;
totalWeight += contribution;
}
// Apply status-based adjustments
totalWeight = AdjustForStatus(totalWeight, evidence.Status, warnings);
// Clamp final weight
totalWeight = Math.Clamp(totalWeight, 0.0, 1.0);
return new TrustWeight
{
Weight = totalWeight,
Factors = factors,
Explanation = GenerateExplanation(evidence, totalWeight),
Warnings = warnings.Count > 0 ? warnings : null
};
}
/// <summary>
/// Computes aggregate trust weight from multiple evidence items.
/// </summary>
public TrustWeight ComputeAggregateWeight(IEnumerable<PatchVerificationTrustContext> evidenceItems)
{
var items = evidenceItems.ToList();
if (items.Count == 0)
{
return new TrustWeight
{
Weight = 0.0,
Factors = new Dictionary<TrustFactor, double>(),
Explanation = "No patch verification evidence available"
};
}
// Compute individual weights
var weights = items.Select(ComputeWeight).ToList();
// Aggregate factors (average of non-zero contributions)
var aggregatedFactors = new Dictionary<TrustFactor, double>();
foreach (var factor in Enum.GetValues<TrustFactor>())
{
var factorValues = weights
.Where(w => w.Factors.ContainsKey(factor))
.Select(w => w.Factors[factor])
.ToList();
if (factorValues.Count > 0)
{
aggregatedFactors[factor] = factorValues.Average();
}
}
// Final weight: weighted average by confidence
var totalWeight = weights.Average(w => w.Weight);
// Aggregate warnings
var allWarnings = weights
.Where(w => w.Warnings is not null)
.SelectMany(w => w.Warnings!)
.Distinct()
.ToList();
return new TrustWeight
{
Weight = totalWeight,
Factors = aggregatedFactors,
Explanation = $"Aggregate of {items.Count} patch verification evidence items",
Warnings = allWarnings.Count > 0 ? allWarnings : null
};
}
private double GetFactorWeight(TrustFactor factor)
{
return _configuration.FactorWeights.TryGetValue(factor, out var weight)
? weight
: 0.0;
}
private static double AdjustForStatus(
double weight,
PatchVerificationTrustStatus status,
List<string> warnings)
{
return status switch
{
PatchVerificationTrustStatus.Verified => weight,
PatchVerificationTrustStatus.PartialMatch => weight * 0.6,
PatchVerificationTrustStatus.Inconclusive =>
AddWarningAndReturn(warnings, "Patch verification inconclusive", weight * 0.2),
PatchVerificationTrustStatus.NotPatched => 0.0,
PatchVerificationTrustStatus.NoPatchData =>
AddWarningAndReturn(warnings, "No patch signature data available", 0.0),
_ => weight
};
}
private static double AddWarningAndReturn(List<string> warnings, string warning, double value)
{
warnings.Add(warning);
return value;
}
private static string GenerateExplanation(PatchVerificationTrustContext evidence, double weight)
{
var parts = new List<string>();
if (evidence.HasFunctionLevelMatch)
{
parts.Add($"function-level match ({evidence.FunctionMatchConfidence:P0})");
}
if (evidence.HasSectionLevelMatch)
{
parts.Add($"section-level match ({evidence.SectionMatchConfidence:P0})");
}
if (evidence.HasSignedAttestation)
{
parts.Add("signed attestation present");
}
if (evidence.HasRuntimeConfirmation)
{
parts.Add("runtime confirmation");
}
var statusStr = evidence.Status switch
{
PatchVerificationTrustStatus.Verified => "Patch verified",
PatchVerificationTrustStatus.PartialMatch => "Partial patch match",
PatchVerificationTrustStatus.Inconclusive => "Verification inconclusive",
PatchVerificationTrustStatus.NotPatched => "Not patched",
PatchVerificationTrustStatus.NoPatchData => "No patch data",
_ => "Unknown status"
};
if (parts.Count > 0)
{
return $"{statusStr} based on: {string.Join(", ", parts)}. Trust weight: {weight:P0}";
}
return $"{statusStr}. Trust weight: {weight:P0}";
}
}
/// <summary>
/// Context for patch verification trust computation.
/// Maps from Scanner.PatchVerification.Models to trust computation inputs.
/// </summary>
public sealed record PatchVerificationTrustContext
{
/// <summary>
/// Overall verification status.
/// </summary>
public required PatchVerificationTrustStatus Status { get; init; }
/// <summary>
/// Whether function-level (CFG/instruction) match was performed.
/// </summary>
public bool HasFunctionLevelMatch { get; init; }
/// <summary>
/// Confidence of function-level match (0.0-1.0).
/// </summary>
public double FunctionMatchConfidence { get; init; }
/// <summary>
/// Whether section-level hash match was performed.
/// </summary>
public bool HasSectionLevelMatch { get; init; }
/// <summary>
/// Confidence of section-level match (0.0-1.0).
/// </summary>
public double SectionMatchConfidence { get; init; }
/// <summary>
/// Whether DSSE-signed attestation is present.
/// </summary>
public bool HasSignedAttestation { get; init; }
/// <summary>
/// Authority score of the attestation issuer (0.0-1.0).
/// Based on issuer directory trust tier.
/// </summary>
public double IssuerAuthorityScore { get; init; }
/// <summary>
/// Whether runtime symbol confirmation is available.
/// </summary>
public bool HasRuntimeConfirmation { get; init; }
/// <summary>
/// Confidence of runtime confirmation (0.0-1.0).
/// </summary>
public double RuntimeConfirmationConfidence { get; init; }
/// <summary>
/// CVE identifier being verified.
/// </summary>
public string? CveId { get; init; }
/// <summary>
/// Binary path that was verified.
/// </summary>
public string? BinaryPath { get; init; }
}
/// <summary>
/// Patch verification status for trust computation.
/// Mirrors Scanner.PatchVerification.Models.PatchVerificationStatus.
/// </summary>
public enum PatchVerificationTrustStatus
{
/// <summary>
/// Patch verified with high confidence.
/// </summary>
Verified,
/// <summary>
/// Partial match - some evidence of patching.
/// </summary>
PartialMatch,
/// <summary>
/// Unable to determine patch status conclusively.
/// </summary>
Inconclusive,
/// <summary>
/// Binary does not appear to be patched.
/// </summary>
NotPatched,
/// <summary>
/// No patch signature data available for comparison.
/// </summary>
NoPatchData
}