Refactor code structure for improved readability and maintainability; optimize performance in key functions.

This commit is contained in:
master
2025-12-22 19:06:31 +02:00
parent dfaa2079aa
commit 4602ccc3a3
1444 changed files with 109919 additions and 8058 deletions

View File

@@ -0,0 +1,136 @@
using System.Collections.Immutable;
namespace StellaOps.Testing.Manifests.Models;
/// <summary>
/// Captures all inputs required to reproduce a scan verdict deterministically.
/// This is the replay key that enables time-travel verification.
/// </summary>
public sealed record RunManifest
{
/// <summary>
/// Unique identifier for this run.
/// </summary>
public required string RunId { get; init; }
/// <summary>
/// Schema version for forward compatibility.
/// </summary>
public string SchemaVersion { get; init; } = "1.0.0";
/// <summary>
/// Artifact digests being scanned (image layers, binaries, etc.).
/// </summary>
public required ImmutableArray<ArtifactDigest> ArtifactDigests { get; init; }
/// <summary>
/// SBOM digests produced or consumed during the run.
/// </summary>
public ImmutableArray<SbomReference> SbomDigests { get; init; } = [];
/// <summary>
/// Vulnerability feed snapshot used for matching.
/// </summary>
public required FeedSnapshot FeedSnapshot { get; init; }
/// <summary>
/// Policy version and lattice rules digest.
/// </summary>
public required PolicySnapshot PolicySnapshot { get; init; }
/// <summary>
/// Tool versions used in the scan pipeline.
/// </summary>
public required ToolVersions ToolVersions { get; init; }
/// <summary>
/// Cryptographic profile: trust roots, key IDs, algorithm set.
/// </summary>
public required CryptoProfile CryptoProfile { get; init; }
/// <summary>
/// Environment profile: postgres-only vs postgres+valkey.
/// </summary>
public required EnvironmentProfile EnvironmentProfile { get; init; }
/// <summary>
/// PRNG seed for any randomized operations (ensures reproducibility).
/// </summary>
public long? PrngSeed { get; init; }
/// <summary>
/// Canonicalization algorithm version for stable JSON output.
/// </summary>
public required string CanonicalizationVersion { get; init; }
/// <summary>
/// UTC timestamp when the run was initiated.
/// </summary>
public required DateTimeOffset InitiatedAt { get; init; }
/// <summary>
/// SHA-256 hash of this manifest (excluding this field).
/// </summary>
public string? ManifestDigest { get; init; }
}
/// <summary>
/// Artifact digest information.
/// </summary>
public sealed record ArtifactDigest(
string Algorithm,
string Digest,
string? MediaType,
string? Reference);
/// <summary>
/// SBOM reference information.
/// </summary>
public sealed record SbomReference(
string Format,
string Digest,
string? Uri);
/// <summary>
/// Feed snapshot reference.
/// </summary>
public sealed record FeedSnapshot(
string FeedId,
string Version,
string Digest,
DateTimeOffset SnapshotAt);
/// <summary>
/// Policy snapshot reference.
/// </summary>
public sealed record PolicySnapshot(
string PolicyVersion,
string LatticeRulesDigest,
ImmutableArray<string> EnabledRules);
/// <summary>
/// Toolchain versions used during the scan.
/// </summary>
public sealed record ToolVersions(
string ScannerVersion,
string SbomGeneratorVersion,
string ReachabilityEngineVersion,
string AttestorVersion,
ImmutableDictionary<string, string> AdditionalTools);
/// <summary>
/// Cryptographic profile for the run.
/// </summary>
public sealed record CryptoProfile(
string ProfileName,
ImmutableArray<string> TrustRootIds,
ImmutableArray<string> AllowedAlgorithms);
/// <summary>
/// Environment profile for determinism.
/// </summary>
public sealed record EnvironmentProfile(
string Name,
bool ValkeyEnabled,
string? PostgresVersion,
string? ValkeyVersion);

