Restructure solution layout by module

This commit is contained in:
master
2025-10-28 15:10:40 +02:00
parent 95daa159c4
commit d870da18ce
4103 changed files with 192899 additions and 187024 deletions

View File

@@ -0,0 +1,188 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using StellaOps.Concelier.Connector.CertBund.Configuration;
using StellaOps.Concelier.Connector.Common.Http;
using StellaOps.Concelier.Connector.Common;
using StellaOps.Concelier.Connector.Common.Fetch;
using StellaOps.Concelier.Connector.Common.Testing;
using StellaOps.Concelier.Storage.Mongo;
using StellaOps.Concelier.Storage.Mongo.Advisories;
using StellaOps.Concelier.Storage.Mongo.Documents;
using StellaOps.Concelier.Storage.Mongo.Dtos;
using StellaOps.Concelier.Testing;
using Xunit;
namespace StellaOps.Concelier.Connector.CertBund.Tests;
[Collection("mongo-fixture")]
public sealed class CertBundConnectorTests : IAsyncLifetime
{
private static readonly Uri FeedUri = new("https://test.local/content/public/securityAdvisory/rss");
private static readonly Uri PortalUri = new("https://test.local/portal/");
private static readonly Uri DetailUri = new("https://test.local/portal/api/securityadvisory?name=WID-SEC-2025-2264");
private readonly MongoIntegrationFixture _fixture;
private readonly CannedHttpMessageHandler _handler;
public CertBundConnectorTests(MongoIntegrationFixture fixture)
{
_fixture = fixture;
_handler = new CannedHttpMessageHandler();
}
[Fact]
public async Task FetchParseMap_ProducesCanonicalAdvisory()
{
await using var provider = await BuildServiceProviderAsync();
SeedResponses();
var connector = provider.GetRequiredService<CertBundConnector>();
await connector.FetchAsync(provider, CancellationToken.None);
await connector.ParseAsync(provider, CancellationToken.None);
await connector.MapAsync(provider, CancellationToken.None);
var advisoryStore = provider.GetRequiredService<IAdvisoryStore>();
var advisories = await advisoryStore.GetRecentAsync(5, CancellationToken.None);
advisories.Should().HaveCount(1);
var advisory = advisories[0];
advisory.AdvisoryKey.Should().Be("WID-SEC-2025-2264");
advisory.Aliases.Should().Contain("CVE-2025-1234");
advisory.AffectedPackages.Should().Contain(package => package.Identifier.Contains("Ivanti"));
advisory.References.Should().Contain(reference => reference.Url == DetailUri.ToString());
advisory.Language.Should().Be("de");
var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(CertBundConnectorPlugin.SourceName, CancellationToken.None);
state.Should().NotBeNull();
state!.Cursor.Should().NotBeNull();
state.Cursor.TryGetValue("pendingDocuments", out var pendingDocs).Should().BeTrue();
pendingDocs!.AsBsonArray.Should().BeEmpty();
state.Cursor.TryGetValue("pendingMappings", out var pendingMappings).Should().BeTrue();
pendingMappings!.AsBsonArray.Should().BeEmpty();
}
[Fact]
public async Task Fetch_PersistsDocumentWithMetadata()
{
await using var provider = await BuildServiceProviderAsync();
SeedResponses();
var connector = provider.GetRequiredService<CertBundConnector>();
await connector.FetchAsync(provider, CancellationToken.None);
var documentStore = provider.GetRequiredService<IDocumentStore>();
var document = await documentStore.FindBySourceAndUriAsync(CertBundConnectorPlugin.SourceName, DetailUri.ToString(), CancellationToken.None);
document.Should().NotBeNull();
document!.Metadata.Should().ContainKey("certbund.advisoryId").WhoseValue.Should().Be("WID-SEC-2025-2264");
document.Metadata.Should().ContainKey("certbund.category");
document.Metadata.Should().ContainKey("certbund.published");
document.Status.Should().Be(DocumentStatuses.PendingParse);
var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(CertBundConnectorPlugin.SourceName, CancellationToken.None);
state.Should().NotBeNull();
state!.Cursor.Should().NotBeNull();
state.Cursor.TryGetValue("pendingDocuments", out var pendingDocs).Should().BeTrue();
pendingDocs!.AsBsonArray.Should().HaveCount(1);
}
private async Task<ServiceProvider> BuildServiceProviderAsync()
{
await _fixture.Client.DropDatabaseAsync(_fixture.Database.DatabaseNamespace.DatabaseName);
_handler.Clear();
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
services.AddSingleton(_handler);
services.AddMongoStorage(options =>
{
options.ConnectionString = _fixture.Runner.ConnectionString;
options.DatabaseName = _fixture.Database.DatabaseNamespace.DatabaseName;
options.CommandTimeout = TimeSpan.FromSeconds(5);
});
services.AddSourceCommon();
services.AddCertBundConnector(options =>
{
options.FeedUri = FeedUri;
options.PortalBootstrapUri = PortalUri;
options.DetailApiUri = new Uri("https://test.local/portal/api/securityadvisory");
options.RequestDelay = TimeSpan.Zero;
options.MaxAdvisoriesPerFetch = 10;
options.MaxKnownAdvisories = 32;
});
services.Configure<HttpClientFactoryOptions>(CertBundOptions.HttpClientName, builderOptions =>
{
builderOptions.HttpMessageHandlerBuilderActions.Add(builder =>
{
builder.PrimaryHandler = _handler;
});
});
var provider = services.BuildServiceProvider();
var bootstrapper = provider.GetRequiredService<MongoBootstrapper>();
await bootstrapper.InitializeAsync(CancellationToken.None);
return provider;
}
private void SeedResponses()
{
AddJsonResponse(DetailUri, ReadFixture("certbund-detail.json"));
AddXmlResponse(FeedUri, ReadFixture("certbund-feed.xml"), "application/rss+xml");
AddHtmlResponse(PortalUri, "<html><body>OK</body></html>");
}
private void AddJsonResponse(Uri uri, string json, string? etag = null)
{
_handler.AddResponse(uri, () =>
{
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent(json, Encoding.UTF8, "application/json"),
};
if (!string.IsNullOrWhiteSpace(etag))
{
response.Headers.ETag = new EntityTagHeaderValue(etag);
}
return response;
});
}
private void AddXmlResponse(Uri uri, string xml, string contentType)
{
_handler.AddResponse(uri, () => new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent(xml, Encoding.UTF8, contentType),
});
}
private void AddHtmlResponse(Uri uri, string html)
{
_handler.AddResponse(uri, () => new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent(html, Encoding.UTF8, "text/html"),
});
}
private static string ReadFixture(string fileName)
=> System.IO.File.ReadAllText(System.IO.Path.Combine(AppContext.BaseDirectory, "Fixtures", fileName));
public Task InitializeAsync() => Task.CompletedTask;
public Task DisposeAsync() => Task.CompletedTask;
}

