search and ai stabilization work, localization stablized.

This commit is contained in:
master
2026-02-24 23:29:36 +02:00
parent 4f947a8b61
commit b07d27772e
766 changed files with 55299 additions and 3221 deletions

View File

@@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Http.HttpResults;
using StellaOps.Replay.Core.FeedSnapshots;
using static StellaOps.Localization.T;
namespace StellaOps.Replay.WebService;
@@ -26,7 +27,7 @@ public static class PointInTimeQueryEndpoints
// GET /v1/pit/advisory/{cveId} - Query advisory state at a point in time
group.MapGet("/{cveId}", QueryAdvisoryAsync)
.WithName("QueryAdvisoryAtPointInTime")
.WithDescription("Returns the advisory state for a specific CVE at a given point-in-time timestamp from the specified provider. Uses the nearest captured feed snapshot to reconstruct the advisory as it appeared at that moment.")
.WithDescription(_t("replay.pit.query_description"))
.Produces<AdvisoryQueryResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status400BadRequest);
@@ -34,21 +35,21 @@ public static class PointInTimeQueryEndpoints
// POST /v1/pit/advisory/cross-provider - Query advisory across multiple providers
group.MapPost("/cross-provider", QueryCrossProviderAsync)
.WithName("QueryCrossProviderAdvisory")
.WithDescription("Queries advisory state across multiple feed providers at a single point in time and returns per-provider results along with a consensus summary of severity and fix status.")
.WithDescription(_t("replay.pit.cross_provider_description"))
.Produces<CrossProviderQueryResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
// GET /v1/pit/advisory/{cveId}/timeline - Get advisory timeline
group.MapGet("/{cveId}/timeline", GetAdvisoryTimelineAsync)
.WithName("GetAdvisoryTimeline")
.WithDescription("Returns the change timeline for a specific CVE from a given provider within an optional time range. Each entry identifies the snapshot digest and the type of change observed.")
.WithDescription(_t("replay.pit.timeline_description"))
.Produces<AdvisoryTimelineResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
// POST /v1/pit/advisory/diff - Compare advisory at two points in time
group.MapPost("/diff", CompareAdvisoryAtTimesAsync)
.WithName("CompareAdvisoryAtTimes")
.WithDescription("Produces a field-level diff of a CVE advisory between two distinct points in time from the same provider, identifying severity, fix-status, and metadata changes.")
.WithDescription(_t("replay.pit.diff_description"))
.Produces<AdvisoryDiffResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
@@ -59,28 +60,28 @@ public static class PointInTimeQueryEndpoints
// POST /v1/pit/snapshots - Capture a feed snapshot
snapshotsGroup.MapPost("/", CaptureSnapshotAsync)
.WithName("CaptureFeedSnapshot")
.WithDescription("Captures and stores a feed snapshot for a specific provider, computing its content-addressable digest. Returns 201 Created with the digest and whether an existing snapshot was reused.")
.WithDescription(_t("replay.snapshot.capture_description"))
.Produces<SnapshotCaptureResponse>(StatusCodes.Status201Created)
.ProducesProblem(StatusCodes.Status400BadRequest);
// GET /v1/pit/snapshots/{digest} - Get a snapshot by digest
snapshotsGroup.MapGet("/{digest}", GetSnapshotAsync)
.WithName("GetFeedSnapshot")
.WithDescription("Returns snapshot metadata for a specific content-addressable digest including provider ID, feed type, and capture timestamp. Returns 404 if the digest is not stored.")
.WithDescription(_t("replay.snapshot.get_description"))
.Produces<SnapshotResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
// GET /v1/pit/snapshots/{digest}/verify - Verify snapshot integrity
snapshotsGroup.MapGet("/{digest}/verify", VerifySnapshotIntegrityAsync)
.WithName("VerifySnapshotIntegrity")
.WithDescription("Verifies the integrity of a stored snapshot by recomputing its content digest and comparing it against the stored value. Returns a verification result with expected and actual digest values.")
.WithDescription(_t("replay.snapshot.verify_description"))
.Produces<SnapshotVerificationResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
// POST /v1/pit/snapshots/bundle - Create a snapshot bundle
snapshotsGroup.MapPost("/bundle", CreateSnapshotBundleAsync)
.WithName("CreateSnapshotBundle")
.WithDescription("Creates a composite snapshot bundle from multiple providers at a given point in time, returning the bundle digest, completeness flag, and the list of any missing providers.")
.WithDescription(_t("replay.snapshot.bundle_description"))
.Produces<SnapshotBundleResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
}
@@ -97,7 +98,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "missing_provider",
detail: "Provider ID is required");
detail: _t("replay.error.missing_provider"));
}
if (!queryParams.PointInTime.HasValue)
@@ -105,7 +106,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "missing_point_in_time",
detail: "Point-in-time timestamp is required");
detail: _t("replay.error.missing_point_in_time"));
}
var result = await resolver.ResolveAdvisoryAsync(
@@ -142,7 +143,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "missing_cve_id",
detail: "CVE ID is required");
detail: _t("replay.error.missing_cve_id"));
}
if (request.ProviderIds is null || request.ProviderIds.Count == 0)
@@ -150,7 +151,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "missing_providers",
detail: "At least one provider ID is required");
detail: _t("replay.error.missing_providers"));
}
var result = await resolver.ResolveCrossProviderAsync(
@@ -238,7 +239,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "missing_required_fields",
detail: "CVE ID and Provider ID are required");
detail: _t("replay.pit.cve_and_provider_required"));
}
var diff = await resolver.CompareAtTimesAsync(
@@ -275,7 +276,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "missing_required_fields",
detail: "Provider ID and feed data are required");
detail: _t("replay.pit.provider_and_feed_required"));
}
var result = await snapshotService.CaptureSnapshotAsync(
@@ -294,7 +295,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "capture_failed",
detail: result.Error ?? "Failed to capture snapshot");
detail: result.Error ?? _t("replay.error.capture_failed"));
}
return TypedResults.Created(
@@ -368,7 +369,7 @@ public static class PointInTimeQueryEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "missing_providers",
detail: "At least one provider ID is required");
detail: _t("replay.error.missing_providers"));
}
var bundle = await snapshotService.CreateBundleAsync(

View File

@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http.HttpResults;
using Serilog;
using Serilog.Events;
using StellaOps.Audit.ReplayToken;
using StellaOps.Localization;
using StellaOps.Auth.Abstractions;
using StellaOps.Auth.ServerIntegration;
using StellaOps.Auth.ServerIntegration.Tenancy;
@@ -114,6 +115,9 @@ builder.Services.AddAuthorization(options =>
builder.Services.AddStellaOpsTenantServices();
builder.Services.AddStellaOpsCors(builder.Environment, builder.Configuration);
builder.Services.AddStellaOpsLocalization(builder.Configuration);
builder.Services.AddTranslationBundle(System.Reflection.Assembly.GetExecutingAssembly());
// Stella Router integration
var routerEnabled = builder.Services.AddRouterMicroservice(
builder.Configuration,
@@ -144,6 +148,7 @@ app.UseExceptionHandler(exceptionApp =>
});
app.UseStellaOpsCors();
app.UseStellaOpsLocalization();
app.UseAuthentication();
app.UseAuthorization();
app.UseStellaOpsTenantMiddleware();
@@ -425,8 +430,10 @@ app.MapGet("/.well-known/openapi", (HttpContext context) =>
.WithName("ReplayOpenApiDocument")
.Produces(StatusCodes.Status200OK);
await app.LoadTranslationsAsync();
app.TryRefreshStellaRouterEndpoints(routerEnabled);
app.Run();
await app.RunAsync().ConfigureAwait(false);
static bool TryGetTenant(HttpContext httpContext, out ProblemHttpResult? problem, out string tenantId)
{

View File

@@ -22,7 +22,13 @@
<ProjectReference Include="..\..\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj" />
<ProjectReference Include="..\..\Telemetry\StellaOps.Telemetry.Core\StellaOps.Telemetry.Core\StellaOps.Telemetry.Core.csproj" />
<ProjectReference Include="..\__Libraries\StellaOps.Replay.Core\StellaOps.Replay.Core.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Localization\StellaOps.Localization.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Translations\*.json" />
</ItemGroup>
<PropertyGroup Label="StellaOpsReleaseVersion">
<Version>1.0.0-alpha1</Version>
<InformationalVersion>1.0.0-alpha1</InformationalVersion>

View File

@@ -0,0 +1,31 @@
{
"_meta": { "locale": "en-US", "namespace": "replay", "version": "1.0" },
"replay.verdict.execute_description": "Executes a deterministic verdict replay from an audit bundle, re-evaluating the original policy with the stored inputs. Returns whether the replayed verdict matches the original, drift items, and an optional divergence report.",
"replay.verdict.verify_description": "Checks whether an audit bundle is eligible for deterministic replay. Returns a confidence score, eligibility flags, and the expected outcome without executing the replay.",
"replay.verdict.status_description": "Returns the stored replay history for a given audit manifest ID including total replay count, success/failure counts, and the timestamp of the last replay.",
"replay.verdict.compare_description": "Compares two replay execution results and produces a structured divergence report identifying field-level differences with per-divergence severity ratings.",
"replay.error.bundle_read_failed": "Failed to read audit bundle",
"replay.error.replay_not_eligible": "Replay is not eligible.",
"replay.error.context_init_failed": "Failed to initialize replay context",
"replay.error.missing_provider": "Provider ID is required",
"replay.error.missing_point_in_time": "Point-in-time timestamp is required",
"replay.error.missing_cve_id": "CVE ID is required",
"replay.error.missing_providers": "At least one provider ID is required",
"replay.error.missing_required_fields": "Required fields are missing",
"replay.error.capture_failed": "Failed to capture snapshot",
"replay.pit.query_description": "Returns the advisory state for a specific CVE at a given point-in-time timestamp from the specified provider. Uses the nearest captured feed snapshot to reconstruct the advisory as it appeared at that moment.",
"replay.pit.cross_provider_description": "Queries advisory state across multiple feed providers at a single point in time and returns per-provider results along with a consensus summary of severity and fix status.",
"replay.pit.timeline_description": "Returns the change timeline for a specific CVE from a given provider within an optional time range. Each entry identifies the snapshot digest and the type of change observed.",
"replay.pit.diff_description": "Produces a field-level diff of a CVE advisory between two distinct points in time from the same provider, identifying severity, fix-status, and metadata changes.",
"replay.snapshot.capture_description": "Captures and stores a feed snapshot for a specific provider, computing its content-addressable digest. Returns 201 Created with the digest and whether an existing snapshot was reused.",
"replay.snapshot.get_description": "Returns snapshot metadata for a specific content-addressable digest including provider ID, feed type, and capture timestamp. Returns 404 if the digest is not stored.",
"replay.snapshot.verify_description": "Verifies the integrity of a stored snapshot by recomputing its content digest and comparing it against the stored value. Returns a verification result with expected and actual digest values.",
"replay.snapshot.bundle_description": "Creates a composite snapshot bundle from multiple providers at a given point in time, returning the bundle digest, completeness flag, and the list of any missing providers.",
"replay.pit.cve_and_provider_required": "CVE ID and Provider ID are required",
"replay.pit.provider_and_feed_required": "Provider ID and feed data are required"
}

View File

@@ -8,6 +8,7 @@
using Microsoft.AspNetCore.Http.HttpResults;
using StellaOps.AuditPack.Models;
using StellaOps.AuditPack.Services;
using static StellaOps.Localization.T;
namespace StellaOps.Replay.WebService;
@@ -30,7 +31,7 @@ public static class VerdictReplayEndpoints
// POST /v1/replay/verdict - Execute verdict replay
group.MapPost("/", ExecuteReplayAsync)
.WithName("ExecuteVerdictReplay")
.WithDescription("Executes a deterministic verdict replay from an audit bundle, re-evaluating the original policy with the stored inputs. Returns whether the replayed verdict matches the original, drift items, and an optional divergence report.")
.WithDescription(_t("replay.verdict.execute_description"))
.Produces<VerdictReplayResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status404NotFound);
@@ -38,21 +39,21 @@ public static class VerdictReplayEndpoints
// POST /v1/replay/verdict/verify - Verify replay eligibility
group.MapPost("/verify", VerifyEligibilityAsync)
.WithName("VerifyReplayEligibility")
.WithDescription("Checks whether an audit bundle is eligible for deterministic replay. Returns a confidence score, eligibility flags, and the expected outcome without executing the replay.")
.WithDescription(_t("replay.verdict.verify_description"))
.Produces<ReplayEligibilityResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
// GET /v1/replay/verdict/{manifestId}/status - Get replay status
group.MapGet("/{manifestId}/status", GetReplayStatusAsync)
.WithName("GetReplayStatus")
.WithDescription("Returns the stored replay history for a given audit manifest ID including total replay count, success/failure counts, and the timestamp of the last replay.")
.WithDescription(_t("replay.verdict.status_description"))
.Produces<ReplayStatusResponse>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
// POST /v1/replay/verdict/compare - Compare two replay executions
group.MapPost("/compare", CompareReplayResultsAsync)
.WithName("CompareReplayResults")
.WithDescription("Compares two replay execution results and produces a structured divergence report identifying field-level differences with per-divergence severity ratings.")
.WithDescription(_t("replay.verdict.compare_description"))
.Produces<ReplayComparisonResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest);
}
@@ -73,7 +74,7 @@ public static class VerdictReplayEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "bundle_read_failed",
detail: bundleResult.Error ?? "Failed to read audit bundle");
detail: bundleResult.Error ?? _t("replay.error.bundle_read_failed"));
}
// Check eligibility
@@ -101,7 +102,7 @@ public static class VerdictReplayEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "context_init_failed",
detail: initResult.Error ?? "Failed to initialize replay context");
detail: initResult.Error ?? _t("replay.error.context_init_failed"));
}
var execOptions = new ReplayExecutionOptions
@@ -165,7 +166,7 @@ public static class VerdictReplayEndpoints
return TypedResults.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "bundle_read_failed",
detail: bundleResult.Error ?? "Failed to read audit bundle");
detail: bundleResult.Error ?? _t("replay.error.bundle_read_failed"));
}
var eligibility = replayPredicate.Evaluate(bundleResult.Manifest, request.CurrentInputState);