View File

@@ -0,0 +1,120 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.io/schemas/run-manifest/v1",
"title": "StellaOps Run Manifest",
"description": "Captures all inputs for deterministic scan replay",
"type": "object",
"required": [
"runId",
"schemaVersion",
"artifactDigests",
"feedSnapshot",
"policySnapshot",
"toolVersions",
"cryptoProfile",
"environmentProfile",
"canonicalizationVersion",
"initiatedAt"
],
"properties": {
"runId": { "type": "string" },
"schemaVersion": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
"artifactDigests": {
"type": "array",
"items": { "$ref": "#/$defs/artifactDigest" },
"minItems": 1
},
"sbomDigests": {
"type": "array",
"items": { "$ref": "#/$defs/sbomReference" }
},
"feedSnapshot": { "$ref": "#/$defs/feedSnapshot" },
"policySnapshot": { "$ref": "#/$defs/policySnapshot" },
"toolVersions": { "$ref": "#/$defs/toolVersions" },
"cryptoProfile": { "$ref": "#/$defs/cryptoProfile" },
"environmentProfile": { "$ref": "#/$defs/environmentProfile" },
"prngSeed": { "type": ["integer", "null"] },
"canonicalizationVersion": { "type": "string" },
"initiatedAt": { "type": "string", "format": "date-time" },
"manifestDigest": { "type": ["string", "null"] }
},
"$defs": {
"artifactDigest": {
"type": "object",
"required": ["algorithm", "digest"],
"properties": {
"algorithm": { "enum": ["sha256", "sha512"] },
"digest": { "type": "string", "pattern": "^[a-f0-9]{64,128}$" },
"mediaType": { "type": ["string", "null"] },
"reference": { "type": ["string", "null"] }
}
},
"sbomReference": {
"type": "object",
"required": ["format", "digest"],
"properties": {
"format": { "type": "string" },
"digest": { "type": "string" },
"uri": { "type": ["string", "null"] }
}
},
"feedSnapshot": {
"type": "object",
"required": ["feedId", "version", "digest", "snapshotAt"],
"properties": {
"feedId": { "type": "string" },
"version": { "type": "string" },
"digest": { "type": "string" },
"snapshotAt": { "type": "string", "format": "date-time" }
}
},
"policySnapshot": {
"type": "object",
"required": ["policyVersion", "latticeRulesDigest", "enabledRules"],
"properties": {
"policyVersion": { "type": "string" },
"latticeRulesDigest": { "type": "string" },
"enabledRules": {
"type": "array",
"items": { "type": "string" }
}
}
},
"toolVersions": {
"type": "object",
"required": ["scannerVersion", "sbomGeneratorVersion", "reachabilityEngineVersion", "attestorVersion", "additionalTools"],
"properties": {
"scannerVersion": { "type": "string" },
"sbomGeneratorVersion": { "type": "string" },
"reachabilityEngineVersion": { "type": "string" },
"attestorVersion": { "type": "string" },
"additionalTools": { "type": "object" }
}
},
"cryptoProfile": {
"type": "object",
"required": ["profileName", "trustRootIds", "allowedAlgorithms"],
"properties": {
"profileName": { "type": "string" },
"trustRootIds": {
"type": "array",
"items": { "type": "string" }
},
"allowedAlgorithms": {
"type": "array",
"items": { "type": "string" }
}
}
},
"environmentProfile": {
"type": "object",
"required": ["name", "valkeyEnabled"],
"properties": {
"name": { "type": "string" },
"valkeyEnabled": { "type": "boolean" },
"postgresVersion": { "type": ["string", "null"] },
"valkeyVersion": { "type": ["string", "null"] }
}
}
}
}

View File

