Files
git.stella-ops.org/src/Findings/StellaOps.Findings.Ledger/Hashing/ProjectionHashing.cs
master 2eb6852d34
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Add unit tests for SBOM ingestion and transformation
- 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.
2025-11-04 07:49:39 +02:00

99 lines
3.8 KiB
C#

using System.Text.Json.Nodes;
using StellaOps.Findings.Ledger.Domain;
namespace StellaOps.Findings.Ledger.Hashing;
public static class ProjectionHashing
{
private const string TenantIdProperty = nameof(FindingProjection.TenantId);
private const string FindingIdProperty = nameof(FindingProjection.FindingId);
private const string PolicyVersionProperty = nameof(FindingProjection.PolicyVersion);
private const string StatusProperty = nameof(FindingProjection.Status);
private const string SeverityProperty = nameof(FindingProjection.Severity);
private const string LabelsProperty = nameof(FindingProjection.Labels);
private const string CurrentEventIdProperty = nameof(FindingProjection.CurrentEventId);
private const string ExplainRefProperty = nameof(FindingProjection.ExplainRef);
private const string PolicyRationaleProperty = nameof(FindingProjection.PolicyRationale);
private const string UpdatedAtProperty = nameof(FindingProjection.UpdatedAt);
public static string ComputeCycleHash(FindingProjection projection)
{
ArgumentNullException.ThrowIfNull(projection);
var envelope = new JsonObject
{
[TenantIdProperty] = projection.TenantId,
[FindingIdProperty] = projection.FindingId,
[PolicyVersionProperty] = projection.PolicyVersion,
[StatusProperty] = projection.Status,
[SeverityProperty] = projection.Severity,
[LabelsProperty] = projection.Labels.DeepClone(),
[CurrentEventIdProperty] = projection.CurrentEventId.ToString(),
[ExplainRefProperty] = projection.ExplainRef,
[PolicyRationaleProperty] = CloneArray(projection.PolicyRationale),
[UpdatedAtProperty] = FormatTimestamp(projection.UpdatedAt)
};
var canonical = LedgerCanonicalJsonSerializer.Canonicalize(envelope);
var canonicalJson = LedgerCanonicalJsonSerializer.Serialize(canonical);
return HashUtilities.ComputeSha256Hex(canonicalJson);
}
private static string FormatTimestamp(DateTimeOffset value)
{
var utc = value.ToUniversalTime();
Span<char> buffer = stackalloc char[24];
WriteFourDigits(buffer, 0, utc.Year);
buffer[4] = '-';
WriteTwoDigits(buffer, 5, utc.Month);
buffer[7] = '-';
WriteTwoDigits(buffer, 8, utc.Day);
buffer[10] = 'T';
WriteTwoDigits(buffer, 11, utc.Hour);
buffer[13] = ':';
WriteTwoDigits(buffer, 14, utc.Minute);
buffer[16] = ':';
WriteTwoDigits(buffer, 17, utc.Second);
buffer[19] = '.';
WriteThreeDigits(buffer, 20, utc.Millisecond);
buffer[23] = 'Z';
return new string(buffer);
}
private static void WriteFourDigits(Span<char> buffer, int offset, int value)
{
buffer[offset] = (char)('0' + (value / 1000) % 10);
buffer[offset + 1] = (char)('0' + (value / 100) % 10);
buffer[offset + 2] = (char)('0' + (value / 10) % 10);
buffer[offset + 3] = (char)('0' + value % 10);
}
private static void WriteTwoDigits(Span<char> buffer, int offset, int value)
{
buffer[offset] = (char)('0' + (value / 10) % 10);
buffer[offset + 1] = (char)('0' + value % 10);
}
private static void WriteThreeDigits(Span<char> buffer, int offset, int value)
{
buffer[offset] = (char)('0' + (value / 100) % 10);
buffer[offset + 1] = (char)('0' + (value / 10) % 10);
buffer[offset + 2] = (char)('0' + value % 10);
}
private static JsonArray CloneArray(JsonArray array)
{
ArgumentNullException.ThrowIfNull(array);
var clone = new JsonArray();
foreach (var item in array)
{
clone.Add(item?.DeepClone());
}
return clone;
}
}