132 lines
5.4 KiB
C#
132 lines
5.4 KiB
C#
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using StellaOps.Excititor.Core;
|
|
using StellaOps.Excititor.Formats.CSAF;
|
|
|
|
namespace StellaOps.Excititor.Formats.CSAF.Tests;
|
|
|
|
public sealed class CsafNormalizerTests
|
|
{
|
|
[Fact]
|
|
public async Task NormalizeAsync_ProducesClaimsPerProductStatus()
|
|
{
|
|
var json = """
|
|
{
|
|
"document": {
|
|
"tracking": {
|
|
"id": "RHSA-2025:0001",
|
|
"version": "3",
|
|
"revision": "3",
|
|
"status": "final",
|
|
"initial_release_date": "2025-10-01T00:00:00Z",
|
|
"current_release_date": "2025-10-10T00:00:00Z"
|
|
},
|
|
"publisher": {
|
|
"name": "Red Hat Product Security",
|
|
"category": "vendor"
|
|
}
|
|
},
|
|
"product_tree": {
|
|
"full_product_names": [
|
|
{
|
|
"product_id": "CSAFPID-0001",
|
|
"name": "Red Hat Enterprise Linux 9",
|
|
"product_identification_helper": {
|
|
"cpe": "cpe:/o:redhat:enterprise_linux:9",
|
|
"purl": "pkg:rpm/redhat/enterprise-linux@9"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"vulnerabilities": [
|
|
{
|
|
"cve": "CVE-2025-0001",
|
|
"title": "Kernel vulnerability",
|
|
"product_status": {
|
|
"known_affected": [ "CSAFPID-0001" ]
|
|
}
|
|
},
|
|
{
|
|
"id": "VEX-0002",
|
|
"title": "Library issue",
|
|
"product_status": {
|
|
"known_not_affected": [ "CSAFPID-0001" ]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
|
|
var rawDocument = new VexRawDocument(
|
|
ProviderId: "excititor:redhat",
|
|
VexDocumentFormat.Csaf,
|
|
new Uri("https://example.com/csaf/rhsa-2025-0001.json"),
|
|
new DateTimeOffset(2025, 10, 11, 0, 0, 0, TimeSpan.Zero),
|
|
"sha256:dummydigest",
|
|
Encoding.UTF8.GetBytes(json),
|
|
ImmutableDictionary<string, string>.Empty);
|
|
|
|
var provider = new VexProvider("excititor:redhat", "Red Hat CSAF", VexProviderKind.Distro);
|
|
var normalizer = new CsafNormalizer(NullLogger<CsafNormalizer>.Instance);
|
|
|
|
var batch = await normalizer.NormalizeAsync(rawDocument, provider, CancellationToken.None);
|
|
|
|
batch.Claims.Should().HaveCount(2);
|
|
|
|
var affectedClaim = batch.Claims.First(c => c.VulnerabilityId == "CVE-2025-0001");
|
|
affectedClaim.Status.Should().Be(VexClaimStatus.Affected);
|
|
affectedClaim.Product.Key.Should().Be("CSAFPID-0001");
|
|
affectedClaim.Product.Purl.Should().Be("pkg:rpm/redhat/enterprise-linux@9");
|
|
affectedClaim.Product.Cpe.Should().Be("cpe:/o:redhat:enterprise_linux:9");
|
|
affectedClaim.Document.Revision.Should().Be("3");
|
|
affectedClaim.FirstSeen.Should().Be(new DateTimeOffset(2025, 10, 1, 0, 0, 0, TimeSpan.Zero));
|
|
affectedClaim.LastSeen.Should().Be(new DateTimeOffset(2025, 10, 10, 0, 0, 0, TimeSpan.Zero));
|
|
affectedClaim.AdditionalMetadata.Should().ContainKey("csaf.tracking.id");
|
|
affectedClaim.AdditionalMetadata["csaf.publisher.name"].Should().Be("Red Hat Product Security");
|
|
|
|
var notAffectedClaim = batch.Claims.First(c => c.VulnerabilityId == "VEX-0002");
|
|
notAffectedClaim.Status.Should().Be(VexClaimStatus.NotAffected);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task NormalizeAsync_PreservesRedHatSpecificMetadata()
|
|
{
|
|
var path = Path.Combine(AppContext.BaseDirectory, "Fixtures", "rhsa-sample.json");
|
|
var json = await File.ReadAllTextAsync(path);
|
|
|
|
var rawDocument = new VexRawDocument(
|
|
ProviderId: "excititor:redhat",
|
|
VexDocumentFormat.Csaf,
|
|
new Uri("https://security.example.com/rhsa-2025-1001.json"),
|
|
new DateTimeOffset(2025, 10, 6, 0, 0, 0, TimeSpan.Zero),
|
|
"sha256:rhdadigest",
|
|
Encoding.UTF8.GetBytes(json),
|
|
ImmutableDictionary<string, string>.Empty);
|
|
|
|
var provider = new VexProvider("excititor:redhat", "Red Hat CSAF", VexProviderKind.Distro);
|
|
var normalizer = new CsafNormalizer(NullLogger<CsafNormalizer>.Instance);
|
|
|
|
var batch = await normalizer.NormalizeAsync(rawDocument, provider, CancellationToken.None);
|
|
batch.Claims.Should().ContainSingle();
|
|
|
|
var claim = batch.Claims[0];
|
|
claim.VulnerabilityId.Should().Be("CVE-2025-1234");
|
|
claim.Status.Should().Be(VexClaimStatus.Affected);
|
|
claim.Product.Key.Should().Be("rh-enterprise-linux-9");
|
|
claim.Product.Name.Should().Be("Red Hat Enterprise Linux 9");
|
|
claim.Product.Purl.Should().Be("pkg:rpm/redhat/enterprise-linux@9");
|
|
claim.Product.Cpe.Should().Be("cpe:/o:redhat:enterprise_linux:9");
|
|
claim.FirstSeen.Should().Be(new DateTimeOffset(2025, 10, 1, 12, 0, 0, TimeSpan.Zero));
|
|
claim.LastSeen.Should().Be(new DateTimeOffset(2025, 10, 5, 10, 0, 0, TimeSpan.Zero));
|
|
|
|
claim.AdditionalMetadata.Should().ContainKey("csaf.tracking.id");
|
|
claim.AdditionalMetadata["csaf.tracking.id"].Should().Be("RHSA-2025:1001");
|
|
claim.AdditionalMetadata["csaf.tracking.status"].Should().Be("final");
|
|
claim.AdditionalMetadata["csaf.publisher.name"].Should().Be("Red Hat Product Security");
|
|
}
|
|
}
|