feat: Initialize Zastava Webhook service with TLS and Authority authentication
- 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.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user