release orchestrator v1 draft and build fixes

This commit is contained in:
master
2026-01-12 12:24:17 +02:00
parent f3de858c59
commit 9873f80830
1598 changed files with 240385 additions and 5944 deletions

View File

@@ -0,0 +1,170 @@
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.Extensions.Logging;
using StellaOps.Scanner.ChangeTrace.Models;
using StellaOps.Scanner.ChangeTrace.Serialization;
namespace StellaOps.Scanner.ChangeTrace.Builder;
/// <summary>
/// Builder for constructing change traces from scan or binary comparisons.
/// </summary>
public sealed class ChangeTraceBuilder : IChangeTraceBuilder
{
private readonly ILogger<ChangeTraceBuilder> _logger;
private readonly TimeProvider _timeProvider;
/// <summary>
/// Current engine version.
/// </summary>
public static string EngineVersion { get; } = Assembly.GetExecutingAssembly()
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
?? "1.0.0";
/// <summary>
/// Initializes a new instance of the ChangeTraceBuilder.
/// </summary>
/// <param name="logger">Logger instance.</param>
/// <param name="timeProvider">Time provider for deterministic timestamps.</param>
public ChangeTraceBuilder(ILogger<ChangeTraceBuilder> logger, TimeProvider timeProvider)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
}
/// <inheritdoc />
public Task<Models.ChangeTrace> FromScanComparisonAsync(
string fromScanId,
string toScanId,
ChangeTraceBuilderOptions? options = null,
CancellationToken ct = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(fromScanId);
ArgumentException.ThrowIfNullOrWhiteSpace(toScanId);
options ??= ChangeTraceBuilderOptions.Default;
_logger.LogInformation("Building change trace from scan comparison: {FromScanId} -> {ToScanId}",
fromScanId, toScanId);
// TODO: Integrate with actual scan repository to fetch scan data
// For now, create a placeholder trace structure
var trace = BuildPlaceholderTrace(fromScanId, toScanId, options);
var finalTrace = FinalizeTrace(trace);
return Task.FromResult(finalTrace);
}
/// <inheritdoc />
public Task<Models.ChangeTrace> FromBinaryComparisonAsync(
string fromBinaryPath,
string toBinaryPath,
ChangeTraceBuilderOptions? options = null,
CancellationToken ct = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(fromBinaryPath);
ArgumentException.ThrowIfNullOrWhiteSpace(toBinaryPath);
if (!File.Exists(fromBinaryPath))
throw new FileNotFoundException("From binary not found", fromBinaryPath);
if (!File.Exists(toBinaryPath))
throw new FileNotFoundException("To binary not found", toBinaryPath);
options ??= ChangeTraceBuilderOptions.Default;
_logger.LogInformation("Building change trace from binary comparison: {FromPath} -> {ToPath}",
fromBinaryPath, toBinaryPath);
// Generate scan IDs from file paths
var fromScanId = $"binary:{Path.GetFileName(fromBinaryPath)}";
var toScanId = $"binary:{Path.GetFileName(toBinaryPath)}";
// TODO: Integrate with BinaryIndex for symbol extraction
// For now, create a placeholder trace structure
var trace = BuildPlaceholderTrace(fromScanId, toScanId, options);
var finalTrace = FinalizeTrace(trace);
return Task.FromResult(finalTrace);
}
private Models.ChangeTrace BuildPlaceholderTrace(
string fromScanId,
string toScanId,
ChangeTraceBuilderOptions options)
{
var now = _timeProvider.GetUtcNow();
var combinedScanId = $"{fromScanId}..{toScanId}";
return new Models.ChangeTrace
{
Subject = new ChangeTraceSubject
{
Type = "scan.comparison",
Digest = $"sha256:{Guid.Empty:N}",
Name = combinedScanId
},
Basis = new ChangeTraceBasis
{
ScanId = combinedScanId,
FromScanId = fromScanId,
ToScanId = toScanId,
Policies = options.Policies,
DiffMethod = options.GetDiffMethods(),
EngineVersion = EngineVersion,
AnalyzedAt = now
},
Deltas = [],
Summary = new ChangeTraceSummary
{
ChangedPackages = 0,
ChangedSymbols = 0,
ChangedBytes = 0,
RiskDelta = 0.0,
Verdict = ChangeTraceVerdict.Neutral
}
};
}
private Models.ChangeTrace FinalizeTrace(Models.ChangeTrace trace)
{
// Compute commitment hash
var hash = ChangeTraceSerializer.ComputeCommitmentHash(trace);
return trace with
{
Commitment = new ChangeTraceCommitment
{
Sha256 = hash,
Algorithm = "RFC8785+SHA256"
}
};
}
/// <summary>
/// Computes the verdict based on risk delta score.
/// </summary>
/// <param name="riskDelta">Risk delta score.</param>
/// <returns>Verdict classification.</returns>
public static ChangeTraceVerdict ComputeVerdict(double riskDelta)
{
return riskDelta switch
{
< -0.3 => ChangeTraceVerdict.RiskDown,
> 0.3 => ChangeTraceVerdict.RiskUp,
_ => ChangeTraceVerdict.Neutral
};
}
/// <summary>
/// Computes trust delta score from before/after scores.
/// Formula: (AfterTrust - BeforeTrust) / max(BeforeTrust, 0.01)
/// </summary>
/// <param name="beforeTrust">Trust score before change.</param>
/// <param name="afterTrust">Trust score after change.</param>
/// <returns>Trust delta score.</returns>
public static double ComputeTrustDelta(double beforeTrust, double afterTrust)
{
var denominator = Math.Max(beforeTrust, 0.01);
return (afterTrust - beforeTrust) / denominator;
}
}

