using System; using System.Collections.Generic; using System.Linq; using StellaOps.Concelier.Models; using StellaOps.Concelier.Storage.Mongo.Documents; namespace StellaOps.Concelier.Connector.Cccs.Internal; internal static class CccsMapper { public static Advisory Map(CccsAdvisoryDto dto, DocumentRecord document, DateTimeOffset recordedAt) { ArgumentNullException.ThrowIfNull(dto); ArgumentNullException.ThrowIfNull(document); var aliases = BuildAliases(dto); var references = BuildReferences(dto, recordedAt); var packages = BuildPackages(dto, recordedAt); var provenance = new[] { new AdvisoryProvenance( CccsConnectorPlugin.SourceName, "advisory", dto.AlertType ?? dto.SerialNumber, recordedAt, new[] { ProvenanceFieldMasks.Advisory }) }; return new Advisory( advisoryKey: dto.SerialNumber, title: dto.Title, summary: dto.Summary, language: dto.Language, published: dto.Published ?? dto.Modified, modified: dto.Modified ?? dto.Published, severity: null, exploitKnown: false, aliases: aliases, references: references, affectedPackages: packages, cvssMetrics: Array.Empty(), provenance: provenance); } private static IReadOnlyList BuildAliases(CccsAdvisoryDto dto) { var aliases = new List(capacity: 4) { dto.SerialNumber, }; if (!string.IsNullOrWhiteSpace(dto.SourceId) && !string.Equals(dto.SourceId, dto.SerialNumber, StringComparison.OrdinalIgnoreCase)) { aliases.Add(dto.SourceId); } foreach (var cve in dto.CveIds) { if (!string.IsNullOrWhiteSpace(cve)) { aliases.Add(cve); } } return aliases .Where(static alias => !string.IsNullOrWhiteSpace(alias)) .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(static alias => alias, StringComparer.OrdinalIgnoreCase) .ToArray(); } private static IReadOnlyList BuildReferences(CccsAdvisoryDto dto, DateTimeOffset recordedAt) { var references = new List { new(dto.CanonicalUrl, "details", "cccs", null, new AdvisoryProvenance( CccsConnectorPlugin.SourceName, "reference", dto.CanonicalUrl, recordedAt, new[] { ProvenanceFieldMasks.References })) }; foreach (var reference in dto.References) { if (string.IsNullOrWhiteSpace(reference.Url)) { continue; } references.Add(new AdvisoryReference( reference.Url, "reference", "cccs", reference.Label, new AdvisoryProvenance( CccsConnectorPlugin.SourceName, "reference", reference.Url, recordedAt, new[] { ProvenanceFieldMasks.References }))); } return references .DistinctBy(static reference => reference.Url, StringComparer.Ordinal) .OrderBy(static reference => reference.Url, StringComparer.Ordinal) .ToArray(); } private static IReadOnlyList BuildPackages(CccsAdvisoryDto dto, DateTimeOffset recordedAt) { if (dto.Products.Count == 0) { return Array.Empty(); } var packages = new List(dto.Products.Count); foreach (var product in dto.Products) { if (string.IsNullOrWhiteSpace(product)) { continue; } var identifier = product.Trim(); var provenance = new AdvisoryProvenance( CccsConnectorPlugin.SourceName, "package", identifier, recordedAt, new[] { ProvenanceFieldMasks.AffectedPackages }); packages.Add(new AffectedPackage( AffectedPackageTypes.Vendor, identifier, platform: null, versionRanges: Array.Empty(), statuses: Array.Empty(), provenance: new[] { provenance }, normalizedVersions: Array.Empty())); } return packages.Count == 0 ? Array.Empty() : packages .DistinctBy(static package => package.Identifier, StringComparer.OrdinalIgnoreCase) .OrderBy(static package => package.Identifier, StringComparer.OrdinalIgnoreCase) .ToArray(); } }