up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-26 07:47:08 +02:00
parent 56e2f64d07
commit 1c782897f7
184 changed files with 8991 additions and 649 deletions

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Replay.Core;
using StellaOps.Scanner.WebService.Domain;
using StellaOps.Scanner.WebService.Services;
namespace StellaOps.Scanner.WebService.Replay;
internal interface IRecordModeService
{
Task<(ReplayRunRecord Run, IReadOnlyList<ReplayBundleRecord> Bundles)> BuildAsync(
string scanId,
ReplayManifest manifest,
ReplayBundleWriteResult inputBundle,
ReplayBundleWriteResult outputBundle,
string sbomDigest,
string findingsDigest,
string? vexDigest = null,
string? logDigest = null,
IEnumerable<(ReplayBundleWriteResult Result, string Type)>? additionalBundles = null);
Task<ReplayArtifacts?> AttachAsync(
ScanId scanId,
ReplayManifest manifest,
ReplayBundleWriteResult inputBundle,
ReplayBundleWriteResult outputBundle,
string sbomDigest,
string findingsDigest,
IScanCoordinator coordinator,
string? vexDigest = null,
string? logDigest = null,
IEnumerable<(ReplayBundleWriteResult Result, string Type)>? additionalBundles = null,
CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Replay.Core;
using StellaOps.Scanner.Core.Replay;
using StellaOps.Scanner.WebService.Domain;
using StellaOps.Scanner.WebService.Services;
namespace StellaOps.Scanner.WebService.Replay;
/// <summary>
/// Prepares replay run metadata from WebService scan results. This is a thin façade that will be invoked
/// once record-mode wiring lands in the scan pipeline.
/// </summary>
internal sealed class RecordModeService : IRecordModeService
{
private readonly RecordModeAssembler _assembler;
public RecordModeService(TimeProvider? timeProvider = null)
{
_assembler = new RecordModeAssembler(timeProvider);
}
public Task<(ReplayRunRecord Run, IReadOnlyList<ReplayBundleRecord> Bundles)> BuildAsync(
string scanId,
ReplayManifest manifest,
ReplayBundleWriteResult inputBundle,
ReplayBundleWriteResult outputBundle,
string sbomDigest,
string findingsDigest,
string? vexDigest = null,
string? logDigest = null,
IEnumerable<(ReplayBundleWriteResult Result, string Type)>? additionalBundles = null)
{
ArgumentNullException.ThrowIfNull(manifest);
var run = _assembler.BuildRun(scanId, manifest, sbomDigest, findingsDigest, vexDigest, logDigest);
var bundles = _assembler.BuildBundles(inputBundle, outputBundle, additionalBundles);
return Task.FromResult((run, bundles));
}
public async Task<ReplayArtifacts?> AttachAsync(
ScanId scanId,
ReplayManifest manifest,
ReplayBundleWriteResult inputBundle,
ReplayBundleWriteResult outputBundle,
string sbomDigest,
string findingsDigest,
IScanCoordinator coordinator,
string? vexDigest = null,
string? logDigest = null,
IEnumerable<(ReplayBundleWriteResult Result, string Type)>? additionalBundles = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(coordinator);
var (run, bundles) = await BuildAsync(
scanId.Value,
manifest,
inputBundle,
outputBundle,
sbomDigest,
findingsDigest,
vexDigest,
logDigest,
additionalBundles).ConfigureAwait(false);
var replay = BuildArtifacts(run.ManifestHash, bundles);
var attached = await coordinator.AttachReplayAsync(scanId, replay, cancellationToken).ConfigureAwait(false);
return attached ? replay : null;
}
private static ReplayArtifacts BuildArtifacts(string manifestHash, IReadOnlyList<ReplayBundleRecord> bundles)
{
ArgumentException.ThrowIfNullOrWhiteSpace(manifestHash);
ArgumentNullException.ThrowIfNull(bundles);
var summaries = bundles
.Select(bundle => new ReplayBundleSummary(
bundle.Type,
NormalizeDigest(bundle.Id),
bundle.Location,
bundle.Size))
.ToList();
return new ReplayArtifacts(manifestHash, summaries);
}
private static string NormalizeDigest(string digest)
{
if (string.IsNullOrWhiteSpace(digest))
{
return string.Empty;
}
var trimmed = digest.Trim().ToLowerInvariant();
return trimmed.StartsWith("sha256:", StringComparison.Ordinal)
? trimmed
: $"sha256:{trimmed}";
}
}