up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-24 07:52:25 +02:00
parent 5970f0d9bd
commit 150b3730ef
215 changed files with 8119 additions and 740 deletions

View File

@@ -20,10 +20,14 @@ public sealed record AdvisoryLinkset(
public sealed record AdvisoryLinksetNormalized(
IReadOnlyList<string>? Purls,
IReadOnlyList<string>? Cpes,
IReadOnlyList<string>? Versions,
IReadOnlyList<Dictionary<string, object?>>? Ranges,
IReadOnlyList<Dictionary<string, object?>>? Severities)
{
public List<string>? CpesToList()
=> Cpes is null ? null : Cpes.ToList();
public List<BsonDocument>? RangesToBson()
=> Ranges is null ? null : Ranges.Select(BsonDocumentHelper.FromDictionary).ToList();

View File

@@ -12,7 +12,7 @@ internal static class AdvisoryLinksetNormalization
public static AdvisoryLinksetNormalized? FromRawLinkset(RawLinkset linkset)
{
ArgumentNullException.ThrowIfNull(linkset);
return Build(linkset.PackageUrls);
return Build(linkset.PackageUrls, linkset.Cpes);
}
public static AdvisoryLinksetNormalized? FromPurls(IEnumerable<string>? purls)
@@ -22,7 +22,7 @@ internal static class AdvisoryLinksetNormalization
return null;
}
return Build(purls);
return Build(purls, Enumerable.Empty<string>());
}
public static (AdvisoryLinksetNormalized? normalized, double? confidence, IReadOnlyList<AdvisoryLinksetConflict> conflicts) FromRawLinksetWithConfidence(
@@ -31,7 +31,7 @@ internal static class AdvisoryLinksetNormalization
{
ArgumentNullException.ThrowIfNull(linkset);
var normalized = Build(linkset.PackageUrls);
var normalized = Build(linkset.PackageUrls, linkset.Cpes);
var inputs = new[]
{
@@ -51,18 +51,19 @@ internal static class AdvisoryLinksetNormalization
return (normalized, coerced, conflicts);
}
private static AdvisoryLinksetNormalized? Build(IEnumerable<string> purlValues)
private static AdvisoryLinksetNormalized? Build(IEnumerable<string> purlValues, IEnumerable<string>? cpeValues)
{
var normalizedPurls = NormalizePurls(purlValues);
var normalizedCpes = NormalizeCpes(cpeValues);
var versions = ExtractVersions(normalizedPurls);
var ranges = BuildVersionRanges(normalizedPurls);
if (normalizedPurls.Count == 0 && versions.Count == 0 && ranges.Count == 0)
if (normalizedPurls.Count == 0 && normalizedCpes.Count == 0 && versions.Count == 0 && ranges.Count == 0)
{
return null;
}
return new AdvisoryLinksetNormalized(normalizedPurls, versions, ranges, null);
return new AdvisoryLinksetNormalized(normalizedPurls, normalizedCpes, versions, ranges, null);
}
private static List<string> NormalizePurls(IEnumerable<string> purls)
@@ -147,6 +148,31 @@ internal static class AdvisoryLinksetNormalization
return ranges;
}
private static List<string> NormalizeCpes(IEnumerable<string>? cpes)
{
if (cpes is null)
{
return new List<string>(capacity: 0);
}
var distinct = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var cpe in cpes)
{
var normalized = Validation.TrimToNull(cpe);
if (normalized is null)
{
continue;
}
if (LinksetNormalization.TryNormalizeCpe(normalized, out var canonical) && !string.IsNullOrEmpty(canonical))
{
distinct.Add(canonical);
}
}
return distinct.ToList();
}
private static bool LooksLikeRange(string value)
{
return value.IndexOfAny(new[] { '^', '~', '*', ' ', ',', '|', '>' , '<' }) >= 0 ||

View File

@@ -61,6 +61,11 @@ public sealed class AdvisoryLinksetNormalizedDocument
public List<string>? Purls { get; set; }
= new();
[BsonElement("cpes")]
[BsonIgnoreIfNull]
public List<string>? Cpes { get; set; }
= new();
[BsonElement("versions")]
[BsonIgnoreIfNull]
public List<string>? Versions { get; set; }

View File

@@ -125,6 +125,7 @@ internal sealed class ConcelierMongoLinksetStore : IMongoAdvisoryLinksetStore
Normalized = linkset.Normalized is null ? null : new AdvisoryLinksetNormalizedDocument
{
Purls = linkset.Normalized.Purls is null ? null : new List<string>(linkset.Normalized.Purls),
Cpes = linkset.Normalized.Cpes is null ? null : new List<string>(linkset.Normalized.Cpes),
Versions = linkset.Normalized.Versions is null ? null : new List<string>(linkset.Normalized.Versions),
Ranges = linkset.Normalized.RangesToBson(),
Severities = linkset.Normalized.SeveritiesToBson(),
@@ -141,6 +142,7 @@ internal sealed class ConcelierMongoLinksetStore : IMongoAdvisoryLinksetStore
doc.Observations.ToImmutableArray(),
doc.Normalized is null ? null : new CoreLinksets.AdvisoryLinksetNormalized(
doc.Normalized.Purls,
doc.Normalized.Cpes,
doc.Normalized.Versions,
doc.Normalized.Ranges?.Select(ToDictionary).ToList(),
doc.Normalized.Severities?.Select(ToDictionary).ToList()),

View File

@@ -214,6 +214,7 @@ internal sealed class EnsureLinkNotMergeCollectionsMigration : IMongoMigration
{ "properties", new BsonDocument
{
{ "purls", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "string") } } },
{ "cpes", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "string") } } },
{ "versions", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "string") } } },
{ "ranges", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "object") } } },
{ "severities", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "object") } } }