Resolve Concelier/Excititor merge conflicts
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Vndr.Vmware/StellaOps.Concelier.Connector.Vndr.Vmware.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Storage.Mongo/StellaOps.Concelier.Storage.Mongo.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Vmware/Fixtures/*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,275 @@
|
||||
[
|
||||
{
|
||||
"advisoryKey": "VMSA-2024-0001",
|
||||
"affectedPackages": [
|
||||
{
|
||||
"identifier": "VMware ESXi 7.0",
|
||||
"platform": null,
|
||||
"provenance": [
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "affected",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMware ESXi 7.0"
|
||||
}
|
||||
],
|
||||
"statuses": [],
|
||||
"type": "vendor",
|
||||
"versionRanges": [
|
||||
{
|
||||
"fixedVersion": "7.0u3f",
|
||||
"introducedVersion": "7.0",
|
||||
"lastAffectedVersion": null,
|
||||
"primitives": {
|
||||
"evr": null,
|
||||
"hasVendorExtensions": true,
|
||||
"nevra": null,
|
||||
"semVer": {
|
||||
"constraintExpression": null,
|
||||
"fixed": null,
|
||||
"fixedInclusive": false,
|
||||
"introduced": "7.0",
|
||||
"introducedInclusive": true,
|
||||
"lastAffected": null,
|
||||
"lastAffectedInclusive": false
|
||||
},
|
||||
"vendorExtensions": {
|
||||
"vmware.product": "VMware ESXi 7.0",
|
||||
"vmware.version.raw": "7.0",
|
||||
"vmware.fixedVersion.raw": "7.0u3f"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "range",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMware ESXi 7.0"
|
||||
},
|
||||
"rangeExpression": "7.0",
|
||||
"rangeKind": "vendor"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"identifier": "VMware vCenter Server 8.0",
|
||||
"platform": null,
|
||||
"provenance": [
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "affected",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMware vCenter Server 8.0"
|
||||
}
|
||||
],
|
||||
"statuses": [],
|
||||
"type": "vendor",
|
||||
"versionRanges": [
|
||||
{
|
||||
"fixedVersion": "8.0a",
|
||||
"introducedVersion": "8.0",
|
||||
"lastAffectedVersion": null,
|
||||
"primitives": {
|
||||
"evr": null,
|
||||
"hasVendorExtensions": true,
|
||||
"nevra": null,
|
||||
"semVer": {
|
||||
"constraintExpression": null,
|
||||
"fixed": null,
|
||||
"fixedInclusive": false,
|
||||
"introduced": "8.0",
|
||||
"introducedInclusive": true,
|
||||
"lastAffected": null,
|
||||
"lastAffectedInclusive": false
|
||||
},
|
||||
"vendorExtensions": {
|
||||
"vmware.product": "VMware vCenter Server 8.0",
|
||||
"vmware.version.raw": "8.0",
|
||||
"vmware.fixedVersion.raw": "8.0a"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "range",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMware vCenter Server 8.0"
|
||||
},
|
||||
"rangeExpression": "8.0",
|
||||
"rangeKind": "vendor"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
"CVE-2024-1000",
|
||||
"CVE-2024-1001",
|
||||
"VMSA-2024-0001"
|
||||
],
|
||||
"cvssMetrics": [],
|
||||
"exploitKnown": false,
|
||||
"language": "en",
|
||||
"modified": "2024-04-01T10:00:00+00:00",
|
||||
"provenance": [
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "document",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "https://vmware.example/api/vmsa/VMSA-2024-0001.json"
|
||||
},
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "mapping",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMSA-2024-0001"
|
||||
}
|
||||
],
|
||||
"published": "2024-04-01T10:00:00+00:00",
|
||||
"references": [
|
||||
{
|
||||
"kind": "kb",
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "reference",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "https://kb.vmware.example/90234"
|
||||
},
|
||||
"sourceTag": "kb",
|
||||
"summary": null,
|
||||
"url": "https://kb.vmware.example/90234"
|
||||
},
|
||||
{
|
||||
"kind": "advisory",
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "reference",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "https://www.vmware.com/security/advisories/VMSA-2024-0001.html"
|
||||
},
|
||||
"sourceTag": "advisory",
|
||||
"summary": null,
|
||||
"url": "https://www.vmware.com/security/advisories/VMSA-2024-0001.html"
|
||||
}
|
||||
],
|
||||
"severity": null,
|
||||
"summary": "Security updates for VMware ESXi 7.0 and vCenter Server 8.0 resolve multiple vulnerabilities.",
|
||||
"title": "VMware ESXi and vCenter Server updates address vulnerabilities"
|
||||
},
|
||||
{
|
||||
"advisoryKey": "VMSA-2024-0002",
|
||||
"affectedPackages": [
|
||||
{
|
||||
"identifier": "VMware Cloud Foundation 5.x",
|
||||
"platform": null,
|
||||
"provenance": [
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "affected",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMware Cloud Foundation 5.x"
|
||||
}
|
||||
],
|
||||
"statuses": [],
|
||||
"type": "vendor",
|
||||
"versionRanges": [
|
||||
{
|
||||
"fixedVersion": "5.1.1",
|
||||
"introducedVersion": "5.1",
|
||||
"lastAffectedVersion": null,
|
||||
"primitives": {
|
||||
"evr": null,
|
||||
"hasVendorExtensions": true,
|
||||
"nevra": null,
|
||||
"semVer": {
|
||||
"constraintExpression": null,
|
||||
"fixed": "5.1.1",
|
||||
"fixedInclusive": false,
|
||||
"introduced": "5.1",
|
||||
"introducedInclusive": true,
|
||||
"lastAffected": null,
|
||||
"lastAffectedInclusive": false
|
||||
},
|
||||
"vendorExtensions": {
|
||||
"vmware.product": "VMware Cloud Foundation 5.x",
|
||||
"vmware.version.raw": "5.1",
|
||||
"vmware.fixedVersion.raw": "5.1.1"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "range",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMware Cloud Foundation 5.x"
|
||||
},
|
||||
"rangeExpression": "5.1",
|
||||
"rangeKind": "vendor"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
"CVE-2024-2000",
|
||||
"VMSA-2024-0002"
|
||||
],
|
||||
"cvssMetrics": [],
|
||||
"exploitKnown": false,
|
||||
"language": "en",
|
||||
"modified": "2024-04-02T09:00:00+00:00",
|
||||
"provenance": [
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "document",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "https://vmware.example/api/vmsa/VMSA-2024-0002.json"
|
||||
},
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "mapping",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "VMSA-2024-0002"
|
||||
}
|
||||
],
|
||||
"published": "2024-04-02T09:00:00+00:00",
|
||||
"references": [
|
||||
{
|
||||
"kind": "kb",
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "reference",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "https://kb.vmware.example/91234"
|
||||
},
|
||||
"sourceTag": "kb",
|
||||
"summary": null,
|
||||
"url": "https://kb.vmware.example/91234"
|
||||
},
|
||||
{
|
||||
"kind": "advisory",
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "reference",
|
||||
"recordedAt": "2024-04-05T00:00:00+00:00",
|
||||
"source": "vmware",
|
||||
"value": "https://www.vmware.com/security/advisories/VMSA-2024-0002.html"
|
||||
},
|
||||
"sourceTag": "advisory",
|
||||
"summary": null,
|
||||
"url": "https://www.vmware.com/security/advisories/VMSA-2024-0002.html"
|
||||
}
|
||||
],
|
||||
"severity": null,
|
||||
"summary": "An update is available for VMware Cloud Foundation components to address a remote code execution vulnerability.",
|
||||
"title": "VMware Cloud Foundation remote code execution vulnerability"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"id": "VMSA-2024-0001",
|
||||
"title": "VMware ESXi and vCenter Server updates address vulnerabilities",
|
||||
"summary": "Security updates for VMware ESXi 7.0 and vCenter Server 8.0 resolve multiple vulnerabilities.",
|
||||
"published": "2024-04-01T10:00:00Z",
|
||||
"modified": "2024-04-01T10:00:00Z",
|
||||
"cves": [
|
||||
"CVE-2024-1000",
|
||||
"CVE-2024-1001"
|
||||
],
|
||||
"affected": [
|
||||
{
|
||||
"product": "VMware ESXi 7.0",
|
||||
"version": "7.0",
|
||||
"fixedVersion": "7.0u3f"
|
||||
},
|
||||
{
|
||||
"product": "VMware vCenter Server 8.0",
|
||||
"version": "8.0",
|
||||
"fixedVersion": "8.0a"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"type": "kb",
|
||||
"url": "https://kb.vmware.example/90234"
|
||||
},
|
||||
{
|
||||
"type": "advisory",
|
||||
"url": "https://www.vmware.com/security/advisories/VMSA-2024-0001.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"id": "VMSA-2024-0002",
|
||||
"title": "VMware Cloud Foundation remote code execution vulnerability",
|
||||
"summary": "An update is available for VMware Cloud Foundation components to address a remote code execution vulnerability.",
|
||||
"published": "2024-04-02T09:00:00Z",
|
||||
"modified": "2024-04-02T09:00:00Z",
|
||||
"cves": [
|
||||
"CVE-2024-2000"
|
||||
],
|
||||
"affected": [
|
||||
{
|
||||
"product": "VMware Cloud Foundation 5.x",
|
||||
"version": "5.1",
|
||||
"fixedVersion": "5.1.1"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"type": "kb",
|
||||
"url": "https://kb.vmware.example/91234"
|
||||
},
|
||||
{
|
||||
"type": "advisory",
|
||||
"url": "https://www.vmware.com/security/advisories/VMSA-2024-0002.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"id": "VMSA-2024-0003",
|
||||
"title": "VMware NSX-T advisory for input validation issue",
|
||||
"summary": "VMware NSX-T has resolved an input validation vulnerability impacting API endpoints.",
|
||||
"published": "2024-04-03T08:15:00Z",
|
||||
"modified": "2024-04-03T08:15:00Z",
|
||||
"cves": [
|
||||
"CVE-2024-3000"
|
||||
],
|
||||
"affected": [
|
||||
{
|
||||
"product": "VMware NSX-T 3.2",
|
||||
"version": "3.2",
|
||||
"fixedVersion": "3.2.3"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"type": "kb",
|
||||
"url": "https://kb.vmware.example/93456"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"id": "VMSA-2024-0001",
|
||||
"url": "https://vmware.example/api/vmsa/VMSA-2024-0001.json",
|
||||
"modified": "2024-04-01T10:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "VMSA-2024-0002",
|
||||
"url": "https://vmware.example/api/vmsa/VMSA-2024-0002.json",
|
||||
"modified": "2024-04-02T09:00:00Z"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"id": "VMSA-2024-0001",
|
||||
"url": "https://vmware.example/api/vmsa/VMSA-2024-0001.json",
|
||||
"modified": "2024-04-01T10:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "VMSA-2024-0002",
|
||||
"url": "https://vmware.example/api/vmsa/VMSA-2024-0002.json",
|
||||
"modified": "2024-04-02T09:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "VMSA-2024-0003",
|
||||
"url": "https://vmware.example/api/vmsa/VMSA-2024-0003.json",
|
||||
"modified": "2024-04-03T08:15:00Z"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
using StellaOps.Concelier.Connector.Common.Http;
|
||||
using StellaOps.Concelier.Connector.Common.Testing;
|
||||
using StellaOps.Concelier.Connector.Vndr.Vmware;
|
||||
using StellaOps.Concelier.Connector.Vndr.Vmware.Configuration;
|
||||
using StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
|
||||
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.Abstractions;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Vndr.Vmware.Tests.Vmware;
|
||||
|
||||
[Collection("mongo-fixture")]
|
||||
public sealed class VmwareConnectorTests : IAsyncLifetime
|
||||
{
|
||||
private readonly MongoIntegrationFixture _fixture;
|
||||
private readonly FakeTimeProvider _timeProvider;
|
||||
private readonly CannedHttpMessageHandler _handler;
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
private static readonly Uri IndexUri = new("https://vmware.example/api/vmsa/index.json");
|
||||
private static readonly Uri DetailOne = new("https://vmware.example/api/vmsa/VMSA-2024-0001.json");
|
||||
private static readonly Uri DetailTwo = new("https://vmware.example/api/vmsa/VMSA-2024-0002.json");
|
||||
private static readonly Uri DetailThree = new("https://vmware.example/api/vmsa/VMSA-2024-0003.json");
|
||||
|
||||
public VmwareConnectorTests(MongoIntegrationFixture fixture, ITestOutputHelper output)
|
||||
{
|
||||
_fixture = fixture;
|
||||
_timeProvider = new FakeTimeProvider(new DateTimeOffset(2024, 4, 5, 0, 0, 0, TimeSpan.Zero));
|
||||
_handler = new CannedHttpMessageHandler();
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FetchParseMap_ProducesSnapshotAndCoversResume()
|
||||
{
|
||||
await using var provider = await BuildServiceProviderAsync();
|
||||
SeedInitialResponses();
|
||||
|
||||
using var metrics = new VmwareMetricCollector();
|
||||
|
||||
var connector = provider.GetRequiredService<VmwareConnector>();
|
||||
|
||||
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(10, CancellationToken.None);
|
||||
var ordered = advisories.OrderBy(static a => a.AdvisoryKey, StringComparer.Ordinal).ToArray();
|
||||
|
||||
var snapshot = Normalize(SnapshotSerializer.ToSnapshot(ordered));
|
||||
var expected = Normalize(ReadFixture("vmware-advisories.snapshot.json"));
|
||||
if (!string.Equals(expected, snapshot, StringComparison.Ordinal))
|
||||
{
|
||||
var actualPath = Path.Combine(AppContext.BaseDirectory, "Vmware", "Fixtures", "vmware-advisories.actual.json");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(actualPath)!);
|
||||
File.WriteAllText(actualPath, snapshot);
|
||||
}
|
||||
|
||||
Assert.Equal(expected, snapshot);
|
||||
|
||||
var psirtCollection = _fixture.Database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.PsirtFlags);
|
||||
var psirtFlags = await psirtCollection.Find(Builders<BsonDocument>.Filter.Empty).ToListAsync();
|
||||
_output.WriteLine("PSIRT flags after initial map: " + string.Join(", ", psirtFlags.Select(flag => flag.GetValue("_id", BsonValue.Create("<missing>")).ToString())));
|
||||
Assert.Equal(2, psirtFlags.Count);
|
||||
Assert.All(psirtFlags, doc => Assert.Equal("VMware", doc["vendor"].AsString));
|
||||
|
||||
var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
|
||||
var state = await stateRepository.TryGetAsync(VmwareConnectorPlugin.SourceName, CancellationToken.None);
|
||||
Assert.NotNull(state);
|
||||
Assert.Empty(state!.Cursor.TryGetValue("pendingDocuments", out var pendingDocs) ? pendingDocs.AsBsonArray : new BsonArray());
|
||||
Assert.Empty(state.Cursor.TryGetValue("pendingMappings", out var pendingMaps) ? pendingMaps.AsBsonArray : new BsonArray());
|
||||
var cursorSnapshot = VmwareCursor.FromBson(state.Cursor);
|
||||
_output.WriteLine($"Initial fetch cache entries: {cursorSnapshot.FetchCache.Count}");
|
||||
foreach (var entry in cursorSnapshot.FetchCache)
|
||||
{
|
||||
_output.WriteLine($"Cache seed: {entry.Key} -> {entry.Value.Sha256}");
|
||||
}
|
||||
|
||||
// Second run with unchanged advisories and one new advisory.
|
||||
SeedUpdateResponses();
|
||||
_timeProvider.Advance(TimeSpan.FromHours(1));
|
||||
|
||||
await connector.FetchAsync(provider, CancellationToken.None);
|
||||
var documentStore = provider.GetRequiredService<IDocumentStore>();
|
||||
var resumeDocOne = await documentStore.FindBySourceAndUriAsync(VmwareConnectorPlugin.SourceName, DetailOne.ToString(), CancellationToken.None);
|
||||
var resumeDocTwo = await documentStore.FindBySourceAndUriAsync(VmwareConnectorPlugin.SourceName, DetailTwo.ToString(), CancellationToken.None);
|
||||
_output.WriteLine($"After resume fetch status: {resumeDocOne?.Status} ({resumeDocOne?.Sha256}), {resumeDocTwo?.Status} ({resumeDocTwo?.Sha256})");
|
||||
Assert.Equal(DocumentStatuses.Mapped, resumeDocOne?.Status);
|
||||
Assert.Equal(DocumentStatuses.Mapped, resumeDocTwo?.Status);
|
||||
await connector.ParseAsync(provider, CancellationToken.None);
|
||||
await connector.MapAsync(provider, CancellationToken.None);
|
||||
|
||||
advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
|
||||
Assert.Equal(3, advisories.Count);
|
||||
Assert.Contains(advisories, advisory => advisory.AdvisoryKey == "VMSA-2024-0003");
|
||||
|
||||
psirtFlags = await psirtCollection.Find(Builders<BsonDocument>.Filter.Empty).ToListAsync();
|
||||
_output.WriteLine("PSIRT flags after resume: " + string.Join(", ", psirtFlags.Select(flag => flag.GetValue("_id", BsonValue.Create("<missing>")).ToString())));
|
||||
Assert.Equal(3, psirtFlags.Count);
|
||||
Assert.Contains(psirtFlags, doc => doc["_id"] == "VMSA-2024-0003");
|
||||
|
||||
var measurements = metrics.Measurements;
|
||||
_output.WriteLine("Captured metrics:");
|
||||
foreach (var measurement in measurements)
|
||||
{
|
||||
_output.WriteLine($"{measurement.Name} -> {measurement.Value}");
|
||||
}
|
||||
|
||||
Assert.Equal(0, Sum(measurements, "vmware.fetch.failures"));
|
||||
Assert.Equal(0, Sum(measurements, "vmware.parse.fail"));
|
||||
Assert.Equal(3, Sum(measurements, "vmware.fetch.items")); // two initial, one new
|
||||
|
||||
var affectedCounts = measurements
|
||||
.Where(m => m.Name == "vmware.map.affected_count")
|
||||
.Select(m => (int)m.Value)
|
||||
.OrderBy(v => v)
|
||||
.ToArray();
|
||||
Assert.Equal(new[] { 1, 1, 2 }, affectedCounts);
|
||||
}
|
||||
|
||||
public Task InitializeAsync() => Task.CompletedTask;
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
_handler.Clear();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
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<TimeProvider>(_timeProvider);
|
||||
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.AddVmwareConnector(opts =>
|
||||
{
|
||||
opts.IndexUri = IndexUri;
|
||||
opts.InitialBackfill = TimeSpan.FromDays(30);
|
||||
opts.ModifiedTolerance = TimeSpan.FromMinutes(5);
|
||||
opts.MaxAdvisoriesPerFetch = 10;
|
||||
opts.RequestDelay = TimeSpan.Zero;
|
||||
});
|
||||
|
||||
services.Configure<HttpClientFactoryOptions>(VmwareOptions.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 SeedInitialResponses()
|
||||
{
|
||||
_handler.AddJsonResponse(IndexUri, ReadFixture("vmware-index-initial.json"));
|
||||
_handler.AddJsonResponse(DetailOne, ReadFixture("vmware-detail-vmsa-2024-0001.json"));
|
||||
_handler.AddJsonResponse(DetailTwo, ReadFixture("vmware-detail-vmsa-2024-0002.json"));
|
||||
}
|
||||
|
||||
private void SeedUpdateResponses()
|
||||
{
|
||||
_handler.AddJsonResponse(IndexUri, ReadFixture("vmware-index-second.json"));
|
||||
_handler.AddJsonResponse(DetailOne, ReadFixture("vmware-detail-vmsa-2024-0001.json"));
|
||||
_handler.AddJsonResponse(DetailTwo, ReadFixture("vmware-detail-vmsa-2024-0002.json"));
|
||||
_handler.AddJsonResponse(DetailThree, ReadFixture("vmware-detail-vmsa-2024-0003.json"));
|
||||
}
|
||||
|
||||
private static string ReadFixture(string name)
|
||||
{
|
||||
var primary = Path.Combine(AppContext.BaseDirectory, "Vmware", "Fixtures", name);
|
||||
if (File.Exists(primary))
|
||||
{
|
||||
return File.ReadAllText(primary);
|
||||
}
|
||||
|
||||
var fallback = Path.Combine(AppContext.BaseDirectory, "Fixtures", name);
|
||||
if (File.Exists(fallback))
|
||||
{
|
||||
return File.ReadAllText(fallback);
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"Fixture '{name}' not found.", name);
|
||||
}
|
||||
|
||||
private static string Normalize(string value)
|
||||
=> value.Replace("\r\n", "\n", StringComparison.Ordinal).TrimEnd();
|
||||
|
||||
private static long Sum(IEnumerable<VmwareMetricCollector.MetricMeasurement> measurements, string name)
|
||||
=> measurements.Where(m => m.Name == name).Sum(m => m.Value);
|
||||
|
||||
private sealed class VmwareMetricCollector : IDisposable
|
||||
{
|
||||
private readonly MeterListener _listener;
|
||||
private readonly ConcurrentBag<MetricMeasurement> _measurements = new();
|
||||
|
||||
public VmwareMetricCollector()
|
||||
{
|
||||
_listener = new MeterListener
|
||||
{
|
||||
InstrumentPublished = (instrument, listener) =>
|
||||
{
|
||||
if (instrument.Meter.Name == VmwareDiagnostics.MeterName)
|
||||
{
|
||||
listener.EnableMeasurementEvents(instrument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
var tagList = new List<KeyValuePair<string, object?>>(tags.Length);
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
tagList.Add(tag);
|
||||
}
|
||||
|
||||
_measurements.Add(new MetricMeasurement(instrument.Name, measurement, tagList));
|
||||
});
|
||||
|
||||
_listener.Start();
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<MetricMeasurement> Measurements => _measurements;
|
||||
|
||||
public void Dispose() => _listener.Dispose();
|
||||
|
||||
public sealed record MetricMeasurement(string Name, long Value, IReadOnlyList<KeyValuePair<string, object?>> Tags);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using MongoDB.Bson;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
using StellaOps.Concelier.Connector.Vndr.Vmware;
|
||||
using StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
|
||||
using StellaOps.Concelier.Storage.Mongo.Documents;
|
||||
using StellaOps.Concelier.Storage.Mongo.Dtos;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Vndr.Vmware.Tests;
|
||||
|
||||
public sealed class VmwareMapperTests
|
||||
{
|
||||
[Fact]
|
||||
public void Map_CreatesCanonicalAdvisory()
|
||||
{
|
||||
var modified = DateTimeOffset.UtcNow;
|
||||
var dto = new VmwareDetailDto
|
||||
{
|
||||
AdvisoryId = "VMSA-2025-0001",
|
||||
Title = "Sample VMware Advisory",
|
||||
Summary = "Summary text",
|
||||
Published = modified.AddDays(-1),
|
||||
Modified = modified,
|
||||
CveIds = new[] { "CVE-2025-0001", "CVE-2025-0002" },
|
||||
References = new[]
|
||||
{
|
||||
new VmwareReferenceDto { Url = "https://kb.vmware.com/some-kb", Type = "KB" },
|
||||
new VmwareReferenceDto { Url = "https://vmsa.vmware.com/vmsa/KB", Type = "Advisory" },
|
||||
},
|
||||
Affected = new[]
|
||||
{
|
||||
new VmwareAffectedProductDto
|
||||
{
|
||||
Product = "VMware vCenter",
|
||||
Version = "7.0",
|
||||
FixedVersion = "7.0u3"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var document = new DocumentRecord(
|
||||
Guid.NewGuid(),
|
||||
VmwareConnectorPlugin.SourceName,
|
||||
"https://vmsa.vmware.com/vmsa/VMSA-2025-0001",
|
||||
DateTimeOffset.UtcNow,
|
||||
"sha256",
|
||||
DocumentStatuses.PendingParse,
|
||||
"application/json",
|
||||
null,
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["vmware.id"] = dto.AdvisoryId,
|
||||
},
|
||||
null,
|
||||
modified,
|
||||
null,
|
||||
null);
|
||||
|
||||
var payload = BsonDocument.Parse(JsonSerializer.Serialize(dto, new JsonSerializerOptions(JsonSerializerDefaults.Web)
|
||||
{
|
||||
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
|
||||
}));
|
||||
|
||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, VmwareConnectorPlugin.SourceName, "vmware.v1", payload, DateTimeOffset.UtcNow);
|
||||
|
||||
var (advisory, flag) = VmwareMapper.Map(dto, document, dtoRecord);
|
||||
|
||||
Assert.Equal(dto.AdvisoryId, advisory.AdvisoryKey);
|
||||
Assert.Contains("CVE-2025-0001", advisory.Aliases);
|
||||
Assert.Contains("CVE-2025-0002", advisory.Aliases);
|
||||
Assert.Single(advisory.AffectedPackages);
|
||||
Assert.Equal("VMware vCenter", advisory.AffectedPackages[0].Identifier);
|
||||
Assert.Single(advisory.AffectedPackages[0].VersionRanges);
|
||||
Assert.Equal("7.0", advisory.AffectedPackages[0].VersionRanges[0].IntroducedVersion);
|
||||
Assert.Equal("7.0u3", advisory.AffectedPackages[0].VersionRanges[0].FixedVersion);
|
||||
Assert.Equal(2, advisory.References.Length);
|
||||
Assert.Equal("https://kb.vmware.com/some-kb", advisory.References[0].Url);
|
||||
Assert.Equal(dto.AdvisoryId, flag.AdvisoryKey);
|
||||
Assert.Equal("VMware", flag.Vendor);
|
||||
Assert.Equal(VmwareConnectorPlugin.SourceName, flag.SourceName);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user