more audit work

This commit is contained in:
master
2026-01-08 10:21:51 +02:00
parent 43c02081ef
commit 51cf4bc16c
546 changed files with 36721 additions and 4003 deletions

View File

@@ -0,0 +1,23 @@
# Determinism Tests Charter
## Mission
Validate CGS determinism and cross-platform hash stability.
## Responsibilities
- Maintain determinism tests for VerdictBuilder and CGS hashing.
- Keep fixtures deterministic and offline-friendly.
- Track sprint tasks in `TASKS.md` and update the sprint tracker.
## Key Paths
- `CgsDeterminismTests.cs`
- `README.md`
## Required Reading
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
- `docs/modules/platform/architecture-overview.md`
- `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`
## Working Agreement
- 1. Use fixed time and IDs; avoid Guid.NewGuid or DateTimeOffset.UtcNow in fixtures.
- 2. Keep evidence JSON deterministic and aligned with canonicalization rules.
- 3. Update `TASKS.md` and sprint statuses when work changes.

View File

@@ -0,0 +1,10 @@
# Determinism Tests Task Board
This board mirrors active sprint tasks for this module.
Source of truth: `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
| Task ID | Status | Notes |
| --- | --- | --- |
| AUDIT-0787-M | DONE | Revalidated 2026-01-07. |
| AUDIT-0787-T | DONE | Revalidated 2026-01-07. |
| AUDIT-0787-A | DONE | Waived (test project; revalidated 2026-01-07). |

View File

@@ -0,0 +1,27 @@
# Fixture Harvester Charter
## Mission
Maintain the fixture harvester CLI and tests for deterministic fixture capture.
## Responsibilities
- Maintain fixture harvesting, validation, and metadata commands.
- Keep fixture outputs deterministic and offline-friendly.
- Track sprint tasks in `TASKS.md` and update the sprint tracker.
## Key Paths
- `Program.cs`
- `Commands/*.cs`
- `FixtureHarvester.csproj`
- `FixtureHarvester.Tests.csproj`
- `*Tests.cs`
## Required Reading
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
- `docs/modules/platform/architecture-overview.md`
- `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`
## Working Agreement
- 1. Use fixed time/ID sources for fixture metadata and sample content.
- 2. Prefer offline defaults; gate live downloads behind explicit flags.
- 3. Keep CLI output ASCII-only and deterministic.
- 4. Update `TASKS.md` and sprint statuses when work changes.

View File

@@ -0,0 +1,13 @@
# Fixture Harvester Task Board
This board mirrors active sprint tasks for this module.
Source of truth: `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
| Task ID | Status | Notes |
| --- | --- | --- |
| AUDIT-0788-M | DONE | Revalidated 2026-01-07 (test project). |
| AUDIT-0788-T | DONE | Revalidated 2026-01-07. |
| AUDIT-0788-A | DONE | Waived (test project; revalidated 2026-01-07). |
| AUDIT-0789-M | DONE | Revalidated 2026-01-07. |
| AUDIT-0789-T | DONE | Revalidated 2026-01-07. |
| AUDIT-0789-A | TODO | Open findings (determinism, HttpClientFactory, ASCII output, path validation, test coverage). |

View File

@@ -1,4 +1,4 @@
{
{
"id": "acr-event-001",
"timestamp": "2024-12-29T12:00:00.0000000Z",
"action": "push",
@@ -6,12 +6,12 @@
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 3028,
"digest": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
"repository": "library/myapp",
"tag": "v1.0.0"
"repository": "stellaops/api-gateway",
"tag": "1.0.0"
},
"request": {
"id": "req-001",
"host": "myregistry.azurecr.io",
"host": "stellaops.azurecr.io",
"method": "PUT"
}
}

View File

@@ -1,17 +1,17 @@
{
{
"push_data": {
"pushed_at": 1703854800,
"images": [],
"tag": "v1.0.0",
"tag": "v2.0.0",
"pusher": "stellaops"
},
"callback_url": "https://registry.hub.docker.com/u/stellaops/myapp/hook/callback",
"callback_url": "https://registry.hub.docker.com/u/stellaops/scanner/hook/callback",
"repository": {
"status": "Active",
"description": "StellaOps application image",
"is_trusted": true,
"repo_name": "stellaops/myapp",
"name": "myapp",
"repo_name": "stellaops/scanner",
"name": "scanner",
"namespace": "stellaops"
}
}

View File

@@ -1,4 +1,4 @@
{
{
"version": "0",
"id": "ecr-event-001",
"detail-type": "ECR Image Action",
@@ -9,8 +9,8 @@
"detail": {
"action-type": "PUSH",
"result": "SUCCESS",
"repository-name": "library/myapp",
"repository-name": "stellaops/scanner",
"image-digest": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
"image-tag": "v1.0.0"
"image-tag": "v3.1.0"
}
}

View File

@@ -1,25 +1,25 @@
{
{
"action": "published",
"package": {
"id": 12345,
"name": "myapp",
"name": "stellaops-cli",
"namespace": "stellaops",
"ecosystem": "container",
"package_type": "container",
"package_version": {
"id": 67890,
"version": "v1.0.0",
"version": "v4.0.0",
"container_metadata": {
"tag": {
"digest": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
"name": "v1.0.0"
"name": "v4.0.0"
}
}
}
},
"repository": {
"id": 111222,
"name": "myapp",
"full_name": "stellaops/myapp"
"name": "stellaops-cli",
"full_name": "stellaops/stellaops-cli"
}
}

View File

@@ -1,4 +1,4 @@
{
{
"type": "PUSH_ARTIFACT",
"occur_at": 1703854800,
"operator": "admin",
@@ -6,15 +6,15 @@
"resources": [
{
"digest": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
"tag": "v1.0.0",
"resource_url": "harbor.example.com/library/myapp:v1.0.0"
"tag": "v1.2.3",
"resource_url": "harbor.example.com/library/nginx:v1.2.3"
}
],
"repository": {
"date_created": 1703850000,
"name": "myapp",
"name": "nginx",
"namespace": "library",
"repo_full_name": "library/myapp",
"repo_full_name": "library/nginx",
"repo_type": "public"
}
}

View File

@@ -1,41 +1,41 @@
{
{
"secret": "",
"ref": "refs/heads/main",
"before": "0000000000000000000000000000000000000000",
"after": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"compare_url": "https://gitea.example.com/stellaops/myapp/compare/main...feature",
"after": "abc123def456789012345678901234567890abcd",
"compare_url": "https://gitea.example.com/stellaops-org/stellaops/compare/main...feature",
"commits": [
{
"id": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"message": "feat: add new feature",
"id": "abc123def456789012345678901234567890abcd",
"message": "feat: add Python wheel analyzer",
"timestamp": "2024-12-29T12:00:00Z",
"author": {
"name": "StellaOps",
"email": "dev@stellaops.org",
"username": "stellaops"
"name": "Developer",
"email": "developer@stellaops.org",
"username": "developer"
}
}
],
"repository": {
"id": 123456789,
"name": "myapp",
"full_name": "stellaops/myapp",
"name": "stellaops",
"full_name": "stellaops-org/stellaops",
"default_branch": "main",
"html_url": "https://gitea.example.com/stellaops/myapp",
"html_url": "https://gitea.example.com/stellaops-org/stellaops",
"owner": {
"id": 123456,
"login": "stellaops",
"login": "stellaops-org",
"email": "org@stellaops.org"
}
},
"pusher": {
"id": 123456,
"login": "stellaops",
"email": "dev@stellaops.org"
"login": "developer",
"email": "developer@stellaops.org"
},
"sender": {
"id": 123456,
"login": "stellaops",
"email": "dev@stellaops.org"
"login": "developer",
"email": "developer@stellaops.org"
}
}

View File

@@ -1,34 +1,34 @@
{
{
"action": "opened",
"number": 42,
"pull_request": {
"id": 1234567,
"number": 42,
"state": "open",
"title": "feat: add new feature",
"title": "feat: add Python wheel analyzer",
"user": {
"login": "stellaops",
"id": 123456,
"login": "developer",
"id": 987654,
"type": "User"
},
"head": {
"sha": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"ref": "feature-branch"
"sha": "abc123def456789012345678901234567890abcd",
"ref": "feature/python-wheel"
},
"base": {
"sha": "b4fe06dafc13gge79dee0ge95517791bf04e744d",
"sha": "def456789012345678901234567890abc123def4",
"ref": "main"
}
},
"repository": {
"id": 123456789,
"name": "myapp",
"full_name": "stellaops/myapp",
"name": "stellaops",
"full_name": "stellaops-org/stellaops",
"default_branch": "main"
},
"sender": {
"login": "stellaops",
"id": 123456,
"login": "developer",
"id": 987654,
"type": "User"
}
}

View File

@@ -1,30 +1,30 @@
{
{
"ref": "refs/heads/main",
"before": "0000000000000000000000000000000000000000",
"after": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"after": "abc123def456789012345678901234567890abcd",
"repository": {
"id": 123456789,
"name": "myapp",
"full_name": "stellaops/myapp",
"name": "stellaops",
"full_name": "stellaops-org/stellaops",
"default_branch": "main",
"html_url": "https://github.com/stellaops/myapp"
"html_url": "https://github.com/stellaops-org/stellaops"
},
"pusher": {
"name": "stellaops",
"email": "ci@stellaops.org"
"name": "developer",
"email": "developer@stellaops.org"
},
"sender": {
"login": "stellaops",
"login": "developer",
"id": 123456,
"type": "User"
},
"head_commit": {
"id": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"message": "feat: add new feature",
"id": "abc123def456789012345678901234567890abcd",
"message": "feat: add Python wheel analyzer",
"timestamp": "2024-12-29T12:00:00Z",
"author": {
"name": "StellaOps",
"email": "dev@stellaops.org"
"name": "Developer",
"email": "developer@stellaops.org"
}
}
}

View File

@@ -1,13 +1,13 @@
{
{
"action": "completed",
"workflow_run": {
"id": 1234567890,
"name": "CI",
"name": "StellaOps CI",
"node_id": "WFR_kwDOGPQW8c8AAAAB",
"head_branch": "main",
"head_sha": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"head_sha": "abc123def456789012345678901234567890abcd",
"path": ".github/workflows/ci.yml",
"run_number": 42,
"run_number": 123,
"event": "push",
"status": "completed",
"conclusion": "success",
@@ -27,13 +27,13 @@
},
"workflow": {
"id": 12345,
"name": "CI",
"name": "StellaOps CI",
"path": ".github/workflows/ci.yml"
},
"repository": {
"id": 123456789,
"name": "myapp",
"full_name": "stellaops/myapp",
"name": "stellaops",
"full_name": "stellaops-org/stellaops",
"default_branch": "main"
},
"sender": {

View File

@@ -1,33 +1,33 @@
{
{
"object_kind": "push",
"event_name": "push",
"before": "0000000000000000000000000000000000000000",
"after": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"after": "abc123def456789012345678901234567890abcd",
"ref": "refs/heads/main",
"checkout_sha": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"checkout_sha": "abc123def456789012345678901234567890abcd",
"user_id": 123456,
"user_name": "StellaOps",
"user_username": "stellaops",
"user_email": "dev@stellaops.org",
"user_name": "Developer",
"user_username": "developer",
"user_email": "developer@stellaops.org",
"project": {
"id": 123456789,
"name": "myapp",
"path_with_namespace": "stellaops/myapp",
"name": "stellaops",
"path_with_namespace": "stellaops-org/stellaops",
"default_branch": "main",
"web_url": "https://gitlab.com/stellaops/myapp"
"web_url": "https://gitlab.com/stellaops-org/stellaops"
},
"repository": {
"name": "myapp",
"url": "git@gitlab.com:stellaops/myapp.git"
"name": "stellaops",
"url": "git@gitlab.com:stellaops-org/stellaops.git"
},
"commits": [
{
"id": "a3ed95caeb02ffe68cdd9fd84406680ae93d633c",
"message": "feat: add new feature",
"id": "abc123def456789012345678901234567890abcd",
"message": "feat: add Python wheel analyzer",
"timestamp": "2024-12-29T12:00:00Z",
"author": {
"name": "StellaOps",
"email": "dev@stellaops.org"
"name": "Developer",
"email": "developer@stellaops.org"
}
}
]

View File

@@ -13,7 +13,11 @@ public sealed class DeterminismManifestReader
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase),
new ObjectValueConverter()
}
};
/// <summary>
@@ -225,6 +229,127 @@ public sealed class DeterminismManifestReader
}
}
private sealed class ObjectValueConverter : JsonConverter<object?>
{
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> ReadValue(ref reader, options);
public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
return;
}
switch (value)
{
case string text:
writer.WriteStringValue(text);
return;
case bool flag:
writer.WriteBooleanValue(flag);
return;
case int intValue:
writer.WriteNumberValue(intValue);
return;
case long longValue:
writer.WriteNumberValue(longValue);
return;
case float floatValue:
writer.WriteNumberValue(floatValue);
return;
case double doubleValue:
writer.WriteNumberValue(doubleValue);
return;
case decimal decimalValue:
writer.WriteNumberValue(decimalValue);
return;
case byte[] bytes:
writer.WriteBase64StringValue(bytes);
return;
case IDictionary<string, object?> dictionary:
writer.WriteStartObject();
foreach (var (key, itemValue) in dictionary)
{
writer.WritePropertyName(key);
Write(writer, itemValue, options);
}
writer.WriteEndObject();
return;
case IEnumerable<object?> list:
writer.WriteStartArray();
foreach (var item in list)
{
Write(writer, item, options);
}
writer.WriteEndArray();
return;
default:
JsonSerializer.Serialize(writer, value, value.GetType(), options);
return;
}
}
private static IDictionary<string, object?> ReadObject(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
var values = new Dictionary<string, object?>(StringComparer.Ordinal);
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return values;
}
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException("Expected property name.");
}
var propertyName = reader.GetString() ?? string.Empty;
if (!reader.Read())
{
throw new JsonException("Unexpected end of JSON.");
}
values[propertyName] = ReadValue(ref reader, options);
}
throw new JsonException("Unexpected end of JSON.");
}
private static IList<object?> ReadArray(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
var values = new List<object?>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
{
return values;
}
values.Add(ReadValue(ref reader, options));
}
throw new JsonException("Unexpected end of JSON.");
}
private static object? ReadValue(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
return reader.TokenType switch
{
JsonTokenType.String => reader.GetString(),
JsonTokenType.Number => reader.TryGetInt64(out var number) ? number : reader.GetDouble(),
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Null => null,
JsonTokenType.StartObject => ReadObject(ref reader, options),
JsonTokenType.StartArray => ReadArray(ref reader, options),
_ => throw new JsonException("Unsupported JSON token.")
};
}
}
private static bool IsSupportedHashAlgorithm(string algorithm)
{
return algorithm switch

View File

@@ -6,6 +6,7 @@
// -----------------------------------------------------------------------------
using System.Net.Http.Json;
using Xunit.Sdk;
namespace StellaOps.Chaos.Router.Tests.Fixtures;
@@ -57,10 +58,18 @@ public class RouterTestFixture : IAsyncLifetime
return JsonContent.Create(request);
}
public ValueTask InitializeAsync()
public async ValueTask InitializeAsync()
{
// Verify router is reachable
return ValueTask.CompletedTask;
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
_ = await _client.GetAsync("/", cts.Token);
}
catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException || ex is OperationCanceledException)
{
throw SkipException.ForSkip(
$"Router not reachable at '{_routerUrl}'. Set ROUTER_URL or start the router service to run chaos tests.");
}
}
public ValueTask DisposeAsync()

View File

@@ -0,0 +1,32 @@
# Integration E2E Integrations Charter
## Mission
Validate integration flows for registry webhooks, SCM webhooks, CI templates, offline mode, and facet admission.
## Responsibilities
- Maintain E2E integration fixtures and helpers.
- Keep outputs deterministic and offline-friendly.
- Track sprint tasks in `TASKS.md` and update the sprint tracker.
## Key Paths
- `CiTemplateTests.cs`
- `DeterminismTests.cs`
- `FacetSealAdmissionE2ETests.cs`
- `OfflineModeTests.cs`
- `RegistryWebhookTests.cs`
- `ScmWebhookTests.cs`
- `Helpers/*.cs`
- `Fixtures/*.cs`
## Required Reading
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
- `docs/modules/platform/architecture-overview.md`
- `docs/modules/sbom-service/architecture.md`
- `docs/modules/signals/architecture.md`
- `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`
## Working Agreement
- 1. Use fixed time and IDs in fixtures and test data.
- 2. Keep templates and outputs deterministic (canonical JSON, stable ordering).
- 3. Keep tests offline; do not require network access for fixtures.
- 4. Update `TASKS.md` and sprint statuses when work changes.

View File

@@ -4,6 +4,7 @@
// Description: Utility class for webhook testing operations
// =============================================================================
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
@@ -357,6 +358,18 @@ public static class WebhookTestHelper
foreach (var part in jsonPath.Split('.'))
{
if (element.ValueKind == JsonValueKind.Array &&
int.TryParse(part, NumberStyles.None, CultureInfo.InvariantCulture, out var index))
{
if (index < 0 || index >= element.GetArrayLength())
{
return null;
}
element = element[index];
continue;
}
if (!element.TryGetProperty(part, out element))
{
return null;
@@ -411,6 +424,18 @@ public static class WebhookTestHelper
var current = element;
foreach (var part in path)
{
if (current.ValueKind == JsonValueKind.Array &&
int.TryParse(part, NumberStyles.None, CultureInfo.InvariantCulture, out var index))
{
if (index < 0 || index >= current.GetArrayLength())
{
return false;
}
current = current[index];
continue;
}
if (current.ValueKind != JsonValueKind.Object ||
!current.TryGetProperty(part, out current))
{

View File

@@ -0,0 +1,10 @@
# Integration E2E Integrations Task Board
This board mirrors active sprint tasks for this module.
Source of truth: `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
| Task ID | Status | Notes |
| --- | --- | --- |
| AUDIT-0790-M | DONE | Revalidated 2026-01-07. |
| AUDIT-0790-T | DONE | Revalidated 2026-01-07. |
| AUDIT-0790-A | DONE | Waived (test project; revalidated 2026-01-07). |

View File

@@ -0,0 +1,26 @@
# Replayable Verdict E2E Charter
## Mission
Validate replayable verdict bundles and determinism across the verdict pipeline.
## Responsibilities
- Maintain replayable verdict bundle fixtures and tests.
- Keep bundle metadata and hashing deterministic.
- Track sprint tasks in `TASKS.md` and update the sprint tracker.
## Key Paths
- `ReplayableVerdictE2ETests.cs`
- `README.md`
- `../fixtures/e2e/`
## Required Reading
- `docs/modules/replay/architecture.md`
- `docs/modules/vex-lens/architecture.md`
- `docs/modules/scanner/architecture.md`
- `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`
## Working Agreement
- 1. Use fixed time and IDs in fixtures and manifest data.
- 2. Keep bundle hashes and outputs deterministic (canonical JSON, stable ordering).
- 3. Avoid non-ASCII glyphs in docs and test output.
- 4. Update `TASKS.md` and sprint statuses when work changes.

View File

@@ -0,0 +1,10 @@
# Replayable Verdict E2E Task Board
This board mirrors active sprint tasks for this module.
Source of truth: `docs/implplan/permament/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
| Task ID | Status | Notes |
| --- | --- | --- |
| AUDIT-0791-M | DONE | Revalidated 2026-01-07. |
| AUDIT-0791-T | DONE | Revalidated 2026-01-07. |
| AUDIT-0791-A | DONE | Waived (test project; revalidated 2026-01-07). |

View File

@@ -27,7 +27,7 @@ namespace StellaOps.ScannerSignals.IntegrationTests;
public sealed class ScannerToSignalsReachabilityTests
{
private static readonly string RepoRoot = LocateRepoRoot();
private static readonly string FixtureRoot = Path.Combine(RepoRoot, "tests", "reachability", "fixtures", "reachbench-2025-expanded", "cases");
private static readonly string FixtureRoot = Path.Combine(RepoRoot, "__Tests", "reachability", "fixtures", "reachbench-2025-expanded", "cases");
[Trait("Category", TestCategories.Unit)]
[Fact]

View File

@@ -56,22 +56,16 @@ public class AuditPackBuilderTests
}
[Fact]
public void PackDigest_IsComputedCorrectly()
public async Task PackDigest_IsComputedCorrectly()
{
// Arrange
var pack = new AuditPack
{
PackId = "test-pack",
Name = "Test Pack",
CreatedAt = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
RunManifest = new RunManifest("scan-1", DateTimeOffset.UtcNow),
EvidenceIndex = new EvidenceIndex([]),
Verdict = new Verdict("verdict-1", "pass"),
OfflineBundle = new BundleManifest("bundle-1", "1.0"),
Contents = new PackContents()
};
var scanResult = new ScanResult("scan-1");
var builder = new AuditPackBuilder();
// Act - digest should be set during build
pack.PackDigest.Should().NotBeNull();
// Act
var pack = await builder.BuildAsync(scanResult, new AuditPackOptions());
// Assert
pack.PackDigest.Should().NotBeNullOrEmpty();
}
}

View File

@@ -159,16 +159,18 @@ public class ReplayAttestationServiceTests
public async Task VerifyAsync_ValidAttestation_ReturnsValid()
{
// Arrange
var service = new ReplayAttestationService(new FakeSigner(), new AcceptAllVerifier());
var manifest = CreateTestManifest();
var result = CreateTestResult();
var attestation = await _service.GenerateAsync(manifest, result);
var attestation = await service.GenerateAsync(manifest, result);
// Act
var verificationResult = await _service.VerifyAsync(attestation);
var verificationResult = await service.VerifyAsync(attestation);
// Assert
verificationResult.IsValid.Should().BeTrue();
verificationResult.Errors.Should().BeEmpty();
verificationResult.SignatureVerified.Should().BeTrue();
}
[Fact]
@@ -255,4 +257,27 @@ public class ReplayAttestationServiceTests
verificationResult.IsValid.Should().BeFalse();
verificationResult.Errors.Should().Contain(e => e.Contains("payload digest"));
}
private sealed class FakeSigner : IReplayAttestationSigner
{
public Task<DsseSignatureResult> SignAsync(byte[] payload, CancellationToken cancellationToken = default)
{
return Task.FromResult(new DsseSignatureResult
{
KeyId = "test-key",
Signature = Convert.ToBase64String(payload.Take(8).ToArray())
});
}
}
private sealed class AcceptAllVerifier : IReplayAttestationSignatureVerifier
{
public Task<ReplayAttestationSignatureVerification> VerifyAsync(
ReplayDsseEnvelope envelope,
byte[] payload,
CancellationToken cancellationToken = default)
{
return Task.FromResult(new ReplayAttestationSignatureVerification { Verified = true });
}
}
}