feat: Implement Policy Engine Evaluation Service and Cache with unit tests
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Temp commit to debug
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using StellaOps.Scanner.Storage.Catalog;
|
||||
|
||||
namespace StellaOps.Scanner.Storage.ObjectStore;
|
||||
|
||||
/// <summary>
|
||||
/// Builds deterministic object keys for scanner artefacts stored in the backing object store.
|
||||
/// </summary>
|
||||
public static class ArtifactObjectKeyBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds an object key for the provided artefact metadata.
|
||||
/// </summary>
|
||||
/// <param name="type">Artefact type.</param>
|
||||
/// <param name="format">Artefact format.</param>
|
||||
/// <param name="digest">Content digest (with or without algorithm prefix).</param>
|
||||
/// <param name="rootPrefix">Optional root prefix to prepend (defaults to <c>scanner</c>).</param>
|
||||
/// <returns>Deterministic storage key.</returns>
|
||||
public static string Build(
|
||||
ArtifactDocumentType type,
|
||||
ArtifactDocumentFormat format,
|
||||
string digest,
|
||||
string? rootPrefix = null)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(digest);
|
||||
|
||||
var normalizedDigest = NormalizeDigest(digest);
|
||||
var digestValue = ExtractDigest(normalizedDigest);
|
||||
|
||||
var prefix = type switch
|
||||
{
|
||||
ArtifactDocumentType.LayerBom => ScannerStorageDefaults.ObjectPrefixes.Layers,
|
||||
ArtifactDocumentType.ImageBom => ScannerStorageDefaults.ObjectPrefixes.Images,
|
||||
ArtifactDocumentType.Index => ScannerStorageDefaults.ObjectPrefixes.Indexes,
|
||||
ArtifactDocumentType.Attestation => ScannerStorageDefaults.ObjectPrefixes.Attestations,
|
||||
ArtifactDocumentType.Diff => "diffs",
|
||||
_ => ScannerStorageDefaults.ObjectPrefixes.Images,
|
||||
};
|
||||
|
||||
var extension = format switch
|
||||
{
|
||||
ArtifactDocumentFormat.CycloneDxJson => "sbom.cdx.json",
|
||||
ArtifactDocumentFormat.CycloneDxProtobuf => "sbom.cdx.pb",
|
||||
ArtifactDocumentFormat.SpdxJson => "sbom.spdx.json",
|
||||
ArtifactDocumentFormat.BomIndex => "bom-index.bin",
|
||||
ArtifactDocumentFormat.DsseJson => "artifact.dsse.json",
|
||||
_ => "artifact.bin",
|
||||
};
|
||||
|
||||
var key = $"{prefix}/{digestValue}/{extension}";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rootPrefix))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
return $"{TrimTrailingSlash(rootPrefix)}/{key}";
|
||||
}
|
||||
|
||||
private static string NormalizeDigest(string digest)
|
||||
=> digest.Contains(':', StringComparison.Ordinal)
|
||||
? digest.Trim()
|
||||
: $"sha256:{digest.Trim()}";
|
||||
|
||||
private static string ExtractDigest(string normalizedDigest)
|
||||
{
|
||||
var parts = normalizedDigest.Split(':', 2, StringSplitOptions.TrimEntries);
|
||||
return parts.Length == 2 ? parts[1] : normalizedDigest;
|
||||
}
|
||||
|
||||
private static string TrimTrailingSlash(string value)
|
||||
=> string.IsNullOrWhiteSpace(value)
|
||||
? string.Empty
|
||||
: value.Trim().TrimEnd('/');
|
||||
}
|
||||
@@ -50,8 +50,12 @@ public sealed class ArtifactStorageService
|
||||
try
|
||||
{
|
||||
var normalizedDigest = $"sha256:{digestHex}";
|
||||
var artifactId = CatalogIdFactory.CreateArtifactId(type, normalizedDigest);
|
||||
var key = BuildObjectKey(type, format, normalizedDigest);
|
||||
var artifactId = CatalogIdFactory.CreateArtifactId(type, normalizedDigest);
|
||||
var key = ArtifactObjectKeyBuilder.Build(
|
||||
type,
|
||||
format,
|
||||
normalizedDigest,
|
||||
_options.ObjectStore.RootPrefix);
|
||||
var descriptor = new ArtifactObjectDescriptor(
|
||||
_options.ObjectStore.BucketName,
|
||||
key,
|
||||
@@ -137,45 +141,4 @@ public sealed class ArtifactStorageService
|
||||
return (bufferStream, total, digestHex);
|
||||
}
|
||||
|
||||
private string BuildObjectKey(ArtifactDocumentType type, ArtifactDocumentFormat format, string digest)
|
||||
{
|
||||
var normalizedDigest = digest.Split(':', 2, StringSplitOptions.TrimEntries)[^1];
|
||||
var prefix = type switch
|
||||
{
|
||||
ArtifactDocumentType.LayerBom => ScannerStorageDefaults.ObjectPrefixes.Layers,
|
||||
ArtifactDocumentType.ImageBom => ScannerStorageDefaults.ObjectPrefixes.Images,
|
||||
ArtifactDocumentType.Diff => "diffs",
|
||||
ArtifactDocumentType.Index => ScannerStorageDefaults.ObjectPrefixes.Indexes,
|
||||
ArtifactDocumentType.Attestation => ScannerStorageDefaults.ObjectPrefixes.Attestations,
|
||||
_ => ScannerStorageDefaults.ObjectPrefixes.Images,
|
||||
};
|
||||
|
||||
var extension = format switch
|
||||
{
|
||||
ArtifactDocumentFormat.CycloneDxJson => "sbom.cdx.json",
|
||||
ArtifactDocumentFormat.CycloneDxProtobuf => "sbom.cdx.pb",
|
||||
ArtifactDocumentFormat.SpdxJson => "sbom.spdx.json",
|
||||
ArtifactDocumentFormat.BomIndex => "bom-index.bin",
|
||||
ArtifactDocumentFormat.DsseJson => "artifact.dsse.json",
|
||||
_ => "artifact.bin",
|
||||
};
|
||||
|
||||
var rootPrefix = _options.ObjectStore.RootPrefix;
|
||||
if (string.IsNullOrWhiteSpace(rootPrefix))
|
||||
{
|
||||
return $"{prefix}/{normalizedDigest}/{extension}";
|
||||
}
|
||||
|
||||
return $"{TrimTrailingSlash(rootPrefix)}/{prefix}/{normalizedDigest}/{extension}";
|
||||
}
|
||||
|
||||
private static string TrimTrailingSlash(string prefix)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(prefix))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return prefix.TrimEnd('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user