save progress

This commit is contained in:
StellaOps Bot
2026-01-02 21:06:27 +02:00
parent f46bde5575
commit 3f197814c5
441 changed files with 21545 additions and 4306 deletions

View File

@@ -6,6 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Org.BouncyCastle.Crypto.Parameters;
using StellaOps.Attestor.Envelope;
using StellaOps.Attestor.GraphRoot.Models;
using Xunit;
@@ -15,6 +16,9 @@ namespace StellaOps.Attestor.GraphRoot.Tests;
public class GraphRootAttestorTests
{
private static readonly TimeProvider FixedTimeProviderInstance =
new FixedTimeProvider(new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero));
private readonly Mock<IMerkleRootComputer> _merkleComputerMock;
private readonly EnvelopeSignatureService _signatureService;
private readonly GraphRootAttestor _attestor;
@@ -28,12 +32,7 @@ public class GraphRootAttestorTests
.Setup(m => m.ComputeRoot(It.IsAny<IReadOnlyList<ReadOnlyMemory<byte>>>()))
.Returns(new byte[32]); // 32-byte hash
// Create a real test key for signing (need both private and public for Ed25519)
var privateKey = new byte[64]; // Ed25519 expanded private key is 64 bytes
var publicKey = new byte[32];
Random.Shared.NextBytes(privateKey);
Random.Shared.NextBytes(publicKey);
_testKey = EnvelopeKey.CreateEd25519Signer(privateKey, publicKey, "test-key-id");
_testKey = CreateDeterministicKey("test-key-id");
_signatureService = new EnvelopeSignatureService();
@@ -41,7 +40,8 @@ public class GraphRootAttestorTests
_merkleComputerMock.Object,
_signatureService,
_ => _testKey,
NullLogger<GraphRootAttestor>.Instance);
NullLogger<GraphRootAttestor>.Instance,
timeProvider: FixedTimeProviderInstance);
}
[Trait("Category", TestCategories.Unit)]
@@ -170,6 +170,40 @@ public class GraphRootAttestorTests
Assert.Contains("sha256:params", digestStrings);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AttestAsync_IncludesEvidenceIdsInLeaves()
{
// Arrange
var request = new GraphRootAttestationRequest
{
GraphType = GraphType.DependencyGraph,
NodeIds = Array.Empty<string>(),
EdgeIds = Array.Empty<string>(),
PolicyDigest = "sha256:policy",
FeedsDigest = "sha256:feeds",
ToolchainDigest = "sha256:toolchain",
ParamsDigest = "sha256:params",
ArtifactDigest = "sha256:artifact",
EvidenceIds = new[] { "evidence-b", "evidence-a" }
};
IReadOnlyList<ReadOnlyMemory<byte>>? capturedLeaves = null;
_merkleComputerMock
.Setup(m => m.ComputeRoot(It.IsAny<IReadOnlyList<ReadOnlyMemory<byte>>>()))
.Callback<IReadOnlyList<ReadOnlyMemory<byte>>>(leaves => capturedLeaves = leaves)
.Returns(new byte[32]);
// Act
await _attestor.AttestAsync(request);
// Assert
Assert.NotNull(capturedLeaves);
var leafStrings = capturedLeaves.Select(l => System.Text.Encoding.UTF8.GetString(l.Span)).ToList();
Assert.Contains("evidence-a", leafStrings);
Assert.Contains("evidence-b", leafStrings);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AttestAsync_NullRequest_ThrowsArgumentNullException()
@@ -187,7 +221,8 @@ public class GraphRootAttestorTests
_merkleComputerMock.Object,
_signatureService,
_ => null,
NullLogger<GraphRootAttestor>.Instance);
NullLogger<GraphRootAttestor>.Instance,
timeProvider: FixedTimeProviderInstance);
var request = CreateValidRequest();
@@ -249,4 +284,32 @@ public class GraphRootAttestorTests
ArtifactDigest = "sha256:artifact345"
};
}
private static EnvelopeKey CreateDeterministicKey(string keyId)
{
var seed = new byte[32];
for (var i = 0; i < seed.Length; i++)
{
seed[i] = (byte)(i + 1);
}
var privateKeyParameters = new Ed25519PrivateKeyParameters(seed, 0);
var publicKeyParameters = privateKeyParameters.GeneratePublicKey();
var publicKey = publicKeyParameters.GetEncoded();
var privateKey = privateKeyParameters.GetEncoded();
return EnvelopeKey.CreateEd25519Signer(privateKey, publicKey, keyId);
}
private sealed class FixedTimeProvider : TimeProvider
{
private readonly DateTimeOffset _fixedTime;
public FixedTimeProvider(DateTimeOffset fixedTime)
{
_fixedTime = fixedTime;
}
public override DateTimeOffset GetUtcNow() => _fixedTime;
}
}

View File

