Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly. - Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps. - Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges. - Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges. - Set up project file for the test project with necessary dependencies and configurations. - Include JSON fixture files for testing purposes.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace StellaOps.Findings.Ledger.Domain;
|
||||
|
||||
public static class LedgerEventConstants
|
||||
{
|
||||
public const string EventFindingCreated = "finding.created";
|
||||
public const string EventFindingStatusChanged = "finding.status_changed";
|
||||
public const string EventFindingSeverityChanged = "finding.severity_changed";
|
||||
public const string EventFindingTagUpdated = "finding.tag_updated";
|
||||
public const string EventFindingCommentAdded = "finding.comment_added";
|
||||
public const string EventFindingAssignmentChanged = "finding.assignment_changed";
|
||||
public const string EventFindingAcceptedRisk = "finding.accepted_risk";
|
||||
public const string EventFindingRemediationPlanAdded = "finding.remediation_plan_added";
|
||||
public const string EventFindingAttachmentAdded = "finding.attachment_added";
|
||||
public const string EventFindingClosed = "finding.closed";
|
||||
|
||||
public static readonly ImmutableHashSet<string> SupportedEventTypes = ImmutableHashSet.Create(StringComparer.Ordinal,
|
||||
EventFindingCreated,
|
||||
EventFindingStatusChanged,
|
||||
EventFindingSeverityChanged,
|
||||
EventFindingTagUpdated,
|
||||
EventFindingCommentAdded,
|
||||
EventFindingAssignmentChanged,
|
||||
EventFindingAcceptedRisk,
|
||||
EventFindingRemediationPlanAdded,
|
||||
EventFindingAttachmentAdded,
|
||||
EventFindingClosed);
|
||||
|
||||
public static readonly ImmutableHashSet<string> SupportedActorTypes = ImmutableHashSet.Create(StringComparer.Ordinal,
|
||||
"system",
|
||||
"operator",
|
||||
"integration");
|
||||
|
||||
public const string EmptyHash = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace StellaOps.Findings.Ledger.Domain;
|
||||
|
||||
public sealed record LedgerEventDraft(
|
||||
string TenantId,
|
||||
Guid ChainId,
|
||||
long SequenceNumber,
|
||||
Guid EventId,
|
||||
string EventType,
|
||||
string PolicyVersion,
|
||||
string FindingId,
|
||||
string ArtifactId,
|
||||
Guid? SourceRunId,
|
||||
string ActorId,
|
||||
string ActorType,
|
||||
DateTimeOffset OccurredAt,
|
||||
DateTimeOffset RecordedAt,
|
||||
JsonObject Payload,
|
||||
JsonObject CanonicalEnvelope,
|
||||
string? ProvidedPreviousHash);
|
||||
|
||||
public sealed record LedgerEventRecord(
|
||||
string TenantId,
|
||||
Guid ChainId,
|
||||
long SequenceNumber,
|
||||
Guid EventId,
|
||||
string EventType,
|
||||
string PolicyVersion,
|
||||
string FindingId,
|
||||
string ArtifactId,
|
||||
Guid? SourceRunId,
|
||||
string ActorId,
|
||||
string ActorType,
|
||||
DateTimeOffset OccurredAt,
|
||||
DateTimeOffset RecordedAt,
|
||||
JsonObject EventBody,
|
||||
string EventHash,
|
||||
string PreviousHash,
|
||||
string MerkleLeafHash,
|
||||
string CanonicalJson);
|
||||
|
||||
public sealed record LedgerChainHead(
|
||||
long SequenceNumber,
|
||||
string EventHash,
|
||||
DateTimeOffset RecordedAt);
|
||||
|
||||
public enum LedgerWriteStatus
|
||||
{
|
||||
Success,
|
||||
Idempotent,
|
||||
ValidationFailed,
|
||||
Conflict
|
||||
}
|
||||
|
||||
public sealed record LedgerWriteResult(
|
||||
LedgerWriteStatus Status,
|
||||
LedgerEventRecord? Record,
|
||||
IReadOnlyList<string> Errors,
|
||||
LedgerEventRecord? ExistingRecord,
|
||||
string? ConflictCode)
|
||||
{
|
||||
public static LedgerWriteResult ValidationFailed(params string[] errors)
|
||||
=> new(LedgerWriteStatus.ValidationFailed, null, errors, null, null);
|
||||
|
||||
public static LedgerWriteResult Conflict(string code, params string[] errors)
|
||||
=> new(LedgerWriteStatus.Conflict, null, errors, null, code);
|
||||
|
||||
public static LedgerWriteResult Idempotent(LedgerEventRecord record)
|
||||
=> new(LedgerWriteStatus.Idempotent, record, Array.Empty<string>(), record, null);
|
||||
|
||||
public static LedgerWriteResult Success(LedgerEventRecord record)
|
||||
=> new(LedgerWriteStatus.Success, record, Array.Empty<string>(), null, null);
|
||||
}
|
||||
|
||||
public sealed class LedgerDuplicateEventException : Exception
|
||||
{
|
||||
public LedgerDuplicateEventException(Guid eventId, Exception innerException)
|
||||
: base($"Ledger event {eventId} already exists.", innerException)
|
||||
{
|
||||
EventId = eventId;
|
||||
}
|
||||
|
||||
public Guid EventId { get; }
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace StellaOps.Findings.Ledger.Domain;
|
||||
|
||||
public sealed record FindingProjection(
|
||||
string TenantId,
|
||||
string FindingId,
|
||||
string PolicyVersion,
|
||||
string Status,
|
||||
decimal? Severity,
|
||||
JsonObject Labels,
|
||||
Guid CurrentEventId,
|
||||
string? ExplainRef,
|
||||
JsonArray PolicyRationale,
|
||||
DateTimeOffset UpdatedAt,
|
||||
string CycleHash);
|
||||
|
||||
public sealed record FindingHistoryEntry(
|
||||
string TenantId,
|
||||
string FindingId,
|
||||
string PolicyVersion,
|
||||
Guid EventId,
|
||||
string Status,
|
||||
decimal? Severity,
|
||||
string ActorId,
|
||||
string? Comment,
|
||||
DateTimeOffset OccurredAt);
|
||||
|
||||
public sealed record TriageActionEntry(
|
||||
string TenantId,
|
||||
Guid ActionId,
|
||||
Guid EventId,
|
||||
string FindingId,
|
||||
string ActionType,
|
||||
JsonObject Payload,
|
||||
DateTimeOffset CreatedAt,
|
||||
string CreatedBy);
|
||||
|
||||
public sealed record ProjectionReduceResult(
|
||||
FindingProjection Projection,
|
||||
FindingHistoryEntry History,
|
||||
TriageActionEntry? Action);
|
||||
|
||||
public sealed record ProjectionCheckpoint(
|
||||
DateTimeOffset LastRecordedAt,
|
||||
Guid LastEventId,
|
||||
DateTimeOffset UpdatedAt)
|
||||
{
|
||||
public static ProjectionCheckpoint Initial(TimeProvider timeProvider)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(timeProvider);
|
||||
|
||||
var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
var now = timeProvider.GetUtcNow();
|
||||
return new ProjectionCheckpoint(epoch, Guid.Empty, now);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user