@@ -0,0 +1,59 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using StellaOps.Canonical.Json;
using StellaOps.Testing.Manifests.Models;
namespace StellaOps.Testing.Manifests.Serialization;
/// <summary>
/// Serialize and hash RunManifest in canonical form.
/// </summary>
public static class RunManifestSerializer
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
{
WriteIndented = false,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
/// <summary>
/// Serializes a manifest to canonical JSON.
/// </summary>
public static string Serialize(RunManifest manifest)
{
var jsonBytes = JsonSerializer.SerializeToUtf8Bytes(manifest, JsonOptions);
var canonicalBytes = CanonJson.CanonicalizeParsedJson(jsonBytes);
return Encoding.UTF8.GetString(canonicalBytes);
}
/// <summary>
/// Deserializes a manifest from JSON.
/// </summary>
public static RunManifest Deserialize(string json)
{
return JsonSerializer.Deserialize<RunManifest>(json, JsonOptions)
?? throw new InvalidOperationException("Failed to deserialize manifest");
}
/// <summary>
/// Computes the SHA-256 digest of a manifest (excluding ManifestDigest).
/// </summary>
public static string ComputeDigest(RunManifest manifest)
{
var withoutDigest = manifest with { ManifestDigest = null };
var json = Serialize(withoutDigest);
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(json));
return Convert.ToHexString(hash).ToLowerInvariant();
}
/// <summary>
/// Returns a manifest with the digest computed and applied.
/// </summary>
public static RunManifest WithDigest(RunManifest manifest)
=> manifest with { ManifestDigest = ComputeDigest(manifest) };
}

View File

