save progress

This commit is contained in:
master
2026-01-09 18:27:36 +02:00
parent e608752924
commit a21d3dbc1f
361 changed files with 63068 additions and 1192 deletions

View File

@@ -11,16 +11,16 @@ namespace StellaOps.Policy.Engine.Storage.InMemory;
/// In-memory implementation of IExceptionRepository for offline/test runs.
/// Provides minimal semantics needed for lifecycle processing.
/// </summary>
public sealed class InMemoryExceptionRepository(TimeProvider timeProvider, IGuidProvider guidProvider) : IExceptionRepository
public sealed class InMemoryExceptionRepository : IExceptionRepository
{
private readonly TimeProvider _timeProvider = timeProvider;
private readonly IGuidProvider _guidProvider = guidProvider;
private readonly ConcurrentDictionary<(string Tenant, Guid Id), ExceptionEntity> _exceptions = new();
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
private readonly ConcurrentDictionary<(string Tenant, Guid Id), ExceptionEntity> _exceptions = new();
public InMemoryExceptionRepository(TimeProvider? timeProvider = null)
public InMemoryExceptionRepository(TimeProvider? timeProvider = null, IGuidProvider? guidProvider = null)
{
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? new SystemGuidProvider();
}
public Task<ExceptionEntity> CreateAsync(ExceptionEntity exception, CancellationToken cancellationToken = default)

View File

@@ -29,7 +29,7 @@ public sealed class BatchSimulationOrchestrator : IBatchSimulationOrchestrator,
{
_simulationService = simulationService ?? throw new ArgumentNullException(nameof(simulationService));
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? GuidProvider.Default;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
// Start background processing
_processingTask = Task.Run(ProcessJobsAsync);

View File

@@ -25,7 +25,7 @@ public sealed class ReviewWorkflowService : IReviewWorkflowService
{
_packStore = packStore ?? throw new ArgumentNullException(nameof(packStore));
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? GuidProvider.Default;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
public async Task<ReviewRequest> SubmitForReviewAsync(

View File

@@ -16,4 +16,8 @@
<PackageReference Include="Microsoft.Extensions.Options" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\__Libraries\StellaOps.Determinism.Abstractions\StellaOps.Determinism.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -6,11 +6,10 @@ namespace StellaOps.Policy.Registry.Storage;
/// <summary>
/// In-memory implementation of IVerificationPolicyStore for testing and development.
/// </summary>
public sealed class InMemoryVerificationPolicyStore(TimeProvider timeProvider) : IVerificationPolicyStore
public sealed class InMemoryVerificationPolicyStore : IVerificationPolicyStore
{
private readonly TimeProvider _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
private readonly ConcurrentDictionary<(Guid TenantId, string PolicyId), VerificationPolicyEntity> _policies = new();
private readonly TimeProvider _timeProvider;
private readonly ConcurrentDictionary<(Guid TenantId, string PolicyId), VerificationPolicyEntity> _policies = new();
public InMemoryVerificationPolicyStore(TimeProvider? timeProvider = null)
{

View File

@@ -71,7 +71,7 @@ public class DeterminismPropertyTests
/// Property: Parallel execution produces consistent results.
/// </summary>
[Fact]
public void Entropy_ParallelExecution_ProducesConsistentResults()
public async Task Entropy_ParallelExecution_ProducesConsistentResults()
{
// Arrange
var calculator = new UncertaintyScoreCalculator(NullLogger<UncertaintyScoreCalculator>.Instance);
@@ -82,7 +82,7 @@ public class DeterminismPropertyTests
.Select(_ => Task.Run(() => calculator.CalculateEntropy(snapshot)))
.ToArray();
Task.WaitAll(tasks);
await Task.WhenAll(tasks);
var results = tasks.Select(t => t.Result).ToList();
// Assert - all results should be identical

View File

@@ -12,7 +12,7 @@ public sealed class RvaBuilderTests
[Fact]
public void Build_ValidInputs_CreatesRva()
{
var rva = new RvaBuilder(_hasher)
var rva = new RvaBuilder(_hasher, TimeProvider.System)
.WithVerdict(RiskVerdictStatus.Pass)
.WithSubject("sha256:abc123", "container-image", "myapp:v1.0")
.WithPolicy("policy-1", "1.0", "sha256:xyz")
@@ -31,7 +31,7 @@ public sealed class RvaBuilderTests
[Fact]
public void Build_MissingSubject_Throws()
{
var builder = new RvaBuilder(_hasher)
var builder = new RvaBuilder(_hasher, TimeProvider.System)
.WithVerdict(RiskVerdictStatus.Pass)
.WithPolicy("p", "1.0", "sha256:x")
.WithKnowledgeSnapshot("ksm:sha256:y");
@@ -45,7 +45,7 @@ public sealed class RvaBuilderTests
[Fact]
public void Build_MissingPolicy_Throws()
{
var builder = new RvaBuilder(_hasher)
var builder = new RvaBuilder(_hasher, TimeProvider.System)
.WithVerdict(RiskVerdictStatus.Pass)
.WithSubject("sha256:abc", "container-image")
.WithKnowledgeSnapshot("ksm:sha256:y");
@@ -59,7 +59,7 @@ public sealed class RvaBuilderTests
[Fact]
public void Build_MissingSnapshot_Throws()
{
var builder = new RvaBuilder(_hasher)
var builder = new RvaBuilder(_hasher, TimeProvider.System)
.WithVerdict(RiskVerdictStatus.Pass)
.WithSubject("sha256:abc", "container-image")
.WithPolicy("p", "1.0", "sha256:x");
@@ -158,7 +158,7 @@ public sealed class RvaBuilderTests
private RvaBuilder CreateBuilder()
{
return new RvaBuilder(_hasher)
return new RvaBuilder(_hasher, TimeProvider.System)
.WithVerdict(RiskVerdictStatus.Pass)
.WithSubject("sha256:test123", "container-image", "test:v1")
.WithPolicy("policy-1", "1.0", "sha256:policy")

View File

@@ -23,7 +23,8 @@ public sealed class RvaVerifierTests
NullLogger<SnapshotService>.Instance);
_verifier = new RvaVerifier(
_snapshotService,
NullLogger<RvaVerifier>.Instance);
NullLogger<RvaVerifier>.Instance,
TimeProvider.System);
}
[Fact]
@@ -124,7 +125,7 @@ public sealed class RvaVerifierTests
private RiskVerdictAttestation CreateValidRva(DateTimeOffset? expiresAt = null)
{
return new RvaBuilder(_hasher)
return new RvaBuilder(_hasher, TimeProvider.System)
.WithVerdict(RiskVerdictStatus.Pass)
.WithSubject("sha256:test123", "container-image", "test:v1")
.WithPolicy("policy-1", "1.0", "sha256:policy")

View File

@@ -15,6 +15,7 @@ using Microsoft.Extensions.Options;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using StellaOps.Auth.Client;
using StellaOps.Determinism;
using StellaOps.Policy.Gateway.Clients;
using StellaOps.Policy.Gateway.Contracts;
using StellaOps.Policy.Gateway.Options;
@@ -41,7 +42,7 @@ public class PolicyEngineClientTests
var optionsMonitor = new TestOptionsMonitor(options);
var tokenClient = new StubTokenClient();
var dpopGenerator = new PolicyGatewayDpopProofGenerator(new StubHostEnvironment(), optionsMonitor, TimeProvider.System, NullLogger<PolicyGatewayDpopProofGenerator>.Instance);
var dpopGenerator = new PolicyGatewayDpopProofGenerator(new StubHostEnvironment(), optionsMonitor, TimeProvider.System, SystemGuidProvider.Instance, NullLogger<PolicyGatewayDpopProofGenerator>.Instance);
var tokenProvider = new PolicyEngineTokenProvider(tokenClient, optionsMonitor, dpopGenerator, TimeProvider.System, NullLogger<PolicyEngineTokenProvider>.Instance);
using var recordingHandler = new RecordingHandler();

View File

@@ -8,12 +8,11 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using StellaOps.Determinism;
using StellaOps.Policy.Gateway.Options;
using StellaOps.Policy.Gateway.Services;
using Xunit;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Policy.Gateway.Tests;
public sealed class PolicyGatewayDpopProofGeneratorTests
@@ -29,6 +28,7 @@ public sealed class PolicyGatewayDpopProofGeneratorTests
new StubHostEnvironment(AppContext.BaseDirectory),
new TestOptionsMonitor(options),
TimeProvider.System,
SystemGuidProvider.Instance,
NullLogger<PolicyGatewayDpopProofGenerator>.Instance);
var exception = Assert.Throws<InvalidOperationException>(() =>
@@ -52,6 +52,7 @@ public sealed class PolicyGatewayDpopProofGeneratorTests
new StubHostEnvironment(tempRoot.FullName),
new TestOptionsMonitor(options),
TimeProvider.System,
SystemGuidProvider.Instance,
NullLogger<PolicyGatewayDpopProofGenerator>.Instance);
var exception = Assert.Throws<FileNotFoundException>(() =>
@@ -82,6 +83,7 @@ public sealed class PolicyGatewayDpopProofGeneratorTests
new StubHostEnvironment(tempRoot.FullName),
new TestOptionsMonitor(options),
TimeProvider.System,
SystemGuidProvider.Instance,
NullLogger<PolicyGatewayDpopProofGenerator>.Instance);
const string accessToken = "sample-access-token";

View File

@@ -8,6 +8,7 @@ using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Time.Testing;
using Moq;
using StellaOps.Determinism;
using StellaOps.Policy.Exceptions.Models;
using StellaOps.Policy.Exceptions.Repositories;
using StellaOps.Policy.Gateway.Services;
@@ -32,6 +33,7 @@ public class ExceptionServiceTests
_repositoryMock.Object,
_notificationMock.Object,
_timeProvider,
new SequentialGuidProvider(),
NullLogger<ExceptionService>.Instance);
}

View File

@@ -2,6 +2,7 @@ using System.Collections.Immutable;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Policy.Exceptions.Models;
using StellaOps.Policy.Exceptions.Repositories;
using StellaOps.Policy.Persistence.Postgres;
@@ -28,7 +29,7 @@ public sealed class ExceptionObjectRepositoryTests : IAsyncLifetime
var options = fixture.Fixture.CreateOptions();
options.SchemaName = fixture.SchemaName;
var dataSource = new PolicyDataSource(Options.Create(options), NullLogger<PolicyDataSource>.Instance);
_repository = new PostgresExceptionObjectRepository(dataSource, NullLogger<PostgresExceptionObjectRepository>.Instance);
_repository = new PostgresExceptionObjectRepository(dataSource, NullLogger<PostgresExceptionObjectRepository>.Instance, TimeProvider.System, SystemGuidProvider.Instance);
}
public ValueTask InitializeAsync() => new(_fixture.TruncateAllTablesAsync());

View File

@@ -173,7 +173,7 @@ public sealed class PostgresExceptionApplicationRepositoryTests : IAsyncLifetime
string findId,
string? vulnId = null,
string eff = "suppress") =>
ExceptionApplication.Create(_tenantId, excId, findId, "affected", "not_affected", "test", eff, vulnId);
ExceptionApplication.Create(_tenantId, excId, findId, "affected", "not_affected", "test", eff, Guid.NewGuid(), DateTimeOffset.UtcNow, vulnId);
}

View File

@@ -2,6 +2,7 @@ using System.Collections.Immutable;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
using StellaOps.Policy.Exceptions.Models;
using StellaOps.Policy.Exceptions.Repositories;
using StellaOps.Policy.Persistence.Postgres;
@@ -29,7 +30,7 @@ public sealed class PostgresExceptionObjectRepositoryTests : IAsyncLifetime
var options = fixture.Fixture.CreateOptions();
options.SchemaName = fixture.SchemaName;
var dataSource = new PolicyDataSource(Options.Create(options), NullLogger<PolicyDataSource>.Instance);
_repository = new PostgresExceptionObjectRepository(dataSource, NullLogger<PostgresExceptionObjectRepository>.Instance);
_repository = new PostgresExceptionObjectRepository(dataSource, NullLogger<PostgresExceptionObjectRepository>.Instance, TimeProvider.System, SystemGuidProvider.Instance);
}
public ValueTask InitializeAsync() => new(_fixture.TruncateAllTablesAsync());

View File

@@ -1,5 +1,6 @@
using System.Collections.Immutable;
using FluentAssertions;
using StellaOps.Determinism;
using StellaOps.Policy.Scoring.Engine;
using StellaOps.Policy.Scoring.Receipts;
using StellaOps.Policy.Scoring.Tests.Fakes;
@@ -25,7 +26,7 @@ public sealed class CvssPipelineIntegrationTests
{
// Arrange
var repository = new InMemoryReceiptRepository();
var builder = new ReceiptBuilder(_v4Engine, repository);
var builder = new ReceiptBuilder(_v4Engine, repository, TimeProvider.System, SystemGuidProvider.Instance);
var policy = CreateTestPolicy();
var baseMetrics = CreateMaxSeverityBaseMetrics();
@@ -61,7 +62,7 @@ public sealed class CvssPipelineIntegrationTests
{
// Arrange
var repository = new InMemoryReceiptRepository();
var builder = new ReceiptBuilder(_v4Engine, repository);
var builder = new ReceiptBuilder(_v4Engine, repository, TimeProvider.System, SystemGuidProvider.Instance);
var policy = CreateTestPolicy();
var baseMetrics = CreateMaxSeverityBaseMetrics();
@@ -149,8 +150,8 @@ public sealed class CvssPipelineIntegrationTests
// Arrange
var repository1 = new InMemoryReceiptRepository();
var repository2 = new InMemoryReceiptRepository();
var builder1 = new ReceiptBuilder(_v4Engine, repository1);
var builder2 = new ReceiptBuilder(_v4Engine, repository2);
var builder1 = new ReceiptBuilder(_v4Engine, repository1, TimeProvider.System, SystemGuidProvider.Instance);
var builder2 = new ReceiptBuilder(_v4Engine, repository2, TimeProvider.System, SystemGuidProvider.Instance);
var policy = CreateTestPolicy();
var baseMetrics = CreateMaxSeverityBaseMetrics();

View File

@@ -2,6 +2,7 @@ using System.Collections.Immutable;
using FluentAssertions;
using System.Linq;
using StellaOps.Attestor.Envelope;
using StellaOps.Determinism;
using StellaOps.Policy.Scoring.Engine;
using StellaOps.Policy.Scoring.Receipts;
using StellaOps.Policy.Scoring.Tests.Fakes;
@@ -60,7 +61,7 @@ public sealed class ReceiptBuilderTests
})
};
var builder = new ReceiptBuilder(_engine, _repository);
var builder = new ReceiptBuilder(_engine, _repository, TimeProvider.System, SystemGuidProvider.Instance);
// Act
var receipt1 = await builder.CreateAsync(request);
@@ -118,7 +119,7 @@ public sealed class ReceiptBuilderTests
var evidence2 = evidence1.Reverse().ToImmutableList();
var builder = new ReceiptBuilder(_engine, _repository);
var builder = new ReceiptBuilder(_engine, _repository, TimeProvider.System, SystemGuidProvider.Instance);
var r1 = await builder.CreateAsync(new CreateReceiptRequest
{
@@ -182,7 +183,7 @@ public sealed class ReceiptBuilderTests
SigningKey = signingKey
};
var builder = new ReceiptBuilder(_engine, _repository);
var builder = new ReceiptBuilder(_engine, _repository, TimeProvider.System, SystemGuidProvider.Instance);
// Act
var receipt = await builder.CreateAsync(request);
@@ -240,7 +241,7 @@ public sealed class ReceiptBuilderTests
})
};
var builder = new ReceiptBuilder(_engine, _repository);
var builder = new ReceiptBuilder(_engine, _repository, TimeProvider.System, SystemGuidProvider.Instance);
// Act
var act = async () => await builder.CreateAsync(request);

