up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-01 21:16:22 +02:00
parent c11d87d252
commit 909d9b6220
208 changed files with 860954 additions and 832 deletions

View File

@@ -0,0 +1,239 @@
using System.Collections.Immutable;
using System.Linq;
using StellaOps.Policy;
using StellaOps.Policy.Engine.Caching;
using StellaOps.Policy.Engine.Evaluation;
using StellaOps.Policy.Engine.Services;
using StellaOps.PolicyDsl;
namespace StellaOps.Policy.Engine.BatchEvaluation;
internal sealed record BatchEvaluationRequestDto(
string TenantId,
IReadOnlyList<BatchEvaluationItemDto> Items,
int? PageSize = null,
string? PageToken = null,
int? BudgetMs = null);
internal sealed record BatchEvaluationItemDto(
string PackId,
int Version,
string SubjectPurl,
string AdvisoryId,
EvaluationSeverityDto Severity,
AdvisoryDto Advisory,
VexEvidenceDto Vex,
SbomDto Sbom,
ExceptionsDto Exceptions,
ReachabilityDto Reachability,
DateTimeOffset? EvaluationTimestamp,
bool BypassCache = false);
internal sealed record EvaluationSeverityDto(string Normalized, decimal? Score = null);
internal sealed record AdvisoryDto(IDictionary<string, string> Metadata, string Source = "unknown");
internal sealed record VexEvidenceDto(IReadOnlyList<VexStatementDto> Statements);
internal sealed record VexStatementDto(string Status, string Justification, string StatementId, DateTimeOffset? Timestamp = null);
internal sealed record SbomDto(IReadOnlyList<string> Tags, IReadOnlyList<ComponentDto>? Components = null);
internal sealed record ComponentDto(
string Name,
string Version,
string Type,
string? Purl = null,
IDictionary<string, string>? Metadata = null);
internal sealed record ExceptionsDto(
IDictionary<string, PolicyExceptionEffect>? Effects = null,
IReadOnlyList<ExceptionInstanceDto>? Instances = null);
internal sealed record ExceptionInstanceDto(
string Id,
string EffectId,
ExceptionScopeDto Scope,
DateTimeOffset CreatedAt,
IDictionary<string, string>? Metadata = null);
internal sealed record ExceptionScopeDto(
IReadOnlyList<string>? RuleNames = null,
IReadOnlyList<string>? Severities = null,
IReadOnlyList<string>? Sources = null,
IReadOnlyList<string>? Tags = null);
internal sealed record ReachabilityDto(
string State,
decimal Confidence = 0m,
decimal Score = 0m,
bool HasRuntimeEvidence = false,
string? Source = null,
string? Method = null,
string? EvidenceRef = null);
internal sealed record BatchEvaluationResultDto(
string PackId,
int Version,
string PolicyDigest,
string Status,
string? Severity,
string? RuleName,
int? Priority,
IReadOnlyDictionary<string, string> Annotations,
IReadOnlyList<string> Warnings,
PolicyExceptionApplication? AppliedException,
string CorrelationId,
bool Cached,
CacheSource CacheSource,
long EvaluationDurationMs);
internal sealed record BatchEvaluationResponseDto(
IReadOnlyList<BatchEvaluationResultDto> Results,
string? NextPageToken,
int Total,
int Returned,
int CacheHits,
int CacheMisses,
long DurationMs,
long? BudgetRemainingMs);
internal static class BatchEvaluationValidator
{
public static bool TryValidate(BatchEvaluationRequestDto request, out string error)
{
if (request is null)
{
error = "Request body is required.";
return false;
}
if (request.Items is null || request.Items.Count == 0)
{
error = "At least one item is required.";
return false;
}
if (request.PageSize is int size && (size <= 0 || size > 500))
{
error = "PageSize must be between 1 and 500.";
return false;
}
if (request.Items.Any(static i => i.EvaluationTimestamp is null))
{
error = "Each item must provide evaluationTimestamp to keep evaluation deterministic.";
return false;
}
error = string.Empty;
return true;
}
}
internal static class BatchEvaluationMapper
{
public static IReadOnlyList<RuntimeEvaluationRequest> ToRuntimeRequests(string tenantId, IEnumerable<BatchEvaluationItemDto> items)
{
return items.Select(item => ToRuntimeRequest(tenantId, item)).ToList();
}
private static RuntimeEvaluationRequest ToRuntimeRequest(string tenantId, BatchEvaluationItemDto item)
{
var severity = new PolicyEvaluationSeverity(
item.Severity.Normalized,
item.Severity.Score);
var advisory = new PolicyEvaluationAdvisory(
item.Advisory.Source,
item.Advisory.Metadata.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase));
var vex = new PolicyEvaluationVexEvidence(
item.Vex.Statements
.Select(stmt => new PolicyEvaluationVexStatement(
stmt.Status,
stmt.Justification,
stmt.StatementId,
stmt.Timestamp))
.ToImmutableArray());
var sbom = new PolicyEvaluationSbom(
item.Sbom.Tags.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase),
(item.Sbom.Components ?? Array.Empty<ComponentDto>())
.Select(comp => new PolicyEvaluationComponent(
comp.Name,
comp.Version,
comp.Type,
comp.Purl,
(comp.Metadata ?? new Dictionary<string, string>())
.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase)))
.ToImmutableArray());
var exceptions = new PolicyEvaluationExceptions(
(item.Exceptions.Effects ?? new Dictionary<string, PolicyExceptionEffect>())
.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase),
(item.Exceptions.Instances ?? Array.Empty<ExceptionInstanceDto>())
.Select(instance => new PolicyEvaluationExceptionInstance(
instance.Id,
instance.EffectId,
new PolicyEvaluationExceptionScope(
Normalize(instance.Scope.RuleNames),
Normalize(instance.Scope.Severities),
Normalize(instance.Scope.Sources),
Normalize(instance.Scope.Tags)),
instance.CreatedAt,
(instance.Metadata ?? new Dictionary<string, string>())
.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase)))
.ToImmutableArray());
var reachability = new PolicyEvaluationReachability(
item.Reachability.State,
item.Reachability.Confidence,
item.Reachability.Score,
item.Reachability.HasRuntimeEvidence,
item.Reachability.Source,
item.Reachability.Method,
item.Reachability.EvidenceRef);
return new RuntimeEvaluationRequest(
PackId: item.PackId,
Version: item.Version,
TenantId: tenantId,
SubjectPurl: item.SubjectPurl,
AdvisoryId: item.AdvisoryId,
Severity: severity,
Advisory: advisory,
Vex: vex,
Sbom: sbom,
Exceptions: exceptions,
Reachability: reachability,
EvaluationTimestamp: item.EvaluationTimestamp,
BypassCache: item.BypassCache);
}
private static ImmutableHashSet<string> Normalize(IReadOnlyList<string>? values)
{
return (values ?? Array.Empty<string>())
.Where(static value => !string.IsNullOrWhiteSpace(value))
.Select(static value => value.Trim())
.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase);
}
}
internal interface IRuntimeEvaluationExecutor
{
Task<RuntimeEvaluationResponse> EvaluateAsync(RuntimeEvaluationRequest request, CancellationToken cancellationToken);
}
internal sealed class RuntimeEvaluationExecutor : IRuntimeEvaluationExecutor
{
private readonly PolicyRuntimeEvaluationService _service;
public RuntimeEvaluationExecutor(PolicyRuntimeEvaluationService service)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
}
public Task<RuntimeEvaluationResponse> EvaluateAsync(RuntimeEvaluationRequest request, CancellationToken cancellationToken) =>
_service.EvaluateAsync(request, cancellationToken);
}