Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// TriageInboxEndpoints.cs
|
||||
// Sprint: SPRINT_3900_0003_0001_exploit_path_inbox_proof_bundles
|
||||
// Description: HTTP endpoints for triage inbox with grouped exploit paths.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Scanner.Triage.Models;
|
||||
using StellaOps.Scanner.Triage.Services;
|
||||
using StellaOps.Scanner.WebService.Security;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Endpoints.Triage;
|
||||
|
||||
/// <summary>
|
||||
/// Endpoints for triage inbox - grouped exploit paths.
|
||||
/// </summary>
|
||||
internal static class TriageInboxEndpoints
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Maps triage inbox endpoints.
|
||||
/// </summary>
|
||||
public static void MapTriageInboxEndpoints(this RouteGroupBuilder apiGroup)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(apiGroup);
|
||||
|
||||
var triageGroup = apiGroup.MapGroup("/triage")
|
||||
.WithTags("Triage");
|
||||
|
||||
// GET /v1/triage/inbox?artifactDigest={digest}&filter={filter}
|
||||
triageGroup.MapGet("/inbox", HandleGetInboxAsync)
|
||||
.WithName("scanner.triage.inbox")
|
||||
.WithDescription("Retrieves triage inbox with grouped exploit paths for an artifact.")
|
||||
.Produces<TriageInboxResponse>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.RequireAuthorization(ScannerPolicies.TriageRead);
|
||||
}
|
||||
|
||||
private static async Task<IResult> HandleGetInboxAsync(
|
||||
string artifactDigest,
|
||||
string? filter,
|
||||
IExploitPathGroupingService groupingService,
|
||||
IFindingQueryService findingService,
|
||||
HttpContext context,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(groupingService);
|
||||
ArgumentNullException.ThrowIfNull(findingService);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(artifactDigest))
|
||||
{
|
||||
return Results.BadRequest(new
|
||||
{
|
||||
type = "validation-error",
|
||||
title = "Invalid artifact digest",
|
||||
detail = "Artifact digest is required."
|
||||
});
|
||||
}
|
||||
|
||||
var findings = await findingService.GetFindingsForArtifactAsync(artifactDigest, cancellationToken);
|
||||
var paths = await groupingService.GroupFindingsAsync(artifactDigest, findings, cancellationToken);
|
||||
|
||||
var filteredPaths = ApplyFilter(paths, filter);
|
||||
|
||||
var response = new TriageInboxResponse
|
||||
{
|
||||
ArtifactDigest = artifactDigest,
|
||||
TotalPaths = paths.Count,
|
||||
FilteredPaths = filteredPaths.Count,
|
||||
Filter = filter,
|
||||
Paths = filteredPaths,
|
||||
GeneratedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ExploitPath> ApplyFilter(
|
||||
IReadOnlyList<ExploitPath> paths,
|
||||
string? filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
return paths;
|
||||
|
||||
return filter.ToLowerInvariant() switch
|
||||
{
|
||||
"actionable" => paths.Where(p => !p.IsQuiet && p.Reachability is ReachabilityStatus.StaticallyReachable or ReachabilityStatus.RuntimeConfirmed).ToList(),
|
||||
"noisy" => paths.Where(p => p.IsQuiet).ToList(),
|
||||
"reachable" => paths.Where(p => p.Reachability is ReachabilityStatus.StaticallyReachable or ReachabilityStatus.RuntimeConfirmed).ToList(),
|
||||
"runtime" => paths.Where(p => p.Reachability == ReachabilityStatus.RuntimeConfirmed).ToList(),
|
||||
"critical" => paths.Where(p => p.RiskScore.CriticalCount > 0).ToList(),
|
||||
"high" => paths.Where(p => p.RiskScore.HighCount > 0).ToList(),
|
||||
_ => paths
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Response for triage inbox endpoint.
|
||||
/// </summary>
|
||||
public sealed record TriageInboxResponse
|
||||
{
|
||||
public required string ArtifactDigest { get; init; }
|
||||
public required int TotalPaths { get; init; }
|
||||
public required int FilteredPaths { get; init; }
|
||||
public string? Filter { get; init; }
|
||||
public required IReadOnlyList<ExploitPath> Paths { get; init; }
|
||||
public required DateTimeOffset GeneratedAt { get; init; }
|
||||
}
|
||||
|
||||
public interface IFindingQueryService
|
||||
{
|
||||
Task<IReadOnlyList<Finding>> GetFindingsForArtifactAsync(string artifactDigest, CancellationToken ct);
|
||||
}
|
||||
Reference in New Issue
Block a user