save audit remarks applications progress

This commit is contained in:
StellaOps Bot
2026-01-04 22:49:53 +02:00
parent 8862e112c4
commit eca4e964d3
48 changed files with 1850 additions and 112 deletions

View File

@@ -12,6 +12,7 @@ using System.Text.Json;
using StellaOps.AirGap.Bundle.Models;
using StellaOps.Concelier.Core.Raw;
using StellaOps.Concelier.RawModels;
using StellaOps.Determinism;
namespace StellaOps.AirGap.Bundle.Services;
@@ -134,12 +135,22 @@ public sealed class InMemoryAdvisoryRawRepository : IAdvisoryRawRepository
{
private readonly Dictionary<string, AdvisoryRawRecord> _records = new();
private readonly object _lock = new();
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
public InMemoryAdvisoryRawRepository(
TimeProvider? timeProvider = null,
IGuidProvider? guidProvider = null)
{
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
public Task<AdvisoryRawUpsertResult> UpsertAsync(AdvisoryRawDocument document, CancellationToken cancellationToken)
{
var contentHash = ComputeHash(document);
var key = $"{document.Tenant}:{contentHash}";
var now = DateTimeOffset.UtcNow;
var now = _timeProvider.GetUtcNow();
lock (_lock)
{
@@ -149,7 +160,7 @@ public sealed class InMemoryAdvisoryRawRepository : IAdvisoryRawRepository
}
var record = new AdvisoryRawRecord(
Id: Guid.NewGuid().ToString(),
Id: _guidProvider.NewGuid().ToString(),
Document: document,
IngestedAt: now,
CreatedAt: now);

View File

@@ -10,6 +10,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using StellaOps.AirGap.Bundle.Models;
using StellaOps.Determinism;
using StellaOps.Excititor.Core;
using StellaOps.Excititor.Core.Storage;
@@ -161,10 +162,14 @@ public sealed class InMemoryVexRawDocumentSink : IVexRawDocumentSink, IVexRawSto
private readonly Dictionary<string, VexRawRecord> _records = new();
private readonly string _tenant;
private readonly object _lock = new();
private readonly TimeProvider _timeProvider;
public InMemoryVexRawDocumentSink(string tenant = "default")
public InMemoryVexRawDocumentSink(
string tenant = "default",
TimeProvider? timeProvider = null)
{
_tenant = tenant;
_timeProvider = timeProvider ?? TimeProvider.System;
}
public ValueTask StoreAsync(VexRawDocument document, CancellationToken cancellationToken)
@@ -183,7 +188,7 @@ public sealed class InMemoryVexRawDocumentSink : IVexRawDocumentSink, IVexRawSto
Metadata: document.Metadata,
Content: document.Content,
InlineContent: true,
RecordedAt: DateTimeOffset.UtcNow);
RecordedAt: _timeProvider.GetUtcNow());
}
}

View File

