using System; using System.Collections.Generic; using System.Text.Json.Serialization; namespace StellaOps.Cli.Services.Models; // CLI-LNM-22-002: VEX observation models for CLI commands /// /// Query options for VEX observations. /// internal sealed class VexObservationQuery { [JsonPropertyName("tenant")] public string Tenant { get; init; } = string.Empty; [JsonPropertyName("vulnerabilityIds")] public IReadOnlyList VulnerabilityIds { get; init; } = Array.Empty(); [JsonPropertyName("productKeys")] public IReadOnlyList ProductKeys { get; init; } = Array.Empty(); [JsonPropertyName("purls")] public IReadOnlyList Purls { get; init; } = Array.Empty(); [JsonPropertyName("cpes")] public IReadOnlyList Cpes { get; init; } = Array.Empty(); [JsonPropertyName("statuses")] public IReadOnlyList Statuses { get; init; } = Array.Empty(); [JsonPropertyName("providerIds")] public IReadOnlyList ProviderIds { get; init; } = Array.Empty(); [JsonPropertyName("limit")] public int? Limit { get; init; } [JsonPropertyName("cursor")] public string? Cursor { get; init; } } /// /// Response from VEX observation query. /// internal sealed class VexObservationResponse { [JsonPropertyName("observations")] public IReadOnlyList Observations { get; init; } = Array.Empty(); [JsonPropertyName("aggregate")] public VexObservationAggregate? Aggregate { get; init; } [JsonPropertyName("nextCursor")] public string? NextCursor { get; init; } [JsonPropertyName("hasMore")] public bool HasMore { get; init; } } /// /// VEX observation document. /// internal sealed class VexObservation { [JsonPropertyName("observationId")] public string ObservationId { get; init; } = string.Empty; [JsonPropertyName("tenant")] public string Tenant { get; init; } = string.Empty; [JsonPropertyName("vulnerabilityId")] public string VulnerabilityId { get; init; } = string.Empty; [JsonPropertyName("providerId")] public string ProviderId { get; init; } = string.Empty; [JsonPropertyName("product")] public VexObservationProduct? Product { get; init; } [JsonPropertyName("status")] public string Status { get; init; } = string.Empty; [JsonPropertyName("justification")] public string? Justification { get; init; } [JsonPropertyName("detail")] public string? Detail { get; init; } [JsonPropertyName("document")] public VexObservationDocument? Document { get; init; } [JsonPropertyName("firstSeen")] public DateTimeOffset FirstSeen { get; init; } [JsonPropertyName("lastSeen")] public DateTimeOffset LastSeen { get; init; } [JsonPropertyName("confidence")] public VexObservationConfidence? Confidence { get; init; } [JsonPropertyName("createdAt")] public DateTimeOffset CreatedAt { get; init; } [JsonPropertyName("updatedAt")] public DateTimeOffset? UpdatedAt { get; init; } } /// /// Product information in VEX observation. /// internal sealed class VexObservationProduct { [JsonPropertyName("key")] public string Key { get; init; } = string.Empty; [JsonPropertyName("name")] public string? Name { get; init; } [JsonPropertyName("version")] public string? Version { get; init; } [JsonPropertyName("purl")] public string? Purl { get; init; } [JsonPropertyName("cpe")] public string? Cpe { get; init; } [JsonPropertyName("componentIdentifiers")] public IReadOnlyList ComponentIdentifiers { get; init; } = Array.Empty(); } /// /// Document reference in VEX observation. /// internal sealed class VexObservationDocument { [JsonPropertyName("format")] public string Format { get; init; } = string.Empty; [JsonPropertyName("digest")] public string Digest { get; init; } = string.Empty; [JsonPropertyName("sourceUri")] public string SourceUri { get; init; } = string.Empty; [JsonPropertyName("revision")] public string? Revision { get; init; } [JsonPropertyName("signature")] public VexObservationSignature? Signature { get; init; } } /// /// Signature metadata for VEX document. /// internal sealed class VexObservationSignature { [JsonPropertyName("type")] public string Type { get; init; } = string.Empty; [JsonPropertyName("subject")] public string? Subject { get; init; } [JsonPropertyName("issuer")] public string? Issuer { get; init; } [JsonPropertyName("keyId")] public string? KeyId { get; init; } [JsonPropertyName("verifiedAt")] public DateTimeOffset? VerifiedAt { get; init; } [JsonPropertyName("transparencyLogReference")] public string? TransparencyLogReference { get; init; } } /// /// Confidence level in VEX observation. /// internal sealed class VexObservationConfidence { [JsonPropertyName("level")] public string Level { get; init; } = string.Empty; [JsonPropertyName("score")] public double? Score { get; init; } [JsonPropertyName("method")] public string? Method { get; init; } } /// /// Aggregate data from VEX observation query. /// internal sealed class VexObservationAggregate { [JsonPropertyName("vulnerabilityIds")] public IReadOnlyList VulnerabilityIds { get; init; } = Array.Empty(); [JsonPropertyName("productKeys")] public IReadOnlyList ProductKeys { get; init; } = Array.Empty(); [JsonPropertyName("purls")] public IReadOnlyList Purls { get; init; } = Array.Empty(); [JsonPropertyName("cpes")] public IReadOnlyList Cpes { get; init; } = Array.Empty(); [JsonPropertyName("providerIds")] public IReadOnlyList ProviderIds { get; init; } = Array.Empty(); [JsonPropertyName("statusCounts")] public IReadOnlyDictionary StatusCounts { get; init; } = new Dictionary(); } /// /// VEX linkset query options. /// internal sealed class VexLinksetQuery { [JsonPropertyName("tenant")] public string Tenant { get; init; } = string.Empty; [JsonPropertyName("vulnerabilityId")] public string VulnerabilityId { get; init; } = string.Empty; [JsonPropertyName("productKeys")] public IReadOnlyList ProductKeys { get; init; } = Array.Empty(); [JsonPropertyName("purls")] public IReadOnlyList Purls { get; init; } = Array.Empty(); [JsonPropertyName("statuses")] public IReadOnlyList Statuses { get; init; } = Array.Empty(); } /// /// VEX linkset response showing linked observations. /// internal sealed class VexLinksetResponse { [JsonPropertyName("vulnerabilityId")] public string VulnerabilityId { get; init; } = string.Empty; [JsonPropertyName("observations")] public IReadOnlyList Observations { get; init; } = Array.Empty(); [JsonPropertyName("summary")] public VexLinksetSummary? Summary { get; init; } [JsonPropertyName("conflicts")] public IReadOnlyList Conflicts { get; init; } = Array.Empty(); } /// /// Summary of VEX linkset. /// internal sealed class VexLinksetSummary { [JsonPropertyName("totalObservations")] public int TotalObservations { get; init; } [JsonPropertyName("providers")] public IReadOnlyList Providers { get; init; } = Array.Empty(); [JsonPropertyName("products")] public IReadOnlyList Products { get; init; } = Array.Empty(); [JsonPropertyName("statusCounts")] public IReadOnlyDictionary StatusCounts { get; init; } = new Dictionary(); [JsonPropertyName("hasConflicts")] public bool HasConflicts { get; init; } } /// /// Conflict between VEX observations. /// internal sealed class VexLinksetConflict { [JsonPropertyName("productKey")] public string ProductKey { get; init; } = string.Empty; [JsonPropertyName("conflictingStatuses")] public IReadOnlyList ConflictingStatuses { get; init; } = Array.Empty(); [JsonPropertyName("observations")] public IReadOnlyList ObservationIds { get; init; } = Array.Empty(); [JsonPropertyName("description")] public string Description { get; init; } = string.Empty; }