105 lines
3.6 KiB
C#
105 lines
3.6 KiB
C#
using System.Net;
|
|
|
|
namespace StellaOps.Concelier.Connector.CertBund.Configuration;
|
|
|
|
public sealed class CertBundOptions
|
|
{
|
|
public const string HttpClientName = "concelier.source.certbund";
|
|
|
|
/// <summary>
|
|
/// RSS feed providing the latest CERT-Bund advisories.
|
|
/// </summary>
|
|
public Uri FeedUri { get; set; } = new("https://wid.cert-bund.de/content/public/securityAdvisory/rss");
|
|
|
|
/// <summary>
|
|
/// Portal endpoint used to bootstrap session cookies (required for the SPA JSON API).
|
|
/// </summary>
|
|
public Uri PortalBootstrapUri { get; set; } = new("https://wid.cert-bund.de/portal/");
|
|
|
|
/// <summary>
|
|
/// Detail API endpoint template; advisory identifier is appended as the <c>name</c> query parameter.
|
|
/// </summary>
|
|
public Uri DetailApiUri { get; set; } = new("https://wid.cert-bund.de/portal/api/securityadvisory");
|
|
|
|
/// <summary>
|
|
/// Optional timeout override for feed/detail requests.
|
|
/// </summary>
|
|
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
|
|
|
|
/// <summary>
|
|
/// Delay applied between successive detail fetches to respect upstream politeness.
|
|
/// </summary>
|
|
public TimeSpan RequestDelay { get; set; } = TimeSpan.FromMilliseconds(250);
|
|
|
|
/// <summary>
|
|
/// Backoff recorded in source state when a fetch attempt fails.
|
|
/// </summary>
|
|
public TimeSpan FailureBackoff { get; set; } = TimeSpan.FromMinutes(5);
|
|
|
|
/// <summary>
|
|
/// Maximum number of advisories to enqueue per fetch iteration.
|
|
/// </summary>
|
|
public int MaxAdvisoriesPerFetch { get; set; } = 50;
|
|
|
|
/// <summary>
|
|
/// Maximum number of advisory identifiers remembered to prevent re-processing.
|
|
/// </summary>
|
|
public int MaxKnownAdvisories { get; set; } = 512;
|
|
|
|
public void Validate()
|
|
{
|
|
if (FeedUri is null || !FeedUri.IsAbsoluteUri)
|
|
{
|
|
throw new InvalidOperationException("CERT-Bund feed URI must be an absolute URI.");
|
|
}
|
|
|
|
if (PortalBootstrapUri is null || !PortalBootstrapUri.IsAbsoluteUri)
|
|
{
|
|
throw new InvalidOperationException("CERT-Bund portal bootstrap URI must be an absolute URI.");
|
|
}
|
|
|
|
if (DetailApiUri is null || !DetailApiUri.IsAbsoluteUri)
|
|
{
|
|
throw new InvalidOperationException("CERT-Bund detail API URI must be an absolute URI.");
|
|
}
|
|
|
|
if (RequestTimeout <= TimeSpan.Zero)
|
|
{
|
|
throw new InvalidOperationException($"{nameof(RequestTimeout)} must be positive.");
|
|
}
|
|
|
|
if (RequestDelay < TimeSpan.Zero)
|
|
{
|
|
throw new InvalidOperationException($"{nameof(RequestDelay)} cannot be negative.");
|
|
}
|
|
|
|
if (FailureBackoff <= TimeSpan.Zero)
|
|
{
|
|
throw new InvalidOperationException($"{nameof(FailureBackoff)} must be positive.");
|
|
}
|
|
|
|
if (MaxAdvisoriesPerFetch <= 0)
|
|
{
|
|
throw new InvalidOperationException($"{nameof(MaxAdvisoriesPerFetch)} must be greater than zero.");
|
|
}
|
|
|
|
if (MaxKnownAdvisories <= 0)
|
|
{
|
|
throw new InvalidOperationException($"{nameof(MaxKnownAdvisories)} must be greater than zero.");
|
|
}
|
|
}
|
|
|
|
public Uri BuildDetailUri(string advisoryId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(advisoryId))
|
|
{
|
|
throw new ArgumentException("Advisory identifier must be provided.", nameof(advisoryId));
|
|
}
|
|
|
|
var builder = new UriBuilder(DetailApiUri);
|
|
var queryPrefix = string.IsNullOrEmpty(builder.Query) ? string.Empty : builder.Query.TrimStart('?') + "&";
|
|
builder.Query = $"{queryPrefix}name={Uri.EscapeDataString(advisoryId)}";
|
|
return builder.Uri;
|
|
}
|
|
}
|