using System.Globalization; using Mongo2Go; using MongoDB.Bson; using MongoDB.Driver; using StellaOps.Excititor.Core; namespace StellaOps.Excititor.Storage.Mongo.Tests; public sealed class MongoVexStoreMappingTests : IAsyncLifetime { private readonly MongoDbRunner _runner; private readonly IMongoDatabase _database; public MongoVexStoreMappingTests() { _runner = MongoDbRunner.Start(); var client = new MongoClient(_runner.ConnectionString); _database = client.GetDatabase("excititor-storage-mapping-tests"); VexMongoMappingRegistry.Register(); } [Fact] public async Task ProviderStore_RoundTrips_WithExtraFields() { var providers = _database.GetCollection(VexMongoCollectionNames.Providers); var providerId = "red-hat"; var document = new BsonDocument { { "_id", providerId }, { "DisplayName", "Red Hat CSAF" }, { "Kind", "vendor" }, { "BaseUris", new BsonArray { "https://example.com/csaf" } }, { "Discovery", new BsonDocument { { "WellKnownMetadata", "https://example.com/.well-known/csaf" }, { "RolIeService", "https://example.com/service/rolie" }, { "UnsupportedField", "ignored" }, } }, { "Trust", new BsonDocument { { "Weight", 0.75 }, { "Cosign", new BsonDocument { { "Issuer", "issuer@example.com" }, { "IdentityPattern", "spiffe://example/*" }, { "Unexpected", true }, } }, { "PgpFingerprints", new BsonArray { "ABCDEF1234567890" } }, { "AnotherIgnoredField", 123 }, } }, { "Enabled", true }, { "UnexpectedRoot", new BsonDocument { { "flag", true } } }, }; await providers.InsertOneAsync(document); var store = new MongoVexProviderStore(_database); var result = await store.FindAsync(providerId, CancellationToken.None); Assert.NotNull(result); Assert.Equal(providerId, result!.Id); Assert.Equal("Red Hat CSAF", result.DisplayName); Assert.Equal(VexProviderKind.Vendor, result.Kind); Assert.Single(result.BaseUris); Assert.Equal("https://example.com/csaf", result.BaseUris[0].ToString()); Assert.Equal("https://example.com/.well-known/csaf", result.Discovery.WellKnownMetadata?.ToString()); Assert.Equal("https://example.com/service/rolie", result.Discovery.RolIeService?.ToString()); Assert.Equal(0.75, result.Trust.Weight); Assert.NotNull(result.Trust.Cosign); Assert.Equal("issuer@example.com", result.Trust.Cosign!.Issuer); Assert.Equal("spiffe://example/*", result.Trust.Cosign!.IdentityPattern); Assert.Contains("ABCDEF1234567890", result.Trust.PgpFingerprints); Assert.True(result.Enabled); } [Fact] public async Task ConsensusStore_IgnoresUnknownFields() { var consensus = _database.GetCollection(VexMongoCollectionNames.Consensus); var vulnerabilityId = "CVE-2025-12345"; var productKey = "pkg:maven/org.example/app@1.2.3"; var consensusId = string.Format(CultureInfo.InvariantCulture, "{0}|{1}", vulnerabilityId.Trim(), productKey.Trim()); var document = new BsonDocument { { "_id", consensusId }, { "VulnerabilityId", vulnerabilityId }, { "Product", new BsonDocument { { "Key", productKey }, { "Name", "Example App" }, { "Version", "1.2.3" }, { "Purl", productKey }, { "Extra", "ignored" }, } }, { "Status", "notaffected" }, { "CalculatedAt", DateTime.UtcNow }, { "Sources", new BsonArray { new BsonDocument { { "ProviderId", "red-hat" }, { "Status", "notaffected" }, { "DocumentDigest", "sha256:123" }, { "Weight", 0.9 }, { "Justification", "componentnotpresent" }, { "Detail", "Vendor statement" }, { "Confidence", new BsonDocument { { "Level", "high" }, { "Score", 0.7 }, { "Method", "review" }, { "Unexpected", "ignored" }, } }, { "UnknownField", true }, }, } }, { "Conflicts", new BsonArray { new BsonDocument { { "ProviderId", "cisco" }, { "Status", "affected" }, { "DocumentDigest", "sha256:999" }, { "Justification", "requiresconfiguration" }, { "Detail", "Different guidance" }, { "Reason", "policy_override" }, { "Other", 1 }, }, } }, { "PolicyVersion", "2025.10" }, { "PolicyRevisionId", "rev-1" }, { "PolicyDigest", "sha256:abc" }, { "Summary", "Vendor confirms not affected." }, { "Unexpected", new BsonDocument { { "foo", "bar" } } }, }; await consensus.InsertOneAsync(document); var store = new MongoVexConsensusStore(_database); var result = await store.FindAsync(vulnerabilityId, productKey, CancellationToken.None); Assert.NotNull(result); Assert.Equal(vulnerabilityId, result!.VulnerabilityId); Assert.Equal(productKey, result.Product.Key); Assert.Equal("Example App", result.Product.Name); Assert.Equal(VexConsensusStatus.NotAffected, result.Status); Assert.Single(result.Sources); var source = result.Sources[0]; Assert.Equal("red-hat", source.ProviderId); Assert.Equal(VexClaimStatus.NotAffected, source.Status); Assert.Equal("sha256:123", source.DocumentDigest); Assert.Equal(0.9, source.Weight); Assert.Equal(VexJustification.ComponentNotPresent, source.Justification); Assert.NotNull(source.Confidence); Assert.Equal("high", source.Confidence!.Level); Assert.Equal(0.7, source.Confidence!.Score); Assert.Equal("review", source.Confidence!.Method); Assert.Single(result.Conflicts); var conflict = result.Conflicts[0]; Assert.Equal("cisco", conflict.ProviderId); Assert.Equal(VexClaimStatus.Affected, conflict.Status); Assert.Equal(VexJustification.RequiresConfiguration, conflict.Justification); Assert.Equal("policy_override", conflict.Reason); Assert.Equal("Vendor confirms not affected.", result.Summary); Assert.Equal("2025.10", result.PolicyVersion); } [Fact] public async Task CacheIndex_RoundTripsGridFsMetadata() { var gridObjectId = ObjectId.GenerateNewId().ToString(); var index = new MongoVexCacheIndex(_database); var signature = new VexQuerySignature("format=csaf|vendor=redhat"); var now = DateTimeOffset.UtcNow; var expires = now.AddHours(12); var entry = new VexCacheEntry( signature, VexExportFormat.Csaf, new VexContentAddress("sha256", "abcdef123456"), now, sizeBytes: 1024, manifestId: "manifest-001", gridFsObjectId: gridObjectId, expiresAt: expires); await index.SaveAsync(entry, CancellationToken.None); var cacheId = string.Format( CultureInfo.InvariantCulture, "{0}|{1}", signature.Value, entry.Format.ToString().ToLowerInvariant()); var cache = _database.GetCollection(VexMongoCollectionNames.Cache); var filter = Builders.Filter.Eq("_id", cacheId); var update = Builders.Update.Set("UnexpectedField", true); await cache.UpdateOneAsync(filter, update); var roundTrip = await index.FindAsync(signature, VexExportFormat.Csaf, CancellationToken.None); Assert.NotNull(roundTrip); Assert.Equal(entry.QuerySignature.Value, roundTrip!.QuerySignature.Value); Assert.Equal(entry.Format, roundTrip.Format); Assert.Equal(entry.Artifact.Digest, roundTrip.Artifact.Digest); Assert.Equal(entry.ManifestId, roundTrip.ManifestId); Assert.Equal(entry.GridFsObjectId, roundTrip.GridFsObjectId); Assert.Equal(entry.SizeBytes, roundTrip.SizeBytes); Assert.NotNull(roundTrip.ExpiresAt); Assert.Equal(expires.ToUnixTimeMilliseconds(), roundTrip.ExpiresAt!.Value.ToUnixTimeMilliseconds()); } public Task InitializeAsync() => Task.CompletedTask; public Task DisposeAsync() { _runner.Dispose(); return Task.CompletedTask; } }