// // SPDX-License-Identifier: AGPL-3.0-or-later // namespace StellaOps.VulnExplorer.WebService.Contracts; using System.Text.Json.Serialization; /// /// Response containing the evidence subgraph for a finding. /// public sealed record EvidenceSubgraphResponse { /// /// Finding identifier this subgraph explains. /// public required string FindingId { get; init; } /// /// Vulnerability identifier (CVE ID or similar). /// public required string VulnId { get; init; } /// /// Root node of the evidence graph (typically the artifact). /// public required EvidenceNode Root { get; init; } /// /// All edges in the evidence graph. /// public required IReadOnlyList Edges { get; init; } /// /// Summary verdict for this finding. /// public required VerdictSummary Verdict { get; init; } /// /// Available triage actions for this finding. /// public required IReadOnlyList AvailableActions { get; init; } /// /// Optional metadata about the evidence collection. /// public EvidenceMetadata? Metadata { get; init; } } /// /// Node in the evidence graph. /// public sealed record EvidenceNode { /// /// Unique identifier for this node. /// public required string Id { get; init; } /// /// Type of evidence this node represents. /// public required EvidenceNodeType Type { get; init; } /// /// Human-readable label for display. /// public required string Label { get; init; } /// /// Optional longer description. /// public string? Description { get; init; } /// /// Type-specific metadata. /// public IReadOnlyDictionary? Metadata { get; init; } /// /// Child nodes (for expandable tree view). /// public IReadOnlyList? Children { get; init; } /// /// Whether this node is expandable (has or can load children). /// public bool IsExpandable { get; init; } /// /// Status indicator for this node (pass/fail/info). /// public EvidenceNodeStatus Status { get; init; } = EvidenceNodeStatus.Info; } /// /// Type of evidence node. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum EvidenceNodeType { /// Container image or artifact. Artifact, /// Software package (PURL). Package, /// Code symbol (function, class, method). Symbol, /// Call path from entry point to vulnerable code. CallPath, /// VEX claim from vendor or internal team. VexClaim, /// Policy rule that affected the verdict. PolicyRule, /// External advisory source (NVD, vendor, etc.). AdvisorySource, /// Scanner evidence (binary analysis, SBOM, etc.). ScannerEvidence, /// Runtime observation (eBPF, traces). RuntimeObservation, /// Configuration or environment context. Configuration, } /// /// Status indicator for evidence nodes. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum EvidenceNodeStatus { /// Informational, no pass/fail. Info, /// Positive indicator (mitigating factor). Pass, /// Negative indicator (risk factor). Fail, /// Needs review or additional information. Warning, /// Unknown or incomplete data. Unknown, } /// /// Edge connecting two evidence nodes. /// public sealed record EvidenceEdge { /// /// Source node identifier. /// public required string SourceId { get; init; } /// /// Target node identifier. /// public required string TargetId { get; init; } /// /// Type of relationship (contains, calls, claims, references, etc.). /// public required string Relationship { get; init; } /// /// Citation linking to source evidence. /// public required EvidenceCitation Citation { get; init; } /// /// Whether this edge represents a reachable path. /// public bool IsReachable { get; init; } /// /// Strength of the relationship (for visualization). /// public double? Weight { get; init; } } /// /// Citation linking to source evidence. /// public sealed record EvidenceCitation { /// /// Source type (scanner, vex:vendor, advisory:nvd, etc.). /// public required string Source { get; init; } /// /// URL to the source evidence (if available). /// public required string SourceUrl { get; init; } /// /// When this evidence was observed/collected. /// public required DateTimeOffset ObservedAt { get; init; } /// /// Confidence score (0.0-1.0) if applicable. /// public double? Confidence { get; init; } /// /// Hash of the evidence (for verification). /// public string? EvidenceHash { get; init; } /// /// Whether this citation is verified/signed. /// public bool IsVerified { get; init; } } /// /// Summary verdict for a finding. /// public sealed record VerdictSummary { /// /// Decision outcome (allow, deny, review). /// public required string Decision { get; init; } /// /// Human-readable explanation paragraph. /// public required string Explanation { get; init; } /// /// Key factors that influenced the decision. /// public required IReadOnlyList KeyFactors { get; init; } /// /// Overall confidence score (0.0-1.0). /// public required double ConfidenceScore { get; init; } /// /// Policy rule IDs that affected this verdict. /// public IReadOnlyList? AppliedPolicies { get; init; } /// /// Timestamp when this verdict was computed. /// public DateTimeOffset? ComputedAt { get; init; } } /// /// Available triage action. /// public sealed record TriageAction { /// /// Unique action identifier. /// public required string ActionId { get; init; } /// /// Type of action. /// public required TriageActionType Type { get; init; } /// /// Display label. /// public required string Label { get; init; } /// /// Optional description of what this action does. /// public string? Description { get; init; } /// /// Whether this action requires confirmation dialog. /// public bool RequiresConfirmation { get; init; } /// /// Whether this action is currently available. /// public bool IsEnabled { get; init; } = true; /// /// Reason if the action is disabled. /// public string? DisabledReason { get; init; } /// /// Additional parameters for the action. /// public IReadOnlyDictionary? Parameters { get; init; } } /// /// Type of triage action. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum TriageActionType { /// Accept vendor's VEX claim. AcceptVendorVex, /// Request additional evidence. RequestEvidence, /// Open diff view to previous version. OpenDiff, /// Create time-boxed policy exception. CreateException, /// Mark as false positive. MarkFalsePositive, /// Escalate to security team. EscalateToSecurityTeam, /// Apply internal VEX claim. ApplyInternalVex, /// Schedule for patching. SchedulePatch, /// Suppress finding. Suppress, } /// /// Metadata about evidence collection. /// public sealed record EvidenceMetadata { /// /// When the evidence was collected. /// public DateTimeOffset CollectedAt { get; init; } /// /// Number of nodes in the graph. /// public int NodeCount { get; init; } /// /// Number of edges in the graph. /// public int EdgeCount { get; init; } /// /// Whether the graph is complete or truncated. /// public bool IsTruncated { get; init; } /// /// Maximum depth of the tree. /// public int MaxDepth { get; init; } /// /// Sources of evidence included. /// public IReadOnlyList? Sources { get; init; } } /// /// Request to execute a triage action. /// public sealed record ExecuteTriageActionRequest { /// /// Finding to apply action to. /// public required string FindingId { get; init; } /// /// Action to execute. /// public required string ActionId { get; init; } /// /// Optional parameters for the action. /// public IReadOnlyDictionary? Parameters { get; init; } /// /// User comment/justification. /// public string? Comment { get; init; } } /// /// Response from triage action execution. /// public sealed record ExecuteTriageActionResponse { /// /// Whether the action succeeded. /// public required bool Success { get; init; } /// /// Result message. /// public required string Message { get; init; } /// /// Updated verdict after action (if changed). /// public VerdictSummary? UpdatedVerdict { get; init; } /// /// Next recommended action (if any). /// public TriageAction? NextAction { get; init; } } /// /// Filters for finding triage. /// public sealed record TriageFilters { /// /// Reachability filter. /// public ReachabilityFilter Reachability { get; init; } = ReachabilityFilter.Reachable; /// /// Patch status filter. /// public PatchStatusFilter PatchStatus { get; init; } = PatchStatusFilter.Unpatched; /// /// VEX status filter. /// public VexStatusFilter VexStatus { get; init; } = VexStatusFilter.Unvexed; /// /// Severity levels to include. /// public IReadOnlyList Severity { get; init; } = new[] { "critical", "high" }; /// /// Whether to show suppressed findings. /// public bool ShowSuppressed { get; init; } } /// /// Reachability filter options. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum ReachabilityFilter { /// Show all findings. All, /// Show only reachable findings. Reachable, /// Show only unreachable findings. Unreachable, /// Show findings with unknown reachability. Unknown, } /// /// Patch status filter options. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum PatchStatusFilter { /// Show all findings. All, /// Show only findings with available patches. Patched, /// Show only findings without available patches. Unpatched, } /// /// VEX status filter options. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum VexStatusFilter { /// Show all findings. All, /// Show only findings with VEX claims. Vexed, /// Show only findings without VEX claims. Unvexed, /// Show findings with conflicting VEX claims. Conflicting, }