View File

@@ -0,0 +1,65 @@
using System.Collections.Immutable;
namespace StellaOps.Scanner.ChangeTrace.Builder;
/// <summary>
/// Options for change trace building.
/// </summary>
public sealed record ChangeTraceBuilderOptions
{
/// <summary>
/// Include package-level diffing. Default: true.
/// </summary>
public bool IncludePackageDiff { get; init; } = true;
/// <summary>
/// Include symbol-level diffing. Default: true.
/// </summary>
public bool IncludeSymbolDiff { get; init; } = true;
/// <summary>
/// Include byte-level diffing. Default: false.
/// </summary>
public bool IncludeByteDiff { get; init; } = false;
/// <summary>
/// Minimum confidence threshold for symbol matches.
/// Default: 0.75.
/// </summary>
public double MinSymbolConfidence { get; init; } = 0.75;
/// <summary>
/// Rolling hash window size for byte diffing.
/// Default: 2048 bytes.
/// </summary>
public int ByteDiffWindowSize { get; init; } = 2048;
/// <summary>
/// Maximum binary size for byte-level analysis (bytes).
/// Default: 10MB.
/// </summary>
public long MaxBinarySize { get; init; } = 10 * 1024 * 1024;
/// <summary>
/// Lattice policies to apply during trust delta computation.
/// Default: ["lattice:default@v3"].
/// </summary>
public ImmutableArray<string> Policies { get; init; } = ["lattice:default@v3"];
/// <summary>
/// Gets the diff methods enabled based on options.
/// </summary>
public ImmutableArray<string> GetDiffMethods()
{
var methods = ImmutableArray.CreateBuilder<string>();
if (IncludePackageDiff) methods.Add("pkg");
if (IncludeSymbolDiff) methods.Add("symbol");
if (IncludeByteDiff) methods.Add("byte");
return methods.ToImmutable();
}
/// <summary>
/// Default options instance.
/// </summary>
public static ChangeTraceBuilderOptions Default { get; } = new();
}

View File

@@ -0,0 +1,35 @@
namespace StellaOps.Scanner.ChangeTrace.Builder;
/// <summary>
/// Builder interface for constructing change traces.
/// </summary>
public interface IChangeTraceBuilder
{
/// <summary>
/// Build change trace from two scan comparisons.
/// </summary>
/// <param name="fromScanId">Scan ID of the "before" state.</param>
/// <param name="toScanId">Scan ID of the "after" state.</param>
/// <param name="options">Builder options for configuring the trace.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Constructed change trace.</returns>
Task<Models.ChangeTrace> FromScanComparisonAsync(
string fromScanId,
string toScanId,
ChangeTraceBuilderOptions? options = null,
CancellationToken ct = default);
/// <summary>
/// Build change trace from two binary files.
/// </summary>
/// <param name="fromBinaryPath">Path to the "before" binary.</param>
/// <param name="toBinaryPath">Path to the "after" binary.</param>
/// <param name="options">Builder options for configuring the trace.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Constructed change trace.</returns>
Task<Models.ChangeTrace> FromBinaryComparisonAsync(
string fromBinaryPath,
string toBinaryPath,
ChangeTraceBuilderOptions? options = null,
CancellationToken ct = default);
}