129 lines
5.1 KiB
C#
129 lines
5.1 KiB
C#
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using StellaOps.Scanner.ProofSpine;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Scanner.WebService.Tests;
|
|
|
|
public sealed class ProofSpineEndpointsTests
|
|
{
|
|
[Fact]
|
|
public async Task GetSpine_ReturnsSpine_WithVerification()
|
|
{
|
|
await using var factory = new ScannerApplicationFactory();
|
|
using var scope = factory.Services.CreateScope();
|
|
|
|
var builder = scope.ServiceProvider.GetRequiredService<ProofSpineBuilder>();
|
|
var repository = scope.ServiceProvider.GetRequiredService<IProofSpineRepository>();
|
|
|
|
var spine = await builder
|
|
.ForArtifact("sha256:feedface")
|
|
.ForVulnerability("CVE-2025-0001")
|
|
.WithPolicyProfile("default")
|
|
.WithScanRun("scan-001")
|
|
.AddSbomSlice("sha256:sbom", new[] { "pkg:a", "pkg:b" }, toolId: "sbom", toolVersion: "1.0.0")
|
|
.AddPolicyEval(
|
|
policyDigest: "sha256:policy",
|
|
factors: new Dictionary<string, string> { ["policy"] = "default" },
|
|
verdict: "not_affected",
|
|
verdictReason: "component_not_present",
|
|
toolId: "policy",
|
|
toolVersion: "1.0.0")
|
|
.BuildAsync();
|
|
|
|
await repository.SaveAsync(spine);
|
|
|
|
var client = factory.CreateClient();
|
|
var response = await client.GetAsync($"/api/v1/spines/{spine.SpineId}");
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
|
|
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
|
Assert.Equal(spine.SpineId, body.GetProperty("spineId").GetString());
|
|
|
|
var segments = body.GetProperty("segments");
|
|
Assert.True(segments.GetArrayLength() > 0);
|
|
Assert.True(body.TryGetProperty("verification", out _));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ListSpinesByScan_ReturnsSummaries_WithSegmentCount()
|
|
{
|
|
await using var factory = new ScannerApplicationFactory();
|
|
using var scope = factory.Services.CreateScope();
|
|
|
|
var builder = scope.ServiceProvider.GetRequiredService<ProofSpineBuilder>();
|
|
var repository = scope.ServiceProvider.GetRequiredService<IProofSpineRepository>();
|
|
|
|
var spine = await builder
|
|
.ForArtifact("sha256:feedface")
|
|
.ForVulnerability("CVE-2025-0002")
|
|
.WithPolicyProfile("default")
|
|
.WithScanRun("scan-002")
|
|
.AddSbomSlice("sha256:sbom", new[] { "pkg:a" }, toolId: "sbom", toolVersion: "1.0.0")
|
|
.AddPolicyEval(
|
|
policyDigest: "sha256:policy",
|
|
factors: new Dictionary<string, string> { ["policy"] = "default" },
|
|
verdict: "affected",
|
|
verdictReason: "reachable",
|
|
toolId: "policy",
|
|
toolVersion: "1.0.0")
|
|
.BuildAsync();
|
|
|
|
await repository.SaveAsync(spine);
|
|
|
|
var client = factory.CreateClient();
|
|
var response = await client.GetAsync("/api/v1/scans/scan-002/spines");
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
|
|
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
|
var items = body.GetProperty("items");
|
|
Assert.Equal(1, items.GetArrayLength());
|
|
Assert.Equal(spine.SpineId, items[0].GetProperty("spineId").GetString());
|
|
Assert.True(items[0].GetProperty("segmentCount").GetInt32() > 0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetSpine_ReturnsInvalidStatus_WhenSegmentTampered()
|
|
{
|
|
await using var factory = new ScannerApplicationFactory();
|
|
using var scope = factory.Services.CreateScope();
|
|
|
|
var builder = scope.ServiceProvider.GetRequiredService<ProofSpineBuilder>();
|
|
var repository = scope.ServiceProvider.GetRequiredService<IProofSpineRepository>();
|
|
|
|
var spine = await builder
|
|
.ForArtifact("sha256:feedface")
|
|
.ForVulnerability("CVE-2025-0003")
|
|
.WithPolicyProfile("default")
|
|
.WithScanRun("scan-003")
|
|
.AddSbomSlice("sha256:sbom", new[] { "pkg:a" }, toolId: "sbom", toolVersion: "1.0.0")
|
|
.AddPolicyEval(
|
|
policyDigest: "sha256:policy",
|
|
factors: new Dictionary<string, string> { ["policy"] = "default" },
|
|
verdict: "affected",
|
|
verdictReason: "reachable",
|
|
toolId: "policy",
|
|
toolVersion: "1.0.0")
|
|
.BuildAsync();
|
|
|
|
var tamperedSegment = spine.Segments[0] with { ResultHash = spine.Segments[0].ResultHash + "00" };
|
|
var tampered = spine with { Segments = new[] { tamperedSegment, spine.Segments[1] } };
|
|
|
|
await repository.SaveAsync(tampered);
|
|
|
|
var client = factory.CreateClient();
|
|
var response = await client.GetAsync($"/api/v1/spines/{spine.SpineId}");
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
|
|
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
|
var segments = body.GetProperty("segments");
|
|
Assert.Equal("invalid", segments[0].GetProperty("status").GetString());
|
|
}
|
|
}
|