// -----------------------------------------------------------------------------
// AttestationChain.cs
// Sprint: SPRINT_3801_0001_0003_chain_verification (CHAIN-002)
// Description: Models for attestation chain verification.
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text.Json.Serialization;
namespace StellaOps.Scanner.WebService.Contracts;
///
/// Represents a chain of attestations for a finding.
///
public sealed record AttestationChain
{
///
/// Content-addressed chain identifier.
///
[JsonPropertyName("chain_id")]
public required string ChainId { get; init; }
///
/// The scan ID this chain belongs to.
///
[JsonPropertyName("scan_id")]
public required string ScanId { get; init; }
///
/// The finding ID (e.g., CVE identifier) this chain is for.
///
[JsonPropertyName("finding_id")]
public required string FindingId { get; init; }
///
/// The root digest (typically the scan/image digest).
///
[JsonPropertyName("root_digest")]
public required string RootDigest { get; init; }
///
/// The attestations in this chain, ordered from root to leaf.
///
[JsonPropertyName("attestations")]
public required ImmutableList Attestations { get; init; }
///
/// Whether the entire chain is verified.
///
[JsonPropertyName("verified")]
public required bool Verified { get; init; }
///
/// When the chain was verified.
///
[JsonPropertyName("verified_at")]
public required DateTimeOffset VerifiedAt { get; init; }
///
/// The chain status.
///
[JsonPropertyName("chain_status")]
public required ChainStatus Status { get; init; }
///
/// When the earliest attestation in the chain expires.
///
[JsonPropertyName("expires_at")]
public DateTimeOffset? ExpiresAt { get; init; }
}
///
/// Represents a single attestation in the chain.
///
public sealed record ChainAttestation
{
///
/// The type of attestation (e.g., "richgraph", "policy_decision", "human_approval").
///
[JsonPropertyName("type")]
public required AttestationType Type { get; init; }
///
/// The attestation ID.
///
[JsonPropertyName("attestation_id")]
public required string AttestationId { get; init; }
///
/// When the attestation was created.
///
[JsonPropertyName("created_at")]
public required DateTimeOffset CreatedAt { get; init; }
///
/// When the attestation expires.
///
[JsonPropertyName("expires_at")]
public required DateTimeOffset ExpiresAt { get; init; }
///
/// Whether the attestation signature verified.
///
[JsonPropertyName("verified")]
public required bool Verified { get; init; }
///
/// The verification status of this attestation.
///
[JsonPropertyName("verification_status")]
public required AttestationVerificationStatus VerificationStatus { get; init; }
///
/// The subject digest this attestation covers.
///
[JsonPropertyName("subject_digest")]
public required string SubjectDigest { get; init; }
///
/// The predicate type URI.
///
[JsonPropertyName("predicate_type")]
public required string PredicateType { get; init; }
///
/// Optional error message if verification failed.
///
[JsonPropertyName("error")]
public string? Error { get; init; }
}
///
/// The type of attestation.
///
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum AttestationType
{
///
/// RichGraph computation attestation.
///
RichGraph,
///
/// Policy decision attestation.
///
PolicyDecision,
///
/// Human approval attestation.
///
HumanApproval,
///
/// SBOM generation attestation.
///
Sbom,
///
/// Vulnerability scan attestation.
///
VulnerabilityScan
}
///
/// The verification status of an attestation.
///
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum AttestationVerificationStatus
{
///
/// Verification succeeded.
///
Valid,
///
/// Attestation has expired.
///
Expired,
///
/// Signature verification failed.
///
InvalidSignature,
///
/// Attestation not found.
///
NotFound,
///
/// Chain link broken (digest mismatch).
///
ChainBroken,
///
/// Attestation has been revoked.
///
Revoked,
///
/// Verification pending.
///
Pending
}
///
/// The overall status of the attestation chain.
///
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ChainStatus
{
///
/// All attestations present and valid.
///
Complete,
///
/// Some attestations missing but core valid.
///
Partial,
///
/// One or more attestations past TTL.
///
Expired,
///
/// Signature verification failed.
///
Invalid,
///
/// Chain link missing or digest mismatch.
///
Broken,
///
/// Chain is empty (no attestations).
///
Empty
}
///
/// Input for chain verification.
///
public sealed record ChainVerificationInput
{
///
/// The scan ID to verify chain for.
///
public required Domain.ScanId ScanId { get; init; }
///
/// The finding ID to verify chain for.
///
public required string FindingId { get; init; }
///
/// The expected root digest.
///
public required string RootDigest { get; init; }
///
/// Optional: specific attestation types to verify.
/// If null, verifies all available attestations.
///
public IReadOnlyList? RequiredTypes { get; init; }
///
/// Whether to require human approval in the chain.
///
public bool RequireHumanApproval { get; init; }
///
/// Grace period for expired attestations (default: 0).
///
public TimeSpan ExpirationGracePeriod { get; init; } = TimeSpan.Zero;
}
///
/// Result of chain verification.
///
public sealed record ChainVerificationResult
{
///
/// Whether verification succeeded.
///
public required bool Success { get; init; }
///
/// The verified chain.
///
public AttestationChain? Chain { get; init; }
///
/// Error message if verification failed.
///
public string? Error { get; init; }
///
/// Detailed verification results per attestation.
///
public IReadOnlyList? Details { get; init; }
///
/// Creates a successful result.
///
public static ChainVerificationResult Succeeded(
AttestationChain chain,
IReadOnlyList? details = null)
=> new()
{
Success = true,
Chain = chain,
Details = details
};
///
/// Creates a failed result.
///
public static ChainVerificationResult Failed(string error, AttestationChain? chain = null)
=> new()
{
Success = false,
Chain = chain,
Error = error
};
}
///
/// Detailed verification result for a single attestation.
///
public sealed record AttestationVerificationDetail
{
///
/// The attestation type.
///
public required AttestationType Type { get; init; }
///
/// The attestation ID.
///
public required string AttestationId { get; init; }
///
/// The verification status.
///
public required AttestationVerificationStatus Status { get; init; }
///
/// Whether the attestation was verified successfully.
///
public required bool Verified { get; init; }
///
/// Time taken for verification.
///
public TimeSpan? VerificationTime { get; init; }
///
/// Error message if verification failed.
///
public string? Error { get; init; }
}