save work
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Attestor.Core.Rekor;
|
||||
using StellaOps.Attestor.Core.Submission;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Scanner.ProofSpine;
|
||||
using StellaOps.Scanner.ProofSpine.Options;
|
||||
using StellaOps.Scanner.Reachability.Attestation;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scanner.Reachability.Tests;
|
||||
|
||||
public sealed class ReachabilityWitnessPublisherIntegrationTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task PublishAsync_WhenStoreInCasEnabled_StoresGraphAndEnvelopeInCas()
|
||||
{
|
||||
var options = Options.Create(new ReachabilityWitnessOptions
|
||||
{
|
||||
Enabled = true,
|
||||
StoreInCas = true,
|
||||
PublishToRekor = false,
|
||||
});
|
||||
|
||||
var cas = new FakeFileContentAddressableStore();
|
||||
var cryptoHash = CryptoHashFactory.CreateDefault();
|
||||
var publisher = new ReachabilityWitnessPublisher(
|
||||
options,
|
||||
cryptoHash,
|
||||
NullLogger<ReachabilityWitnessPublisher>.Instance,
|
||||
cas: cas);
|
||||
|
||||
var graph = CreateTestGraph();
|
||||
var graphBytes = System.Text.Encoding.UTF8.GetBytes("{\"schema\":\"richgraph-v1\",\"nodes\":[],\"edges\":[]}");
|
||||
|
||||
var result = await publisher.PublishAsync(
|
||||
graph,
|
||||
graphBytes,
|
||||
graphHash: "blake3:abc123",
|
||||
subjectDigest: "sha256:def456");
|
||||
|
||||
Assert.Equal("cas://reachability/graphs/abc123", result.CasUri);
|
||||
Assert.Equal(graphBytes, cas.GetBytes("abc123"));
|
||||
Assert.NotNull(cas.GetBytes("abc123.dsse"));
|
||||
Assert.NotEmpty(result.DsseEnvelopeBytes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PublishAsync_WhenRekorEnabled_SubmitsDsseEnvelope()
|
||||
{
|
||||
var rekor = new CapturingRekorClient();
|
||||
var signer = CreateDeterministicSigner(keyId: "reachability-test-key");
|
||||
var cryptoProfile = new TestCryptoProfile("reachability-test-key", "hs256");
|
||||
|
||||
var options = Options.Create(new ReachabilityWitnessOptions
|
||||
{
|
||||
Enabled = true,
|
||||
StoreInCas = false,
|
||||
PublishToRekor = true,
|
||||
RekorUrl = new Uri("https://rekor.test"),
|
||||
RekorBackendName = "primary",
|
||||
SigningKeyId = "reachability-test-key",
|
||||
Tier = AttestationTier.Standard
|
||||
});
|
||||
|
||||
var cryptoHash = CryptoHashFactory.CreateDefault();
|
||||
var publisher = new ReachabilityWitnessPublisher(
|
||||
options,
|
||||
cryptoHash,
|
||||
NullLogger<ReachabilityWitnessPublisher>.Instance,
|
||||
dsseSigningService: signer,
|
||||
cryptoProfile: cryptoProfile,
|
||||
rekorClient: rekor);
|
||||
|
||||
var graph = CreateTestGraph();
|
||||
var result = await publisher.PublishAsync(
|
||||
graph,
|
||||
graphBytes: Array.Empty<byte>(),
|
||||
graphHash: "blake3:abc123",
|
||||
subjectDigest: "sha256:def456");
|
||||
|
||||
Assert.NotNull(rekor.LastRequest);
|
||||
Assert.NotNull(rekor.LastBackend);
|
||||
Assert.Equal("primary", rekor.LastBackend!.Name);
|
||||
Assert.Equal(new Uri("https://rekor.test"), rekor.LastBackend.Url);
|
||||
|
||||
var request = rekor.LastRequest!;
|
||||
Assert.Equal("application/vnd.in-toto+json", request.Bundle.Dsse.PayloadType);
|
||||
Assert.False(string.IsNullOrWhiteSpace(request.Bundle.Dsse.PayloadBase64));
|
||||
Assert.NotEmpty(request.Bundle.Dsse.Signatures);
|
||||
Assert.Equal("reachability-test-key", request.Bundle.Dsse.Signatures[0].KeyId);
|
||||
Assert.False(string.IsNullOrWhiteSpace(request.Meta.BundleSha256));
|
||||
|
||||
Assert.Equal(1234, result.RekorLogIndex);
|
||||
Assert.Equal("rekor-uuid-1234", result.RekorLogId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PublishAsync_WhenAirGapped_SkipsRekorSubmission()
|
||||
{
|
||||
var rekor = new CapturingRekorClient();
|
||||
|
||||
var options = Options.Create(new ReachabilityWitnessOptions
|
||||
{
|
||||
Enabled = true,
|
||||
StoreInCas = false,
|
||||
PublishToRekor = true,
|
||||
RekorUrl = new Uri("https://rekor.test"),
|
||||
Tier = AttestationTier.AirGapped,
|
||||
});
|
||||
|
||||
var cryptoHash = CryptoHashFactory.CreateDefault();
|
||||
var publisher = new ReachabilityWitnessPublisher(
|
||||
options,
|
||||
cryptoHash,
|
||||
NullLogger<ReachabilityWitnessPublisher>.Instance,
|
||||
rekorClient: rekor);
|
||||
|
||||
var graph = CreateTestGraph();
|
||||
var result = await publisher.PublishAsync(
|
||||
graph,
|
||||
graphBytes: Array.Empty<byte>(),
|
||||
graphHash: "blake3:abc123",
|
||||
subjectDigest: "sha256:def456");
|
||||
|
||||
Assert.Null(rekor.LastRequest);
|
||||
Assert.Null(result.RekorLogIndex);
|
||||
Assert.Null(result.RekorLogId);
|
||||
}
|
||||
|
||||
private static RichGraph CreateTestGraph()
|
||||
{
|
||||
return new RichGraph(
|
||||
Schema: "richgraph-v1",
|
||||
Analyzer: new RichGraphAnalyzer("test-analyzer", "1.0.0", null),
|
||||
Nodes: new[]
|
||||
{
|
||||
new RichGraphNode("n1", "sym:dotnet:A", null, null, "dotnet", "method", "A", null, null, null, null),
|
||||
new RichGraphNode("n2", "sym:dotnet:B", null, null, "dotnet", "sink", "B", null, null, null, null)
|
||||
},
|
||||
Edges: new[]
|
||||
{
|
||||
new RichGraphEdge("n1", "n2", "call", null, null, null, 1.0, null)
|
||||
},
|
||||
Roots: null);
|
||||
}
|
||||
|
||||
private static IDsseSigningService CreateDeterministicSigner(string keyId)
|
||||
{
|
||||
var options = Options.Create(new ProofSpineDsseSigningOptions
|
||||
{
|
||||
Mode = "hash",
|
||||
KeyId = keyId,
|
||||
Algorithm = "hs256",
|
||||
AllowDeterministicFallback = true,
|
||||
});
|
||||
|
||||
return new HmacDsseSigningService(
|
||||
options,
|
||||
DefaultCryptoHmac.CreateForTests(),
|
||||
DefaultCryptoHash.CreateForTests());
|
||||
}
|
||||
|
||||
private sealed record TestCryptoProfile(string KeyId, string Algorithm) : ICryptoProfile;
|
||||
|
||||
private sealed class CapturingRekorClient : IRekorClient
|
||||
{
|
||||
public AttestorSubmissionRequest? LastRequest { get; private set; }
|
||||
|
||||
public RekorBackend? LastBackend { get; private set; }
|
||||
|
||||
public Task<RekorSubmissionResponse> SubmitAsync(AttestorSubmissionRequest request, RekorBackend backend, CancellationToken cancellationToken = default)
|
||||
{
|
||||
LastRequest = request;
|
||||
LastBackend = backend;
|
||||
|
||||
return Task.FromResult(new RekorSubmissionResponse
|
||||
{
|
||||
Uuid = "rekor-uuid-1234",
|
||||
Index = 1234,
|
||||
LogUrl = backend.Url.ToString(),
|
||||
Status = "included",
|
||||
Proof = null
|
||||
});
|
||||
}
|
||||
|
||||
public Task<RekorProofResponse?> GetProofAsync(string rekorUuid, RekorBackend backend, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult<RekorProofResponse?>(null);
|
||||
|
||||
public Task<RekorInclusionVerificationResult> VerifyInclusionAsync(string rekorUuid, byte[] payloadDigest, RekorBackend backend, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(RekorInclusionVerificationResult.Failure("not_implemented"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user