96 lines
3.0 KiB
C#
96 lines
3.0 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using StellaOps.Policy.Determinization.Models;
|
|
|
|
namespace StellaOps.Policy.Engine.Gates.Determinization;
|
|
|
|
/// <summary>
|
|
/// Builds signal snapshots for determinization evaluation by querying signal repositories.
|
|
/// </summary>
|
|
public sealed class SignalSnapshotBuilder : ISignalSnapshotBuilder
|
|
{
|
|
private readonly ISignalRepository _signalRepository;
|
|
private readonly TimeProvider _timeProvider;
|
|
private readonly ILogger<SignalSnapshotBuilder> _logger;
|
|
|
|
public SignalSnapshotBuilder(
|
|
ISignalRepository signalRepository,
|
|
TimeProvider timeProvider,
|
|
ILogger<SignalSnapshotBuilder> logger)
|
|
{
|
|
_signalRepository = signalRepository;
|
|
_timeProvider = timeProvider;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<SignalSnapshot> BuildAsync(
|
|
string cveId,
|
|
string componentPurl,
|
|
CancellationToken ct = default)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(cveId);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(componentPurl);
|
|
|
|
_logger.LogDebug(
|
|
"Building signal snapshot for CVE {CveId} on {Purl}",
|
|
cveId,
|
|
componentPurl);
|
|
|
|
var snapshotAt = _timeProvider.GetUtcNow();
|
|
var subjectKey = BuildSubjectKey(cveId, componentPurl);
|
|
|
|
// Query all signals in parallel
|
|
var signalsTask = _signalRepository.GetSignalsAsync(subjectKey, ct);
|
|
var signals = await signalsTask;
|
|
|
|
// Build snapshot from retrieved signals
|
|
var snapshot = SignalSnapshot.Empty(cveId, componentPurl, snapshotAt);
|
|
|
|
foreach (var signal in signals)
|
|
{
|
|
snapshot = ApplySignal(snapshot, signal);
|
|
}
|
|
|
|
_logger.LogDebug(
|
|
"Built signal snapshot for CVE {CveId} on {Purl}: {SignalCount} signals present",
|
|
cveId,
|
|
componentPurl,
|
|
signals.Count);
|
|
|
|
return snapshot;
|
|
}
|
|
|
|
private static string BuildSubjectKey(string cveId, string componentPurl)
|
|
=> $"{cveId}::{componentPurl}";
|
|
|
|
private SignalSnapshot ApplySignal(SignalSnapshot snapshot, Signal signal)
|
|
{
|
|
// This is a placeholder implementation
|
|
// In a real implementation, this would map Signal objects to SignalState<T> instances
|
|
// based on signal type and update the appropriate field in the snapshot
|
|
|
|
return snapshot;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Repository for retrieving signals.
|
|
/// </summary>
|
|
public interface ISignalRepository
|
|
{
|
|
/// <summary>
|
|
/// Get all signals for the given subject key.
|
|
/// </summary>
|
|
Task<IReadOnlyList<Signal>> GetSignalsAsync(string subjectKey, CancellationToken ct = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a signal retrieved from storage.
|
|
/// </summary>
|
|
public sealed record Signal
|
|
{
|
|
public required string Type { get; init; }
|
|
public required string SubjectKey { get; init; }
|
|
public required DateTimeOffset ObservedAt { get; init; }
|
|
public required object? Evidence { get; init; }
|
|
}
|