blockers 2
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-23 14:54:17 +02:00
parent f47d2d1377
commit cce96f3596
100 changed files with 2758 additions and 1912 deletions

View File

@@ -1,3 +1,4 @@
using System;
using FluentAssertions;
using StellaOps.Provenance.Attestation;
using Xunit;

View File

@@ -1,5 +1,7 @@
using System.Text;
using System.Collections.Generic;
using FluentAssertions;
using System.Threading.Tasks;
using StellaOps.Provenance.Attestation;
using Xunit;
@@ -11,16 +13,40 @@ public class PromotionAttestationBuilderTests
public void Produces_canonical_json_for_predicate()
{
var predicate = new PromotionPredicate(
ImageDigest: sha256:img,
SbomDigest: sha256:sbom,
VexDigest: sha256:vex,
PromotionId: prom-1,
RekorEntry: uuid,
Metadata: new Dictionary<string, string>{{env,prod}});
ImageDigest: "sha256:img",
SbomDigest: "sha256:sbom",
VexDigest: "sha256:vex",
PromotionId: "prom-1",
RekorEntry: "uuid",
// Intentionally shuffled input order; canonical JSON must be sorted.
Metadata: new Dictionary<string, string> { { "env", "prod" }, { "region", "us-east" } });
var bytes = PromotionAttestationBuilder.CreateCanonicalJson(predicate);
var json = Encoding.UTF8.GetString(bytes);
json.Should().Be("ImageDigest":"sha256:img");
json.Should().Be("{\"ImageDigest\":\"sha256:img\",\"Metadata\":{\"env\":\"prod\",\"region\":\"us-east\"},\"PromotionId\":\"prom-1\",\"RekorEntry\":\"uuid\",\"SbomDigest\":\"sha256:sbom\",\"VexDigest\":\"sha256:vex\"}");
}
[Fact]
public async Task BuildAsync_adds_predicate_claim_and_signs_payload()
{
var predicate = new PromotionPredicate(
ImageDigest: "sha256:img",
SbomDigest: "sha256:sbom",
VexDigest: "sha256:vex",
PromotionId: "prom-1");
var key = new InMemoryKeyProvider("kid-1", Encoding.UTF8.GetBytes("secret"));
var signer = new HmacSigner(key);
var attestation = await PromotionAttestationBuilder.BuildAsync(
predicate,
signer,
claims: new Dictionary<string, string> { { "traceId", "abc123" } });
attestation.Payload.Should().BeEquivalentTo(PromotionAttestationBuilder.CreateCanonicalJson(predicate));
attestation.Signature.KeyId.Should().Be("kid-1");
attestation.Signature.Claims.Should().ContainKey("predicateType").WhoseValue.Should().Be(PromotionAttestationBuilder.PredicateType);
attestation.Signature.Claims.Should().ContainKey("traceId").WhoseValue.Should().Be("abc123");
}
}

View File

@@ -79,6 +79,6 @@ public class SampleStatementDigestTests
var statements = LoadSamples().Select(pair => pair.Statement).ToArray();
BuildStatementDigest.ComputeMerkleRootHex(statements)
.Should()
.Be("e3a89fe0d08e2b16a6c7f1feb1d82d9e7ef9e8b74363bf60da64f36078d80eea");
.Be("958465d432c9c8497f9ea5c1476cc7f2bea2a87d3ca37d8293586bf73922dd73");
}
}

View File

@@ -5,6 +5,10 @@
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Remove="xunit" />
<PackageReference Remove="xunit.runner.visualstudio" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../StellaOps.Provenance.Attestation/StellaOps.Provenance.Attestation.csproj" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />

View File

@@ -1,5 +1,6 @@
using System.Text;
using FluentAssertions;
using System.Threading.Tasks;
using StellaOps.Provenance.Attestation;
using Xunit;
@@ -7,36 +8,38 @@ namespace StellaOps.Provenance.Attestation.Tests;
public class VerificationTests
{
private const string Payload = "{\"hello\":\"world\"}";
private const string ContentType = "application/json";
[Fact]
public async Task Verifier_accepts_valid_signature()
{
var key = new InMemoryKeyProvider(test-key, Encoding.UTF8.GetBytes(secret));
var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret"));
var signer = new HmacSigner(key);
var verifier = new HmacVerifier(key);
var request = new SignRequest(Encoding.UTF8.GetBytes(payload), application/json);
var request = new SignRequest(Encoding.UTF8.GetBytes(Payload), ContentType);
var signature = await signer.SignAsync(request);
var result = await verifier.VerifyAsync(request, signature);
result.IsValid.Should().BeTrue();
result.Reason.Should().Be(ok);
result.Reason.Should().Be("verified");
}
[Fact]
public async Task Verifier_rejects_tampered_payload()
{
var key = new InMemoryKeyProvider(test-key, Encoding.UTF8.GetBytes(secret));
var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret"));
var signer = new HmacSigner(key);
var verifier = new HmacVerifier(key);
var request = new SignRequest(Encoding.UTF8.GetBytes(payload), application/json);
var request = new SignRequest(Encoding.UTF8.GetBytes(Payload), ContentType);
var signature = await signer.SignAsync(request);
var tampered = new SignRequest(Encoding.UTF8.GetBytes(payload-tampered), application/json);
var tampered = new SignRequest(Encoding.UTF8.GetBytes(Payload + "-tampered"), ContentType);
var result = await verifier.VerifyAsync(tampered, signature);
result.IsValid.Should().BeFalse();
result.Reason.Should().Contain(mismatch);
result.Reason.Should().Be("signature or time invalid");
}
}
EOF}