using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace StellaOps.Scanner.Triage.Entities; /// /// Effective VEX status for a finding after merging multiple VEX sources. /// Preserves provenance pointers for auditability. /// [Table("triage_effective_vex")] public sealed class TriageEffectiveVex { /// /// Unique identifier. /// [Key] [Column("id")] public Guid Id { get; init; } = Guid.NewGuid(); /// /// The finding this VEX status applies to. /// [Column("finding_id")] public Guid FindingId { get; init; } /// /// The effective VEX status after merging. /// [Column("status")] public TriageVexStatus Status { get; init; } /// /// Source domain that provided this VEX (e.g., "excititor"). /// [Required] [Column("source_domain")] public required string SourceDomain { get; init; } /// /// Stable reference string to the source document. /// [Required] [Column("source_ref")] public required string SourceRef { get; init; } /// /// Array of pruned VEX sources with reasons (for merge transparency). /// [Column("pruned_sources", TypeName = "jsonb")] public string? PrunedSourcesJson { get; init; } /// /// Hash of the DSSE envelope if signed. /// [Column("dsse_envelope_hash")] public string? DsseEnvelopeHash { get; init; } /// /// Reference to Rekor/ledger entry for signature verification. /// [Column("signature_ref")] public string? SignatureRef { get; init; } /// /// Issuer of the VEX document. /// [Column("issuer")] public string? Issuer { get; init; } /// /// When this VEX status became valid. /// [Column("valid_from")] public DateTimeOffset ValidFrom { get; init; } = DateTimeOffset.UtcNow; /// /// When this VEX status expires (null = indefinite). /// [Column("valid_to")] public DateTimeOffset? ValidTo { get; init; } /// /// When this record was collected. /// [Column("collected_at")] public DateTimeOffset CollectedAt { get; init; } = DateTimeOffset.UtcNow; // Navigation property [ForeignKey(nameof(FindingId))] public TriageFinding? Finding { get; init; } }