Rename Concelier Source modules to Connector

This commit is contained in:
master
2025-10-18 20:11:18 +03:00
parent 89ede53cc3
commit 052da7a7d0
789 changed files with 1489 additions and 1489 deletions

View File

@@ -0,0 +1,118 @@
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<string>(),
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");
}
}

View File

@@ -0,0 +1,58 @@
using System.Text;
using System.Text.Json;
using StellaOps.Concelier.Connector.CertCc.Internal;
using Xunit;
namespace StellaOps.Concelier.Connector.CertCc.Tests.Internal;
public sealed class CertCcSummaryParserTests
{
[Fact]
public void ParseNotes_ReturnsTokens_FromStringArray()
{
var payload = Encoding.UTF8.GetBytes("{\"notes\":[\"VU#123456\",\"VU#654321\"]}");
var notes = CertCcSummaryParser.ParseNotes(payload);
Assert.Equal(new[] { "VU#123456", "VU#654321" }, notes);
}
[Fact]
public void ParseNotes_DeduplicatesTokens_IgnoringCaseAndWhitespace()
{
var payload = Encoding.UTF8.GetBytes("{\"notes\":[\"VU#123456\",\"vu#123456\",\" 123456 \"]}");
var notes = CertCcSummaryParser.ParseNotes(payload);
Assert.Single(notes);
Assert.Equal("VU#123456", notes[0], ignoreCase: true);
}
[Fact]
public void ParseNotes_ReadsTokens_FromObjectEntries()
{
var payload = Encoding.UTF8.GetBytes("{\"notes\":[{\"id\":\"VU#294418\"},{\"idnumber\":\"257161\"}]}");
var notes = CertCcSummaryParser.ParseNotes(payload);
Assert.Equal(new[] { "VU#294418", "257161" }, notes);
}
[Fact]
public void ParseNotes_SupportsArrayRoot()
{
var payload = Encoding.UTF8.GetBytes("[\"VU#360686\",\"VU#760160\"]");
var notes = CertCcSummaryParser.ParseNotes(payload);
Assert.Equal(new[] { "VU#360686", "VU#760160" }, notes);
}
[Fact]
public void ParseNotes_InvalidStructure_Throws()
{
var payload = Encoding.UTF8.GetBytes("\"invalid\"");
Assert.Throws<JsonException>(() => CertCcSummaryParser.ParseNotes(payload));
}
}

View File

@@ -0,0 +1,95 @@
using System;
using System.Linq;
using Microsoft.Extensions.Options;
using StellaOps.Concelier.Connector.CertCc.Configuration;
using StellaOps.Concelier.Connector.CertCc.Internal;
using StellaOps.Concelier.Connector.Common.Cursors;
using Xunit;
namespace StellaOps.Concelier.Connector.CertCc.Tests.Internal;
public sealed class CertCcSummaryPlannerTests
{
[Fact]
public void CreatePlan_UsesInitialBackfillWindow()
{
var options = Options.Create(new CertCcOptions
{
SummaryWindow = new TimeWindowCursorOptions
{
WindowSize = TimeSpan.FromDays(30),
Overlap = TimeSpan.FromDays(3),
InitialBackfill = TimeSpan.FromDays(120),
MinimumWindowSize = TimeSpan.FromDays(1),
},
});
var timeProvider = new TestTimeProvider(DateTimeOffset.Parse("2025-10-10T12:00:00Z"));
var planner = new CertCcSummaryPlanner(options, timeProvider);
var plan = planner.CreatePlan(state: null);
Assert.Equal(DateTimeOffset.Parse("2025-06-12T12:00:00Z"), plan.Window.Start);
Assert.Equal(DateTimeOffset.Parse("2025-07-12T12:00:00Z"), plan.Window.End);
Assert.Equal(3, plan.Requests.Count);
var monthly = plan.Requests.Where(r => r.Scope == CertCcSummaryScope.Monthly).ToArray();
Assert.Collection(monthly,
request =>
{
Assert.Equal(2025, request.Year);
Assert.Equal(6, request.Month);
Assert.Equal("https://www.kb.cert.org/vuls/api/2025/06/summary/", request.Uri.AbsoluteUri);
},
request =>
{
Assert.Equal(2025, request.Year);
Assert.Equal(7, request.Month);
Assert.Equal("https://www.kb.cert.org/vuls/api/2025/07/summary/", request.Uri.AbsoluteUri);
});
var yearly = plan.Requests.Where(r => r.Scope == CertCcSummaryScope.Yearly).ToArray();
Assert.Single(yearly);
Assert.Equal(2025, yearly[0].Year);
Assert.Null(yearly[0].Month);
Assert.Equal("https://www.kb.cert.org/vuls/api/2025/summary/", yearly[0].Uri.AbsoluteUri);
Assert.Equal(plan.Window.End, plan.NextState.LastWindowEnd);
}
[Fact]
public void CreatePlan_AdvancesWindowRespectingOverlap()
{
var options = Options.Create(new CertCcOptions
{
SummaryWindow = new TimeWindowCursorOptions
{
WindowSize = TimeSpan.FromDays(30),
Overlap = TimeSpan.FromDays(10),
InitialBackfill = TimeSpan.FromDays(90),
MinimumWindowSize = TimeSpan.FromDays(1),
},
});
var timeProvider = new TestTimeProvider(DateTimeOffset.Parse("2025-12-01T00:00:00Z"));
var planner = new CertCcSummaryPlanner(options, timeProvider);
var first = planner.CreatePlan(null);
var second = planner.CreatePlan(first.NextState);
Assert.True(second.Window.Start < second.Window.End);
Assert.Equal(first.Window.End - options.Value.SummaryWindow.Overlap, second.Window.Start);
}
private sealed class TestTimeProvider : TimeProvider
{
private DateTimeOffset _now;
public TestTimeProvider(DateTimeOffset now) => _now = now;
public override DateTimeOffset GetUtcNow() => _now;
public void Advance(TimeSpan delta) => _now = _now.Add(delta);
}
}

View File

@@ -0,0 +1,31 @@
using System.Linq;
using StellaOps.Concelier.Connector.CertCc.Internal;
using Xunit;
namespace StellaOps.Concelier.Connector.CertCc.Tests.Internal;
public sealed class CertCcVendorStatementParserTests
{
[Fact]
public void Parse_ReturnsPatchesForTabDelimitedList()
{
const string statement =
"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 patches = CertCcVendorStatementParser.Parse(statement);
Assert.Equal(11, patches.Count);
Assert.Contains(patches, patch => patch.Product == "V3912" && patch.Version == "4.4.3.6");
Assert.Contains(patches, patch => patch.Product == "V2962" && patch.Version == "4.4.5.1");
Assert.Equal(7, patches.Count(patch => patch.Version == "4.5.1"));
}
[Fact]
public void Parse_ReturnsEmptyWhenStatementMissing()
{
var patches = CertCcVendorStatementParser.Parse(null);
Assert.Empty(patches);
}
}