@@ -0,0 +1,93 @@
using System.Collections.Immutable;
using StellaOps.Testing.Manifests.Models;
using StellaOps.Testing.Manifests.Serialization;
namespace StellaOps.Testing.Manifests.Services;
/// <summary>
/// Captures a RunManifest during scan execution.
/// </summary>
public sealed class ManifestCaptureService : IManifestCaptureService
{
private readonly IFeedVersionProvider _feedProvider;
private readonly IPolicyVersionProvider _policyProvider;
private readonly TimeProvider _timeProvider;
public ManifestCaptureService(
IFeedVersionProvider feedProvider,
IPolicyVersionProvider policyProvider,
TimeProvider? timeProvider = null)
{
_feedProvider = feedProvider;
_policyProvider = policyProvider;
_timeProvider = timeProvider ?? TimeProvider.System;
}
public async Task<RunManifest> CaptureAsync(
ScanContext context,
CancellationToken ct = default)
{
var feedSnapshot = await _feedProvider.GetCurrentSnapshotAsync(ct).ConfigureAwait(false);
var policySnapshot = await _policyProvider.GetCurrentSnapshotAsync(ct).ConfigureAwait(false);
var manifest = new RunManifest
{
RunId = context.RunId,
SchemaVersion = "1.0.0",
ArtifactDigests = context.ArtifactDigests,
SbomDigests = context.GeneratedSboms,
FeedSnapshot = feedSnapshot,
PolicySnapshot = policySnapshot,
ToolVersions = context.ToolVersions ?? GetToolVersions(),
CryptoProfile = context.CryptoProfile,
EnvironmentProfile = context.EnvironmentProfile ?? GetEnvironmentProfile(),
PrngSeed = context.PrngSeed,
CanonicalizationVersion = "1.0.0",
InitiatedAt = _timeProvider.GetUtcNow()
};
return RunManifestSerializer.WithDigest(manifest);
}
private static ToolVersions GetToolVersions() => new(
ScannerVersion: typeof(ManifestCaptureService).Assembly.GetName().Version?.ToString() ?? "unknown",
SbomGeneratorVersion: "unknown",
ReachabilityEngineVersion: "unknown",
AttestorVersion: "unknown",
AdditionalTools: ImmutableDictionary<string, string>.Empty);
private static EnvironmentProfile GetEnvironmentProfile() => new(
Name: Environment.GetEnvironmentVariable("STELLAOPS_ENV_PROFILE") ?? "postgres-only",
ValkeyEnabled: string.Equals(Environment.GetEnvironmentVariable("STELLAOPS_VALKEY_ENABLED"), "true", StringComparison.OrdinalIgnoreCase),
PostgresVersion: Environment.GetEnvironmentVariable("STELLAOPS_POSTGRES_VERSION"),
ValkeyVersion: Environment.GetEnvironmentVariable("STELLAOPS_VALKEY_VERSION"));
}
public interface IManifestCaptureService
{
Task<RunManifest> CaptureAsync(ScanContext context, CancellationToken ct = default);
}
public interface IFeedVersionProvider
{
Task<FeedSnapshot> GetCurrentSnapshotAsync(CancellationToken ct = default);
}
public interface IPolicyVersionProvider
{
Task<PolicySnapshot> GetCurrentSnapshotAsync(CancellationToken ct = default);
}
/// <summary>
/// Input context required to capture a RunManifest.
/// </summary>
public sealed record ScanContext
{
public required string RunId { get; init; }
public required ImmutableArray<ArtifactDigest> ArtifactDigests { get; init; }
public ImmutableArray<SbomReference> GeneratedSboms { get; init; } = [];
public required CryptoProfile CryptoProfile { get; init; }
public ToolVersions? ToolVersions { get; init; }
public EnvironmentProfile? EnvironmentProfile { get; init; }
public long? PrngSeed { get; init; }
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Json.Schema.Net" Version="7.2.0" />
<PackageReference Include="System.Collections.Immutable" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.Canonical.Json\StellaOps.Canonical.Json.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Schemas\*.json" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,64 @@
using System.Text.Json;
using Json.Schema;
using StellaOps.Testing.Manifests.Models;
using StellaOps.Testing.Manifests.Serialization;
namespace StellaOps.Testing.Manifests.Validation;
/// <summary>
/// Validates RunManifest instances against schema and invariants.
/// </summary>
public sealed class RunManifestValidator : IRunManifestValidator
{
private readonly JsonSchema _schema;
public RunManifestValidator()
{
var schemaJson = SchemaLoader.LoadSchema("run-manifest.schema.json");
_schema = JsonSchema.FromText(schemaJson);
}
public ValidationResult Validate(RunManifest manifest)
{
var errors = new List<ValidationError>();
var json = RunManifestSerializer.Serialize(manifest);
var schemaResult = _schema.Evaluate(JsonDocument.Parse(json));
if (!schemaResult.IsValid)
{
foreach (var error in schemaResult.Errors)
{
errors.Add(new ValidationError("Schema", error.Message));
}
}
if (manifest.ArtifactDigests.Length == 0)
{
errors.Add(new ValidationError("ArtifactDigests", "At least one artifact required"));
}
if (manifest.FeedSnapshot.SnapshotAt > manifest.InitiatedAt)
{
errors.Add(new ValidationError("FeedSnapshot", "Feed snapshot cannot be after run initiation"));
}
if (manifest.ManifestDigest is not null)
{
var computed = RunManifestSerializer.ComputeDigest(manifest);
if (!string.Equals(computed, manifest.ManifestDigest, StringComparison.OrdinalIgnoreCase))
{
errors.Add(new ValidationError("ManifestDigest", "Digest mismatch"));
}
}
return new ValidationResult(errors.Count == 0, errors);
}
}
public interface IRunManifestValidator
{
ValidationResult Validate(RunManifest manifest);
}
public sealed record ValidationResult(bool IsValid, IReadOnlyList<ValidationError> Errors);
public sealed record ValidationError(string Field, string Message);

View File

@@ -0,0 +1,27 @@
using System.Reflection;
namespace StellaOps.Testing.Manifests.Validation;
internal static class SchemaLoader
{
public static string LoadSchema(string fileName)
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = assembly.GetManifestResourceNames()
.FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase));
if (resourceName is null)
{
throw new InvalidOperationException($"Schema resource not found: {fileName}");
}
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream is null)
{
throw new InvalidOperationException($"Schema resource not available: {resourceName}");
}
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
}