@@ -63,6 +63,7 @@ public class GraphRootModelsTests
public void GraphRootPredicate_RequiredProperties_Set()
{
// Arrange & Act
var fixedTime = new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
var predicate = new GraphRootPredicate
{
GraphType = "DependencyGraph",
@@ -79,7 +80,7 @@ public class GraphRootModelsTests
ParamsDigest = "sha256:pr"
},
CanonVersion = "stella:canon:v1",
ComputedAt = DateTimeOffset.UtcNow,
ComputedAt = fixedTime,
ComputedBy = "test",
ComputedByVersion = "1.0.0"
};
@@ -97,6 +98,7 @@ public class GraphRootModelsTests
public void GraphRootAttestation_HasCorrectDefaults()
{
// Arrange & Act
var fixedTime = new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
var attestation = new GraphRootAttestation
{
Subject = new[]
@@ -123,7 +125,7 @@ public class GraphRootModelsTests
ParamsDigest = "sha256:pr"
},
CanonVersion = "v1",
ComputedAt = DateTimeOffset.UtcNow,
ComputedAt = fixedTime,
ComputedBy = "test",
ComputedByVersion = "1.0"
}

View File

@@ -18,6 +18,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using Org.BouncyCastle.Crypto.Parameters;
using StellaOps.Attestor.Core.Rekor;
using StellaOps.Attestor.Core.Submission;
using StellaOps.Attestor.Envelope;
@@ -33,16 +34,23 @@ namespace StellaOps.Attestor.GraphRoot.Tests;
/// </summary>
public class GraphRootPipelineIntegrationTests
{
private static readonly TimeProvider FixedTimeProviderInstance =
new FixedTimeProvider(new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero));
#region Helpers
private static (EnvelopeKey Key, byte[] PublicKey) CreateTestKey()
{
// Generate a real Ed25519 key pair for testing
var privateKey = new byte[64]; // Ed25519 expanded private key
var publicKey = new byte[32];
Random.Shared.NextBytes(privateKey);
Random.Shared.NextBytes(publicKey);
var seed = new byte[32];
for (var i = 0; i < seed.Length; i++)
{
seed[i] = (byte)(i + 1);
}
var privateKeyParameters = new Ed25519PrivateKeyParameters(seed, 0);
var publicKeyParameters = privateKeyParameters.GeneratePublicKey();
var publicKey = publicKeyParameters.GetEncoded();
var privateKey = privateKeyParameters.GetEncoded();
var key = EnvelopeKey.CreateEd25519Signer(privateKey, publicKey, "test-integration-key");
return (key, publicKey);
}
@@ -58,7 +66,8 @@ public class GraphRootPipelineIntegrationTests
_ => key,
NullLogger<GraphRootAttestor>.Instance,
rekorClient,
Options.Create(options ?? new GraphRootAttestorOptions()));
Options.Create(options ?? new GraphRootAttestorOptions()),
FixedTimeProviderInstance);
}
private static GraphRootAttestationRequest CreateRealisticRequest(
@@ -69,7 +78,7 @@ public class GraphRootPipelineIntegrationTests
var nodeIds = Enumerable.Range(1, nodeCount)
.Select(i =>
{
var content = $"node-{i}-content-{Guid.NewGuid()}";
var content = $"node-{i}-content";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(content));
return $"sha256:{Convert.ToHexStringLower(hash)}";
})
@@ -102,7 +111,7 @@ public class GraphRootPipelineIntegrationTests
ToolchainDigest = toolchainDigest,
ParamsDigest = paramsDigest,
ArtifactDigest = artifactDigest,
EvidenceIds = [$"evidence-{Guid.NewGuid()}", $"evidence-{Guid.NewGuid()}"]
EvidenceIds = ["evidence-1", "evidence-2"]
};
}
@@ -219,7 +228,7 @@ public class GraphRootPipelineIntegrationTests
Uuid = "test-uuid-12345",
Index = 42,
Status = "included",
IntegratedTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
IntegratedTime = new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero).ToUnixTimeSeconds()
});
var options = new GraphRootAttestorOptions
@@ -348,8 +357,7 @@ public class GraphRootPipelineIntegrationTests
// Assert
Assert.False(verifyResult.IsValid);
Assert.Contains("Root mismatch", verifyResult.FailureReason);
Assert.NotEqual(verifyResult.ExpectedRoot, verifyResult.ComputedRoot);
Assert.Contains("Predicate node IDs do not match", verifyResult.FailureReason);
}
[Trait("Category", TestCategories.Unit)]
@@ -375,7 +383,7 @@ public class GraphRootPipelineIntegrationTests
// Assert
Assert.False(verifyResult.IsValid);
Assert.Contains("Root mismatch", verifyResult.FailureReason);
Assert.Contains("Predicate edge IDs do not match", verifyResult.FailureReason);
}
[Trait("Category", TestCategories.Unit)]
@@ -401,7 +409,7 @@ public class GraphRootPipelineIntegrationTests
// Assert
Assert.False(verifyResult.IsValid);
Assert.NotEqual(request.NodeIds.Count, verifyResult.NodeCount);
Assert.Contains("Predicate node IDs do not match", verifyResult.FailureReason);
}
[Trait("Category", TestCategories.Unit)]
@@ -425,6 +433,7 @@ public class GraphRootPipelineIntegrationTests
// Assert
Assert.False(verifyResult.IsValid);
Assert.Contains("Predicate node IDs do not match", verifyResult.FailureReason);
}
#endregion
@@ -543,4 +552,16 @@ public class GraphRootPipelineIntegrationTests
}
#endregion
private sealed class FixedTimeProvider : TimeProvider
{
private readonly DateTimeOffset _fixedTime;
public FixedTimeProvider(DateTimeOffset fixedTime)
{
_fixedTime = fixedTime;
}
public override DateTimeOffset GetUtcNow() => _fixedTime;
}
}