// Licensed to StellaOps under the AGPL-3.0-or-later license. using System.Collections.Immutable; using StellaOps.ReachGraph.Deduplication; using StellaOps.ReachGraph.Schema; using StellaOps.VexLens.Delta; using StellaOps.VexLens.Models; namespace StellaOps.VexLens.NoiseGate; /// /// Central interface for noise-gating operations on vulnerability graphs. /// /// /// The noise gate provides three core capabilities: /// /// Edge deduplication: Collapses semantically equivalent edges from multiple sources /// Verdict resolution: Applies stability damping to prevent flip-flopping /// Delta reporting: Computes meaningful changes between snapshots /// /// public interface INoiseGate { /// /// Deduplicates edges based on semantic equivalence. /// /// The edges to deduplicate. /// Cancellation token. /// Deduplicated edges with merged provenance. Task> DedupeEdgesAsync( IReadOnlyList edges, CancellationToken cancellationToken = default); /// /// Resolves a verdict by applying stability damping. /// /// The verdict resolution request. /// Cancellation token. /// The resolved verdict with damping decision. Task ResolveVerdictAsync( VerdictResolutionRequest request, CancellationToken cancellationToken = default); /// /// Applies noise-gating to a graph snapshot. /// /// The gating request. /// Cancellation token. /// The gated graph snapshot. Task GateAsync( NoiseGateRequest request, CancellationToken cancellationToken = default); /// /// Computes a delta report between two snapshots. /// /// The previous snapshot. /// The current snapshot. /// Optional report options. /// Cancellation token. /// The delta report. Task DiffAsync( GatedGraphSnapshot fromSnapshot, GatedGraphSnapshot toSnapshot, DeltaReportOptions? options = null, CancellationToken cancellationToken = default); } /// /// Request to resolve a verdict with stability damping. /// public sealed record VerdictResolutionRequest { /// /// Gets the unique key for this verdict (e.g., "artifact:cve"). /// public required string Key { get; init; } /// /// Gets the vulnerability ID. /// public required string VulnerabilityId { get; init; } /// /// Gets the product key (PURL or other identifier). /// public required string ProductKey { get; init; } /// /// Gets the proposed VEX status. /// public required VexStatus ProposedStatus { get; init; } /// /// Gets the proposed confidence score. /// public required double ProposedConfidence { get; init; } /// /// Gets the rationale class for the verdict. /// public string? RationaleClass { get; init; } /// /// Gets the justification for the verdict. /// public VexJustification? Justification { get; init; } /// /// Gets the contributing sources. /// public IReadOnlyList? ContributingSources { get; init; } /// /// Gets the tenant ID for multi-tenant deployments. /// public string? TenantId { get; init; } } /// /// Result of verdict resolution with damping decision. /// public sealed record ResolvedVerdict { /// /// Gets the vulnerability ID. /// public required string VulnerabilityId { get; init; } /// /// Gets the product key. /// public required string ProductKey { get; init; } /// /// Gets the final VEX status. /// public required VexStatus Status { get; init; } /// /// Gets the final confidence score. /// public required double Confidence { get; init; } /// /// Gets the rationale class. /// public string? RationaleClass { get; init; } /// /// Gets the justification. /// public VexJustification? Justification { get; init; } /// /// Gets whether the verdict was surfaced (not damped). /// public required bool WasSurfaced { get; init; } /// /// Gets the damping reason if applicable. /// public string? DampingReason { get; init; } /// /// Gets the previous status if available. /// public VexStatus? PreviousStatus { get; init; } /// /// Gets the previous confidence if available. /// public double? PreviousConfidence { get; init; } /// /// Gets the contributing sources. /// public ImmutableArray ContributingSources { get; init; } = []; /// /// Gets the timestamp of resolution. /// public required DateTimeOffset ResolvedAt { get; init; } } /// /// Request to apply noise-gating to a graph. /// public sealed record NoiseGateRequest { /// /// Gets the reachability graph to gate. /// public required ReachGraphMinimal Graph { get; init; } /// /// Gets the verdicts to include. /// public required IReadOnlyList Verdicts { get; init; } /// /// Gets the snapshot ID. /// public required string SnapshotId { get; init; } /// /// Gets the tenant ID. /// public string? TenantId { get; init; } /// /// Gets whether to compute a previous snapshot diff. /// public bool ComputeDiff { get; init; } = true; /// /// Gets the previous snapshot ID for diff computation. /// public string? PreviousSnapshotId { get; init; } } /// /// A gated graph snapshot with deduplicated edges and resolved verdicts. /// public sealed record GatedGraphSnapshot { /// /// Gets the unique snapshot identifier. /// public required string SnapshotId { get; init; } /// /// Gets the snapshot digest for integrity verification. /// public required string Digest { get; init; } /// /// Gets the artifact this snapshot describes. /// public required ReachGraphArtifact Artifact { get; init; } /// /// Gets the deduplicated edges. /// public required ImmutableArray Edges { get; init; } /// /// Gets the resolved verdicts. /// public required ImmutableArray Verdicts { get; init; } /// /// Gets the verdicts that were damped (not surfaced). /// public ImmutableArray DampedVerdicts { get; init; } = []; /// /// Gets the timestamp when this snapshot was created. /// public required DateTimeOffset CreatedAt { get; init; } /// /// Gets the gating statistics. /// public required GatingStatistics Statistics { get; init; } } /// /// Statistics from a noise-gating operation. /// public sealed record GatingStatistics { /// /// Gets the original edge count before deduplication. /// public required int OriginalEdgeCount { get; init; } /// /// Gets the edge count after deduplication. /// public required int DeduplicatedEdgeCount { get; init; } /// /// Gets the edge reduction percentage. /// public double EdgeReductionPercent => OriginalEdgeCount > 0 ? (1.0 - (double)DeduplicatedEdgeCount / OriginalEdgeCount) * 100.0 : 0.0; /// /// Gets the total verdict count. /// public required int TotalVerdictCount { get; init; } /// /// Gets the surfaced verdict count. /// public required int SurfacedVerdictCount { get; init; } /// /// Gets the damped verdict count. /// public required int DampedVerdictCount { get; init; } /// /// Gets the duration of the gating operation. /// public required TimeSpan Duration { get; init; } }