256 lines
7.1 KiB
C#
256 lines
7.1 KiB
C#
// -----------------------------------------------------------------------------
|
|
// IReplayVerificationService.cs
|
|
// Sprint: SPRINT_20251228_007_BE_sbom_lineage_graph_ii (LIN-BE-033)
|
|
// Task: Replay verification endpoint
|
|
// Description: Interface for verifying replay hashes and detecting drift.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
namespace StellaOps.SbomService.Services;
|
|
|
|
/// <summary>
|
|
/// Service for verifying replay hashes and detecting drift in security evaluations.
|
|
/// </summary>
|
|
public interface IReplayVerificationService
|
|
{
|
|
/// <summary>
|
|
/// Verifies a replay hash by re-computing it with provided or current inputs.
|
|
/// </summary>
|
|
/// <param name="request">Verification request.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Verification result with match status and drift details.</returns>
|
|
Task<ReplayVerificationResult> VerifyAsync(
|
|
ReplayVerificationRequest request,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Compares two replay hashes and identifies drift between them.
|
|
/// </summary>
|
|
/// <param name="hashA">First replay hash.</param>
|
|
/// <param name="hashB">Second replay hash.</param>
|
|
/// <param name="tenantId">Tenant identifier.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>Drift analysis between the two evaluations.</returns>
|
|
Task<ReplayDriftAnalysis> CompareDriftAsync(
|
|
string hashA,
|
|
string hashB,
|
|
string tenantId,
|
|
CancellationToken ct = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Request for replay verification.
|
|
/// </summary>
|
|
public sealed record ReplayVerificationRequest
|
|
{
|
|
/// <summary>
|
|
/// The replay hash to verify.
|
|
/// </summary>
|
|
public required string ReplayHash { get; init; }
|
|
|
|
/// <summary>
|
|
/// Tenant identifier.
|
|
/// </summary>
|
|
public required string TenantId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional: SBOM digest to use for verification.
|
|
/// If not provided, will try to lookup from stored hash metadata.
|
|
/// </summary>
|
|
public string? SbomDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional: Feeds snapshot digest to use.
|
|
/// If not provided, uses current feeds.
|
|
/// </summary>
|
|
public string? FeedsSnapshotDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional: Policy version to use.
|
|
/// If not provided, uses current policy.
|
|
/// </summary>
|
|
public string? PolicyVersion { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional: VEX verdicts digest to use.
|
|
/// If not provided, uses current VEX state.
|
|
/// </summary>
|
|
public string? VexVerdictsDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional: Timestamp to use for verification.
|
|
/// If not provided, uses current time.
|
|
/// </summary>
|
|
public DateTimeOffset? Timestamp { get; init; }
|
|
|
|
/// <summary>
|
|
/// Whether to freeze time to the original evaluation timestamp.
|
|
/// </summary>
|
|
public bool FreezeTime { get; init; } = true;
|
|
|
|
/// <summary>
|
|
/// Whether to re-evaluate policy with frozen feeds.
|
|
/// </summary>
|
|
public bool ReEvaluatePolicy { get; init; } = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Result of replay verification.
|
|
/// </summary>
|
|
public sealed record ReplayVerificationResult
|
|
{
|
|
/// <summary>
|
|
/// Whether the replay hash matches.
|
|
/// </summary>
|
|
public required bool IsMatch { get; init; }
|
|
|
|
/// <summary>
|
|
/// The expected replay hash.
|
|
/// </summary>
|
|
public required string ExpectedHash { get; init; }
|
|
|
|
/// <summary>
|
|
/// The computed replay hash.
|
|
/// </summary>
|
|
public required string ComputedHash { get; init; }
|
|
|
|
/// <summary>
|
|
/// Overall verification status.
|
|
/// </summary>
|
|
public required ReplayVerificationStatus Status { get; init; }
|
|
|
|
/// <summary>
|
|
/// The inputs used for the expected hash (from storage).
|
|
/// </summary>
|
|
public ReplayHashInputs? ExpectedInputs { get; init; }
|
|
|
|
/// <summary>
|
|
/// The inputs used to compute the verification hash.
|
|
/// </summary>
|
|
public ReplayHashInputs? ComputedInputs { get; init; }
|
|
|
|
/// <summary>
|
|
/// Field-level differences between expected and computed.
|
|
/// </summary>
|
|
public ImmutableArray<ReplayFieldDrift> Drifts { get; init; } = ImmutableArray<ReplayFieldDrift>.Empty;
|
|
|
|
/// <summary>
|
|
/// When the verification was performed.
|
|
/// </summary>
|
|
public DateTimeOffset VerifiedAt { get; init; } = DateTimeOffset.UtcNow;
|
|
|
|
/// <summary>
|
|
/// Optional message with additional context.
|
|
/// </summary>
|
|
public string? Message { get; init; }
|
|
|
|
/// <summary>
|
|
/// Error message if verification failed.
|
|
/// </summary>
|
|
public string? Error { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verification status enumeration.
|
|
/// </summary>
|
|
public enum ReplayVerificationStatus
|
|
{
|
|
/// <summary>
|
|
/// Hash matches exactly - evaluation is reproducible.
|
|
/// </summary>
|
|
Match,
|
|
|
|
/// <summary>
|
|
/// Hash doesn't match - drift detected.
|
|
/// </summary>
|
|
Drift,
|
|
|
|
/// <summary>
|
|
/// Unable to lookup original inputs.
|
|
/// </summary>
|
|
InputsNotFound,
|
|
|
|
/// <summary>
|
|
/// Verification failed due to error.
|
|
/// </summary>
|
|
Error
|
|
}
|
|
|
|
/// <summary>
|
|
/// Field-level drift in replay verification.
|
|
/// </summary>
|
|
public sealed record ReplayFieldDrift
|
|
{
|
|
/// <summary>
|
|
/// Name of the field that drifted.
|
|
/// </summary>
|
|
public required string FieldName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Expected value (from original evaluation).
|
|
/// </summary>
|
|
public required string ExpectedValue { get; init; }
|
|
|
|
/// <summary>
|
|
/// Actual/computed value.
|
|
/// </summary>
|
|
public required string ActualValue { get; init; }
|
|
|
|
/// <summary>
|
|
/// Severity of the drift: "info", "warning", "critical".
|
|
/// </summary>
|
|
public required string Severity { get; init; }
|
|
|
|
/// <summary>
|
|
/// Human-readable description of the drift impact.
|
|
/// </summary>
|
|
public string? Description { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analysis of drift between two replay evaluations.
|
|
/// </summary>
|
|
public sealed record ReplayDriftAnalysis
|
|
{
|
|
/// <summary>
|
|
/// First replay hash.
|
|
/// </summary>
|
|
public required string HashA { get; init; }
|
|
|
|
/// <summary>
|
|
/// Second replay hash.
|
|
/// </summary>
|
|
public required string HashB { get; init; }
|
|
|
|
/// <summary>
|
|
/// Whether the hashes are identical.
|
|
/// </summary>
|
|
public required bool IsIdentical { get; init; }
|
|
|
|
/// <summary>
|
|
/// Inputs for first hash.
|
|
/// </summary>
|
|
public ReplayHashInputs? InputsA { get; init; }
|
|
|
|
/// <summary>
|
|
/// Inputs for second hash.
|
|
/// </summary>
|
|
public ReplayHashInputs? InputsB { get; init; }
|
|
|
|
/// <summary>
|
|
/// Field-level drifts between A and B.
|
|
/// </summary>
|
|
public ImmutableArray<ReplayFieldDrift> Drifts { get; init; } = ImmutableArray<ReplayFieldDrift>.Empty;
|
|
|
|
/// <summary>
|
|
/// Summary of drift severity.
|
|
/// </summary>
|
|
public required string DriftSummary { get; init; }
|
|
|
|
/// <summary>
|
|
/// When the analysis was performed.
|
|
/// </summary>
|
|
public DateTimeOffset AnalyzedAt { get; init; } = DateTimeOffset.UtcNow;
|
|
}
|