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