using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; namespace StellaOps.Scanner.Analyzers.OS; public sealed class OSPackageFileEvidence : IComparable { public OSPackageFileEvidence( string path, string? layerDigest = null, string? sha256 = null, long? sizeBytes = null, bool? isConfigFile = null, IDictionary? digests = null) { ArgumentException.ThrowIfNullOrWhiteSpace(path); Path = Normalize(path); LayerDigest = NormalizeDigest(layerDigest); var digestMap = digests is null ? new SortedDictionary(StringComparer.OrdinalIgnoreCase) : new SortedDictionary(digests, StringComparer.OrdinalIgnoreCase); if (!string.IsNullOrWhiteSpace(sha256)) { digestMap["sha256"] = NormalizeHash(sha256)!; } Digests = new ReadOnlyDictionary(digestMap); Sha256 = Digests.TryGetValue("sha256", out var normalizedSha256) ? normalizedSha256 : null; SizeBytes = sizeBytes; IsConfigFile = isConfigFile; } public string Path { get; } public string? LayerDigest { get; } public string? Sha256 { get; } public IReadOnlyDictionary Digests { get; } public long? SizeBytes { get; } public bool? IsConfigFile { get; } public int CompareTo(OSPackageFileEvidence? other) { if (other is null) { return 1; } return string.CompareOrdinal(Path, other.Path); } public override string ToString() => $"{Path} ({SizeBytes?.ToString("N0", CultureInfo.InvariantCulture) ?? "?"} bytes)"; private static string Normalize(string path) { var trimmed = path.Trim(); if (!trimmed.StartsWith('/')) { trimmed = "/" + trimmed; } return trimmed.Replace('\\', '/'); } private static string? NormalizeDigest(string? digest) { if (string.IsNullOrWhiteSpace(digest)) { return null; } var trimmed = digest.Trim(); if (!trimmed.Contains(':', StringComparison.Ordinal)) { return trimmed.ToLowerInvariant(); } var parts = trimmed.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); return parts.Length == 2 ? $"{parts[0].ToLowerInvariant()}:{parts[1].ToLowerInvariant()}" : trimmed.ToLowerInvariant(); } private static string? NormalizeHash(string? hash) { if (string.IsNullOrWhiteSpace(hash)) { return null; } return hash.Trim().ToLowerInvariant(); } }