Files
git.stella-ops.org/src/Scanner/StellaOps.Scanner.WebService/Contracts/AttestationChain.cs
StellaOps Bot 5fc469ad98 feat: Add VEX Status Chip component and integration tests for reachability drift detection
- Introduced `VexStatusChipComponent` to display VEX status with color coding and tooltips.
- Implemented integration tests for reachability drift detection, covering various scenarios including drift detection, determinism, and error handling.
- Enhanced `ScannerToSignalsReachabilityTests` with a null implementation of `ICallGraphSyncService` for better test isolation.
- Updated project references to include the new Reachability Drift library.
2025-12-20 01:26:42 +02:00

367 lines
9.2 KiB
C#

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