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
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-24 07:52:25 +02:00
parent 5970f0d9bd
commit 150b3730ef
215 changed files with 8119 additions and 740 deletions

View File

@@ -0,0 +1,31 @@
using System.Text.Json.Serialization;
namespace StellaOps.Policy.Engine.BatchContext;
internal sealed record BatchContextRequest(
[property: JsonPropertyName("tenant_id")] string TenantId,
[property: JsonPropertyName("policy_profile_hash")] string PolicyProfileHash,
[property: JsonPropertyName("knobs_version")] string KnobsVersion,
[property: JsonPropertyName("overlay_hash")] string OverlayHash,
[property: JsonPropertyName("items")] IReadOnlyList<BatchContextItem> Items,
[property: JsonPropertyName("options")] BatchContextOptions Options);
internal sealed record BatchContextItem(
[property: JsonPropertyName("component_purl")] string ComponentPurl,
[property: JsonPropertyName("advisory_id")] string AdvisoryId);
internal sealed record BatchContextOptions(
[property: JsonPropertyName("include_reachability")] bool IncludeReachability);
internal sealed record BatchContextResponse(
[property: JsonPropertyName("context_id")] string ContextId,
[property: JsonPropertyName("expires_at")] string ExpiresAt,
[property: JsonPropertyName("knobs_version")] string KnobsVersion,
[property: JsonPropertyName("overlay_hash")] string OverlayHash,
[property: JsonPropertyName("items")] IReadOnlyList<BatchContextResolvedItem> Items);
internal sealed record BatchContextResolvedItem(
[property: JsonPropertyName("component_purl")] string ComponentPurl,
[property: JsonPropertyName("advisory_id")] string AdvisoryId,
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("trace_ref")] string TraceRef);

View File

@@ -0,0 +1,82 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace StellaOps.Policy.Engine.BatchContext;
/// <summary>
/// Creates deterministic batch context responses for advisory AI (POLICY-ENGINE-31-002).
/// </summary>
internal sealed class BatchContextService
{
private readonly TimeProvider _timeProvider;
public BatchContextService(TimeProvider timeProvider)
{
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
}
public BatchContextResponse Create(BatchContextRequest request)
{
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.Items is null || request.Items.Count == 0)
{
throw new ArgumentException("items are required", nameof(request.Items));
}
var sortedItems = request.Items
.OrderBy(i => i.ComponentPurl, StringComparer.Ordinal)
.ThenBy(i => i.AdvisoryId, StringComparer.Ordinal)
.ToList();
var status = request.Options?.IncludeReachability == true ? "pending-reachability" : "pending";
var resolved = sortedItems
.Select(i => new BatchContextResolvedItem(
i.ComponentPurl,
i.AdvisoryId,
status,
ComputeTraceRef(request.TenantId, i)))
.ToList();
var expires = _timeProvider.GetUtcNow().AddHours(1).ToString("O");
var contextId = ComputeContextId(request, sortedItems);
return new BatchContextResponse(
contextId,
ExpiresAt: expires,
KnobsVersion: request.KnobsVersion,
OverlayHash: request.OverlayHash,
Items: resolved);
}
private static string ComputeContextId(BatchContextRequest request, IReadOnlyList<BatchContextItem> sortedItems)
{
var canonical = new
{
tenant = request.TenantId,
profile = request.PolicyProfileHash,
knobs = request.KnobsVersion,
overlay = request.OverlayHash,
items = sortedItems.Select(i => new { i.ComponentPurl, i.AdvisoryId }).ToArray(),
includeReachability = request.Options?.IncludeReachability ?? false
};
var json = JsonSerializer.Serialize(canonical, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
});
Span<byte> hash = stackalloc byte[16];
SHA256.HashData(Encoding.UTF8.GetBytes(json), hash);
return Convert.ToHexString(hash);
}
private static string ComputeTraceRef(string tenant, BatchContextItem item)
{
var stable = $"{tenant}|{item.ComponentPurl}|{item.AdvisoryId}";
Span<byte> hash = stackalloc byte[12];
SHA256.HashData(Encoding.UTF8.GetBytes(stable), hash);
return Convert.ToHexString(hash);
}
}