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