@@ -9,6 +9,7 @@ using System.IO.Compression;
using System.Formats.Tar;
using System.Text.Json;
using StellaOps.AirGap.Bundle.Models;
using StellaOps.Determinism;
namespace StellaOps.AirGap.Bundle.Services;
@@ -25,15 +26,21 @@ public sealed class KnowledgeSnapshotImporter : IKnowledgeSnapshotImporter
private readonly IAdvisoryImportTarget? _advisoryTarget;
private readonly IVexImportTarget? _vexTarget;
private readonly IPolicyImportTarget? _policyTarget;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
public KnowledgeSnapshotImporter(
IAdvisoryImportTarget? advisoryTarget = null,
IVexImportTarget? vexTarget = null,
IPolicyImportTarget? policyTarget = null)
IPolicyImportTarget? policyTarget = null,
TimeProvider? timeProvider = null,
IGuidProvider? guidProvider = null)
{
_advisoryTarget = advisoryTarget;
_vexTarget = vexTarget;
_policyTarget = policyTarget;
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
/// <summary>
@@ -48,10 +55,10 @@ public sealed class KnowledgeSnapshotImporter : IKnowledgeSnapshotImporter
if (!File.Exists(request.BundlePath))
{
return SnapshotImportResult.Failed("Bundle file not found");
return SnapshotImportResult.Failed("Bundle file not found", _timeProvider);
}
var tempDir = Path.Combine(Path.GetTempPath(), $"import-{Guid.NewGuid():N}");
var tempDir = Path.Combine(Path.GetTempPath(), $"import-{_guidProvider.NewGuid():N}");
Directory.CreateDirectory(tempDir);
try
@@ -63,21 +70,21 @@ public sealed class KnowledgeSnapshotImporter : IKnowledgeSnapshotImporter
var manifestPath = Path.Combine(tempDir, "manifest.json");
if (!File.Exists(manifestPath))
{
return SnapshotImportResult.Failed("Manifest not found in bundle");
return SnapshotImportResult.Failed("Manifest not found in bundle", _timeProvider);
}
var manifestBytes = await File.ReadAllBytesAsync(manifestPath, cancellationToken);
var manifest = JsonSerializer.Deserialize<KnowledgeSnapshotManifest>(manifestBytes, JsonOptions);
if (manifest is null)
{
return SnapshotImportResult.Failed("Failed to parse manifest");
return SnapshotImportResult.Failed("Failed to parse manifest", _timeProvider);
}
var result = new SnapshotImportResult
{
Success = true,
BundleId = manifest.BundleId,
StartedAt = DateTimeOffset.UtcNow
StartedAt = _timeProvider.GetUtcNow()
};
var errors = new List<string>();
@@ -148,7 +155,7 @@ public sealed class KnowledgeSnapshotImporter : IKnowledgeSnapshotImporter
result = result with
{
CompletedAt = DateTimeOffset.UtcNow,
CompletedAt = _timeProvider.GetUtcNow(),
Statistics = stats,
Errors = errors.Count > 0 ? [.. errors] : null,
Success = errors.Count == 0 || !request.FailOnAnyError
@@ -158,7 +165,7 @@ public sealed class KnowledgeSnapshotImporter : IKnowledgeSnapshotImporter
}
catch (Exception ex)
{
return SnapshotImportResult.Failed($"Import failed: {ex.Message}");
return SnapshotImportResult.Failed($"Import failed: {ex.Message}", _timeProvider);
}
finally
{
@@ -422,13 +429,17 @@ public sealed record SnapshotImportResult
public IReadOnlyList<string>? Errors { get; init; }
public string? Error { get; init; }
public static SnapshotImportResult Failed(string error) => new()
public static SnapshotImportResult Failed(string error, TimeProvider? timeProvider = null)
{
Success = false,
Error = error,
StartedAt = DateTimeOffset.UtcNow,
CompletedAt = DateTimeOffset.UtcNow
};
var now = (timeProvider ?? TimeProvider.System).GetUtcNow();
return new()
{
Success = false,
Error = error,
StartedAt = now,
CompletedAt = now
};
}
}
public sealed record ImportStatistics

View File

@@ -9,6 +9,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using StellaOps.AirGap.Bundle.Models;
using StellaOps.Determinism;
namespace StellaOps.AirGap.Bundle.Services;
@@ -26,13 +27,16 @@ public sealed class PolicyRegistryImportTarget : IPolicyImportTarget
private readonly IPolicyPackImportStore _store;
private readonly string _tenantId;
private readonly TimeProvider _timeProvider;
public PolicyRegistryImportTarget(
IPolicyPackImportStore store,
string tenantId = "default")
string tenantId = "default",
TimeProvider? timeProvider = null)
{
_store = store ?? throw new ArgumentNullException(nameof(store));
_tenantId = tenantId;
_timeProvider = timeProvider ?? TimeProvider.System;
}
/// <inheritdoc />
@@ -83,7 +87,7 @@ public sealed class PolicyRegistryImportTarget : IPolicyImportTarget
Version: data.Version ?? "1.0.0",
Content: data.Content,
Metadata: bundle.Metadata,
ImportedAt: DateTimeOffset.UtcNow);
ImportedAt: _timeProvider.GetUtcNow());
await _store.SaveAsync(pack, cancellationToken);
created++;

View File

@@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Canonical.Json\StellaOps.Canonical.Json.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Determinism.Abstractions\StellaOps.Determinism.Abstractions.csproj" />
<ProjectReference Include="..\..\..\Concelier\__Libraries\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj" />
<ProjectReference Include="..\..\..\Concelier\__Libraries\StellaOps.Concelier.RawModels\StellaOps.Concelier.RawModels.csproj" />
<ProjectReference Include="..\..\..\Excititor\__Libraries\StellaOps.Excititor.Core\StellaOps.Excititor.Core.csproj" />