View File

@@ -0,0 +1,36 @@
{
"name": "WID-SEC-2025-2264",
"title": "Ivanti Endpoint Manager: Mehrere Schwachstellen ermöglichen Codeausführung",
"summary": "Ein entfernter, anonymer Angreifer kann mehrere Schwachstellen in Ivanti Endpoint Manager ausnutzen.",
"description": "<p>Ivanti Endpoint Manager weist mehrere Schwachstellen auf.</p><p>Ein Angreifer kann beliebigen Code ausführen.</p>",
"severity": "hoch",
"language": "de",
"published": "2025-10-14T06:24:49Z",
"updated": "2025-10-14T07:00:00Z",
"cveIds": [
"CVE-2025-1234",
"CVE-2025-5678"
],
"references": [
{
"url": "https://example.com/vendor/advisory",
"label": "Vendor Advisory"
},
{
"url": "https://example.com/mitre",
"label": "MITRE"
}
],
"products": [
{
"vendor": "Ivanti",
"name": "Endpoint Manager",
"versions": "2023.1 bis 2024.2"
},
{
"vendor": "Ivanti",
"name": "Endpoint Manager Cloud",
"versions": "alle"
}
]
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>BSI Warn- und Informationsdienst</title>
<link>https://wid.cert-bund.de/portal/wid/securityadvisory</link>
<description>Test feed</description>
<pubDate>Tue, 14 Oct 2025 07:06:21 GMT</pubDate>
<item>
<title>[hoch] Ivanti Endpoint Manager: Mehrere Schwachstellen ermöglichen Codeausführung</title>
<link>https://wid.cert-bund.de/portal/wid/securityadvisory?name=WID-SEC-2025-2264</link>
<category>hoch</category>
<pubDate>Tue, 14 Oct 2025 06:24:49 GMT</pubDate>
</item>
</channel>
</rss>

View File

@@ -0,0 +1,23 @@
<?xml version='1.0' encoding='utf-8'?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.CertBund/StellaOps.Concelier.Connector.CertBund.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
</ItemGroup>
<ItemGroup>
<None Update="Fixtures\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fixtures\*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>