using System; using System.Globalization; using MongoDB.Bson; using StellaOps.Concelier.Models; using StellaOps.Concelier.Connector.CertCc.Internal; using StellaOps.Concelier.Storage.Mongo.Documents; using StellaOps.Concelier.Storage.Mongo.Dtos; using Xunit; namespace StellaOps.Concelier.Connector.CertCc.Tests.Internal; public sealed class CertCcMapperTests { private static readonly DateTimeOffset PublishedAt = DateTimeOffset.Parse("2025-10-03T11:35:31Z", CultureInfo.InvariantCulture); [Fact] public void Map_ProducesCanonicalAdvisoryWithVendorPrimitives() { const string vendorStatement = "The issue is confirmed, and here is the patch list\n\n" + "V3912/V3910/V2962/V1000B\t4.4.3.6/4.4.5.1\n" + "V2927/V2865/V2866\t4.5.1\n" + "V2765/V2766/V2763/V2135\t4.5.1"; var vendor = new CertCcVendorDto( "DrayTek Corporation", ContactDate: PublishedAt.AddDays(-10), StatementDate: PublishedAt.AddDays(-5), Updated: PublishedAt, Statement: vendorStatement, Addendum: null, References: new[] { "https://www.draytek.com/support/resources?type=version" }); var vendorStatus = new CertCcVendorStatusDto( Vendor: "DrayTek Corporation", CveId: "CVE-2025-10547", Status: "Affected", Statement: null, References: Array.Empty(), DateAdded: PublishedAt, DateUpdated: PublishedAt); var vulnerability = new CertCcVulnerabilityDto( CveId: "CVE-2025-10547", Description: null, DateAdded: PublishedAt, DateUpdated: PublishedAt); var metadata = new CertCcNoteMetadata( VuId: "VU#294418", IdNumber: "294418", Title: "Vigor routers running DrayOS RCE via EasyVPN", Overview: "Overview", Summary: "Summary", Published: PublishedAt, Updated: PublishedAt.AddMinutes(5), Created: PublishedAt, Revision: 2, CveIds: new[] { "CVE-2025-10547" }, PublicUrls: new[] { "https://www.draytek.com/about/security-advisory/use-of-uninitialized-variable-vulnerabilities/", "https://www.draytek.com/support/resources?type=version" }, PrimaryUrl: "https://www.kb.cert.org/vuls/id/294418/"); var dto = new CertCcNoteDto( metadata, Vendors: new[] { vendor }, VendorStatuses: new[] { vendorStatus }, Vulnerabilities: new[] { vulnerability }); var document = new DocumentRecord( Guid.NewGuid(), "cert-cc", "https://www.kb.cert.org/vuls/id/294418/", PublishedAt, Sha256: new string('0', 64), Status: "pending-map", ContentType: "application/json", Headers: null, Metadata: null, Etag: null, LastModified: PublishedAt, GridFsId: null); var dtoRecord = new DtoRecord( Id: Guid.NewGuid(), DocumentId: document.Id, SourceName: "cert-cc", SchemaVersion: "certcc.vince.note.v1", Payload: new BsonDocument(), ValidatedAt: PublishedAt.AddMinutes(1)); var advisory = CertCcMapper.Map(dto, document, dtoRecord, "cert-cc"); Assert.Equal("certcc/vu-294418", advisory.AdvisoryKey); Assert.Contains("VU#294418", advisory.Aliases); Assert.Contains("CVE-2025-10547", advisory.Aliases); Assert.Equal("en", advisory.Language); Assert.Equal(PublishedAt, advisory.Published); Assert.Contains(advisory.References, reference => reference.Url.Contains("/vuls/id/294418", StringComparison.OrdinalIgnoreCase)); var affected = Assert.Single(advisory.AffectedPackages); Assert.Equal("vendor", affected.Type); Assert.Equal("DrayTek Corporation", affected.Identifier); Assert.Contains(affected.Statuses, status => status.Status == AffectedPackageStatusCatalog.Affected); var range = Assert.Single(affected.VersionRanges); Assert.NotNull(range.Primitives); Assert.NotNull(range.Primitives!.VendorExtensions); Assert.Contains(range.Primitives.VendorExtensions!, kvp => kvp.Key == "certcc.vendor.patches"); Assert.NotEmpty(affected.NormalizedVersions); Assert.Contains(affected.NormalizedVersions, rule => rule.Scheme == "certcc.vendor" && rule.Value == "4.5.1"); } }