notify doctors work, audit work, new product advisory sprints

This commit is contained in:
master
2026-01-13 08:36:29 +02:00
parent b8868a5f13
commit 9ca7cb183e
343 changed files with 24492 additions and 3544 deletions

View File

@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Policy;
using StellaOps.Scanner.WebService.Diagnostics;
using StellaOps.Scanner.WebService.Options;
using StellaOps.Scanner.Surface.Env;
@@ -26,12 +27,12 @@ internal static class HealthEndpoints
group.MapGet("/healthz", HandleHealth)
.WithName("scanner.health")
.Produces<HealthDocument>(StatusCodes.Status200OK)
.AllowAnonymous();
.RequireAuthorization(ScannerPolicies.ScansRead);
group.MapGet("/readyz", HandleReady)
.WithName("scanner.ready")
.Produces<ReadyDocument>(StatusCodes.Status200OK)
.AllowAnonymous();
.RequireAuthorization(ScannerPolicies.ScansRead);
}
private static IResult HandleHealth(

View File

@@ -20,6 +20,7 @@ using RuntimePolicyVerdict = StellaOps.Zastava.Core.Contracts.PolicyVerdict;
namespace StellaOps.Scanner.WebService.Endpoints;
// Suppress ASPDEPR002 for current minimal API route usage; revisit during endpoint modernization.
#pragma warning disable ASPDEPR002
internal static class PolicyEndpoints

View File

@@ -16,6 +16,7 @@ using StellaOps.Scanner.WebService.Services;
namespace StellaOps.Scanner.WebService.Endpoints;
// Suppress ASPDEPR002 for current minimal API route usage; revisit during endpoint modernization.
#pragma warning disable ASPDEPR002
internal static class ReportEndpoints

View File

@@ -134,45 +134,51 @@ internal static class WebhookEndpoints
StatusCodes.Status400BadRequest);
}
if (string.IsNullOrWhiteSpace(source.WebhookSecretRef))
{
logger.LogWarning("Webhook secret not configured for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Webhook secret is not configured",
StatusCodes.Status401Unauthorized);
}
// Determine signature to use
var signature = signatureSha256 ?? signatureSha1 ?? gitlabToken ?? ExtractBearerToken(authorization);
// Verify signature if source has a webhook secret reference
if (!string.IsNullOrEmpty(source.WebhookSecretRef))
if (string.IsNullOrEmpty(signature))
{
if (string.IsNullOrEmpty(signature))
{
logger.LogWarning("Webhook received without signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Missing webhook signature",
StatusCodes.Status401Unauthorized);
}
logger.LogWarning("Webhook received without signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Missing webhook signature",
StatusCodes.Status401Unauthorized);
}
// Resolve the webhook secret from the credential store
var secretCredential = await credentialResolver.ResolveAsync(source.WebhookSecretRef, ct);
var webhookSecret = secretCredential?.Token ?? secretCredential?.Password;
// Resolve the webhook secret from the credential store
var secretCredential = await credentialResolver.ResolveAsync(source.WebhookSecretRef, ct);
var webhookSecret = secretCredential?.Token ?? secretCredential?.Password;
if (string.IsNullOrEmpty(webhookSecret))
{
logger.LogWarning("Failed to resolve webhook secret for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.InternalError,
"Failed to resolve webhook secret",
StatusCodes.Status500InternalServerError);
}
if (string.IsNullOrEmpty(webhookSecret))
{
logger.LogWarning("Failed to resolve webhook secret for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.InternalError,
"Failed to resolve webhook secret",
StatusCodes.Status500InternalServerError);
}
if (!webhookHandler.VerifyWebhookSignature(payloadBytes, signature, webhookSecret))
{
logger.LogWarning("Invalid webhook signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Invalid webhook signature",
StatusCodes.Status401Unauthorized);
}
if (!webhookHandler.VerifyWebhookSignature(payloadBytes, signature, webhookSecret))
{
logger.LogWarning("Invalid webhook signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Invalid webhook signature",
StatusCodes.Status401Unauthorized);
}
// Parse the payload
@@ -446,6 +452,16 @@ internal static class WebhookEndpoints
StatusCodes.Status400BadRequest);
}
if (string.IsNullOrWhiteSpace(source.WebhookSecretRef))
{
logger.LogWarning("Webhook secret not configured for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Webhook secret is not configured",
StatusCodes.Status401Unauthorized);
}
// Get signature from header
string? signature = signatureHeader switch
{
@@ -456,42 +472,38 @@ internal static class WebhookEndpoints
_ => null
};
// Verify signature if source has a webhook secret reference
if (!string.IsNullOrEmpty(source.WebhookSecretRef))
if (string.IsNullOrEmpty(signature))
{
if (string.IsNullOrEmpty(signature))
{
logger.LogWarning("Webhook received without signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Missing webhook signature",
StatusCodes.Status401Unauthorized);
}
logger.LogWarning("Webhook received without signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Missing webhook signature",
StatusCodes.Status401Unauthorized);
}
// Resolve the webhook secret from the credential store
var secretCredential = await credentialResolver.ResolveAsync(source.WebhookSecretRef, ct);
var webhookSecret = secretCredential?.Token ?? secretCredential?.Password;
// Resolve the webhook secret from the credential store
var secretCredential = await credentialResolver.ResolveAsync(source.WebhookSecretRef, ct);
var webhookSecret = secretCredential?.Token ?? secretCredential?.Password;
if (string.IsNullOrEmpty(webhookSecret))
{
logger.LogWarning("Failed to resolve webhook secret for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.InternalError,
"Failed to resolve webhook secret",
StatusCodes.Status500InternalServerError);
}
if (string.IsNullOrEmpty(webhookSecret))
{
logger.LogWarning("Failed to resolve webhook secret for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.InternalError,
"Failed to resolve webhook secret",
StatusCodes.Status500InternalServerError);
}
if (!webhookHandler.VerifyWebhookSignature(payloadBytes, signature, webhookSecret))
{
logger.LogWarning("Invalid webhook signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Invalid webhook signature",
StatusCodes.Status401Unauthorized);
}
if (!webhookHandler.VerifyWebhookSignature(payloadBytes, signature, webhookSecret))
{
logger.LogWarning("Invalid webhook signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create(
context,
ProblemTypes.Authentication,
"Invalid webhook signature",
StatusCodes.Status401Unauthorized);
}
// Parse the payload