feat: Add VEX Lens CI and Load Testing Plan
- Introduced a comprehensive CI job structure for VEX Lens, including build, test, linting, and load testing. - Defined load test parameters and SLOs for VEX Lens API and Issuer Directory. - Created Grafana dashboards and alerting mechanisms for monitoring API performance and error rates. - Established offline posture guidelines for CI jobs and load testing. feat: Implement deterministic projection verification script - Added `verify_projection.sh` script for verifying the integrity of projection exports against expected hashes. - Ensured robust error handling for missing files and hash mismatches. feat: Develop Vuln Explorer CI and Ops Plan - Created CI jobs for Vuln Explorer, including build, test, and replay verification. - Implemented backup and disaster recovery strategies for MongoDB and Redis. - Established Merkle anchoring verification and automation for ledger projector. feat: Introduce EventEnvelopeHasher for hashing event envelopes - Implemented `EventEnvelopeHasher` to compute SHA256 hashes for event envelopes. feat: Add Risk Store and Dashboard components - Developed `RiskStore` for managing risk data and state. - Created `RiskDashboardComponent` for displaying risk profiles with filtering capabilities. - Implemented unit tests for `RiskStore` and `RiskDashboardComponent`. feat: Enhance Vulnerability Detail Component - Developed `VulnerabilityDetailComponent` for displaying detailed information about vulnerabilities. - Implemented error handling for missing vulnerability IDs and loading failures.
This commit is contained in:
@@ -2,6 +2,8 @@ using System.Collections.Immutable;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Orchestrator.Core;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public sealed record EventEnvelope(
|
||||
[property: JsonPropertyName("schemaVersion")] string SchemaVersion,
|
||||
|
||||
@@ -26,7 +26,8 @@ public static class CanonicalJsonHasher
|
||||
public static string ToCanonicalJson<T>(T value)
|
||||
{
|
||||
var node = JsonSerializer.SerializeToNode(value, SerializerOptions) ?? new JsonObject();
|
||||
var ordered = OrderNode(node);
|
||||
// Work on a detached copy to avoid parent conflicts.
|
||||
var ordered = OrderNode(node.Clone());
|
||||
return ordered.ToJsonString(SerializerOptions);
|
||||
}
|
||||
|
||||
@@ -49,18 +50,18 @@ public static class CanonicalJsonHasher
|
||||
var orderedObj = new JsonObject();
|
||||
foreach (var kvp in obj.OrderBy(x => x.Key, StringComparer.Ordinal))
|
||||
{
|
||||
orderedObj.Add(kvp.Key, kvp.Value is null ? null : OrderNode(kvp.Value));
|
||||
orderedObj.Add(kvp.Key, kvp.Value is null ? null : OrderNode(kvp.Value.Clone()));
|
||||
}
|
||||
return orderedObj;
|
||||
case JsonArray arr:
|
||||
var orderedArr = new JsonArray();
|
||||
foreach (var item in arr)
|
||||
{
|
||||
orderedArr.Add(item is null ? null : OrderNode(item));
|
||||
orderedArr.Add(item is null ? null : OrderNode(item.Clone()));
|
||||
}
|
||||
return orderedArr;
|
||||
default:
|
||||
return node; // primitives stay as-is
|
||||
return node.Clone(); // primitives stay as-is
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using StellaOps.Orchestrator.Core.Domain.Events;
|
||||
|
||||
namespace StellaOps.Orchestrator.Core.Hashing;
|
||||
|
||||
public static class EventEnvelopeHasher
|
||||
{
|
||||
public static string Compute(EventEnvelope envelope)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(envelope);
|
||||
return CanonicalJsonHasher.ComputeCanonicalSha256(envelope);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using StellaOps.Orchestrator.Core;
|
||||
using StellaOps.Orchestrator.Core.Hashing;
|
||||
|
||||
namespace StellaOps.Orchestrator.Tests;
|
||||
|
||||
@@ -52,4 +53,40 @@ public class EventEnvelopeTests
|
||||
Assert.Equal(envelope.Job.Id, roundtrip.Job.Id);
|
||||
Assert.Equal(envelope.Actor.Subject, roundtrip.Actor.Subject);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Hash_IsDeterministic()
|
||||
{
|
||||
var job = new EventJob(
|
||||
Id: "job_123",
|
||||
Type: "pack-run",
|
||||
RunId: "run_123",
|
||||
Attempt: 1,
|
||||
LeaseId: "lease_1",
|
||||
TaskRunnerId: "tr_9",
|
||||
Status: "scheduled",
|
||||
Reason: null,
|
||||
PayloadDigest: "sha256:deadbeef",
|
||||
Artifacts: ImmutableArray.Create<EventArtifact>(),
|
||||
Provenance: ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
var actor = new EventActor("worker-sdk-go", ImmutableArray.Create("orch:quota"));
|
||||
|
||||
var envelope = EventEnvelope.Create(
|
||||
eventType: "job.scheduled",
|
||||
tenantId: "tenant-alpha",
|
||||
job: job,
|
||||
actor: actor,
|
||||
projectId: "proj-1",
|
||||
correlationId: "corr-123",
|
||||
occurredAt: new DateTimeOffset(2025, 12, 1, 12, 0, 0, TimeSpan.Zero),
|
||||
eventId: "evt-fixed",
|
||||
idempotencyKey: "fixed-key");
|
||||
|
||||
var hash1 = EventEnvelopeHasher.Compute(envelope);
|
||||
var hash2 = EventEnvelopeHasher.Compute(envelope);
|
||||
|
||||
Assert.Equal(hash1, hash2);
|
||||
Assert.Equal(64, hash1.Length);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user