Repair triage artifact scope and evidence contracts

This commit is contained in:
master
2026-03-11 14:25:59 +02:00
parent 4dc5db4efb
commit 9dd8592a2a
27 changed files with 1598 additions and 282 deletions

View File

@@ -0,0 +1,51 @@
using System.Net;
using System.Net.Http.Json;
using StellaOps.Scanner.WebService.Contracts;
using StellaOps.TestKit;
namespace StellaOps.Scanner.WebService.Tests;
public sealed class TriageControllerDemoContractsTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ArtifactGatedBuckets_ReturnsDeterministicSummaryForArtifactWorkspace()
{
await using var factory = ScannerApplicationFactory.CreateLightweight();
await factory.InitializeAsync();
using var client = factory.CreateClient();
var response = await client.GetAsync(
"/api/v1/triage/artifacts/asset-web-prod/gated-buckets",
TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var payload = await response.Content.ReadFromJsonAsync<GatedBucketsSummaryDto>(cancellationToken: TestContext.Current.CancellationToken);
Assert.NotNull(payload);
Assert.True(payload!.TotalHiddenCount >= 1);
Assert.True(payload.UnreachableCount >= 1);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DemoFindingEvidence_ReturnsUnifiedEvidenceWithoutDatabaseBacking()
{
await using var factory = ScannerApplicationFactory.CreateLightweight();
await factory.InitializeAsync();
using var client = factory.CreateClient();
const string findingId = "11111111-1111-1111-1111-111111111111";
var response = await client.GetAsync(
$"/api/v1/triage/findings/{findingId}/evidence?includeReplayCommand=true&includeReachability=true&includeVex=true&includeAttestations=true&includeDeltas=true",
TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var payload = await response.Content.ReadFromJsonAsync<UnifiedEvidenceResponseDto>(cancellationToken: TestContext.Current.CancellationToken);
Assert.NotNull(payload);
Assert.Equal(findingId, payload!.FindingId);
Assert.NotNull(payload.Reachability);
Assert.NotEmpty(payload.Attestations ?? []);
}
}

View File

@@ -0,0 +1,80 @@
using System.Net;
using System.Net.Http.Json;
using StellaOps.Scanner.WebService.Controllers;
using StellaOps.TestKit;
namespace StellaOps.Scanner.WebService.Tests;
public sealed class VulnerabilitiesControllerTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task List_ReturnsDeterministicReachabilityAwarePayload()
{
await using var factory = ScannerApplicationFactory.CreateLightweight();
await factory.InitializeAsync();
using var client = factory.CreateClient();
var firstResponse = await client.GetAsync("/api/v1/vulnerabilities?includeReachability=true", TestContext.Current.CancellationToken);
var secondResponse = await client.GetAsync("/api/v1/vulnerabilities?includeReachability=true", TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, firstResponse.StatusCode);
Assert.Equal(HttpStatusCode.OK, secondResponse.StatusCode);
var first = await firstResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
var second = await secondResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
Assert.Equal(first, second);
var payload = await firstResponse.Content.ReadFromJsonAsync<ScannerVulnerabilitiesResponseDto>(cancellationToken: TestContext.Current.CancellationToken);
Assert.NotNull(payload);
Assert.NotEmpty(payload!.Items);
Assert.All(payload.Items, item => Assert.False(string.IsNullOrWhiteSpace(item.VulnId)));
Assert.Contains(payload.Items, item => item.ReachabilityStatus is not null);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task List_AppliesSeverityAndReachabilityFilters()
{
await using var factory = ScannerApplicationFactory.CreateLightweight();
await factory.InitializeAsync();
using var client = factory.CreateClient();
var payload = await client.GetFromJsonAsync<ScannerVulnerabilitiesResponseDto>(
"/api/v1/vulnerabilities?severity=critical&reachability=reachable&includeReachability=true",
TestContext.Current.CancellationToken);
Assert.NotNull(payload);
Assert.NotEmpty(payload!.Items);
Assert.All(payload.Items, item =>
{
Assert.Equal("critical", item.Severity);
Assert.Equal("reachable", item.ReachabilityStatus);
});
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DetailAndStats_ReturnExpectedContracts()
{
await using var factory = ScannerApplicationFactory.CreateLightweight();
await factory.InitializeAsync();
using var client = factory.CreateClient();
var detail = await client.GetFromJsonAsync<ScannerVulnerabilityDto>(
"/api/v1/vulnerabilities/vuln-001",
TestContext.Current.CancellationToken);
var stats = await client.GetFromJsonAsync<ScannerVulnerabilityStatsDto>(
"/api/v1/vulnerabilities/status",
TestContext.Current.CancellationToken);
Assert.NotNull(detail);
Assert.Equal("CVE-2021-44228", detail!.CveId);
Assert.False(string.IsNullOrWhiteSpace(detail.FindingId));
Assert.NotEmpty(detail.AffectedComponents);
Assert.NotNull(stats);
Assert.True(stats!.Total >= 1);
Assert.Equal(stats.BySeverity.Values.Sum(), stats.Total);
}
}