Resolve Concelier/Excititor merge conflicts
This commit is contained in:
		| @@ -0,0 +1,66 @@ | ||||
| using StellaOps.Zastava.Core.Contracts; | ||||
|  | ||||
| namespace StellaOps.Zastava.Core.Tests.Contracts; | ||||
|  | ||||
| public sealed class ZastavaContractVersionsTests | ||||
| { | ||||
|     [Theory] | ||||
|     [InlineData("zastava.runtime.event@v1", "zastava.runtime.event", 1, 0)] | ||||
|     [InlineData("zastava.runtime.event@v1.0", "zastava.runtime.event", 1, 0)] | ||||
|     [InlineData("zastava.admission.decision@v1.2", "zastava.admission.decision", 1, 2)] | ||||
|     public void TryParse_ParsesCanonicalForms(string input, string schema, int major, int minor) | ||||
|     { | ||||
|         var success = ZastavaContractVersions.ContractVersion.TryParse(input, out var contract); | ||||
|  | ||||
|         Assert.True(success); | ||||
|         Assert.Equal(schema, contract.Schema); | ||||
|         Assert.Equal(new Version(major, minor), contract.Version); | ||||
|         Assert.Equal($"{schema}@v{major}.{minor}", contract.ToString()); | ||||
|     } | ||||
|  | ||||
|     [Theory] | ||||
|     [InlineData("")] | ||||
|     [InlineData("zastava.runtime.event")] | ||||
|     [InlineData("runtime@1.0")] | ||||
|     [InlineData("zastava.runtime.event@vinvalid")] | ||||
|     public void TryParse_InvalidInputs_ReturnsFalse(string input) | ||||
|     { | ||||
|         var success = ZastavaContractVersions.ContractVersion.TryParse(input, out _); | ||||
|  | ||||
|         Assert.False(success); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public void IsRuntimeEventSupported_RespectsMajorCompatibility() | ||||
|     { | ||||
|         Assert.True(ZastavaContractVersions.IsRuntimeEventSupported("zastava.runtime.event@v1")); | ||||
|         Assert.True(ZastavaContractVersions.IsRuntimeEventSupported("zastava.runtime.event@v1.0")); | ||||
|         Assert.False(ZastavaContractVersions.IsRuntimeEventSupported("zastava.runtime.event@v2.0")); | ||||
|         Assert.False(ZastavaContractVersions.IsRuntimeEventSupported("zastava.admission.decision@v1")); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public void NegotiateRuntimeEvent_PicksHighestCommonVersion() | ||||
|     { | ||||
|         var negotiated = ZastavaContractVersions.NegotiateRuntimeEvent(new[] | ||||
|         { | ||||
|             "zastava.runtime.event@v1.0", | ||||
|             "zastava.runtime.event@v0.9", | ||||
|             "zastava.admission.decision@v1" | ||||
|         }); | ||||
|  | ||||
|         Assert.Equal("zastava.runtime.event@v1.0", negotiated.ToString()); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public void NegotiateRuntimeEvent_FallsBackToLocalWhenNoMatch() | ||||
|     { | ||||
|         var negotiated = ZastavaContractVersions.NegotiateRuntimeEvent(new[] | ||||
|         { | ||||
|             "zastava.runtime.event@v2.0", | ||||
|             "zastava.admission.decision@v2.0" | ||||
|         }); | ||||
|  | ||||
|         Assert.Equal(ZastavaContractVersions.RuntimeEvent.ToString(), negotiated.ToString()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,204 @@ | ||||
| using System.Text; | ||||
| using StellaOps.Zastava.Core.Contracts; | ||||
| using StellaOps.Zastava.Core.Hashing; | ||||
| using StellaOps.Zastava.Core.Serialization; | ||||
|  | ||||
| namespace StellaOps.Zastava.Core.Tests.Serialization; | ||||
|  | ||||
| public sealed class ZastavaCanonicalJsonSerializerTests | ||||
| { | ||||
|     [Fact] | ||||
|     public void Serialize_RuntimeEventEnvelope_ProducesDeterministicOrdering() | ||||
|     { | ||||
|         var runtimeEvent = new RuntimeEvent | ||||
|         { | ||||
|             EventId = "evt-123", | ||||
|             When = DateTimeOffset.Parse("2025-10-19T12:34:56Z"), | ||||
|             Kind = RuntimeEventKind.ContainerStart, | ||||
|             Tenant = "tenant-01", | ||||
|             Node = "node-a", | ||||
|             Runtime = new RuntimeEngine | ||||
|             { | ||||
|                 Engine = "containerd", | ||||
|                 Version = "1.7.19" | ||||
|             }, | ||||
|             Workload = new RuntimeWorkload | ||||
|             { | ||||
|                 Platform = "kubernetes", | ||||
|                 Namespace = "payments", | ||||
|                 Pod = "api-7c9fbbd8b7-ktd84", | ||||
|                 Container = "api", | ||||
|                 ContainerId = "containerd://abc", | ||||
|                 ImageRef = "ghcr.io/acme/api@sha256:abcd", | ||||
|                 Owner = new RuntimeWorkloadOwner | ||||
|                 { | ||||
|                     Kind = "Deployment", | ||||
|                     Name = "api" | ||||
|                 } | ||||
|             }, | ||||
|             Process = new RuntimeProcess | ||||
|             { | ||||
|                 Pid = 12345, | ||||
|                 Entrypoint = new[] { "/entrypoint.sh", "--serve" }, | ||||
|                 EntryTrace = new[] | ||||
|                 { | ||||
|                     new RuntimeEntryTrace | ||||
|                     { | ||||
|                         File = "/entrypoint.sh", | ||||
|                         Line = 3, | ||||
|                         Op = "exec", | ||||
|                         Target = "/usr/bin/python3" | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             LoadedLibraries = new[] | ||||
|             { | ||||
|                 new RuntimeLoadedLibrary | ||||
|                 { | ||||
|                     Path = "/lib/x86_64-linux-gnu/libssl.so.3", | ||||
|                     Inode = 123456, | ||||
|                     Sha256 = "abc123" | ||||
|                 } | ||||
|             }, | ||||
|             Posture = new RuntimePosture | ||||
|             { | ||||
|                 ImageSigned = true, | ||||
|                 SbomReferrer = "present", | ||||
|                 Attestation = new RuntimeAttestation | ||||
|                 { | ||||
|                     Uuid = "rekor-uuid", | ||||
|                     Verified = true | ||||
|                 } | ||||
|             }, | ||||
|             Delta = new RuntimeDelta | ||||
|             { | ||||
|                 BaselineImageDigest = "sha256:abcd", | ||||
|                 ChangedFiles = new[] { "/opt/app/server.py" }, | ||||
|                 NewBinaries = new[] | ||||
|                 { | ||||
|                     new RuntimeNewBinary | ||||
|                     { | ||||
|                         Path = "/usr/local/bin/helper", | ||||
|                         Sha256 = "def456" | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             Evidence = new[] | ||||
|             { | ||||
|                 new RuntimeEvidence | ||||
|                 { | ||||
|                     Signal = "procfs.maps", | ||||
|                     Value = "/lib/.../libssl.so.3@0x7f..." | ||||
|                 } | ||||
|             }, | ||||
|             Annotations = new Dictionary<string, string> | ||||
|             { | ||||
|                 ["source"] = "unit-test" | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         var envelope = RuntimeEventEnvelope.Create(runtimeEvent, ZastavaContractVersions.RuntimeEvent); | ||||
|         var json = ZastavaCanonicalJsonSerializer.Serialize(envelope); | ||||
|  | ||||
|         var expectedOrder = new[] | ||||
|         { | ||||
|             "\"schemaVersion\"", | ||||
|             "\"event\"", | ||||
|             "\"eventId\"", | ||||
|             "\"when\"", | ||||
|             "\"kind\"", | ||||
|             "\"tenant\"", | ||||
|             "\"node\"", | ||||
|             "\"runtime\"", | ||||
|             "\"engine\"", | ||||
|             "\"version\"", | ||||
|             "\"workload\"", | ||||
|             "\"platform\"", | ||||
|             "\"namespace\"", | ||||
|             "\"pod\"", | ||||
|             "\"container\"", | ||||
|             "\"containerId\"", | ||||
|             "\"imageRef\"", | ||||
|             "\"owner\"", | ||||
|             "\"kind\"", | ||||
|             "\"name\"", | ||||
|             "\"process\"", | ||||
|             "\"pid\"", | ||||
|             "\"entrypoint\"", | ||||
|             "\"entryTrace\"", | ||||
|             "\"loadedLibs\"", | ||||
|             "\"posture\"", | ||||
|             "\"imageSigned\"", | ||||
|             "\"sbomReferrer\"", | ||||
|             "\"attestation\"", | ||||
|             "\"uuid\"", | ||||
|             "\"verified\"", | ||||
|             "\"delta\"", | ||||
|             "\"baselineImageDigest\"", | ||||
|             "\"changedFiles\"", | ||||
|             "\"newBinaries\"", | ||||
|             "\"path\"", | ||||
|             "\"sha256\"", | ||||
|             "\"evidence\"", | ||||
|             "\"signal\"", | ||||
|             "\"value\"", | ||||
|             "\"annotations\"", | ||||
|             "\"source\"" | ||||
|         }; | ||||
|  | ||||
|         var cursor = -1; | ||||
|         foreach (var token in expectedOrder) | ||||
|         { | ||||
|             var position = json.IndexOf(token, cursor + 1, StringComparison.Ordinal); | ||||
|             Assert.True(position > cursor, $"Property token {token} not found in the expected order."); | ||||
|             cursor = position; | ||||
|         } | ||||
|  | ||||
|         Assert.DoesNotContain("  ", json, StringComparison.Ordinal); | ||||
|         Assert.StartsWith("{\"schemaVersion\"", json, StringComparison.Ordinal); | ||||
|         Assert.EndsWith("}}", json, StringComparison.Ordinal); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public void ComputeMultihash_ProducesStableBase64UrlDigest() | ||||
|     { | ||||
|         var decision = AdmissionDecisionEnvelope.Create( | ||||
|             new AdmissionDecision | ||||
|             { | ||||
|                 AdmissionId = "admission-123", | ||||
|                 Namespace = "payments", | ||||
|                 PodSpecDigest = "sha256:deadbeef", | ||||
|                 Images = new[] | ||||
|                 { | ||||
|                     new AdmissionImageVerdict | ||||
|                     { | ||||
|                         Name = "ghcr.io/acme/api:1.2.3", | ||||
|                         Resolved = "ghcr.io/acme/api@sha256:abcd", | ||||
|                         Signed = true, | ||||
|                         HasSbomReferrers = true, | ||||
|                         PolicyVerdict = PolicyVerdict.Pass, | ||||
|                         Reasons = Array.Empty<string>(), | ||||
|                         Rekor = new AdmissionRekorEvidence | ||||
|                         { | ||||
|                             Uuid = "xyz", | ||||
|                             Verified = true | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 Decision = AdmissionDecisionOutcome.Allow, | ||||
|                 TtlSeconds = 300 | ||||
|             }, | ||||
|             ZastavaContractVersions.AdmissionDecision); | ||||
|  | ||||
|         var canonicalJson = ZastavaCanonicalJsonSerializer.Serialize(decision); | ||||
|         var expectedDigestBytes = SHA256.HashData(Encoding.UTF8.GetBytes(canonicalJson)); | ||||
|         var expected = $"sha256-{Convert.ToBase64String(expectedDigestBytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')}"; | ||||
|  | ||||
|         var hash = ZastavaHashing.ComputeMultihash(decision); | ||||
|  | ||||
|         Assert.Equal(expected, hash); | ||||
|  | ||||
|         var sha512 = ZastavaHashing.ComputeMultihash(Encoding.UTF8.GetBytes(canonicalJson), "sha512"); | ||||
|         Assert.StartsWith("sha512-", sha512, StringComparison.Ordinal); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net10.0</TargetFramework> | ||||
|     <LangVersion>preview</LangVersion> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|     <Nullable>enable</Nullable> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="../StellaOps.Zastava.Core/StellaOps.Zastava.Core.csproj" /> | ||||
|     <ProjectReference Include="../StellaOps.Authority/StellaOps.Auth.Client/StellaOps.Auth.Client.csproj" /> | ||||
|     <ProjectReference Include="../StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOps.Auth.Abstractions.csproj" /> | ||||
|     <ProjectReference Include="../StellaOps.Auth.Security/StellaOps.Auth.Security.csproj" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
		Reference in New Issue
	
	Block a user