wip: doctor/cli/docs/api to vector db consolidation; api hardening for descriptions, tenant, and scopes; migrations and conversions of all DALs to EF v10
This commit is contained in:
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.RiskEngine.Core.Contracts;
|
||||
using StellaOps.RiskEngine.Core.Providers;
|
||||
using StellaOps.RiskEngine.WebService.Security;
|
||||
|
||||
namespace StellaOps.RiskEngine.WebService.Endpoints;
|
||||
|
||||
@@ -18,7 +19,8 @@ public static class ExploitMaturityEndpoints
|
||||
public static IEndpointRouteBuilder MapExploitMaturityEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("/exploit-maturity")
|
||||
.WithTags("ExploitMaturity");
|
||||
.WithTags("ExploitMaturity")
|
||||
.RequireAuthorization(RiskEnginePolicies.Read);
|
||||
|
||||
// GET /exploit-maturity/{cveId} - Assess exploit maturity for a CVE
|
||||
group.MapGet("/{cveId}", async (
|
||||
@@ -38,7 +40,7 @@ public static class ExploitMaturityEndpoints
|
||||
})
|
||||
.WithName("GetExploitMaturity")
|
||||
.WithSummary("Assess exploit maturity for a CVE")
|
||||
.WithDescription("Returns unified maturity level based on EPSS, KEV, and in-the-wild signals.")
|
||||
.WithDescription("Returns a unified exploit maturity assessment for the specified CVE by aggregating EPSS probability, KEV catalog membership, and in-the-wild exploitation signals. The result includes the overall maturity level, per-provider signal breakdown, and a composite confidence score.")
|
||||
.Produces<ExploitMaturityResult>()
|
||||
.ProducesProblem(400);
|
||||
|
||||
@@ -62,7 +64,7 @@ public static class ExploitMaturityEndpoints
|
||||
})
|
||||
.WithName("GetExploitMaturityLevel")
|
||||
.WithSummary("Get exploit maturity level for a CVE")
|
||||
.WithDescription("Returns the maturity level without full signal details.");
|
||||
.WithDescription("Returns only the resolved maturity level enum value for the specified CVE without the full per-provider signal breakdown. Use this lightweight variant when only the top-level classification is needed. Returns 404 if the maturity level could not be determined.");
|
||||
|
||||
// GET /exploit-maturity/{cveId}/history - Get maturity history
|
||||
group.MapGet("/{cveId}/history", async (
|
||||
@@ -82,7 +84,7 @@ public static class ExploitMaturityEndpoints
|
||||
})
|
||||
.WithName("GetExploitMaturityHistory")
|
||||
.WithSummary("Get exploit maturity history for a CVE")
|
||||
.WithDescription("Returns historical maturity level changes for a CVE.");
|
||||
.WithDescription("Returns the chronological history of maturity level assessments for the specified CVE, ordered from oldest to newest. Each entry records the maturity level, the contributing signals, and the timestamp of assessment. Useful for tracking escalation from theoretical to active exploitation.");
|
||||
|
||||
// POST /exploit-maturity/batch - Batch assess multiple CVEs
|
||||
group.MapPost("/batch", async (
|
||||
@@ -115,7 +117,8 @@ public static class ExploitMaturityEndpoints
|
||||
})
|
||||
.WithName("BatchAssessExploitMaturity")
|
||||
.WithSummary("Batch assess exploit maturity for multiple CVEs")
|
||||
.WithDescription("Returns maturity assessments for all requested CVEs.");
|
||||
.WithDescription("Submits a list of CVE IDs for bulk exploit maturity assessment and returns results for all successfully evaluated CVEs plus a separate errors array for any that could not be resolved. Duplicate CVE IDs are deduplicated before evaluation.")
|
||||
.RequireAuthorization(RiskEnginePolicies.Operate);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.Auth.Abstractions;
|
||||
using StellaOps.Auth.ServerIntegration;
|
||||
using StellaOps.RiskEngine.Core.Contracts;
|
||||
using StellaOps.RiskEngine.Core.Providers;
|
||||
using StellaOps.RiskEngine.Core.Services;
|
||||
using StellaOps.RiskEngine.Infrastructure.Stores;
|
||||
using StellaOps.RiskEngine.WebService.Endpoints;
|
||||
using StellaOps.RiskEngine.WebService.Security;
|
||||
using StellaOps.Router.AspNet;
|
||||
using System.Linq;
|
||||
|
||||
@@ -31,6 +33,14 @@ builder.Services.AddSingleton<IEpssSource, NullEpssSource>();
|
||||
builder.Services.AddSingleton<IKevSource, NullKevSource>();
|
||||
builder.Services.AddSingleton<IExploitMaturityService, ExploitMaturityService>();
|
||||
|
||||
// Authentication and authorization
|
||||
builder.Services.AddStellaOpsResourceServerAuthentication(builder.Configuration);
|
||||
builder.Services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddStellaOpsScopePolicy(RiskEnginePolicies.Read, StellaOpsScopes.RiskEngineRead);
|
||||
options.AddStellaOpsScopePolicy(RiskEnginePolicies.Operate, StellaOpsScopes.RiskEngineOperate);
|
||||
});
|
||||
|
||||
// Stella Router integration
|
||||
var routerEnabled = builder.Services.AddRouterMicroservice(
|
||||
builder.Configuration,
|
||||
@@ -50,13 +60,18 @@ if (app.Environment.IsDevelopment())
|
||||
}
|
||||
|
||||
app.UseStellaOpsCors();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.TryUseStellaRouter(routerEnabled);
|
||||
|
||||
// Map exploit maturity endpoints
|
||||
app.MapExploitMaturityEndpoints();
|
||||
|
||||
app.MapGet("/risk-scores/providers", (IRiskScoreProviderRegistry registry) =>
|
||||
Results.Ok(new { providers = registry.ProviderNames.OrderBy(n => n, StringComparer.OrdinalIgnoreCase) }));
|
||||
Results.Ok(new { providers = registry.ProviderNames.OrderBy(n => n, StringComparer.OrdinalIgnoreCase) }))
|
||||
.WithName("ListRiskScoreProviders")
|
||||
.WithDescription("Returns the sorted list of registered risk score provider names. Use this to discover which scoring strategies are available before submitting job or simulation requests.")
|
||||
.RequireAuthorization(RiskEnginePolicies.Read);
|
||||
|
||||
app.MapPost("/risk-scores/jobs", async (
|
||||
ScoreRequest request,
|
||||
@@ -74,14 +89,20 @@ app.MapPost("/risk-scores/jobs", async (
|
||||
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 });
|
||||
});
|
||||
})
|
||||
.WithName("CreateRiskScoreJob")
|
||||
.WithDescription("Enqueues a risk scoring job for the specified subject and provider, immediately executes it synchronously, and returns a 202 Accepted response with the job ID and computed result. The provider must be registered or the job will fail with an error in the result payload.")
|
||||
.RequireAuthorization(RiskEnginePolicies.Operate);
|
||||
|
||||
app.MapGet("/risk-scores/jobs/{jobId:guid}", (
|
||||
Guid jobId,
|
||||
[FromServices] IRiskScoreResultStore store) =>
|
||||
store.TryGet(jobId, out var result)
|
||||
? Results.Ok(result)
|
||||
: Results.NotFound());
|
||||
: Results.NotFound())
|
||||
.WithName("GetRiskScoreJob")
|
||||
.WithDescription("Returns the stored risk score result for the specified job ID. Returns 404 if the job ID is not found in the result store, which may occur if the store has been cleared or the ID is invalid.")
|
||||
.RequireAuthorization(RiskEnginePolicies.Read);
|
||||
|
||||
app.MapPost("/risk-scores/simulations", async (
|
||||
IReadOnlyCollection<ScoreRequest> requests,
|
||||
@@ -90,7 +111,10 @@ app.MapPost("/risk-scores/simulations", async (
|
||||
{
|
||||
var results = await EvaluateAsync(requests, registry, ct).ConfigureAwait(false);
|
||||
return Results.Ok(new { results });
|
||||
});
|
||||
})
|
||||
.WithName("RunRiskScoreSimulation")
|
||||
.WithDescription("Evaluates a collection of risk score requests against the registered providers and returns the full result list. Unlike the job endpoint, simulations do not persist results. Requests for unregistered providers are returned with a failure flag and error message.")
|
||||
.RequireAuthorization(RiskEnginePolicies.Operate);
|
||||
|
||||
app.MapPost("/risk-scores/simulations/summary", async (
|
||||
IReadOnlyCollection<ScoreRequest> requests,
|
||||
@@ -113,7 +137,10 @@ app.MapPost("/risk-scores/simulations/summary", async (
|
||||
};
|
||||
|
||||
return Results.Ok(new { summary, results });
|
||||
});
|
||||
})
|
||||
.WithName("GetRiskScoreSimulationSummary")
|
||||
.WithDescription("Evaluates a collection of risk score requests and returns both the full result list and an aggregate summary including average, minimum, and maximum scores plus the top-three highest-scoring subjects. Use this variant when a dashboard-style overview is required alongside per-subject detail.")
|
||||
.RequireAuthorization(RiskEnginePolicies.Operate);
|
||||
|
||||
// Refresh Router endpoint cache
|
||||
app.TryRefreshStellaRouterEndpoints(routerEnabled);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) StellaOps. Licensed under the BUSL-1.1.
|
||||
|
||||
namespace StellaOps.RiskEngine.WebService.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Named authorization policy constants for the Risk Engine service.
|
||||
/// Policies are registered via AddStellaOpsScopePolicy in Program.cs.
|
||||
/// </summary>
|
||||
internal static class RiskEnginePolicies
|
||||
{
|
||||
/// <summary>Policy for querying risk score providers and job results. Requires risk-engine:read scope.</summary>
|
||||
public const string Read = "RiskEngine.Read";
|
||||
|
||||
/// <summary>Policy for submitting risk score jobs and simulations. Requires risk-engine:operate scope.</summary>
|
||||
public const string Operate = "RiskEngine.Operate";
|
||||
}
|
||||
Reference in New Issue
Block a user