Add unit tests for SBOM ingestion and transformation
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:
master
2025-11-04 07:49:39 +02:00
parent f72c5c513a
commit 2eb6852d34
491 changed files with 39445 additions and 3917 deletions

View File

@@ -0,0 +1,189 @@
using System.Text.Json.Nodes;
using Microsoft.Extensions.Logging;
using StellaOps.Findings.Ledger.Domain;
namespace StellaOps.Findings.Ledger.Infrastructure.Policy;
public sealed class InlinePolicyEvaluationService : IPolicyEvaluationService
{
private readonly ILogger<InlinePolicyEvaluationService> _logger;
public InlinePolicyEvaluationService(ILogger<InlinePolicyEvaluationService> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task<PolicyEvaluationResult> EvaluateAsync(
LedgerEventRecord record,
FindingProjection? existingProjection,
CancellationToken cancellationToken)
{
if (record is null)
{
throw new ArgumentNullException(nameof(record));
}
var eventObject = record.EventBody["event"]?.AsObject();
if (eventObject is null)
{
_logger.LogWarning("Ledger event {EventId} missing canonical event payload; falling back to existing projection.", record.EventId);
return Task.FromResult(CreateFallback(existingProjection));
}
var payload = eventObject["payload"] as JsonObject;
var status = ExtractString(payload, "status");
var severity = ExtractDecimal(payload, "severity");
var explainRef = ExtractString(payload, "explainRef") ?? ExtractString(payload, "explain_ref");
var labels = ExtractLabels(payload, existingProjection);
var rationale = ExtractRationale(payload, explainRef);
var result = new PolicyEvaluationResult(
status,
severity,
labels,
explainRef,
rationale);
return Task.FromResult(result);
}
private static PolicyEvaluationResult CreateFallback(FindingProjection? existingProjection)
{
var labels = existingProjection?.Labels is not null
? (JsonObject)existingProjection.Labels.DeepClone()
: new JsonObject();
var rationale = existingProjection?.PolicyRationale is not null
? CloneArray(existingProjection.PolicyRationale)
: new JsonArray();
return new PolicyEvaluationResult(
existingProjection?.Status,
existingProjection?.Severity,
labels,
existingProjection?.ExplainRef,
rationale);
}
private static JsonObject ExtractLabels(JsonObject? payload, FindingProjection? existingProjection)
{
var labels = existingProjection?.Labels is not null
? (JsonObject)existingProjection.Labels.DeepClone()
: new JsonObject();
if (payload is null)
{
return labels;
}
if (payload.TryGetPropertyValue("labels", out var labelsNode) && labelsNode is JsonObject labelUpdates)
{
foreach (var property in labelUpdates)
{
if (property.Value is null || property.Value.GetValueKind() == JsonValueKind.Null)
{
labels.Remove(property.Key);
}
else
{
labels[property.Key] = property.Value.DeepClone();
}
}
}
if (payload.TryGetPropertyValue("labelsRemove", out var removeNode) && removeNode is JsonArray removeArray)
{
foreach (var item in removeArray)
{
if (item is JsonValue value && value.TryGetValue(out string? key) && !string.IsNullOrWhiteSpace(key))
{
labels.Remove(key);
}
}
}
return labels;
}
private static JsonArray ExtractRationale(JsonObject? payload, string? explainRef)
{
if (payload?.TryGetPropertyValue("rationaleRefs", out var rationaleNode) == true &&
rationaleNode is JsonArray rationaleRefs)
{
return CloneArray(rationaleRefs);
}
var rationale = new JsonArray();
if (!string.IsNullOrWhiteSpace(explainRef))
{
rationale.Add(explainRef);
}
return rationale;
}
private static string? ExtractString(JsonObject? obj, string propertyName)
{
if (obj is null)
{
return null;
}
if (!obj.TryGetPropertyValue(propertyName, out var value) || value is null)
{
return null;
}
if (value is JsonValue jsonValue && jsonValue.TryGetValue(out string? text))
{
return string.IsNullOrWhiteSpace(text) ? null : text;
}
return value.ToString();
}
private static decimal? ExtractDecimal(JsonObject? obj, string propertyName)
{
if (obj is null)
{
return null;
}
if (!obj.TryGetPropertyValue(propertyName, out var value) || value is null)
{
return null;
}
if (value is JsonValue jsonValue)
{
if (jsonValue.TryGetValue(out decimal decimalValue))
{
return decimalValue;
}
if (jsonValue.TryGetValue(out double doubleValue))
{
return Convert.ToDecimal(doubleValue);
}
}
if (decimal.TryParse(value.ToString(), out var parsed))
{
return parsed;
}
return null;
}
private static JsonArray CloneArray(JsonArray array)
{
var clone = new JsonArray();
foreach (var item in array)
{
clone.Add(item?.DeepClone());
}
return clone;
}
}