Refactor code structure for improved readability and maintainability; optimize performance in key functions.

This commit is contained in:
master
2025-12-22 19:06:31 +02:00
parent dfaa2079aa
commit 4602ccc3a3
1444 changed files with 109919 additions and 8058 deletions

View File

@@ -0,0 +1,234 @@
using System.Collections.Immutable;
using System.Security.Cryptography;
using System.Text;
using StellaOps.DeltaVerdict.Models;
namespace StellaOps.DeltaVerdict.Engine;
public interface IDeltaComputationEngine
{
DeltaVerdict.Models.DeltaVerdict ComputeDelta(Verdict baseVerdict, Verdict headVerdict);
}
public sealed class DeltaComputationEngine : IDeltaComputationEngine
{
private readonly TimeProvider _timeProvider;
public DeltaComputationEngine(TimeProvider? timeProvider = null)
{
_timeProvider = timeProvider ?? TimeProvider.System;
}
public DeltaVerdict.Models.DeltaVerdict ComputeDelta(Verdict baseVerdict, Verdict headVerdict)
{
ArgumentNullException.ThrowIfNull(baseVerdict);
ArgumentNullException.ThrowIfNull(headVerdict);
var baseComponents = baseVerdict.Components
.ToDictionary(c => c.Purl, c => c, StringComparer.Ordinal);
var headComponents = headVerdict.Components
.ToDictionary(c => c.Purl, c => c, StringComparer.Ordinal);
var addedComponents = ComputeAddedComponents(baseComponents, headComponents);
var removedComponents = ComputeRemovedComponents(baseComponents, headComponents);
var changedComponents = ComputeChangedComponents(baseComponents, headComponents);
var baseVulns = baseVerdict.Vulnerabilities
.ToDictionary(v => v.Id, v => v, StringComparer.Ordinal);
var headVulns = headVerdict.Vulnerabilities
.ToDictionary(v => v.Id, v => v, StringComparer.Ordinal);
var addedVulns = ComputeAddedVulnerabilities(baseVulns, headVulns);
var removedVulns = ComputeRemovedVulnerabilities(baseVulns, headVulns);
var changedStatuses = ComputeStatusChanges(baseVulns, headVulns);
var riskDelta = ComputeRiskScoreDelta(baseVerdict.RiskScore, headVerdict.RiskScore);
var totalChanges = addedComponents.Length + removedComponents.Length + changedComponents.Length
+ addedVulns.Length + removedVulns.Length + changedStatuses.Length;
var summary = new DeltaSummary(
ComponentsAdded: addedComponents.Length,
ComponentsRemoved: removedComponents.Length,
ComponentsChanged: changedComponents.Length,
VulnerabilitiesAdded: addedVulns.Length,
VulnerabilitiesRemoved: removedVulns.Length,
VulnerabilityStatusChanges: changedStatuses.Length,
TotalChanges: totalChanges,
Magnitude: ClassifyMagnitude(totalChanges));
return new DeltaVerdict.Models.DeltaVerdict
{
DeltaId = ComputeDeltaId(baseVerdict, headVerdict),
SchemaVersion = "1.0.0",
BaseVerdict = CreateVerdictReference(baseVerdict),
HeadVerdict = CreateVerdictReference(headVerdict),
AddedComponents = addedComponents,
RemovedComponents = removedComponents,
ChangedComponents = changedComponents,
AddedVulnerabilities = addedVulns,
RemovedVulnerabilities = removedVulns,
ChangedVulnerabilityStatuses = changedStatuses,
RiskScoreDelta = riskDelta,
Summary = summary,
ComputedAt = _timeProvider.GetUtcNow()
};
}
private static ImmutableArray<ComponentDelta> ComputeAddedComponents(
IReadOnlyDictionary<string, Component> baseComponents,
IReadOnlyDictionary<string, Component> headComponents)
{
return headComponents
.Where(kv => !baseComponents.ContainsKey(kv.Key))
.OrderBy(kv => kv.Key, StringComparer.Ordinal)
.Select(kv => new ComponentDelta(
kv.Value.Purl,
kv.Value.Name,
kv.Value.Version,
kv.Value.Type,
kv.Value.Vulnerabilities))
.ToImmutableArray();
}
private static ImmutableArray<ComponentDelta> ComputeRemovedComponents(
IReadOnlyDictionary<string, Component> baseComponents,
IReadOnlyDictionary<string, Component> headComponents)
{
return baseComponents
.Where(kv => !headComponents.ContainsKey(kv.Key))
.OrderBy(kv => kv.Key, StringComparer.Ordinal)
.Select(kv => new ComponentDelta(
kv.Value.Purl,
kv.Value.Name,
kv.Value.Version,
kv.Value.Type,
kv.Value.Vulnerabilities))
.ToImmutableArray();
}
private static ImmutableArray<ComponentVersionDelta> ComputeChangedComponents(
IReadOnlyDictionary<string, Component> baseComponents,
IReadOnlyDictionary<string, Component> headComponents)
{
return baseComponents
.Where(kv => headComponents.TryGetValue(kv.Key, out var head)
&& !string.Equals(kv.Value.Version, head.Version, StringComparison.Ordinal))
.OrderBy(kv => kv.Key, StringComparer.Ordinal)
.Select(kv =>
{
var baseComponent = kv.Value;
var headComponent = headComponents[kv.Key];
var fixedVulns = baseComponent.Vulnerabilities
.Except(headComponent.Vulnerabilities, StringComparer.Ordinal)
.OrderBy(v => v, StringComparer.Ordinal)
.ToImmutableArray();
var introducedVulns = headComponent.Vulnerabilities
.Except(baseComponent.Vulnerabilities, StringComparer.Ordinal)
.OrderBy(v => v, StringComparer.Ordinal)
.ToImmutableArray();
return new ComponentVersionDelta(
baseComponent.Purl,
baseComponent.Name,
baseComponent.Version,
headComponent.Version,
fixedVulns,
introducedVulns);
})
.ToImmutableArray();
}
private static ImmutableArray<VulnerabilityDelta> ComputeAddedVulnerabilities(
IReadOnlyDictionary<string, Vulnerability> baseVulns,
IReadOnlyDictionary<string, Vulnerability> headVulns)
{
return headVulns
.Where(kv => !baseVulns.ContainsKey(kv.Key))
.OrderBy(kv => kv.Key, StringComparer.Ordinal)
.Select(kv => new VulnerabilityDelta(
kv.Value.Id,
kv.Value.Severity,
kv.Value.CvssScore,
kv.Value.ComponentPurl,
kv.Value.ReachabilityStatus))
.ToImmutableArray();
}
private static ImmutableArray<VulnerabilityDelta> ComputeRemovedVulnerabilities(
IReadOnlyDictionary<string, Vulnerability> baseVulns,
IReadOnlyDictionary<string, Vulnerability> headVulns)
{
return baseVulns
.Where(kv => !headVulns.ContainsKey(kv.Key))
.OrderBy(kv => kv.Key, StringComparer.Ordinal)
.Select(kv => new VulnerabilityDelta(
kv.Value.Id,
kv.Value.Severity,
kv.Value.CvssScore,
kv.Value.ComponentPurl,
kv.Value.ReachabilityStatus))
.ToImmutableArray();
}
private static ImmutableArray<VulnerabilityStatusDelta> ComputeStatusChanges(
IReadOnlyDictionary<string, Vulnerability> baseVulns,
IReadOnlyDictionary<string, Vulnerability> headVulns)
{
var deltas = new List<VulnerabilityStatusDelta>();
foreach (var (id, baseVuln) in baseVulns.OrderBy(kv => kv.Key, StringComparer.Ordinal))
{
if (!headVulns.TryGetValue(id, out var headVuln))
{
continue;
}
var oldStatus = baseVuln.Status ?? baseVuln.ReachabilityStatus ?? "unknown";
var newStatus = headVuln.Status ?? headVuln.ReachabilityStatus ?? "unknown";
if (!string.Equals(oldStatus, newStatus, StringComparison.OrdinalIgnoreCase))
{
deltas.Add(new VulnerabilityStatusDelta(id, oldStatus, newStatus, null));
}
}
return deltas.ToImmutableArray();
}
private static RiskScoreDelta ComputeRiskScoreDelta(decimal oldScore, decimal newScore)
{
var change = newScore - oldScore;
var percentChange = oldScore > 0 ? (change / oldScore) * 100 : (newScore > 0 ? 100 : 0);
var trend = change switch
{
< 0 => RiskTrend.Improved,
> 0 => RiskTrend.Degraded,
_ => RiskTrend.Stable
};
return new RiskScoreDelta(oldScore, newScore, change, percentChange, trend);
}
private static DeltaMagnitude ClassifyMagnitude(int totalChanges) => totalChanges switch
{
0 => DeltaMagnitude.None,
<= 5 => DeltaMagnitude.Minimal,
<= 20 => DeltaMagnitude.Small,
<= 50 => DeltaMagnitude.Medium,
<= 100 => DeltaMagnitude.Large,
_ => DeltaMagnitude.Major
};
private static VerdictReference CreateVerdictReference(Verdict verdict)
=> new(verdict.VerdictId, verdict.Digest, verdict.ArtifactRef, verdict.ScannedAt);
private static string ComputeDeltaId(Verdict baseVerdict, Verdict headVerdict)
{
var baseKey = baseVerdict.Digest ?? baseVerdict.VerdictId;
var headKey = headVerdict.Digest ?? headVerdict.VerdictId;
var input = $"{baseKey}:{headKey}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(input));
return Convert.ToHexString(hash).ToLowerInvariant();
}
}