|
|
|
|
@@ -150,6 +150,14 @@ public sealed class ReceiptSidebarModelsTests
|
|
|
|
|
public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
{
|
|
|
|
|
private static readonly Guid AnchorGuid = Guid.Parse("11111111-1111-1111-1111-111111111111");
|
|
|
|
|
|
|
|
|
|
// Valid 64-char lowercase hex digests for test fixtures
|
|
|
|
|
private const string DigestAbc123 = "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1";
|
|
|
|
|
private const string DigestDefault = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
|
|
|
|
private const string DigestAbc = "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca";
|
|
|
|
|
private const string DigestCtx = "cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1cc1c";
|
|
|
|
|
private const string DigestFallback = "fb00fb00fb00fb00fb00fb00fb00fb00fb00fb00fb00fb00fb00fb00fb00fb00";
|
|
|
|
|
|
|
|
|
|
private readonly TestSidebarMeterFactory _meterFactory = new();
|
|
|
|
|
private readonly ReceiptSidebarService _sut;
|
|
|
|
|
|
|
|
|
|
@@ -165,7 +173,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_maps_bundle_id()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:abc123");
|
|
|
|
|
var receipt = CreateReceipt(DigestAbc123);
|
|
|
|
|
var detail = _sut.FormatReceipt(receipt);
|
|
|
|
|
detail.BundleId.Should().Contain("abc123");
|
|
|
|
|
}
|
|
|
|
|
@@ -173,7 +181,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_maps_anchor_id()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x");
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault);
|
|
|
|
|
var detail = _sut.FormatReceipt(receipt);
|
|
|
|
|
detail.AnchorId.Should().Be(AnchorGuid.ToString());
|
|
|
|
|
}
|
|
|
|
|
@@ -181,7 +189,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_maps_verifier_version()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x");
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault);
|
|
|
|
|
var detail = _sut.FormatReceipt(receipt);
|
|
|
|
|
detail.VerifierVersion.Should().Be("2.1.0");
|
|
|
|
|
}
|
|
|
|
|
@@ -189,7 +197,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_all_pass_returns_verified()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("dsse-signature", VerificationResult.Pass),
|
|
|
|
|
MakeCheck("rekor-inclusion", VerificationResult.Pass)
|
|
|
|
|
]);
|
|
|
|
|
@@ -201,7 +209,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_mixed_returns_partially_verified()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("dsse-signature", VerificationResult.Pass),
|
|
|
|
|
MakeCheck("policy-check", VerificationResult.Fail)
|
|
|
|
|
]);
|
|
|
|
|
@@ -213,7 +221,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_all_fail_returns_failed()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("sig", VerificationResult.Fail),
|
|
|
|
|
MakeCheck("hash", VerificationResult.Fail)
|
|
|
|
|
]);
|
|
|
|
|
@@ -225,7 +233,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_no_checks_returns_unverified()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", []);
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, []);
|
|
|
|
|
var detail = _sut.FormatReceipt(receipt);
|
|
|
|
|
detail.VerificationStatus.Should().Be(ReceiptVerificationStatus.Unverified);
|
|
|
|
|
}
|
|
|
|
|
@@ -233,7 +241,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_sets_dsse_verified_when_dsse_check_passes()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("dsse-envelope-signature", VerificationResult.Pass)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
@@ -244,7 +252,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_dsse_not_verified_when_dsse_check_fails()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("dsse-envelope-signature", VerificationResult.Fail)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
@@ -255,7 +263,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_sets_rekor_verified_when_rekor_check_passes()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("rekor-inclusion-proof", VerificationResult.Pass, logIndex: 100)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
@@ -266,7 +274,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_rekor_not_verified_when_absent()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("basic-hash", VerificationResult.Pass)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
@@ -277,7 +285,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_maps_check_details()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("sig-check", VerificationResult.Pass, keyId: "key-1", details: "Valid signature")
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
@@ -294,7 +302,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_formats_expected_actual_when_no_details()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
new VerificationCheck
|
|
|
|
|
{
|
|
|
|
|
Check = "digest-match",
|
|
|
|
|
@@ -312,7 +320,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_maps_tool_digests()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", toolDigests: new Dictionary<string, string>
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, toolDigests: new Dictionary<string, string>
|
|
|
|
|
{
|
|
|
|
|
["verifier"] = "sha256:vvv",
|
|
|
|
|
["scanner"] = "sha256:sss"
|
|
|
|
|
@@ -327,7 +335,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void FormatReceipt_null_tool_digests_stays_null()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x");
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault);
|
|
|
|
|
var detail = _sut.FormatReceipt(receipt);
|
|
|
|
|
detail.ToolDigests.Should().BeNull();
|
|
|
|
|
}
|
|
|
|
|
@@ -352,7 +360,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetDetailAsync_returns_detail_for_registered_receipt()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:abc");
|
|
|
|
|
var receipt = CreateReceipt(DigestAbc);
|
|
|
|
|
_sut.Register(receipt);
|
|
|
|
|
|
|
|
|
|
var request = new ReceiptSidebarRequest { BundleId = receipt.ProofBundleId.ToString() };
|
|
|
|
|
@@ -365,7 +373,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetDetailAsync_excludes_checks_when_requested()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:abc", [
|
|
|
|
|
var receipt = CreateReceipt(DigestAbc, [
|
|
|
|
|
MakeCheck("sig", VerificationResult.Pass)
|
|
|
|
|
]);
|
|
|
|
|
_sut.Register(receipt);
|
|
|
|
|
@@ -384,7 +392,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetDetailAsync_excludes_tool_digests_when_not_requested()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:abc", toolDigests: new Dictionary<string, string>
|
|
|
|
|
var receipt = CreateReceipt(DigestAbc, toolDigests: new Dictionary<string, string>
|
|
|
|
|
{
|
|
|
|
|
["tool"] = "sha256:ttt"
|
|
|
|
|
});
|
|
|
|
|
@@ -420,7 +428,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetContextAsync_returns_registered_context()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:ctx");
|
|
|
|
|
var receipt = CreateReceipt(DigestCtx);
|
|
|
|
|
var detail = _sut.FormatReceipt(receipt);
|
|
|
|
|
var ctx = new VexReceiptSidebarContext
|
|
|
|
|
{
|
|
|
|
|
@@ -439,7 +447,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetContextAsync_falls_back_to_receipt_only_context()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:fallback");
|
|
|
|
|
var receipt = CreateReceipt(DigestFallback);
|
|
|
|
|
_sut.Register(receipt);
|
|
|
|
|
|
|
|
|
|
var result = await _sut.GetContextAsync(receipt.ProofBundleId.ToString());
|
|
|
|
|
@@ -465,7 +473,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void DeriveVerificationStatus_handles_single_pass()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("only", VerificationResult.Pass)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
@@ -476,7 +484,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void DeriveVerificationStatus_handles_single_fail()
|
|
|
|
|
{
|
|
|
|
|
var receipt = CreateReceipt("sha256:x", [
|
|
|
|
|
var receipt = CreateReceipt(DigestDefault, [
|
|
|
|
|
MakeCheck("only", VerificationResult.Fail)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
@@ -496,7 +504,7 @@ public sealed class ReceiptSidebarServiceTests : IDisposable
|
|
|
|
|
[Fact]
|
|
|
|
|
public void RegisterContext_throws_on_null_or_empty_bundleId()
|
|
|
|
|
{
|
|
|
|
|
var detail = _sut.FormatReceipt(CreateReceipt("sha256:x", []));
|
|
|
|
|
var detail = _sut.FormatReceipt(CreateReceipt(DigestDefault, []));
|
|
|
|
|
var ctx = new VexReceiptSidebarContext { Receipt = detail };
|
|
|
|
|
|
|
|
|
|
var act1 = () => _sut.RegisterContext(null!, ctx);
|
|
|
|
|
|