partly or unimplemented features - now implemented
This commit is contained in:
@@ -35,6 +35,9 @@ public static class GoldenSetServiceCollectionExtensions
|
||||
services.TryAddSingleton<ISinkRegistry, SinkRegistry>();
|
||||
services.TryAddSingleton<IGoldenSetValidator, GoldenSetValidator>();
|
||||
|
||||
// Cross-distro coverage matrix for backport validation
|
||||
services.TryAddSingleton<ICrossDistroCoverageService, CrossDistroCoverageService>();
|
||||
|
||||
// Memory cache (if not already registered)
|
||||
services.AddMemoryCache();
|
||||
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// CrossDistroCoverageModels.cs
|
||||
// Sprint: SPRINT_20260208_027_BinaryIndex_cross_distro_golden_set_for_backport_validation
|
||||
// Task: T1 — Cross-distro coverage matrix models for backport validation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace StellaOps.BinaryIndex.GoldenSet;
|
||||
|
||||
/// <summary>
|
||||
/// Supported Linux distributions for cross-distro backport validation.
|
||||
/// </summary>
|
||||
public enum DistroFamily
|
||||
{
|
||||
/// <summary>Alpine Linux (musl libc, APK).</summary>
|
||||
Alpine = 0,
|
||||
|
||||
/// <summary>Debian / Ubuntu (glibc, DEB).</summary>
|
||||
Debian = 1,
|
||||
|
||||
/// <summary>RHEL / CentOS / Fedora (glibc, RPM).</summary>
|
||||
Rhel = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backport status for a given CVE on a specific distribution version.
|
||||
/// </summary>
|
||||
public enum BackportStatus
|
||||
{
|
||||
/// <summary>Fix has not been applied (still vulnerable).</summary>
|
||||
NotPatched = 0,
|
||||
|
||||
/// <summary>Fix has been backported to the package version.</summary>
|
||||
Backported = 1,
|
||||
|
||||
/// <summary>The component was removed or is not applicable.</summary>
|
||||
NotApplicable = 2,
|
||||
|
||||
/// <summary>Backport status is unknown or not yet validated.</summary>
|
||||
Unknown = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single distro-version coverage entry in the cross-distro matrix.
|
||||
/// Tracks whether a given CVE's fix has been backported to a specific distro version.
|
||||
/// </summary>
|
||||
public sealed record DistroCoverageEntry
|
||||
{
|
||||
/// <summary>Distribution family (Alpine, Debian, RHEL).</summary>
|
||||
public required DistroFamily Distro { get; init; }
|
||||
|
||||
/// <summary>Distro release version (e.g., "3.18", "bookworm", "9").</summary>
|
||||
public required string Version { get; init; }
|
||||
|
||||
/// <summary>Package name in the distro's packaging system.</summary>
|
||||
public required string PackageName { get; init; }
|
||||
|
||||
/// <summary>Package version string in the distro's format.</summary>
|
||||
public required string PackageVersion { get; init; }
|
||||
|
||||
/// <summary>Backport status for this entry.</summary>
|
||||
public required BackportStatus Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the golden set definition has been validated against
|
||||
/// a real binary from this distro version.
|
||||
/// </summary>
|
||||
public bool Validated { get; init; }
|
||||
|
||||
/// <summary>When this entry was last validated (null if never).</summary>
|
||||
public DateTimeOffset? ValidatedAt { get; init; }
|
||||
|
||||
/// <summary>Optional notes (e.g., patch commit hash, advisory URL).</summary>
|
||||
public string? Notes { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A curated high-impact CVE entry with cross-distro coverage information.
|
||||
/// Represents one row in the "golden set" of curated cross-distro test cases.
|
||||
/// </summary>
|
||||
public sealed record CuratedCveEntry
|
||||
{
|
||||
/// <summary>CVE identifier (e.g., "CVE-2014-0160").</summary>
|
||||
public required string CveId { get; init; }
|
||||
|
||||
/// <summary>Affected component (e.g., "openssl", "sudo").</summary>
|
||||
public required string Component { get; init; }
|
||||
|
||||
/// <summary>Human-readable vulnerability name (e.g., "Heartbleed").</summary>
|
||||
public string? CommonName { get; init; }
|
||||
|
||||
/// <summary>CVSS score (0.0 – 10.0).</summary>
|
||||
public double? CvssScore { get; init; }
|
||||
|
||||
/// <summary>CWE identifiers associated with this CVE.</summary>
|
||||
public ImmutableArray<string> CweIds { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Per-distro coverage entries showing backport status.
|
||||
/// Keyed by (Distro, Version) for efficient lookup.
|
||||
/// </summary>
|
||||
public required ImmutableArray<DistroCoverageEntry> Coverage { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the golden set definition for this CVE.
|
||||
/// Null if not yet linked to a validated golden set.
|
||||
/// </summary>
|
||||
public string? GoldenSetId { get; init; }
|
||||
|
||||
/// <summary>When this curated entry was created.</summary>
|
||||
public required DateTimeOffset CreatedAt { get; init; }
|
||||
|
||||
/// <summary>When this curated entry was last updated.</summary>
|
||||
public DateTimeOffset? UpdatedAt { get; init; }
|
||||
|
||||
/// <summary>Number of distro-version entries that have been validated.</summary>
|
||||
public int ValidatedCount => Coverage.IsDefaultOrEmpty ? 0 : Coverage.Count(c => c.Validated);
|
||||
|
||||
/// <summary>Total number of distro-version entries.</summary>
|
||||
public int TotalEntries => Coverage.IsDefaultOrEmpty ? 0 : Coverage.Length;
|
||||
|
||||
/// <summary>Coverage ratio [0.0, 1.0].</summary>
|
||||
public double CoverageRatio => TotalEntries == 0 ? 0.0 : (double)ValidatedCount / TotalEntries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregated coverage summary across all curated CVEs.
|
||||
/// </summary>
|
||||
public sealed record CrossDistroCoverageSummary
|
||||
{
|
||||
/// <summary>Total curated CVEs in the matrix.</summary>
|
||||
public required int TotalCves { get; init; }
|
||||
|
||||
/// <summary>Total distro-version entries across all CVEs.</summary>
|
||||
public required int TotalEntries { get; init; }
|
||||
|
||||
/// <summary>Number of validated entries.</summary>
|
||||
public required int ValidatedEntries { get; init; }
|
||||
|
||||
/// <summary>Number of entries where the fix is backported.</summary>
|
||||
public required int BackportedCount { get; init; }
|
||||
|
||||
/// <summary>Number of entries where the component is not patched.</summary>
|
||||
public required int NotPatchedCount { get; init; }
|
||||
|
||||
/// <summary>Per-distro breakdown.</summary>
|
||||
public required ImmutableDictionary<DistroFamily, DistroBreakdown> ByDistro { get; init; }
|
||||
|
||||
/// <summary>Overall validation coverage ratio [0.0, 1.0].</summary>
|
||||
public double OverallCoverage => TotalEntries == 0 ? 0.0 : (double)ValidatedEntries / TotalEntries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-distro breakdown within the coverage summary.
|
||||
/// </summary>
|
||||
public sealed record DistroBreakdown
|
||||
{
|
||||
/// <summary>Number of entries for this distro family.</summary>
|
||||
public required int EntryCount { get; init; }
|
||||
|
||||
/// <summary>Number of validated entries for this distro.</summary>
|
||||
public required int ValidatedCount { get; init; }
|
||||
|
||||
/// <summary>Number of backported entries for this distro.</summary>
|
||||
public required int BackportedCount { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query parameters for filtering curated CVE entries.
|
||||
/// </summary>
|
||||
public sealed record CuratedCveQuery
|
||||
{
|
||||
/// <summary>Filter by component name (case-insensitive substring).</summary>
|
||||
public string? Component { get; init; }
|
||||
|
||||
/// <summary>Filter by distro family.</summary>
|
||||
public DistroFamily? Distro { get; init; }
|
||||
|
||||
/// <summary>Filter by backport status.</summary>
|
||||
public BackportStatus? Status { get; init; }
|
||||
|
||||
/// <summary>Only return entries that haven't been validated yet.</summary>
|
||||
public bool OnlyUnvalidated { get; init; }
|
||||
|
||||
/// <summary>Maximum results to return.</summary>
|
||||
public int Limit { get; init; } = 100;
|
||||
|
||||
/// <summary>Offset for paging.</summary>
|
||||
public int Offset { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// CrossDistroCoverageService.cs
|
||||
// Sprint: SPRINT_20260208_027_BinaryIndex_cross_distro_golden_set_for_backport_validation
|
||||
// Task: T1 — Cross-distro coverage matrix service implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.Metrics;
|
||||
|
||||
namespace StellaOps.BinaryIndex.GoldenSet;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory implementation of the cross-distro coverage matrix.
|
||||
/// Manages curated CVE entries with per-distro backport validation status.
|
||||
/// </summary>
|
||||
public sealed class CrossDistroCoverageService : ICrossDistroCoverageService
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, CuratedCveEntry> _entries = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private readonly Counter<long> _upsertCounter;
|
||||
private readonly Counter<long> _queryCounter;
|
||||
private readonly Counter<long> _seedCounter;
|
||||
private readonly Counter<long> _validatedCounter;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new cross-distro coverage service with OTel instrumentation.
|
||||
/// </summary>
|
||||
public CrossDistroCoverageService(IMeterFactory meterFactory, TimeProvider? timeProvider = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(meterFactory);
|
||||
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
|
||||
var meter = meterFactory.Create("StellaOps.BinaryIndex.GoldenSet.CrossDistro");
|
||||
_upsertCounter = meter.CreateCounter<long>("crossdistro.upsert.total", description: "CVE entries upserted");
|
||||
_queryCounter = meter.CreateCounter<long>("crossdistro.query.total", description: "Coverage queries executed");
|
||||
_seedCounter = meter.CreateCounter<long>("crossdistro.seed.total", description: "Built-in entries seeded");
|
||||
_validatedCounter = meter.CreateCounter<long>("crossdistro.validated.total", description: "Entries marked as validated");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<CuratedCveEntry> UpsertAsync(CuratedCveEntry entry, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(entry);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(entry.CveId);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
var updated = entry with { UpdatedAt = now };
|
||||
_entries[entry.CveId] = updated;
|
||||
|
||||
_upsertCounter.Add(1);
|
||||
return Task.FromResult(updated);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<CuratedCveEntry?> GetByCveIdAsync(string cveId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(cveId);
|
||||
|
||||
_entries.TryGetValue(cveId, out var entry);
|
||||
return Task.FromResult(entry);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<ImmutableArray<CuratedCveEntry>> QueryAsync(CuratedCveQuery query, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(query);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
_queryCounter.Add(1);
|
||||
|
||||
IEnumerable<CuratedCveEntry> results = _entries.Values;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(query.Component))
|
||||
{
|
||||
results = results.Where(e =>
|
||||
e.Component.Contains(query.Component, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (query.Distro is { } distro)
|
||||
{
|
||||
results = results.Where(e =>
|
||||
!e.Coverage.IsDefaultOrEmpty &&
|
||||
e.Coverage.Any(c => c.Distro == distro));
|
||||
}
|
||||
|
||||
if (query.Status is { } status)
|
||||
{
|
||||
results = results.Where(e =>
|
||||
!e.Coverage.IsDefaultOrEmpty &&
|
||||
e.Coverage.Any(c => c.Status == status));
|
||||
}
|
||||
|
||||
if (query.OnlyUnvalidated)
|
||||
{
|
||||
results = results.Where(e =>
|
||||
!e.Coverage.IsDefaultOrEmpty &&
|
||||
e.Coverage.Any(c => !c.Validated));
|
||||
}
|
||||
|
||||
var ordered = results
|
||||
.OrderBy(e => e.CveId, StringComparer.OrdinalIgnoreCase)
|
||||
.Skip(query.Offset)
|
||||
.Take(query.Limit)
|
||||
.ToImmutableArray();
|
||||
|
||||
return Task.FromResult(ordered);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<CrossDistroCoverageSummary> GetSummaryAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var allEntries = _entries.Values.ToList();
|
||||
var allCoverage = allEntries
|
||||
.Where(e => !e.Coverage.IsDefaultOrEmpty)
|
||||
.SelectMany(e => e.Coverage)
|
||||
.ToList();
|
||||
|
||||
var byDistro = new Dictionary<DistroFamily, DistroBreakdown>();
|
||||
foreach (var distro in Enum.GetValues<DistroFamily>())
|
||||
{
|
||||
var distroEntries = allCoverage.Where(c => c.Distro == distro).ToList();
|
||||
byDistro[distro] = new DistroBreakdown
|
||||
{
|
||||
EntryCount = distroEntries.Count,
|
||||
ValidatedCount = distroEntries.Count(c => c.Validated),
|
||||
BackportedCount = distroEntries.Count(c => c.Status == BackportStatus.Backported)
|
||||
};
|
||||
}
|
||||
|
||||
var summary = new CrossDistroCoverageSummary
|
||||
{
|
||||
TotalCves = allEntries.Count,
|
||||
TotalEntries = allCoverage.Count,
|
||||
ValidatedEntries = allCoverage.Count(c => c.Validated),
|
||||
BackportedCount = allCoverage.Count(c => c.Status == BackportStatus.Backported),
|
||||
NotPatchedCount = allCoverage.Count(c => c.Status == BackportStatus.NotPatched),
|
||||
ByDistro = byDistro.ToImmutableDictionary()
|
||||
};
|
||||
|
||||
return Task.FromResult(summary);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<bool> SetValidatedAsync(
|
||||
string cveId,
|
||||
DistroFamily distro,
|
||||
string version,
|
||||
bool validated,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(cveId);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(version);
|
||||
|
||||
if (!_entries.TryGetValue(cveId, out var entry))
|
||||
return Task.FromResult(false);
|
||||
|
||||
if (entry.Coverage.IsDefaultOrEmpty)
|
||||
return Task.FromResult(false);
|
||||
|
||||
var index = -1;
|
||||
for (var i = 0; i < entry.Coverage.Length; i++)
|
||||
{
|
||||
var candidate = entry.Coverage[i];
|
||||
if (candidate.Distro == distro &&
|
||||
candidate.Version.Equals(version, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
return Task.FromResult(false);
|
||||
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
var updated = entry.Coverage[index] with
|
||||
{
|
||||
Validated = validated,
|
||||
ValidatedAt = validated ? now : null
|
||||
};
|
||||
|
||||
var newCoverage = entry.Coverage.SetItem(index, updated);
|
||||
_entries[cveId] = entry with { Coverage = newCoverage, UpdatedAt = now };
|
||||
|
||||
_validatedCounter.Add(1);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<int> SeedBuiltInEntriesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
var seeded = 0;
|
||||
|
||||
foreach (var entry in CreateBuiltInEntries(now))
|
||||
{
|
||||
if (_entries.TryAdd(entry.CveId, entry))
|
||||
seeded++;
|
||||
}
|
||||
|
||||
_seedCounter.Add(seeded);
|
||||
return Task.FromResult(seeded);
|
||||
}
|
||||
|
||||
// ── Built-in curated CVEs for cross-distro backport validation ─────
|
||||
|
||||
internal static ImmutableArray<CuratedCveEntry> CreateBuiltInEntries(DateTimeOffset createdAt)
|
||||
{
|
||||
return
|
||||
[
|
||||
// OpenSSL Heartbleed — buffer over-read in TLS heartbeat extension
|
||||
new CuratedCveEntry
|
||||
{
|
||||
CveId = "CVE-2014-0160",
|
||||
Component = "openssl",
|
||||
CommonName = "Heartbleed",
|
||||
CvssScore = 7.5,
|
||||
CweIds = ["CWE-126"],
|
||||
GoldenSetId = "CVE-2014-0160",
|
||||
CreatedAt = createdAt,
|
||||
Coverage =
|
||||
[
|
||||
Entry(DistroFamily.Alpine, "3.9", "openssl", "1.0.2k-r0", BackportStatus.NotPatched),
|
||||
Entry(DistroFamily.Alpine, "3.18", "openssl", "3.1.1-r0", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "wheezy", "openssl", "1.0.1e-2+deb7u7", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "bookworm", "openssl", "3.0.11-1~deb12u1", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "6", "openssl", "1.0.1e-16.el6_5.7", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "9", "openssl", "3.0.7-17.el9", BackportStatus.Backported),
|
||||
]
|
||||
},
|
||||
|
||||
// sudo Baron Samedit — heap-based buffer overflow in sudoers parsing
|
||||
new CuratedCveEntry
|
||||
{
|
||||
CveId = "CVE-2021-3156",
|
||||
Component = "sudo",
|
||||
CommonName = "Baron Samedit",
|
||||
CvssScore = 7.8,
|
||||
CweIds = ["CWE-122"],
|
||||
GoldenSetId = "CVE-2021-3156",
|
||||
CreatedAt = createdAt,
|
||||
Coverage =
|
||||
[
|
||||
Entry(DistroFamily.Alpine, "3.12", "sudo", "1.9.5p2-r0", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Alpine, "3.18", "sudo", "1.9.13p3-r0", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "buster", "sudo", "1.8.27-1+deb10u3", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "bookworm", "sudo", "1.9.13p3-1+deb12u1", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "7", "sudo", "1.8.23-10.el7_9.3", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "9", "sudo", "1.9.5p2-9.el9", BackportStatus.Backported),
|
||||
]
|
||||
},
|
||||
|
||||
// glibc — stack buffer overflow in __nss_hostname_digits_dots
|
||||
new CuratedCveEntry
|
||||
{
|
||||
CveId = "CVE-2015-0235",
|
||||
Component = "glibc",
|
||||
CommonName = "GHOST",
|
||||
CvssScore = 10.0,
|
||||
CweIds = ["CWE-787"],
|
||||
GoldenSetId = "CVE-2015-0235",
|
||||
CreatedAt = createdAt,
|
||||
Coverage =
|
||||
[
|
||||
Entry(DistroFamily.Alpine, "3.18", "musl", "1.2.4-r0", BackportStatus.NotApplicable),
|
||||
Entry(DistroFamily.Debian, "wheezy", "eglibc", "2.13-38+deb7u8", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "bookworm", "glibc", "2.36-9+deb12u3", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "6", "glibc", "2.12-1.149.el6_6.5", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "9", "glibc", "2.34-60.el9", BackportStatus.Backported),
|
||||
]
|
||||
},
|
||||
|
||||
// curl — SOCKS5 heap-based buffer overflow
|
||||
new CuratedCveEntry
|
||||
{
|
||||
CveId = "CVE-2023-38545",
|
||||
Component = "curl",
|
||||
CommonName = "SOCKS5 heap overflow",
|
||||
CvssScore = 9.8,
|
||||
CweIds = ["CWE-787"],
|
||||
GoldenSetId = "CVE-2023-38545",
|
||||
CreatedAt = createdAt,
|
||||
Coverage =
|
||||
[
|
||||
Entry(DistroFamily.Alpine, "3.18", "curl", "8.4.0-r0", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "bookworm", "curl", "7.88.1-10+deb12u4", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "bullseye", "curl", "7.74.0-1.3+deb11u10", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "8", "curl", "7.61.1-30.el8_8.4", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "9", "curl", "8.0.1-1.el9", BackportStatus.Backported),
|
||||
]
|
||||
},
|
||||
|
||||
// OpenSSH — regreSSHion (signal handler race condition)
|
||||
new CuratedCveEntry
|
||||
{
|
||||
CveId = "CVE-2024-6387",
|
||||
Component = "openssh",
|
||||
CommonName = "regreSSHion",
|
||||
CvssScore = 8.1,
|
||||
CweIds = ["CWE-362"],
|
||||
GoldenSetId = "CVE-2024-6387",
|
||||
CreatedAt = createdAt,
|
||||
Coverage =
|
||||
[
|
||||
Entry(DistroFamily.Alpine, "3.18", "openssh", "9.3_p2-r0", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Alpine, "3.20", "openssh", "9.7_p1-r4", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Debian, "bookworm", "openssh", "1:9.2p1-2+deb12u3", BackportStatus.Backported),
|
||||
Entry(DistroFamily.Rhel, "8", "openssh", "8.0p1-19.el8_8", BackportStatus.NotPatched),
|
||||
Entry(DistroFamily.Rhel, "9", "openssh", "8.7p1-38.el9_4.1", BackportStatus.Backported),
|
||||
]
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private static DistroCoverageEntry Entry(
|
||||
DistroFamily distro,
|
||||
string version,
|
||||
string package,
|
||||
string packageVersion,
|
||||
BackportStatus status) => new()
|
||||
{
|
||||
Distro = distro,
|
||||
Version = version,
|
||||
PackageName = package,
|
||||
PackageVersion = packageVersion,
|
||||
Status = status
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// ICrossDistroCoverageService.cs
|
||||
// Sprint: SPRINT_20260208_027_BinaryIndex_cross_distro_golden_set_for_backport_validation
|
||||
// Task: T1 — Interface for cross-distro coverage matrix management
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace StellaOps.BinaryIndex.GoldenSet;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the cross-distro coverage matrix for curated CVEs,
|
||||
/// enabling backport validation across Alpine, Debian, and RHEL.
|
||||
/// </summary>
|
||||
public interface ICrossDistroCoverageService
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds or updates a curated CVE entry with its cross-distro coverage data.
|
||||
/// </summary>
|
||||
Task<CuratedCveEntry> UpsertAsync(CuratedCveEntry entry, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a curated CVE entry by its CVE ID.
|
||||
/// </summary>
|
||||
Task<CuratedCveEntry?> GetByCveIdAsync(string cveId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Queries curated CVE entries with filtering.
|
||||
/// </summary>
|
||||
Task<ImmutableArray<CuratedCveEntry>> QueryAsync(CuratedCveQuery query, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Computes a summary of cross-distro coverage across all curated CVEs.
|
||||
/// </summary>
|
||||
Task<CrossDistroCoverageSummary> GetSummaryAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Marks a specific distro coverage entry as validated (or not).
|
||||
/// </summary>
|
||||
Task<bool> SetValidatedAsync(
|
||||
string cveId,
|
||||
DistroFamily distro,
|
||||
string version,
|
||||
bool validated,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Seeds the coverage matrix with built-in high-impact CVE entries.
|
||||
/// Idempotent: only adds entries that don't already exist.
|
||||
/// </summary>
|
||||
Task<int> SeedBuiltInEntriesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user