Initial commit (history squashed)
This commit is contained in:
130
src/StellaOps.Feedser.Source.Cve.Tests/Cve/CveConnectorTests.cs
Normal file
130
src/StellaOps.Feedser.Source.Cve.Tests/Cve/CveConnectorTests.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Feedser.Models;
|
||||
using StellaOps.Feedser.Source.Common.Fetch;
|
||||
using StellaOps.Feedser.Source.Common.Testing;
|
||||
using StellaOps.Feedser.Source.Cve.Configuration;
|
||||
using StellaOps.Feedser.Testing;
|
||||
using StellaOps.Feedser.Storage.Mongo;
|
||||
using StellaOps.Feedser.Storage.Mongo.Advisories;
|
||||
using StellaOps.Feedser.Storage.Mongo.Documents;
|
||||
using StellaOps.Feedser.Storage.Mongo.Dtos;
|
||||
|
||||
namespace StellaOps.Feedser.Source.Cve.Tests;
|
||||
|
||||
[Collection("mongo-fixture")]
|
||||
public sealed class CveConnectorTests : IAsyncLifetime
|
||||
{
|
||||
private readonly MongoIntegrationFixture _fixture;
|
||||
private ConnectorTestHarness? _harness;
|
||||
|
||||
public CveConnectorTests(MongoIntegrationFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FetchParseMap_EmitsCanonicalAdvisory()
|
||||
{
|
||||
var initialTime = new DateTimeOffset(2024, 10, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
await EnsureHarnessAsync(initialTime);
|
||||
var harness = _harness!;
|
||||
|
||||
var since = initialTime - TimeSpan.FromDays(30);
|
||||
var listUri = new Uri($"https://cve.test/api/cve?time_modified.gte={Uri.EscapeDataString(since.ToString("O"))}&time_modified.lte={Uri.EscapeDataString(initialTime.ToString("O"))}&page=1&size=5");
|
||||
harness.Handler.AddJsonResponse(listUri, ReadFixture("Fixtures/cve-list.json"));
|
||||
harness.Handler.SetFallback(request =>
|
||||
{
|
||||
if (request.RequestUri is null)
|
||||
{
|
||||
return new HttpResponseMessage(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.Equals("https://cve.test/api/cve/CVE-2024-0001", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(ReadFixture("Fixtures/cve-CVE-2024-0001.json"), Encoding.UTF8, "application/json")
|
||||
};
|
||||
}
|
||||
|
||||
return new HttpResponseMessage(HttpStatusCode.NotFound);
|
||||
});
|
||||
|
||||
var connector = new CveConnectorPlugin().Create(harness.ServiceProvider);
|
||||
|
||||
await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None);
|
||||
await connector.ParseAsync(harness.ServiceProvider, CancellationToken.None);
|
||||
await connector.MapAsync(harness.ServiceProvider, CancellationToken.None);
|
||||
|
||||
var advisoryStore = harness.ServiceProvider.GetRequiredService<IAdvisoryStore>();
|
||||
var advisory = await advisoryStore.FindAsync("CVE-2024-0001", CancellationToken.None);
|
||||
Assert.NotNull(advisory);
|
||||
|
||||
var snapshot = SnapshotSerializer.ToSnapshot(advisory!).Replace("\r\n", "\n").TrimEnd();
|
||||
var expected = ReadFixture("Fixtures/expected-CVE-2024-0001.json").Replace("\r\n", "\n").TrimEnd();
|
||||
|
||||
if (!string.Equals(expected, snapshot, StringComparison.Ordinal))
|
||||
{
|
||||
var actualPath = Path.Combine(AppContext.BaseDirectory, "Fixtures", "expected-CVE-2024-0001.actual.json");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(actualPath)!);
|
||||
File.WriteAllText(actualPath, snapshot);
|
||||
}
|
||||
|
||||
Assert.Equal(expected, snapshot);
|
||||
harness.Handler.AssertNoPendingResponses();
|
||||
}
|
||||
|
||||
private async Task EnsureHarnessAsync(DateTimeOffset initialTime)
|
||||
{
|
||||
if (_harness is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var harness = new ConnectorTestHarness(_fixture, initialTime, CveOptions.HttpClientName);
|
||||
await harness.EnsureServiceProviderAsync(services =>
|
||||
{
|
||||
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
|
||||
services.AddCveConnector(options =>
|
||||
{
|
||||
options.BaseEndpoint = new Uri("https://cve.test/api/", UriKind.Absolute);
|
||||
options.ApiOrg = "test-org";
|
||||
options.ApiUser = "test-user";
|
||||
options.ApiKey = "test-key";
|
||||
options.InitialBackfill = TimeSpan.FromDays(30);
|
||||
options.PageSize = 5;
|
||||
options.MaxPagesPerFetch = 2;
|
||||
options.RequestDelay = TimeSpan.Zero;
|
||||
});
|
||||
});
|
||||
|
||||
_harness = harness;
|
||||
}
|
||||
|
||||
private static string ReadFixture(string relativePath)
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, relativePath);
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
if (_harness is not null)
|
||||
{
|
||||
await _harness.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"dataType": "CVE_RECORD",
|
||||
"dataVersion": "5.0",
|
||||
"cveMetadata": {
|
||||
"cveId": "CVE-2024-0001",
|
||||
"assignerShortName": "ExampleOrg",
|
||||
"state": "PUBLISHED",
|
||||
"dateReserved": "2024-01-01T00:00:00Z",
|
||||
"datePublished": "2024-09-10T12:00:00Z",
|
||||
"dateUpdated": "2024-09-15T12:00:00Z"
|
||||
},
|
||||
"containers": {
|
||||
"cna": {
|
||||
"title": "Example Product Remote Code Execution",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "An example vulnerability allowing remote attackers to execute arbitrary code."
|
||||
}
|
||||
],
|
||||
"affected": [
|
||||
{
|
||||
"vendor": "ExampleVendor",
|
||||
"product": "ExampleProduct",
|
||||
"platform": "linux",
|
||||
"defaultStatus": "affected",
|
||||
"versions": [
|
||||
{
|
||||
"status": "affected",
|
||||
"version": "1.0.0",
|
||||
"lessThan": "1.2.0",
|
||||
"versionType": "semver"
|
||||
},
|
||||
{
|
||||
"status": "unaffected",
|
||||
"version": "1.2.0",
|
||||
"versionType": "semver"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://example.com/security/advisory",
|
||||
"name": "Vendor Advisory",
|
||||
"tags": [
|
||||
"vendor-advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://cve.example.com/CVE-2024-0001",
|
||||
"tags": [
|
||||
"third-party-advisory"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metrics": [
|
||||
{
|
||||
"cvssV3_1": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"baseScore": 9.8,
|
||||
"baseSeverity": "CRITICAL"
|
||||
}
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
"GHSA-xxxx-yyyy-zzzz"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"dataType": "CVE_RECORD_LIST",
|
||||
"dataVersion": "5.0",
|
||||
"data": [
|
||||
{
|
||||
"cveMetadata": {
|
||||
"cveId": "CVE-2024-0001",
|
||||
"state": "PUBLISHED",
|
||||
"dateUpdated": "2024-09-15T12:00:00Z"
|
||||
}
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"totalCount": 1,
|
||||
"itemsPerPage": 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"advisoryKey": "CVE-2024-0001",
|
||||
"affectedPackages": [
|
||||
{
|
||||
"identifier": "examplevendor:exampleproduct",
|
||||
"platform": "linux",
|
||||
"provenance": [
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "affected",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "examplevendor:exampleproduct"
|
||||
}
|
||||
],
|
||||
"statuses": [
|
||||
{
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "affected-status",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "examplevendor:exampleproduct"
|
||||
},
|
||||
"status": "affected"
|
||||
},
|
||||
{
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "affected-status",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "examplevendor:exampleproduct"
|
||||
},
|
||||
"status": "not_affected"
|
||||
}
|
||||
],
|
||||
"type": "vendor",
|
||||
"versionRanges": [
|
||||
{
|
||||
"fixedVersion": "1.2.0",
|
||||
"introducedVersion": "1.0.0",
|
||||
"lastAffectedVersion": null,
|
||||
"primitives": {
|
||||
"evr": null,
|
||||
"hasVendorExtensions": true,
|
||||
"nevra": null,
|
||||
"semVer": null,
|
||||
"vendorExtensions": {
|
||||
"vendor": "ExampleVendor",
|
||||
"product": "ExampleProduct",
|
||||
"platform": "linux"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "affected-range",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "examplevendor:exampleproduct"
|
||||
},
|
||||
"rangeExpression": "version=1.0.0, < 1.2.0",
|
||||
"rangeKind": "semver"
|
||||
},
|
||||
{
|
||||
"fixedVersion": null,
|
||||
"introducedVersion": "1.2.0",
|
||||
"lastAffectedVersion": null,
|
||||
"primitives": {
|
||||
"evr": null,
|
||||
"hasVendorExtensions": true,
|
||||
"nevra": null,
|
||||
"semVer": null,
|
||||
"vendorExtensions": {
|
||||
"vendor": "ExampleVendor",
|
||||
"product": "ExampleProduct",
|
||||
"platform": "linux"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "affected-range",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "examplevendor:exampleproduct"
|
||||
},
|
||||
"rangeExpression": "version=1.2.0",
|
||||
"rangeKind": "semver"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"aliases": [
|
||||
"CVE-2024-0001",
|
||||
"GHSA-xxxx-yyyy-zzzz"
|
||||
],
|
||||
"cvssMetrics": [
|
||||
{
|
||||
"baseScore": 9.8,
|
||||
"baseSeverity": "critical",
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "cvss",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "cve/CVE-2024-0001"
|
||||
},
|
||||
"vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"version": "3.1"
|
||||
}
|
||||
],
|
||||
"exploitKnown": false,
|
||||
"language": "en",
|
||||
"modified": "2024-09-15T12:00:00+00:00",
|
||||
"provenance": [
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "document",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "cve/CVE-2024-0001"
|
||||
},
|
||||
{
|
||||
"fieldMask": [],
|
||||
"kind": "mapping",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "CVE-2024-0001"
|
||||
}
|
||||
],
|
||||
"published": "2024-09-10T12:00:00+00:00",
|
||||
"references": [
|
||||
{
|
||||
"kind": "third-party-advisory",
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "reference",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "https://cve.example.com/CVE-2024-0001"
|
||||
},
|
||||
"sourceTag": null,
|
||||
"summary": null,
|
||||
"url": "https://cve.example.com/CVE-2024-0001"
|
||||
},
|
||||
{
|
||||
"kind": "vendor-advisory",
|
||||
"provenance": {
|
||||
"fieldMask": [],
|
||||
"kind": "reference",
|
||||
"recordedAt": "2024-10-01T00:00:00+00:00",
|
||||
"source": "cve",
|
||||
"value": "https://example.com/security/advisory"
|
||||
},
|
||||
"sourceTag": "Vendor Advisory",
|
||||
"summary": null,
|
||||
"url": "https://example.com/security/advisory"
|
||||
}
|
||||
],
|
||||
"severity": "critical",
|
||||
"summary": "An example vulnerability allowing remote attackers to execute arbitrary code.",
|
||||
"title": "Example Product Remote Code Execution"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.Feedser.Models/StellaOps.Feedser.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Feedser.Source.Cve/StellaOps.Feedser.Source.Cve.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Feedser.Source.Common/StellaOps.Feedser.Source.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Feedser.Storage.Mongo/StellaOps.Feedser.Storage.Mongo.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Feedser.Testing/StellaOps.Feedser.Testing.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Fixtures/*.json" CopyToOutputDirectory="Always" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user