using System.Collections.Immutable; namespace StellaOps.Scanner.Delta; /// /// Scans only changed layers between two image versions for efficient delta scanning. /// public interface IDeltaLayerScanner { /// /// Performs a delta scan comparing two image versions. /// /// Reference to the old/baseline image. /// Reference to the new image to scan. /// Delta scan options. /// Cancellation token. /// Delta scan result with changed layers and composite SBOM. Task ScanDeltaAsync( string oldImage, string newImage, DeltaScanOptions? options = null, CancellationToken cancellationToken = default); /// /// Identifies layer changes between two images without scanning. /// /// Reference to the old image. /// Reference to the new image. /// Cancellation token. /// Layer change summary. Task IdentifyLayerChangesAsync( string oldImage, string newImage, CancellationToken cancellationToken = default); } /// /// Options for delta scanning. /// public sealed record DeltaScanOptions { /// /// Whether to force a full scan even for unchanged layers. /// Default is false. /// public bool ForceFullScan { get; init; } = false; /// /// Whether to use cached per-layer SBOMs for unchanged layers. /// Default is true. /// public bool UseCachedSboms { get; init; } = true; /// /// Maximum age of cached SBOMs to consider valid (in days). /// Default is 30 days. /// public int MaxCacheAgeDays { get; init; } = 30; /// /// SBOM format to produce (CycloneDX or SPDX). /// Default is CycloneDX. /// public string SbomFormat { get; init; } = "cyclonedx"; /// /// Whether to include layer attribution in the SBOM. /// Default is true. /// public bool IncludeLayerAttribution { get; init; } = true; /// /// Target platform for multi-arch images. /// If null, uses default platform. /// public string? Platform { get; init; } } /// /// Result of a delta scan operation. /// public sealed record DeltaScanResult { /// /// Old image reference. /// public required string OldImage { get; init; } /// /// Old image manifest digest. /// public required string OldManifestDigest { get; init; } /// /// New image reference. /// public required string NewImage { get; init; } /// /// New image manifest digest. /// public required string NewManifestDigest { get; init; } /// /// Layers that were added in the new image. /// public ImmutableArray AddedLayers { get; init; } = []; /// /// Layers that were removed from the old image. /// public ImmutableArray RemovedLayers { get; init; } = []; /// /// Layers that are unchanged between images. /// public ImmutableArray UnchangedLayers { get; init; } = []; /// /// Composite SBOM for the new image (combining cached + newly scanned). /// public string? CompositeSbom { get; init; } /// /// SBOM format (cyclonedx or spdx). /// public string? SbomFormat { get; init; } /// /// Total scan duration. /// public TimeSpan ScanDuration { get; init; } /// /// Duration spent scanning only added layers. /// public TimeSpan AddedLayersScanDuration { get; init; } /// /// Number of components found in added layers. /// public int AddedComponentCount { get; init; } /// /// Number of components from cached layers. /// public int CachedComponentCount { get; init; } /// /// Whether the scan used cached SBOMs. /// public bool UsedCache { get; init; } /// /// Percentage of layers that were reused from cache. /// public double LayerReuseRatio => (AddedLayers.Length + UnchangedLayers.Length) > 0 ? (double)UnchangedLayers.Length / (AddedLayers.Length + UnchangedLayers.Length) : 0; /// /// When the scan was performed. /// public DateTimeOffset ScannedAt { get; init; } = DateTimeOffset.UtcNow; } /// /// Information about a layer change. /// public sealed record LayerChangeInfo { /// /// Compressed layer digest. /// public required string LayerDigest { get; init; } /// /// Uncompressed content hash (diffID). /// public required string DiffId { get; init; } /// /// Layer index in the image. /// public int LayerIndex { get; init; } /// /// Size of the layer in bytes. /// public long Size { get; init; } /// /// Media type of the layer. /// public string? MediaType { get; init; } /// /// Whether this layer's SBOM was retrieved from cache. /// public bool FromCache { get; init; } /// /// Number of components found in this layer. /// public int ComponentCount { get; init; } } /// /// Summary of layer changes between two images. /// public sealed record LayerChangeSummary { /// /// Old image reference. /// public required string OldImage { get; init; } /// /// New image reference. /// public required string NewImage { get; init; } /// /// Total layers in old image. /// public int OldLayerCount { get; init; } /// /// Total layers in new image. /// public int NewLayerCount { get; init; } /// /// Number of layers added. /// public int AddedCount { get; init; } /// /// Number of layers removed. /// public int RemovedCount { get; init; } /// /// Number of layers unchanged. /// public int UnchangedCount { get; init; } /// /// Estimated scan savings (layers we don't need to scan). /// public double EstimatedSavingsRatio => NewLayerCount > 0 ? (double)UnchangedCount / NewLayerCount : 0; /// /// DiffIDs of added layers. /// public ImmutableArray AddedDiffIds { get; init; } = []; /// /// DiffIDs of removed layers. /// public ImmutableArray RemovedDiffIds { get; init; } = []; /// /// DiffIDs of unchanged layers. /// public ImmutableArray UnchangedDiffIds { get; init; } = []; }