work
Some checks failed
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
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-25 08:01:23 +02:00
parent d92973d6fd
commit 6bee1fdcf5
207 changed files with 12816 additions and 2295 deletions

View File

@@ -0,0 +1,87 @@
using StellaOps.VulnExplorer.Api.Models;
namespace StellaOps.VulnExplorer.Api.Data;
internal static class SampleData
{
private static readonly VulnSummary[] summaries =
{
new(
Id: "vuln-0001",
Severity: "HIGH",
Score: 8.2,
Kev: true,
Exploitability: "known",
FixAvailable: true,
CveIds: new[] { "CVE-2025-0001" },
Purls: new[] { "pkg:maven/org.example/app@1.2.3" },
PolicyVersion: "policy-main",
RationaleId: "rat-0001"),
new(
Id: "vuln-0002",
Severity: "MEDIUM",
Score: 5.4,
Kev: false,
Exploitability: "unknown",
FixAvailable: false,
CveIds: new[] { "CVE-2024-2222" },
Purls: new[] { "pkg:npm/foo@4.5.6" },
PolicyVersion: "policy-main",
RationaleId: "rat-0002")
};
private static readonly VulnDetail[] details =
{
new(
Id: "vuln-0001",
Severity: "HIGH",
Score: 8.2,
Kev: true,
Exploitability: "known",
FixAvailable: true,
CveIds: summaries[0].CveIds,
Purls: summaries[0].Purls,
Summary: "Example vulnerable library with RCE.",
AffectedPackages: new[]
{
new PackageAffect("pkg:maven/org.example/app", new[] { "1.2.3" })
},
AdvisoryRefs: new[]
{
new AdvisoryRef("https://example.com/advisory/0001", "Upstream advisory")
},
FirstSeen: DateTimeOffset.Parse("2025-01-01T00:00:00Z"),
LastSeen: DateTimeOffset.Parse("2025-11-01T00:00:00Z"),
PolicyVersion: summaries[0].PolicyVersion,
RationaleId: summaries[0].RationaleId,
Provenance: new EvidenceProvenance("ledger-1", "evidence-1")),
new(
Id: "vuln-0002",
Severity: "MEDIUM",
Score: 5.4,
Kev: false,
Exploitability: "unknown",
FixAvailable: false,
CveIds: summaries[1].CveIds,
Purls: summaries[1].Purls,
Summary: "Prototype pollution risk.",
AffectedPackages: new[]
{
new PackageAffect("pkg:npm/foo", new[] { "4.5.6" })
},
AdvisoryRefs: Array.Empty<AdvisoryRef>(),
FirstSeen: DateTimeOffset.Parse("2024-06-10T00:00:00Z"),
LastSeen: DateTimeOffset.Parse("2025-08-15T00:00:00Z"),
PolicyVersion: summaries[1].PolicyVersion,
RationaleId: summaries[1].RationaleId,
Provenance: new EvidenceProvenance("ledger-2", "evidence-2"))
};
public static IReadOnlyList<VulnSummary> Summaries => summaries;
public static bool TryGetDetail(string id, out VulnDetail? detail)
{
detail = details.FirstOrDefault(d => string.Equals(d.Id, id, StringComparison.Ordinal));
return detail is not null;
}
}

View File

@@ -0,0 +1,39 @@
namespace StellaOps.VulnExplorer.Api.Models;
public sealed record VulnSummary(
string Id,
string Severity,
double Score,
bool Kev,
string Exploitability,
bool FixAvailable,
IReadOnlyList<string> CveIds,
IReadOnlyList<string> Purls,
string PolicyVersion,
string RationaleId);
public sealed record VulnDetail(
string Id,
string Severity,
double Score,
bool Kev,
string Exploitability,
bool FixAvailable,
IReadOnlyList<string> CveIds,
IReadOnlyList<string> Purls,
string Summary,
IReadOnlyList<PackageAffect> AffectedPackages,
IReadOnlyList<AdvisoryRef> AdvisoryRefs,
DateTimeOffset FirstSeen,
DateTimeOffset LastSeen,
string PolicyVersion,
string RationaleId,
EvidenceProvenance Provenance);
public sealed record PackageAffect(string Purl, IReadOnlyList<string> Versions);
public sealed record AdvisoryRef(string Url, string Title);
public sealed record EvidenceProvenance(string LedgerEntryId, string EvidenceBundleId);
public sealed record VulnListResponse(IReadOnlyList<VulnSummary> Items, string? NextPageToken);

View File

@@ -0,0 +1,115 @@
using System.Globalization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OpenApi;
using StellaOps.VulnExplorer.Api.Data;
using StellaOps.VulnExplorer.Api.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.MapGet("/v1/vulns", (
HttpRequest request,
[AsParameters] VulnFilter filter) =>
{
var tenant = request.Headers["x-stella-tenant"].ToString();
if (string.IsNullOrWhiteSpace(tenant))
{
return Results.BadRequest(new { error = "x-stella-tenant required" });
}
var data = ApplyFilter(SampleData.Summaries, filter);
var pageSize = Math.Clamp(filter.PageSize ?? 50, 1, 200);
var offset = ParsePageToken(filter.PageToken);
var page = data.Skip(offset).Take(pageSize).ToArray();
var nextOffset = offset + page.Length;
var next = nextOffset < data.Count ? nextOffset.ToString(CultureInfo.InvariantCulture) : null;
var response = new VulnListResponse(page, next);
return Results.Ok(response);
})
.WithOpenApi();
app.MapGet("/v1/vulns/{id}", (HttpRequest request, string id) =>
{
var tenant = request.Headers["x-stella-tenant"].ToString();
if (string.IsNullOrWhiteSpace(tenant))
{
return Results.BadRequest(new { error = "x-stella-tenant required" });
}
return SampleData.TryGetDetail(id, out var detail) && detail is not null
? Results.Ok(detail)
: Results.NotFound();
})
.WithOpenApi();
app.Run();
static int ParsePageToken(string? token) =>
int.TryParse(token, out var offset) && offset >= 0 ? offset : 0;
static IReadOnlyList<VulnSummary> ApplyFilter(IReadOnlyList<VulnSummary> source, VulnFilter filter)
{
IEnumerable<VulnSummary> query = source;
if (filter.Cve?.Count > 0)
{
var set = filter.Cve.ToHashSet(StringComparer.OrdinalIgnoreCase);
query = query.Where(v => v.CveIds.Any(set.Contains));
}
if (filter.Purl?.Count > 0)
{
var set = filter.Purl.ToHashSet(StringComparer.OrdinalIgnoreCase);
query = query.Where(v => v.Purls.Any(set.Contains));
}
if (filter.Severity?.Count > 0)
{
var set = filter.Severity.ToHashSet(StringComparer.OrdinalIgnoreCase);
query = query.Where(v => set.Contains(v.Severity));
}
if (filter.Exploitability is not null)
{
query = query.Where(v => string.Equals(v.Exploitability, filter.Exploitability, StringComparison.OrdinalIgnoreCase));
}
if (filter.FixAvailable is not null)
{
query = query.Where(v => v.FixAvailable == filter.FixAvailable);
}
// deterministic ordering: score desc, id asc
query = query
.OrderByDescending(v => v.Score)
.ThenBy(v => v.Id, StringComparer.Ordinal);
return query.ToArray();
}
public record VulnFilter(
[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 = "exploitability")] string? Exploitability,
[FromQuery(Name = "fixAvailable")] bool? FixAvailable);
public partial class Program { }
public partial class Program { }

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<RootNamespace>StellaOps.VulnExplorer.Api</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}