feat: Add initial implementation of Vulnerability Resolver Jobs
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Created project for StellaOps.Scanner.Analyzers.Native.Tests with necessary dependencies. - Documented roles and guidelines in AGENTS.md for Scheduler module. - Implemented IResolverJobService interface and InMemoryResolverJobService for handling resolver jobs. - Added ResolverBacklogNotifier and ResolverBacklogService for monitoring job metrics. - Developed API endpoints for managing resolver jobs and retrieving metrics. - Defined models for resolver job requests and responses. - Integrated dependency injection for resolver job services. - Implemented ImpactIndexSnapshot for persisting impact index data. - Introduced SignalsScoringOptions for configurable scoring weights in reachability scoring. - Added unit tests for ReachabilityScoringService and RuntimeFactsIngestionService. - Created dotnet-filter.sh script to handle command-line arguments for dotnet. - Established nuget-prime project for managing package downloads.
This commit is contained in:
@@ -32,10 +32,21 @@ public sealed class LedgerEventWriteService : ILedgerEventWriteService
|
||||
public async Task<LedgerWriteResult> AppendAsync(LedgerEventDraft draft, CancellationToken cancellationToken)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
using var activity = LedgerTelemetry.StartLedgerAppend(draft);
|
||||
using var scope = _logger.BeginScope(new Dictionary<string, object?>
|
||||
{
|
||||
["tenant"] = draft.TenantId,
|
||||
["chainId"] = draft.ChainId,
|
||||
["sequence"] = draft.SequenceNumber,
|
||||
["eventId"] = draft.EventId,
|
||||
["eventType"] = draft.EventType,
|
||||
["policyVersion"] = draft.PolicyVersion
|
||||
});
|
||||
|
||||
var validationErrors = ValidateDraft(draft);
|
||||
if (validationErrors.Count > 0)
|
||||
{
|
||||
LedgerTelemetry.MarkError(activity, "validation_failed");
|
||||
return LedgerWriteResult.ValidationFailed([.. validationErrors]);
|
||||
}
|
||||
|
||||
@@ -45,6 +56,7 @@ public sealed class LedgerEventWriteService : ILedgerEventWriteService
|
||||
var canonicalJson = LedgerCanonicalJsonSerializer.Serialize(draft.CanonicalEnvelope);
|
||||
if (!string.Equals(existing.CanonicalJson, canonicalJson, StringComparison.Ordinal))
|
||||
{
|
||||
LedgerTelemetry.MarkError(activity, "event_id_conflict");
|
||||
return LedgerWriteResult.Conflict(
|
||||
"event_id_conflict",
|
||||
$"Event '{draft.EventId}' already exists with a different payload.");
|
||||
@@ -58,6 +70,7 @@ public sealed class LedgerEventWriteService : ILedgerEventWriteService
|
||||
var expectedSequence = chainHead is null ? 1 : chainHead.SequenceNumber + 1;
|
||||
if (draft.SequenceNumber != expectedSequence)
|
||||
{
|
||||
LedgerTelemetry.MarkError(activity, "sequence_mismatch");
|
||||
return LedgerWriteResult.Conflict(
|
||||
"sequence_mismatch",
|
||||
$"Sequence number '{draft.SequenceNumber}' does not match expected '{expectedSequence}'.");
|
||||
@@ -66,6 +79,7 @@ public sealed class LedgerEventWriteService : ILedgerEventWriteService
|
||||
var previousHash = chainHead?.EventHash ?? LedgerEventConstants.EmptyHash;
|
||||
if (draft.ProvidedPreviousHash is not null && !string.Equals(draft.ProvidedPreviousHash, previousHash, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LedgerTelemetry.MarkError(activity, "previous_hash_mismatch");
|
||||
return LedgerWriteResult.Conflict(
|
||||
"previous_hash_mismatch",
|
||||
$"Provided previous hash '{draft.ProvidedPreviousHash}' does not match chain head hash '{previousHash}'.");
|
||||
@@ -93,7 +107,8 @@ public sealed class LedgerEventWriteService : ILedgerEventWriteService
|
||||
hashResult.EventHash,
|
||||
previousHash,
|
||||
hashResult.MerkleLeafHash,
|
||||
hashResult.CanonicalJson);
|
||||
hashResult.CanonicalJson,
|
||||
draft.EvidenceBundleReference);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -102,10 +117,29 @@ public sealed class LedgerEventWriteService : ILedgerEventWriteService
|
||||
|
||||
stopwatch.Stop();
|
||||
LedgerMetrics.RecordWriteSuccess(stopwatch.Elapsed, draft.TenantId, draft.EventType, DetermineSource(draft));
|
||||
LedgerTelemetry.MarkAppendOutcome(activity, record, stopwatch.Elapsed);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Ledger append committed for tenant {Tenant} chain {ChainId} seq {Sequence} event {EventId} ({EventType}) hash {Hash} prev {PrevHash}.",
|
||||
record.TenantId,
|
||||
record.ChainId,
|
||||
record.SequenceNumber,
|
||||
record.EventId,
|
||||
record.EventType,
|
||||
record.EventHash,
|
||||
record.PreviousHash);
|
||||
LedgerTimeline.EmitLedgerAppended(_logger, record, evidenceBundleRef: null);
|
||||
}
|
||||
catch (Exception ex) when (IsDuplicateKeyException(ex))
|
||||
{
|
||||
_logger.LogWarning(ex, "Ledger append detected concurrent duplicate for {EventId}", draft.EventId);
|
||||
LedgerTelemetry.MarkError(activity, "duplicate_event");
|
||||
_logger.LogWarning(
|
||||
ex,
|
||||
"Ledger append detected concurrent duplicate for tenant {Tenant} chain {ChainId} seq {Sequence} event {EventId}.",
|
||||
draft.TenantId,
|
||||
draft.ChainId,
|
||||
draft.SequenceNumber,
|
||||
draft.EventId);
|
||||
var persisted = await _repository.GetByEventIdAsync(draft.TenantId, draft.EventId, cancellationToken).ConfigureAwait(false);
|
||||
if (persisted is null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user