Files
git.stella-ops.org/src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.WebService/Program.cs
StellaOps Bot 9f6e6f7fb3
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
up
2025-11-25 22:09:44 +02:00

126 lines
4.3 KiB
C#

using Microsoft.AspNetCore.Mvc;
using System.Linq;
using StellaOps.RiskEngine.Core.Contracts;
using StellaOps.RiskEngine.Core.Providers;
using StellaOps.RiskEngine.Core.Services;
using StellaOps.RiskEngine.Infrastructure.Stores;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddSingleton<RiskScoreQueue>();
builder.Services.AddSingleton<IRiskScoreResultStore, InMemoryRiskScoreResultStore>();
builder.Services.AddSingleton<IRiskScoreProviderRegistry>(_ =>
new RiskScoreProviderRegistry(new IRiskScoreProvider[]
{
new DefaultTransformsProvider(),
new CvssKevProvider(new NullCvssSource(), new NullKevSource()),
new VexGateProvider(),
new FixExposureProvider()
}));
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.MapGet("/risk-scores/providers", (IRiskScoreProviderRegistry registry) =>
Results.Ok(new { providers = registry.ProviderNames.OrderBy(n => n, StringComparer.OrdinalIgnoreCase) }));
app.MapPost("/risk-scores/jobs", async (
ScoreRequest request,
[FromServices] RiskScoreQueue queue,
[FromServices] IRiskScoreProviderRegistry registry,
[FromServices] IRiskScoreResultStore store,
CancellationToken ct) =>
{
var normalized = new ScoreRequest(
request.Provider,
request.Subject,
request.Signals ?? new Dictionary<string, double>());
var jobId = await queue.EnqueueWithIdAsync(normalized, ct).ConfigureAwait(false);
var worker = new RiskScoreWorker(queue, registry, store, TimeProvider.System);
var result = await worker.ProcessNextAsync(ct).ConfigureAwait(false);
return Results.Accepted($"/risk-scores/jobs/{jobId}", new { jobId, result });
});
app.MapGet("/risk-scores/jobs/{jobId:guid}", (
Guid jobId,
[FromServices] IRiskScoreResultStore store) =>
store.TryGet(jobId, out var result)
? Results.Ok(result)
: Results.NotFound());
app.MapPost("/risk-scores/simulations", async (
IReadOnlyCollection<ScoreRequest> requests,
[FromServices] IRiskScoreProviderRegistry registry,
CancellationToken ct) =>
{
var results = await EvaluateAsync(requests, registry, ct).ConfigureAwait(false);
return Results.Ok(new { results });
});
app.MapPost("/risk-scores/simulations/summary", async (
IReadOnlyCollection<ScoreRequest> requests,
[FromServices] IRiskScoreProviderRegistry registry,
CancellationToken ct) =>
{
var results = await EvaluateAsync(requests, registry, ct).ConfigureAwait(false);
var scores = results.Select(r => r.Score).ToArray();
var summary = new
{
averageScore = scores.Length == 0 ? 0d : scores.Average(),
minScore = scores.Length == 0 ? 0d : scores.Min(),
maxScore = scores.Length == 0 ? 0d : scores.Max(),
topMovers = results
.OrderByDescending(r => r.Score)
.ThenBy(r => r.Subject, StringComparer.Ordinal)
.Take(3)
.ToArray()
};
return Results.Ok(new { summary, results });
});
app.Run();
static async Task<List<RiskScoreResult>> EvaluateAsync(
IReadOnlyCollection<ScoreRequest> requests,
IRiskScoreProviderRegistry registry,
CancellationToken ct)
{
var results = new List<RiskScoreResult>(requests.Count);
foreach (var req in requests)
{
var normalized = new ScoreRequest(
req.Provider,
req.Subject,
req.Signals ?? new Dictionary<string, double>());
if (!registry.TryGet(normalized.Provider, out var provider))
{
results.Add(new RiskScoreResult(Guid.NewGuid(), normalized.Provider, normalized.Subject, 0d, false, "Provider not registered", normalized.Signals, TimeProvider.System.GetUtcNow()));
continue;
}
try
{
var score = await provider.ScoreAsync(normalized, ct).ConfigureAwait(false);
results.Add(new RiskScoreResult(Guid.NewGuid(), normalized.Provider, normalized.Subject, score, true, null, normalized.Signals, TimeProvider.System.GetUtcNow()));
}
catch (Exception ex)
{
results.Add(new RiskScoreResult(Guid.NewGuid(), normalized.Provider, normalized.Subject, 0d, false, ex.Message, normalized.Signals, TimeProvider.System.GetUtcNow()));
}
}
return results;
}