up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
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 StellaOps.Concelier.Bson;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Connector.Cccs;
|
||||
using StellaOps.Concelier.Connector.Cccs.Configuration;
|
||||
using StellaOps.Concelier.Connector.Common;
|
||||
@@ -15,13 +15,13 @@ using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Cccs.Tests;
|
||||
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Cccs.Tests;
|
||||
|
||||
[Collection(ConcelierFixtureCollection.Name)]
|
||||
public sealed class CccsConnectorTests
|
||||
{
|
||||
private static readonly Uri FeedUri = new("https://test.local/api/cccs/threats/v1/get?lang=en&content_type=cccs_threat");
|
||||
private static readonly Uri FeedUri = new("https://test.local/api/cccs/threats/v1/get?lang=en&content_type=cccs_threat");
|
||||
private static readonly Uri TaxonomyUri = new("https://test.local/api/cccs/taxonomy/v1/get?lang=en&vocabulary=cccs_alert_type");
|
||||
|
||||
private readonly ConcelierPostgresFixture _fixture;
|
||||
@@ -30,7 +30,7 @@ public sealed class CccsConnectorTests
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task FetchParseMap_ProducesCanonicalAdvisory()
|
||||
{
|
||||
@@ -45,26 +45,26 @@ public sealed class CccsConnectorTests
|
||||
var advisoryStore = harness.ServiceProvider.GetRequiredService<IAdvisoryStore>();
|
||||
var advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
|
||||
advisories.Should().HaveCount(1);
|
||||
|
||||
var advisory = advisories[0];
|
||||
advisory.AdvisoryKey.Should().Be("TEST-001");
|
||||
advisory.Title.Should().Be("Test Advisory Title");
|
||||
advisory.Aliases.Should().Contain(new[] { "TEST-001", "CVE-2020-1234", "CVE-2021-9999" });
|
||||
advisory.References.Should().Contain(reference => reference.Url == "https://example.com/details");
|
||||
advisory.References.Should().Contain(reference => reference.Url == "https://www.cyber.gc.ca/en/contact-cyber-centre?lang=en");
|
||||
advisory.AffectedPackages.Should().ContainSingle(pkg => pkg.Identifier == "Vendor Widget 1.0");
|
||||
advisory.AffectedPackages.Should().Contain(pkg => pkg.Identifier == "Vendor Widget 2.0");
|
||||
|
||||
|
||||
var advisory = advisories[0];
|
||||
advisory.AdvisoryKey.Should().Be("TEST-001");
|
||||
advisory.Title.Should().Be("Test Advisory Title");
|
||||
advisory.Aliases.Should().Contain(new[] { "TEST-001", "CVE-2020-1234", "CVE-2021-9999" });
|
||||
advisory.References.Should().Contain(reference => reference.Url == "https://example.com/details");
|
||||
advisory.References.Should().Contain(reference => reference.Url == "https://www.cyber.gc.ca/en/contact-cyber-centre?lang=en");
|
||||
advisory.AffectedPackages.Should().ContainSingle(pkg => pkg.Identifier == "Vendor Widget 1.0");
|
||||
advisory.AffectedPackages.Should().Contain(pkg => pkg.Identifier == "Vendor Widget 2.0");
|
||||
|
||||
var stateRepository = harness.ServiceProvider.GetRequiredService<ISourceStateRepository>();
|
||||
var state = await stateRepository.TryGetAsync(CccsConnectorPlugin.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();
|
||||
}
|
||||
|
||||
state!.Cursor.Should().NotBeNull();
|
||||
state.Cursor.TryGetValue("pendingDocuments", out var pendingDocs).Should().BeTrue();
|
||||
pendingDocs!.AsDocumentArray.Should().BeEmpty();
|
||||
state.Cursor.TryGetValue("pendingMappings", out var pendingMappings).Should().BeTrue();
|
||||
pendingMappings!.AsDocumentArray.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Fetch_PersistsRawDocumentWithMetadata()
|
||||
{
|
||||
@@ -79,10 +79,10 @@ public sealed class CccsConnectorTests
|
||||
document.Should().NotBeNull();
|
||||
document!.Status.Should().Be(DocumentStatuses.PendingParse);
|
||||
document.Metadata.Should().ContainKey("cccs.language").WhoseValue.Should().Be("en");
|
||||
document.Metadata.Should().ContainKey("cccs.serialNumber").WhoseValue.Should().Be("TEST-001");
|
||||
document.ContentType.Should().Be("application/json");
|
||||
}
|
||||
|
||||
document.Metadata.Should().ContainKey("cccs.serialNumber").WhoseValue.Should().Be("TEST-001");
|
||||
document.ContentType.Should().Be("application/json");
|
||||
}
|
||||
|
||||
private async Task<ConnectorTestHarness> BuildHarnessAsync()
|
||||
{
|
||||
var initialTime = new DateTimeOffset(2025, 10, 12, 0, 0, 0, TimeSpan.Zero);
|
||||
@@ -114,11 +114,11 @@ public sealed class CccsConnectorTests
|
||||
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);
|
||||
}
|
||||
};
|
||||
if (!string.IsNullOrWhiteSpace(etag))
|
||||
{
|
||||
response.Headers.ETag = new EntityTagHeaderValue(etag);
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
|
||||
@@ -1,92 +1,92 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Concelier.Connector.Cccs.Internal;
|
||||
using StellaOps.Concelier.Connector.Common.Html;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Cccs.Tests.Internal;
|
||||
|
||||
public sealed class CccsHtmlParserTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
private static readonly HtmlContentSanitizer Sanitizer = new();
|
||||
private static readonly CccsHtmlParser Parser = new(Sanitizer);
|
||||
|
||||
public CccsHtmlParserTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output ?? throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ParserCases()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
"cccs-raw-advisory.json",
|
||||
"TEST-001",
|
||||
"en",
|
||||
new[] { "Vendor Widget 1.0", "Vendor Widget 2.0" },
|
||||
new[]
|
||||
{
|
||||
"https://example.com/details",
|
||||
"https://www.cyber.gc.ca/en/contact-cyber-centre?lang=en"
|
||||
},
|
||||
new[] { "CVE-2020-1234", "CVE-2021-9999" }
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"cccs-raw-advisory-fr.json",
|
||||
"TEST-002-FR",
|
||||
"fr",
|
||||
new[] { "Produit Exemple 3.1", "Produit Exemple 3.2", "Variante 3.2.1" },
|
||||
new[]
|
||||
{
|
||||
"https://exemple.ca/details",
|
||||
"https://www.cyber.gc.ca/fr/contact-centre-cyber"
|
||||
},
|
||||
new[] { "CVE-2024-1111" }
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ParserCases))]
|
||||
public void Parse_ExtractsExpectedFields(
|
||||
string fixtureName,
|
||||
string expectedSerial,
|
||||
string expectedLanguage,
|
||||
string[] expectedProducts,
|
||||
string[] expectedReferenceUrls,
|
||||
string[] expectedCves)
|
||||
{
|
||||
var raw = LoadFixture<CccsRawAdvisoryDocument>(fixtureName);
|
||||
|
||||
var dto = Parser.Parse(raw);
|
||||
|
||||
_output.WriteLine("Products: {0}", string.Join("|", dto.Products));
|
||||
_output.WriteLine("References: {0}", string.Join("|", dto.References.Select(r => $"{r.Url} ({r.Label})")));
|
||||
_output.WriteLine("CVEs: {0}", string.Join("|", dto.CveIds));
|
||||
|
||||
dto.SerialNumber.Should().Be(expectedSerial);
|
||||
dto.Language.Should().Be(expectedLanguage);
|
||||
dto.Products.Should().BeEquivalentTo(expectedProducts);
|
||||
foreach (var url in expectedReferenceUrls)
|
||||
{
|
||||
dto.References.Should().Contain(reference => reference.Url == url);
|
||||
}
|
||||
|
||||
dto.CveIds.Should().BeEquivalentTo(expectedCves);
|
||||
dto.ContentHtml.Should().Contain("<ul>").And.Contain("<li>");
|
||||
dto.ContentHtml.Should().Contain("<h2", because: "heading structure must survive sanitisation for UI rendering");
|
||||
}
|
||||
|
||||
internal static T LoadFixture<T>(string fileName)
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "Fixtures", fileName);
|
||||
var json = File.ReadAllText(path);
|
||||
return JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions(JsonSerializerDefaults.Web))!;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Concelier.Connector.Cccs.Internal;
|
||||
using StellaOps.Concelier.Connector.Common.Html;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Cccs.Tests.Internal;
|
||||
|
||||
public sealed class CccsHtmlParserTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
private static readonly HtmlContentSanitizer Sanitizer = new();
|
||||
private static readonly CccsHtmlParser Parser = new(Sanitizer);
|
||||
|
||||
public CccsHtmlParserTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output ?? throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ParserCases()
|
||||
{
|
||||
yield return new object[]
|
||||
{
|
||||
"cccs-raw-advisory.json",
|
||||
"TEST-001",
|
||||
"en",
|
||||
new[] { "Vendor Widget 1.0", "Vendor Widget 2.0" },
|
||||
new[]
|
||||
{
|
||||
"https://example.com/details",
|
||||
"https://www.cyber.gc.ca/en/contact-cyber-centre?lang=en"
|
||||
},
|
||||
new[] { "CVE-2020-1234", "CVE-2021-9999" }
|
||||
};
|
||||
|
||||
yield return new object[]
|
||||
{
|
||||
"cccs-raw-advisory-fr.json",
|
||||
"TEST-002-FR",
|
||||
"fr",
|
||||
new[] { "Produit Exemple 3.1", "Produit Exemple 3.2", "Variante 3.2.1" },
|
||||
new[]
|
||||
{
|
||||
"https://exemple.ca/details",
|
||||
"https://www.cyber.gc.ca/fr/contact-centre-cyber"
|
||||
},
|
||||
new[] { "CVE-2024-1111" }
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ParserCases))]
|
||||
public void Parse_ExtractsExpectedFields(
|
||||
string fixtureName,
|
||||
string expectedSerial,
|
||||
string expectedLanguage,
|
||||
string[] expectedProducts,
|
||||
string[] expectedReferenceUrls,
|
||||
string[] expectedCves)
|
||||
{
|
||||
var raw = LoadFixture<CccsRawAdvisoryDocument>(fixtureName);
|
||||
|
||||
var dto = Parser.Parse(raw);
|
||||
|
||||
_output.WriteLine("Products: {0}", string.Join("|", dto.Products));
|
||||
_output.WriteLine("References: {0}", string.Join("|", dto.References.Select(r => $"{r.Url} ({r.Label})")));
|
||||
_output.WriteLine("CVEs: {0}", string.Join("|", dto.CveIds));
|
||||
|
||||
dto.SerialNumber.Should().Be(expectedSerial);
|
||||
dto.Language.Should().Be(expectedLanguage);
|
||||
dto.Products.Should().BeEquivalentTo(expectedProducts);
|
||||
foreach (var url in expectedReferenceUrls)
|
||||
{
|
||||
dto.References.Should().Contain(reference => reference.Url == url);
|
||||
}
|
||||
|
||||
dto.CveIds.Should().BeEquivalentTo(expectedCves);
|
||||
dto.ContentHtml.Should().Contain("<ul>").And.Contain("<li>");
|
||||
dto.ContentHtml.Should().Contain("<h2", because: "heading structure must survive sanitisation for UI rendering");
|
||||
}
|
||||
|
||||
internal static T LoadFixture<T>(string fileName)
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "Fixtures", fileName);
|
||||
var json = File.ReadAllText(path);
|
||||
return JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions(JsonSerializerDefaults.Web))!;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user