- Added Program.cs to set up the web application with Serilog for logging, health check endpoints, and a placeholder admission endpoint. - Configured Kestrel server to use TLS 1.3 and handle client certificates appropriately. - Created StellaOps.Zastava.Webhook.csproj with necessary dependencies including Serilog and Polly. - Documented tasks in TASKS.md for the Zastava Webhook project, outlining current work and exit criteria for each task.
101 lines
2.8 KiB
C#
101 lines
2.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Globalization;
|
|
|
|
namespace StellaOps.Scanner.Analyzers.OS;
|
|
|
|
public sealed class OSPackageFileEvidence : IComparable<OSPackageFileEvidence>
|
|
{
|
|
public OSPackageFileEvidence(
|
|
string path,
|
|
string? layerDigest = null,
|
|
string? sha256 = null,
|
|
long? sizeBytes = null,
|
|
bool? isConfigFile = null,
|
|
IDictionary<string, string>? digests = null)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(path);
|
|
Path = Normalize(path);
|
|
LayerDigest = NormalizeDigest(layerDigest);
|
|
var digestMap = digests is null
|
|
? new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
: new SortedDictionary<string, string>(digests, StringComparer.OrdinalIgnoreCase);
|
|
|
|
if (!string.IsNullOrWhiteSpace(sha256))
|
|
{
|
|
digestMap["sha256"] = NormalizeHash(sha256)!;
|
|
}
|
|
|
|
Digests = new ReadOnlyDictionary<string, string>(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<string, string> 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();
|
|
}
|
|
}
|