This commit is contained in:
master
2026-02-04 19:59:20 +02:00
parent 557feefdc3
commit 5548cf83bf
1479 changed files with 53557 additions and 40339 deletions

View File

@@ -0,0 +1,6 @@
namespace StellaOps.Replay.Loaders;
public sealed class DigestMismatchException : Exception
{
public DigestMismatchException(string message) : base(message) { }
}

View File

@@ -0,0 +1,6 @@
namespace StellaOps.Replay.Loaders;
public sealed class FeedNotFoundException : Exception
{
public FeedNotFoundException(string message) : base(message) { }
}

View File

@@ -21,24 +21,25 @@ public sealed class FeedSnapshotLoader : IFeedLoader
public async Task<FeedSnapshot> LoadByDigestAsync(string digest, CancellationToken ct = default)
{
_logger.LogDebug("Loading feed snapshot with digest {Digest}", digest);
var normalizedDigest = SnapshotDigestGuard.EnsureSha256Hex(digest, nameof(digest));
_logger.LogDebug("Loading feed snapshot with digest {Digest}", normalizedDigest);
var localPath = GetLocalPath(digest);
var localPath = GetLocalPath(normalizedDigest);
if (File.Exists(localPath))
{
var feed = await LoadFromFileAsync(localPath, ct).ConfigureAwait(false);
VerifyDigest(feed, digest);
VerifyDigest(feed, normalizedDigest);
return feed;
}
var storedFeed = await _storage.GetByDigestAsync(digest, ct).ConfigureAwait(false);
var storedFeed = await _storage.GetByDigestAsync(normalizedDigest, ct).ConfigureAwait(false);
if (storedFeed is not null)
{
VerifyDigest(storedFeed, digest);
VerifyDigest(storedFeed, normalizedDigest);
return storedFeed;
}
throw new FeedNotFoundException($"Feed snapshot not found: {digest}");
throw new FeedNotFoundException($"Feed snapshot not found: {normalizedDigest}");
}
private static void VerifyDigest(FeedSnapshot feed, string expected)
@@ -66,18 +67,3 @@ public sealed class FeedSnapshotLoader : IFeedLoader
return CanonicalJsonSerializer.Deserialize<FeedSnapshot>(json);
}
}
public interface IFeedStorage
{
Task<FeedSnapshot?> GetByDigestAsync(string digest, CancellationToken ct = default);
}
public sealed class FeedNotFoundException : Exception
{
public FeedNotFoundException(string message) : base(message) { }
}
public sealed class DigestMismatchException : Exception
{
public DigestMismatchException(string message) : base(message) { }
}

View File

@@ -0,0 +1,8 @@
using StellaOps.Testing.Manifests.Models;
namespace StellaOps.Replay.Loaders;
public interface IFeedStorage
{
Task<FeedSnapshot?> GetByDigestAsync(string digest, CancellationToken ct = default);
}

View File

@@ -0,0 +1,8 @@
using StellaOps.Testing.Manifests.Models;
namespace StellaOps.Replay.Loaders;
public interface IPolicyStorage
{
Task<PolicySnapshot?> GetByDigestAsync(string digest, CancellationToken ct = default);
}

View File

@@ -0,0 +1,6 @@
namespace StellaOps.Replay.Loaders;
public sealed class PolicyNotFoundException : Exception
{
public PolicyNotFoundException(string message) : base(message) { }
}

View File

@@ -21,24 +21,25 @@ public sealed class PolicySnapshotLoader : IPolicyLoader
public async Task<PolicySnapshot> LoadByDigestAsync(string digest, CancellationToken ct = default)
{
_logger.LogDebug("Loading policy snapshot with digest {Digest}", digest);
var normalizedDigest = SnapshotDigestGuard.EnsureSha256Hex(digest, nameof(digest));
_logger.LogDebug("Loading policy snapshot with digest {Digest}", normalizedDigest);
var localPath = GetLocalPath(digest);
var localPath = GetLocalPath(normalizedDigest);
if (File.Exists(localPath))
{
var policy = await LoadFromFileAsync(localPath, ct).ConfigureAwait(false);
VerifyDigest(policy, digest);
VerifyDigest(policy, normalizedDigest);
return policy;
}
var stored = await _storage.GetByDigestAsync(digest, ct).ConfigureAwait(false);
var stored = await _storage.GetByDigestAsync(normalizedDigest, ct).ConfigureAwait(false);
if (stored is not null)
{
VerifyDigest(stored, digest);
VerifyDigest(stored, normalizedDigest);
return stored;
}
throw new PolicyNotFoundException($"Policy snapshot not found: {digest}");
throw new PolicyNotFoundException($"Policy snapshot not found: {normalizedDigest}");
}
private static void VerifyDigest(PolicySnapshot policy, string expected)
@@ -66,13 +67,3 @@ public sealed class PolicySnapshotLoader : IPolicyLoader
return CanonicalJsonSerializer.Deserialize<PolicySnapshot>(json);
}
}
public interface IPolicyStorage
{
Task<PolicySnapshot?> GetByDigestAsync(string digest, CancellationToken ct = default);
}
public sealed class PolicyNotFoundException : Exception
{
public PolicyNotFoundException(string message) : base(message) { }
}

View File

@@ -0,0 +1,33 @@
namespace StellaOps.Replay.Loaders;
internal static class SnapshotDigestGuard
{
private const int HexLength = 64;
internal static string EnsureSha256Hex(string digest, string parameterName)
{
if (string.IsNullOrWhiteSpace(digest))
throw new ArgumentException($"{parameterName} is required.", parameterName);
var trimmed = digest.Trim();
if (trimmed.Length != HexLength || !IsHex(trimmed))
throw new FormatException($"{parameterName} must be {HexLength} hexadecimal characters.");
return trimmed.ToLowerInvariant();
}
private static bool IsHex(ReadOnlySpan<char> value)
{
foreach (var c in value)
{
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
{
continue;
}
return false;
}
return true;
}
}