up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
This commit is contained in:
@@ -50,6 +50,13 @@ internal static class SampleData
|
||||
{
|
||||
new AdvisoryRef("https://example.com/advisory/0001", "Upstream advisory")
|
||||
},
|
||||
Rationale: new PolicyRationale("rat-0001", "High severity RCE with known exploit; fix available"),
|
||||
Paths: new[] { "/src/app/Program.cs", "/src/lib/utils/net.cs" },
|
||||
Evidence: new[]
|
||||
{
|
||||
new EvidenceRef("sbom", "sbom-0001", "Inventory evidence"),
|
||||
new EvidenceRef("vex", "vex-0001", "Vendor statement")
|
||||
},
|
||||
FirstSeen: DateTimeOffset.Parse("2025-01-01T00:00:00Z"),
|
||||
LastSeen: DateTimeOffset.Parse("2025-11-01T00:00:00Z"),
|
||||
PolicyVersion: summaries[0].PolicyVersion,
|
||||
@@ -70,6 +77,12 @@ internal static class SampleData
|
||||
new PackageAffect("pkg:npm/foo", new[] { "4.5.6" })
|
||||
},
|
||||
AdvisoryRefs: Array.Empty<AdvisoryRef>(),
|
||||
Rationale: new PolicyRationale("rat-0002", "Medium severity; no exploit observed; fix unavailable"),
|
||||
Paths: new[] { "/app/node_modules/foo/index.js" },
|
||||
Evidence: new[]
|
||||
{
|
||||
new EvidenceRef("sbom", "sbom-0002", "Inventory evidence")
|
||||
},
|
||||
FirstSeen: DateTimeOffset.Parse("2024-06-10T00:00:00Z"),
|
||||
LastSeen: DateTimeOffset.Parse("2025-08-15T00:00:00Z"),
|
||||
PolicyVersion: summaries[1].PolicyVersion,
|
||||
|
||||
@@ -24,6 +24,9 @@ public sealed record VulnDetail(
|
||||
string Summary,
|
||||
IReadOnlyList<PackageAffect> AffectedPackages,
|
||||
IReadOnlyList<AdvisoryRef> AdvisoryRefs,
|
||||
PolicyRationale Rationale,
|
||||
IReadOnlyList<string> Paths,
|
||||
IReadOnlyList<EvidenceRef> Evidence,
|
||||
DateTimeOffset FirstSeen,
|
||||
DateTimeOffset LastSeen,
|
||||
string PolicyVersion,
|
||||
@@ -34,6 +37,10 @@ public sealed record PackageAffect(string Purl, IReadOnlyList<string> Versions);
|
||||
|
||||
public sealed record AdvisoryRef(string Url, string Title);
|
||||
|
||||
public sealed record EvidenceRef(string Kind, string Reference, string? Title = null);
|
||||
|
||||
public sealed record EvidenceProvenance(string LedgerEntryId, string EvidenceBundleId);
|
||||
|
||||
public sealed record PolicyRationale(string Id, string Summary);
|
||||
|
||||
public sealed record VulnListResponse(IReadOnlyList<VulnSummary> Items, string? NextPageToken);
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.OpenApi;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using StellaOps.VulnExplorer.Api.Data;
|
||||
using StellaOps.VulnExplorer.Api.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = "StellaOps Vuln Explorer API",
|
||||
Version = "v1",
|
||||
Description = "Deterministic vulnerability listing/detail endpoints"
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
}
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.MapGet("/v1/vulns", (
|
||||
HttpRequest request,
|
||||
[AsParameters] VulnFilter filter) =>
|
||||
app.MapGet("/v1/vulns", ([AsParameters] VulnFilter filter) =>
|
||||
{
|
||||
var tenant = request.Headers["x-stella-tenant"].ToString();
|
||||
if (string.IsNullOrWhiteSpace(tenant))
|
||||
if (string.IsNullOrWhiteSpace(filter.Tenant))
|
||||
{
|
||||
return Results.BadRequest(new { error = "x-stella-tenant required" });
|
||||
}
|
||||
@@ -40,9 +48,8 @@ app.MapGet("/v1/vulns", (
|
||||
})
|
||||
.WithOpenApi();
|
||||
|
||||
app.MapGet("/v1/vulns/{id}", (HttpRequest request, string id) =>
|
||||
app.MapGet("/v1/vulns/{id}", ([FromHeader(Name = "x-stella-tenant")] string? tenant, string id) =>
|
||||
{
|
||||
var tenant = request.Headers["x-stella-tenant"].ToString();
|
||||
if (string.IsNullOrWhiteSpace(tenant))
|
||||
{
|
||||
return Results.BadRequest(new { error = "x-stella-tenant required" });
|
||||
@@ -63,19 +70,19 @@ static IReadOnlyList<VulnSummary> ApplyFilter(IReadOnlyList<VulnSummary> source,
|
||||
{
|
||||
IEnumerable<VulnSummary> query = source;
|
||||
|
||||
if (filter.Cve?.Count > 0)
|
||||
if (filter.Cve is { Length: > 0 })
|
||||
{
|
||||
var set = filter.Cve.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
query = query.Where(v => v.CveIds.Any(set.Contains));
|
||||
}
|
||||
|
||||
if (filter.Purl?.Count > 0)
|
||||
if (filter.Purl is { Length: > 0 })
|
||||
{
|
||||
var set = filter.Purl.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
query = query.Where(v => v.Purls.Any(set.Contains));
|
||||
}
|
||||
|
||||
if (filter.Severity?.Count > 0)
|
||||
if (filter.Severity is { Length: > 0 })
|
||||
{
|
||||
var set = filter.Severity.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
query = query.Where(v => set.Contains(v.Severity));
|
||||
@@ -100,13 +107,13 @@ static IReadOnlyList<VulnSummary> ApplyFilter(IReadOnlyList<VulnSummary> source,
|
||||
}
|
||||
|
||||
public record VulnFilter(
|
||||
[FromHeader(Name = "x-stella-tenant")] string Tenant,
|
||||
[FromHeader(Name = "x-stella-tenant")] string? Tenant,
|
||||
[FromQuery(Name = "policyVersion")] string? PolicyVersion,
|
||||
[FromQuery(Name = "pageSize")] int? PageSize,
|
||||
[FromQuery(Name = "pageToken")] string? PageToken,
|
||||
[FromQuery(Name = "cve")] IReadOnlyList<string>? Cve,
|
||||
[FromQuery(Name = "purl")] IReadOnlyList<string>? Purl,
|
||||
[FromQuery(Name = "severity")] IReadOnlyList<string>? Severity,
|
||||
[FromQuery(Name = "cve")] string[]? Cve,
|
||||
[FromQuery(Name = "purl")] string[]? Purl,
|
||||
[FromQuery(Name = "severity")] string[]? Severity,
|
||||
[FromQuery(Name = "exploitability")] string? Exploitability,
|
||||
[FromQuery(Name = "fixAvailable")] bool? FixAvailable);
|
||||
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
7
src/VulnExplorer/StellaOps.VulnExplorer.Api/TASKS.md
Normal file
7
src/VulnExplorer/StellaOps.VulnExplorer.Api/TASKS.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Vuln Explorer API Tasks (Sprint 0129-0001-0001)
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| VULN-API-29-001 | DONE (2025-11-25) | OpenAPI v1 draft published at `docs/modules/vuln-explorer/openapi/vuln-explorer.v1.yaml` with tenant header, filters, deterministic paging. |
|
||||
| VULN-API-29-002 | DONE (2025-11-25) | Implemented `/v1/vulns` list + `/v1/vulns/{id}` detail with deterministic paging/filtering, sample data, Swagger UI; tests green (`tests/TestResults/vuln-explorer/api.trx`). |
|
||||
| VULN-API-29-003 | DONE (2025-11-25) | Detail endpoint now returns rationale, paths, evidence references; covered by Vuln Explorer API integration tests. |
|
||||
Reference in New Issue
Block a user