save development progress
This commit is contained in:
@@ -13,10 +13,10 @@ using StellaOps.Concelier.Connector.Distro.Ubuntu.Configuration;
|
||||
using StellaOps.Concelier.Connector.Distro.Ubuntu.Internal;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Core.Canonical;
|
||||
using StellaOps.Plugin;
|
||||
using StellaOps.Cryptography;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Distro.Ubuntu;
|
||||
|
||||
@@ -32,6 +32,7 @@ public sealed class UbuntuConnector : IFeedConnector
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly ILogger<UbuntuConnector> _logger;
|
||||
private readonly ICryptoHash _hash;
|
||||
private readonly ICanonicalAdvisoryService? _canonicalService;
|
||||
|
||||
private static readonly Action<ILogger, string, int, Exception?> LogMapped =
|
||||
LoggerMessage.Define<string, int>(
|
||||
@@ -49,7 +50,8 @@ public sealed class UbuntuConnector : IFeedConnector
|
||||
IOptions<UbuntuOptions> options,
|
||||
TimeProvider? timeProvider,
|
||||
ILogger<UbuntuConnector> logger,
|
||||
ICryptoHash cryptoHash)
|
||||
ICryptoHash cryptoHash,
|
||||
ICanonicalAdvisoryService? canonicalService = null)
|
||||
{
|
||||
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
||||
_rawDocumentStorage = rawDocumentStorage ?? throw new ArgumentNullException(nameof(rawDocumentStorage));
|
||||
@@ -62,6 +64,7 @@ public sealed class UbuntuConnector : IFeedConnector
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||
_canonicalService = canonicalService; // Optional - canonical ingest
|
||||
}
|
||||
|
||||
public string SourceName => UbuntuConnectorPlugin.SourceName;
|
||||
@@ -230,6 +233,13 @@ public sealed class UbuntuConnector : IFeedConnector
|
||||
var advisory = UbuntuMapper.Map(notice, document, _timeProvider.GetUtcNow());
|
||||
await _advisoryStore.UpsertAsync(advisory, cancellationToken).ConfigureAwait(false);
|
||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.Mapped, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Ingest to canonical advisory service if available
|
||||
if (_canonicalService is not null)
|
||||
{
|
||||
await IngestToCanonicalAsync(advisory, document.FetchedAt, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
pending.Remove(documentId);
|
||||
|
||||
LogMapped(_logger, notice.NoticeId, advisory.AffectedPackages.Length, null);
|
||||
@@ -537,4 +547,89 @@ public sealed class UbuntuConnector : IFeedConnector
|
||||
packages,
|
||||
references);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ingests Ubuntu notice to canonical advisory service for deduplication.
|
||||
/// Creates one RawAdvisory per affected package.
|
||||
/// </summary>
|
||||
private async Task IngestToCanonicalAsync(
|
||||
Advisory advisory,
|
||||
DateTimeOffset fetchedAt,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (_canonicalService is null || advisory.AffectedPackages.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Find primary CVE from aliases
|
||||
var cve = advisory.Aliases
|
||||
.FirstOrDefault(a => a.StartsWith("CVE-", StringComparison.OrdinalIgnoreCase))
|
||||
?? advisory.AdvisoryKey;
|
||||
|
||||
// Extract CWE weaknesses
|
||||
var weaknesses = advisory.Cwes
|
||||
.Where(w => w.Identifier.StartsWith("CWE-", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(w => w.Identifier)
|
||||
.ToList();
|
||||
|
||||
// Create one RawAdvisory per affected package
|
||||
foreach (var affected in advisory.AffectedPackages)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(affected.Identifier))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build version range JSON
|
||||
string? versionRangeJson = null;
|
||||
if (!affected.VersionRanges.IsEmpty)
|
||||
{
|
||||
var firstRange = affected.VersionRanges[0];
|
||||
var rangeObj = new
|
||||
{
|
||||
introduced = firstRange.IntroducedVersion,
|
||||
@fixed = firstRange.FixedVersion,
|
||||
last_affected = firstRange.LastAffectedVersion
|
||||
};
|
||||
versionRangeJson = JsonSerializer.Serialize(rangeObj);
|
||||
}
|
||||
|
||||
var rawAdvisory = new RawAdvisory
|
||||
{
|
||||
SourceAdvisoryId = advisory.AdvisoryKey,
|
||||
Cve = cve,
|
||||
AffectsKey = affected.Identifier,
|
||||
VersionRangeJson = versionRangeJson,
|
||||
Weaknesses = weaknesses,
|
||||
PatchLineage = null,
|
||||
Severity = advisory.Severity,
|
||||
Title = advisory.Title,
|
||||
Summary = advisory.Summary,
|
||||
VendorStatus = VendorStatus.Affected,
|
||||
RawPayloadJson = null, // Ubuntu notices don't need raw payload
|
||||
FetchedAt = fetchedAt
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _canonicalService.IngestAsync(SourceName, rawAdvisory, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Canonical ingest for {AdvisoryKey}/{AffectsKey}: {Decision} (canonical={CanonicalId})",
|
||||
advisory.AdvisoryKey, affected.Identifier, result.Decision, result.CanonicalId);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
ex,
|
||||
"Failed to ingest {AdvisoryKey}/{AffectsKey} to canonical service",
|
||||
advisory.AdvisoryKey, affected.Identifier);
|
||||
// Don't fail the mapping operation for canonical ingest failures
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user