consolidation of some of the modules, localization fixes, product advisories work, qa work
This commit is contained in:
@@ -100,6 +100,12 @@ public static class GovernanceEndpoints
|
||||
.WithName("ValidateRiskProfile")
|
||||
.WithDescription("Validate a candidate risk profile configuration without persisting it. Checks for required fields (name, at least one signal) and emits warnings when signal weights do not sum to 1.0. Used by policy authoring tools to provide inline validation feedback before profile creation.");
|
||||
|
||||
// Risk Budget endpoints
|
||||
governance.MapGet("/risk-budget/dashboard", GetRiskBudgetDashboardAsync)
|
||||
.RequireAuthorization(policy => policy.RequireStellaOpsScopes(StellaOpsScopes.PolicyRead))
|
||||
.WithName("GetRiskBudgetDashboard")
|
||||
.WithDescription("Retrieve the current risk budget dashboard including utilization, headroom, top contributors, active alerts, and KPIs for the tenant.");
|
||||
|
||||
// Audit endpoints
|
||||
governance.MapGet("/audit/events", GetAuditEventsAsync)
|
||||
.RequireAuthorization(policy => policy.RequireStellaOpsScopes(StellaOpsScopes.PolicyAudit))
|
||||
@@ -539,6 +545,117 @@ public static class GovernanceEndpoints
|
||||
return Task.FromResult(Results.Ok(response));
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Risk Budget Handlers
|
||||
// ========================================================================
|
||||
|
||||
private static Task<IResult> GetRiskBudgetDashboardAsync(
|
||||
HttpContext httpContext,
|
||||
[FromServices] TimeProvider timeProvider,
|
||||
[FromQuery] string? tenantId,
|
||||
[FromQuery] string? projectId)
|
||||
{
|
||||
var tenant = tenantId ?? GetTenantId(httpContext) ?? "default";
|
||||
var now = timeProvider.GetUtcNow();
|
||||
var traceId = httpContext.TraceIdentifier;
|
||||
|
||||
var periodStart = new DateTimeOffset(now.Year, ((now.Month - 1) / 3) * 3 + 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
var periodEnd = periodStart.AddMonths(3).AddSeconds(-1);
|
||||
|
||||
var response = new
|
||||
{
|
||||
config = new
|
||||
{
|
||||
id = "budget-001",
|
||||
tenantId = tenant,
|
||||
projectId = projectId ?? (string?)null,
|
||||
name = $"Q{(now.Month - 1) / 3 + 1} {now.Year} Risk Budget",
|
||||
totalBudget = 1000,
|
||||
warningThreshold = 70,
|
||||
criticalThreshold = 90,
|
||||
period = "quarterly",
|
||||
periodStart = periodStart.ToString("O", CultureInfo.InvariantCulture),
|
||||
periodEnd = periodEnd.ToString("O", CultureInfo.InvariantCulture),
|
||||
createdAt = periodStart.AddDays(-15).ToString("O", CultureInfo.InvariantCulture),
|
||||
updatedAt = now.ToString("O", CultureInfo.InvariantCulture)
|
||||
},
|
||||
currentRiskPoints = 720,
|
||||
headroom = 280,
|
||||
utilizationPercent = 72.0,
|
||||
status = "warning",
|
||||
timeSeries = Enumerable.Range(0, 5).Select(i =>
|
||||
{
|
||||
var ts = now.AddDays(-28 + i * 7);
|
||||
var actual = 600 + i * 30;
|
||||
return new
|
||||
{
|
||||
timestamp = ts.ToString("O", CultureInfo.InvariantCulture),
|
||||
actual,
|
||||
budget = 1000,
|
||||
headroom = 1000 - actual
|
||||
};
|
||||
}).ToList(),
|
||||
updatedAt = now.ToString("O", CultureInfo.InvariantCulture),
|
||||
traceId,
|
||||
topContributors = new[]
|
||||
{
|
||||
new { identifier = "pkg:npm/lodash@4.17.20", type = "component", displayName = "lodash", riskPoints = 120, percentOfBudget = 12.0, trend = "stable", delta24h = 0 },
|
||||
new { identifier = "CVE-2024-1234", type = "vulnerability", displayName = "CVE-2024-1234", riskPoints = 95, percentOfBudget = 9.5, trend = "increasing", delta24h = 10 },
|
||||
new { identifier = "vulnerability", type = "category", displayName = "Vulnerabilities", riskPoints = 450, percentOfBudget = 45.0, trend = "stable", delta24h = 5 }
|
||||
},
|
||||
activeAlerts = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
id = "alert-001",
|
||||
threshold = new { level = 70, severity = "medium", actions = new[] { new { type = "notify", channels = new[] { "slack" } } } },
|
||||
currentUtilization = 72.0,
|
||||
triggeredAt = now.AddDays(-1).ToString("O", CultureInfo.InvariantCulture),
|
||||
acknowledged = false,
|
||||
acknowledgedBy = (string?)null,
|
||||
acknowledgedAt = (string?)null
|
||||
}
|
||||
},
|
||||
governance = new
|
||||
{
|
||||
id = "budget-001",
|
||||
tenantId = tenant,
|
||||
name = $"Q{(now.Month - 1) / 3 + 1} {now.Year} Risk Budget",
|
||||
totalBudget = 1000,
|
||||
warningThreshold = 70,
|
||||
criticalThreshold = 90,
|
||||
period = "quarterly",
|
||||
periodStart = periodStart.ToString("O", CultureInfo.InvariantCulture),
|
||||
periodEnd = periodEnd.ToString("O", CultureInfo.InvariantCulture),
|
||||
createdAt = periodStart.AddDays(-15).ToString("O", CultureInfo.InvariantCulture),
|
||||
updatedAt = now.ToString("O", CultureInfo.InvariantCulture),
|
||||
thresholds = new[]
|
||||
{
|
||||
new { level = 70, severity = "medium", actions = new object[] { new { type = "notify", channels = new[] { "slack", "email" } } } },
|
||||
new { level = 90, severity = "high", actions = new object[] { new { type = "notify", channels = new[] { "slack", "email" } }, new { type = "require_approval" } } },
|
||||
new { level = 100, severity = "critical", actions = new object[] { new { type = "block_deploys" }, new { type = "escalate" } } }
|
||||
},
|
||||
enforceHardLimits = true,
|
||||
gracePeriodHours = 24,
|
||||
autoReset = true,
|
||||
carryoverPercent = 0
|
||||
},
|
||||
kpis = new
|
||||
{
|
||||
headroom = 280,
|
||||
headroomDelta24h = -20,
|
||||
unknownsDelta24h = 3,
|
||||
riskRetired7d = 45,
|
||||
exceptionsExpiring = 2,
|
||||
burnRate = 8.5,
|
||||
projectedDaysToExceeded = 33,
|
||||
traceId
|
||||
}
|
||||
};
|
||||
|
||||
return Task.FromResult(Results.Ok(response));
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Audit Handlers
|
||||
// ========================================================================
|
||||
|
||||
Reference in New Issue
Block a user