View File

@@ -222,7 +222,7 @@ public class ProofLedgerTests
var originalRootHash = ledger.RootHash();
// Act
var json = ledger.ToJson();
var json = ledger.ToJson(DateTimeOffset.UtcNow);
var restored = ProofLedger.FromJson(json);
// Assert
@@ -236,7 +236,7 @@ public class ProofLedgerTests
// Arrange
var nodes = CreateTestNodes();
var ledger = ProofLedger.FromNodes(nodes);
var json = ledger.ToJson();
var json = ledger.ToJson(DateTimeOffset.UtcNow);
// Tamper with the JSON (9.0 serializes as 9 without decimal point)
var tampered = json.Replace("\"total\":9,", "\"total\":8,");

View File

@@ -274,7 +274,7 @@ public class VexNormalizerTests
var normalizer = new CsafVexNormalizer();
var subject = CreateTestSubject();
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.KnownAffected);
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.KnownAffected, DateTimeOffset.UtcNow);
Assert.Contains(claim.Assertions, a =>
a.Atom == SecurityAtom.Present && a.Value == true);
@@ -288,7 +288,7 @@ public class VexNormalizerTests
var normalizer = new CsafVexNormalizer();
var subject = CreateTestSubject();
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.KnownNotAffected);
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.KnownNotAffected, DateTimeOffset.UtcNow);
Assert.Contains(claim.Assertions, a =>
a.Atom == SecurityAtom.Applies && a.Value == false);
@@ -300,7 +300,7 @@ public class VexNormalizerTests
var normalizer = new CsafVexNormalizer();
var subject = CreateTestSubject();
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.Fixed);
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.Fixed, DateTimeOffset.UtcNow);
Assert.Contains(claim.Assertions, a =>
a.Atom == SecurityAtom.Fixed && a.Value == true);
@@ -312,7 +312,7 @@ public class VexNormalizerTests
var normalizer = new CsafVexNormalizer();
var subject = CreateTestSubject();
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.UnderInvestigation);
var claim = normalizer.NormalizeStatement(subject, CsafProductStatus.UnderInvestigation, DateTimeOffset.UtcNow);
Assert.Empty(claim.Assertions);
}
@@ -326,6 +326,7 @@ public class VexNormalizerTests
var claim = normalizer.NormalizeStatement(
subject,
CsafProductStatus.KnownNotAffected,
DateTimeOffset.UtcNow,
CsafFlagLabel.VulnerableCodeNotInExecutePath);
Assert.Contains(claim.Assertions, a =>
@@ -341,6 +342,7 @@ public class VexNormalizerTests
var claim = normalizer.NormalizeStatement(
subject,
CsafProductStatus.KnownNotAffected,
DateTimeOffset.UtcNow,
CsafFlagLabel.ComponentNotPresent);
Assert.Contains(claim.Assertions, a =>