This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Feedser.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Canonical affected package descriptor with deterministic ordering of ranges and provenance.
|
||||
/// </summary>
|
||||
public sealed record AffectedPackage
|
||||
{
|
||||
public static AffectedPackage Empty { get; } = new(
|
||||
AffectedPackageTypes.SemVer,
|
||||
identifier: "unknown",
|
||||
platform: null,
|
||||
versionRanges: Array.Empty<AffectedVersionRange>(),
|
||||
statuses: Array.Empty<AffectedPackageStatus>(),
|
||||
provenance: Array.Empty<AdvisoryProvenance>());
|
||||
|
||||
[JsonConstructor]
|
||||
public AffectedPackage(
|
||||
string type,
|
||||
string identifier,
|
||||
string? platform = null,
|
||||
IEnumerable<AffectedVersionRange>? versionRanges = null,
|
||||
IEnumerable<AffectedPackageStatus>? statuses = null,
|
||||
IEnumerable<AdvisoryProvenance>? provenance = null)
|
||||
{
|
||||
Type = Validation.EnsureNotNullOrWhiteSpace(type, nameof(type)).ToLowerInvariant();
|
||||
Identifier = Validation.EnsureNotNullOrWhiteSpace(identifier, nameof(identifier));
|
||||
Platform = Validation.TrimToNull(platform);
|
||||
|
||||
VersionRanges = (versionRanges ?? Array.Empty<AffectedVersionRange>())
|
||||
.Distinct(AffectedVersionRangeEqualityComparer.Instance)
|
||||
.OrderBy(static range => range, AffectedVersionRangeComparer.Instance)
|
||||
.ToImmutableArray();
|
||||
|
||||
Statuses = (statuses ?? Array.Empty<AffectedPackageStatus>())
|
||||
.Where(static status => status is not null)
|
||||
.Distinct(AffectedPackageStatusEqualityComparer.Instance)
|
||||
.OrderBy(static status => status.Status, StringComparer.Ordinal)
|
||||
.ThenBy(static status => status.Provenance.Source, StringComparer.Ordinal)
|
||||
.ThenBy(static status => status.Provenance.Kind, StringComparer.Ordinal)
|
||||
.ThenBy(static status => status.Provenance.RecordedAt)
|
||||
.ToImmutableArray();
|
||||
|
||||
Provenance = (provenance ?? Array.Empty<AdvisoryProvenance>())
|
||||
.Where(static p => p is not null)
|
||||
.OrderBy(static p => p.Source, StringComparer.Ordinal)
|
||||
.ThenBy(static p => p.Kind, StringComparer.Ordinal)
|
||||
.ThenBy(static p => p.RecordedAt)
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Semantic type of the coordinates (rpm, deb, cpe, semver, vendor, ics-vendor).
|
||||
/// </summary>
|
||||
public string Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Canonical identifier for the package (NEVRA, PackageURL, CPE string, vendor slug, etc.).
|
||||
/// </summary>
|
||||
public string Identifier { get; }
|
||||
|
||||
public string? Platform { get; }
|
||||
|
||||
public ImmutableArray<AffectedVersionRange> VersionRanges { get; }
|
||||
|
||||
public ImmutableArray<AffectedPackageStatus> Statuses { get; }
|
||||
|
||||
public ImmutableArray<AdvisoryProvenance> Provenance { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Known values for <see cref="AffectedPackage.Type"/>.
|
||||
/// </summary>
|
||||
public static class AffectedPackageTypes
|
||||
{
|
||||
public const string Rpm = "rpm";
|
||||
public const string Deb = "deb";
|
||||
public const string Cpe = "cpe";
|
||||
public const string SemVer = "semver";
|
||||
public const string Vendor = "vendor";
|
||||
public const string IcsVendor = "ics-vendor";
|
||||
}
|
||||
Reference in New Issue
Block a user