up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-03 00:10:19 +02:00
parent ea1d58a89b
commit 37cba83708
158 changed files with 147438 additions and 867 deletions

View File

@@ -83,18 +83,30 @@ internal static class PolicyEndpoints
return operation;
});
policyGroup.MapPost("/overlay", HandlePolicyOverlayAsync)
.WithName("scanner.policy.overlay")
.Produces<PolicyOverlayResponseDto>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.RequireAuthorization(ScannerPolicies.Reports)
.WithOpenApi(operation =>
{
operation.Summary = "Request policy overlays for graph nodes.";
operation.Description = "Returns deterministic policy overlays with runtime evidence for graph nodes (Cartographer integration). Overlay IDs are computed as sha256(tenant|nodeId|overlayKind).";
return operation;
});
}
policyGroup.MapPost("/overlay", HandlePolicyOverlayAsync)
.WithName("scanner.policy.overlay")
.Produces<PolicyOverlayResponseDto>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.RequireAuthorization(ScannerPolicies.Reports)
.WithOpenApi(operation =>
{
operation.Summary = "Request policy overlays for graph nodes.";
operation.Description = "Returns deterministic policy overlays with runtime evidence for graph nodes (Cartographer integration). Overlay IDs are computed as sha256(tenant|nodeId|overlayKind).";
return operation;
});
policyGroup.MapPost("/linksets", HandleLinksetSummaryAsync)
.WithName("scanner.policy.linksets")
.Produces<LinksetSummaryResponseDto>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.RequireAuthorization(ScannerPolicies.Reports)
.WithOpenApi(operation =>
{
operation.Summary = "Fetch advisory linkset summaries with optional policy overlay.";
operation.Description = "Returns linkset severities/conflicts for advisory IDs and, when requested, runtime policy overlay for the provided image digest.";
return operation;
});
}
private static IResult HandleSchemaAsync(HttpContext context)
{
@@ -188,11 +200,11 @@ internal static class PolicyEndpoints
return Json(payload);
}
private static async Task<IResult> HandleRuntimePolicyAsync(
RuntimePolicyRequestDto request,
IRuntimePolicyService runtimePolicyService,
HttpContext context,
CancellationToken cancellationToken)
private static async Task<IResult> HandleRuntimePolicyAsync(
RuntimePolicyRequestDto request,
IRuntimePolicyService runtimePolicyService,
HttpContext context,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(runtimePolicyService);
@@ -273,8 +285,96 @@ internal static class PolicyEndpoints
var evaluation = await runtimePolicyService.EvaluateAsync(evaluationRequest, cancellationToken).ConfigureAwait(false);
var resultPayload = MapRuntimePolicyResponse(evaluation);
return Json(resultPayload);
}
return Json(resultPayload);
}
private static async Task<IResult> HandleLinksetSummaryAsync(
LinksetSummaryRequestDto request,
ILinksetResolver linksetResolver,
IRuntimePolicyService runtimePolicyService,
HttpContext context,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(linksetResolver);
ArgumentNullException.ThrowIfNull(runtimePolicyService);
if (request.AdvisoryIds is null || request.AdvisoryIds.Count == 0)
{
return ProblemResultFactory.Create(
context,
ProblemTypes.Validation,
"Invalid linkset request",
StatusCodes.Status400BadRequest,
detail: "advisoryIds must include at least one value.");
}
if (request.IncludePolicyOverlay && string.IsNullOrWhiteSpace(request.ImageDigest))
{
return ProblemResultFactory.Create(
context,
ProblemTypes.Validation,
"Invalid linkset request",
StatusCodes.Status400BadRequest,
detail: "imageDigest is required when includePolicyOverlay is true.");
}
var linksets = await linksetResolver.ResolveByAdvisoryIdsAsync(request.AdvisoryIds, cancellationToken).ConfigureAwait(false);
RuntimePolicyImageResponseDto? policy = null;
if (request.IncludePolicyOverlay && !string.IsNullOrWhiteSpace(request.ImageDigest))
{
var runtimeRequest = new RuntimePolicyRequestDto
{
Images = new[] { request.ImageDigest!.Trim() }
};
var evaluation = await runtimePolicyService.EvaluateAsync(
new RuntimePolicyEvaluationRequest(
runtimeRequest.Namespace,
new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(StringComparer.Ordinal)),
runtimeRequest.Images),
cancellationToken).ConfigureAwait(false);
if (evaluation.Results.TryGetValue(request.ImageDigest!.Trim(), out var decision))
{
RuntimePolicyRekorDto? rekor = null;
if (decision.Rekor is not null)
{
rekor = new RuntimePolicyRekorDto
{
Uuid = decision.Rekor.Uuid,
Url = decision.Rekor.Url,
Verified = decision.Rekor.Verified
};
}
policy = new RuntimePolicyImageResponseDto
{
PolicyVerdict = decision.PolicyVerdict.ToString().ToLowerInvariant(),
Signed = decision.Signed,
HasSbomReferrers = decision.HasSbomReferrers,
HasSbomLegacy = decision.HasSbomReferrers,
Reasons = decision.Reasons,
Rekor = rekor,
Confidence = Math.Round(decision.Confidence, 6, MidpointRounding.AwayFromZero),
Quieted = decision.Quieted,
QuietedBy = decision.QuietedBy,
Metadata = decision.Metadata is { Count: > 0 } ? JsonSerializer.Serialize(decision.Metadata) : null,
BuildIds = decision.BuildIds,
Linksets = decision.Linksets
};
}
}
var response = new LinksetSummaryResponseDto
{
Linksets = linksets,
Policy = policy
};
return Json(response);
}
private static string NormalizeSegment(string segment)
{
@@ -322,15 +422,16 @@ internal static class PolicyEndpoints
Signed = decision.Signed,
HasSbomReferrers = decision.HasSbomReferrers,
HasSbomLegacy = decision.HasSbomReferrers,
Reasons = decision.Reasons.ToArray(),
Rekor = rekor,
Confidence = Math.Round(decision.Confidence, 6, MidpointRounding.AwayFromZero),
Quieted = decision.Quieted,
QuietedBy = decision.QuietedBy,
Metadata = metadata,
BuildIds = decision.BuildIds is { Count: > 0 } ? decision.BuildIds.ToArray() : null
};
}
Reasons = decision.Reasons.ToArray(),
Rekor = rekor,
Confidence = Math.Round(decision.Confidence, 6, MidpointRounding.AwayFromZero),
Quieted = decision.Quieted,
QuietedBy = decision.QuietedBy,
Metadata = metadata,
BuildIds = decision.BuildIds is { Count: > 0 } ? decision.BuildIds.ToArray() : null,
Linksets = decision.Linksets is { Count: > 0 } ? decision.Linksets.ToArray() : null
};
}
return new RuntimePolicyResponseDto
{