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,208 @@
// -----------------------------------------------------------------------------
// ChangeTraceAttestationService.cs
// Sprint: SPRINT_20260112_200_005_ATTEST_predicate
// Description: Service for generating change trace DSSE attestations.
// -----------------------------------------------------------------------------
using System.Collections.Immutable;
using StellaOps.Attestor.ProofChain.Predicates;
using StellaOps.Attestor.ProofChain.Signing;
using StellaOps.Attestor.ProofChain.Statements;
using StellaOps.Scanner.ChangeTrace.Models;
using ChangeTraceModel = StellaOps.Scanner.ChangeTrace.Models.ChangeTrace;
using DsseEnvelope = StellaOps.Attestor.ProofChain.Signing.DsseEnvelope;
namespace StellaOps.Attestor.ProofChain.ChangeTrace;
/// <summary>
/// Service for generating change trace DSSE attestations.
/// </summary>
public sealed class ChangeTraceAttestationService : IChangeTraceAttestationService
{
private readonly IProofChainSigner _signer;
private readonly TimeProvider _timeProvider;
/// <summary>
/// Create a new change trace attestation service.
/// </summary>
/// <param name="signer">Proof chain signer for envelope generation.</param>
/// <param name="timeProvider">Time provider for timestamp generation.</param>
public ChangeTraceAttestationService(
IProofChainSigner signer,
TimeProvider timeProvider)
{
_signer = signer ?? throw new ArgumentNullException(nameof(signer));
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
}
/// <inheritdoc />
public async Task<DsseEnvelope> GenerateAttestationAsync(
ChangeTraceModel trace,
ChangeTraceAttestationOptions? options = null,
CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(trace);
options ??= ChangeTraceAttestationOptions.Default;
var predicate = MapToPredicate(trace, options);
var statement = CreateStatement(trace, predicate);
return await _signer.SignStatementAsync(
statement,
SigningKeyProfile.Evidence,
ct).ConfigureAwait(false);
}
/// <summary>
/// Map a change trace model to its attestation predicate.
/// </summary>
private ChangeTracePredicate MapToPredicate(
ChangeTraceModel trace,
ChangeTraceAttestationOptions options)
{
var deltas = trace.Deltas
.Take(options.MaxDeltas)
.Select(d => new ChangeTraceDeltaEntry
{
Purl = d.Purl,
FromVersion = d.FromVersion,
ToVersion = d.ToVersion,
ChangeType = d.ChangeType.ToString(),
Explain = d.Explain.ToString(),
SymbolsChanged = d.Evidence.SymbolsChanged,
BytesChanged = d.Evidence.BytesChanged,
Confidence = d.Evidence.Confidence,
TrustDeltaScore = d.TrustDelta?.Score ?? 0,
CveIds = d.Evidence.CveIds,
Functions = d.Evidence.Functions
})
.ToImmutableArray();
var proofSteps = trace.Deltas
.Where(d => d.TrustDelta is not null)
.SelectMany(d => d.TrustDelta!.ProofSteps)
.Distinct()
.Take(options.MaxProofSteps)
.ToImmutableArray();
var aggregateReachability = AggregateReachabilityImpact(trace.Deltas);
var aggregateExploitability = DetermineExploitabilityFromScore(trace.Summary.RiskDelta);
return new ChangeTracePredicate
{
FromDigest = trace.Basis.FromScanId ?? trace.Subject.Digest,
ToDigest = trace.Basis.ToScanId ?? trace.Subject.Digest,
TenantId = options.TenantId,
Deltas = deltas,
Summary = new ChangeTracePredicateSummary
{
ChangedPackages = trace.Summary.ChangedPackages,
ChangedSymbols = trace.Summary.ChangedSymbols,
ChangedBytes = trace.Summary.ChangedBytes,
RiskDelta = trace.Summary.RiskDelta,
Verdict = trace.Summary.Verdict.ToString().ToLowerInvariant()
},
TrustDelta = new TrustDeltaRecord
{
Score = trace.Summary.RiskDelta,
BeforeScore = trace.Summary.BeforeRiskScore,
AfterScore = trace.Summary.AfterRiskScore,
ReachabilityImpact = aggregateReachability.ToString().ToLowerInvariant(),
ExploitabilityImpact = aggregateExploitability.ToString().ToLowerInvariant()
},
ProofSteps = proofSteps,
DiffMethods = trace.Basis.DiffMethod,
Policies = trace.Basis.Policies,
AnalyzedAt = trace.Basis.AnalyzedAt,
AlgorithmVersion = trace.Basis.EngineVersion,
CommitmentHash = trace.Commitment?.Sha256
};
}
/// <summary>
/// Create an in-toto statement from the change trace and predicate.
/// </summary>
private ChangeTraceStatement CreateStatement(
ChangeTraceModel trace,
ChangeTracePredicate predicate)
{
var subjectName = trace.Subject.Purl ?? trace.Subject.Name ?? trace.Subject.Digest;
var digest = ParseDigest(trace.Subject.Digest);
return new ChangeTraceStatement
{
Subject =
[
new Subject
{
Name = subjectName,
Digest = digest
}
],
Predicate = predicate
};
}
/// <summary>
/// Parse a digest string into a dictionary of algorithm:value pairs.
/// </summary>
private static IReadOnlyDictionary<string, string> ParseDigest(string digestString)
{
var result = new Dictionary<string, string>(StringComparer.Ordinal);
if (string.IsNullOrEmpty(digestString))
{
return result;
}
// Handle "algorithm:value" format
var colonIndex = digestString.IndexOf(':', StringComparison.Ordinal);
if (colonIndex > 0)
{
var algorithm = digestString[..colonIndex];
var value = digestString[(colonIndex + 1)..];
result[algorithm] = value;
}
else
{
// Assume SHA-256 if no algorithm prefix
result["sha256"] = digestString;
}
return result;
}
/// <summary>
/// Aggregate reachability impact from multiple deltas.
/// </summary>
private static ReachabilityImpact AggregateReachabilityImpact(
ImmutableArray<PackageDelta> deltas)
{
// Priority: Introduced > Increased > Reduced > Eliminated > Unchanged
if (deltas.Any(d => d.TrustDelta?.ReachabilityImpact == ReachabilityImpact.Introduced))
return ReachabilityImpact.Introduced;
if (deltas.Any(d => d.TrustDelta?.ReachabilityImpact == ReachabilityImpact.Increased))
return ReachabilityImpact.Increased;
if (deltas.Any(d => d.TrustDelta?.ReachabilityImpact == ReachabilityImpact.Reduced))
return ReachabilityImpact.Reduced;
if (deltas.Any(d => d.TrustDelta?.ReachabilityImpact == ReachabilityImpact.Eliminated))
return ReachabilityImpact.Eliminated;
return ReachabilityImpact.Unchanged;
}
/// <summary>
/// Determine exploitability impact from overall risk delta score.
/// </summary>
private static ExploitabilityImpact DetermineExploitabilityFromScore(double riskDelta)
{
return riskDelta switch
{
<= -0.5 => ExploitabilityImpact.Eliminated,
< -0.3 => ExploitabilityImpact.Down,
>= 0.5 => ExploitabilityImpact.Introduced,
> 0.3 => ExploitabilityImpact.Up,
_ => ExploitabilityImpact.Unchanged
};
}
}

View File

@@ -0,0 +1,58 @@
// -----------------------------------------------------------------------------
// IChangeTraceAttestationService.cs
// Sprint: SPRINT_20260112_200_005_ATTEST_predicate
// Description: Interface for generating change trace attestations.
// -----------------------------------------------------------------------------
using StellaOps.Attestor.ProofChain.Signing;
namespace StellaOps.Attestor.ProofChain.ChangeTrace;
/// <summary>
/// Service for generating change trace DSSE attestations.
/// </summary>
public interface IChangeTraceAttestationService
{
/// <summary>
/// Generate DSSE envelope for a change trace.
/// </summary>
/// <param name="trace">The change trace to attest.</param>
/// <param name="options">Optional attestation options.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>DSSE envelope containing the change trace attestation.</returns>
Task<DsseEnvelope> GenerateAttestationAsync(
Scanner.ChangeTrace.Models.ChangeTrace trace,
ChangeTraceAttestationOptions? options = null,
CancellationToken ct = default);
}
/// <summary>
/// Options for change trace attestation generation.
/// </summary>
public sealed record ChangeTraceAttestationOptions
{
/// <summary>
/// Default attestation options.
/// </summary>
public static readonly ChangeTraceAttestationOptions Default = new();
/// <summary>
/// Tenant ID for multi-tenant isolation.
/// </summary>
public string TenantId { get; init; } = "default";
/// <summary>
/// Whether to include raw trace in attestation metadata.
/// </summary>
public bool IncludeRawTrace { get; init; }
/// <summary>
/// Maximum number of proof steps to include.
/// </summary>
public int MaxProofSteps { get; init; } = 50;
/// <summary>
/// Maximum number of deltas to include in the predicate.
/// </summary>
public int MaxDeltas { get; init; } = 1000;
}

View File

@@ -0,0 +1,238 @@
// -----------------------------------------------------------------------------
// ChangeTracePredicate.cs
// Sprint: SPRINT_20260112_200_005_ATTEST_predicate
// Description: DSSE predicate for change trace attestations.
// -----------------------------------------------------------------------------
using System.Collections.Immutable;
using System.Text.Json.Serialization;
namespace StellaOps.Attestor.ProofChain.Predicates;
/// <summary>
/// DSSE predicate for change trace attestations.
/// predicateType: stella.ops/changetrace@v1
/// </summary>
public sealed record ChangeTracePredicate
{
/// <summary>
/// Predicate type URI for change trace attestations.
/// </summary>
public const string PredicateTypeUri = "stella.ops/changetrace@v1";
/// <summary>
/// Digest of the "from" artifact.
/// </summary>
[JsonPropertyName("fromDigest")]
public required string FromDigest { get; init; }
/// <summary>
/// Digest of the "to" artifact.
/// </summary>
[JsonPropertyName("toDigest")]
public required string ToDigest { get; init; }
/// <summary>
/// Tenant ID for multi-tenant isolation.
/// </summary>
[JsonPropertyName("tenantId")]
public required string TenantId { get; init; }
/// <summary>
/// Package-level deltas.
/// </summary>
[JsonPropertyName("deltas")]
public ImmutableArray<ChangeTraceDeltaEntry> Deltas { get; init; } = [];
/// <summary>
/// Summary of all changes.
/// </summary>
[JsonPropertyName("summary")]
public required ChangeTracePredicateSummary Summary { get; init; }
/// <summary>
/// Trust delta with proof steps.
/// </summary>
[JsonPropertyName("trustDelta")]
public required TrustDeltaRecord TrustDelta { get; init; }
/// <summary>
/// Human-readable proof steps explaining the verdict.
/// </summary>
[JsonPropertyName("proofSteps")]
public ImmutableArray<string> ProofSteps { get; init; } = [];
/// <summary>
/// Diff methods used (pkg, symbol, byte).
/// </summary>
[JsonPropertyName("diffMethods")]
public ImmutableArray<string> DiffMethods { get; init; } = [];
/// <summary>
/// Lattice policies applied.
/// </summary>
[JsonPropertyName("policies")]
public ImmutableArray<string> Policies { get; init; } = [];
/// <summary>
/// When the analysis was performed.
/// </summary>
[JsonPropertyName("analyzedAt")]
public required DateTimeOffset AnalyzedAt { get; init; }
/// <summary>
/// Algorithm/engine version for reproducibility.
/// </summary>
[JsonPropertyName("algorithmVersion")]
public string AlgorithmVersion { get; init; } = "1.0";
/// <summary>
/// Commitment hash for deterministic verification.
/// </summary>
[JsonPropertyName("commitmentHash")]
public string? CommitmentHash { get; init; }
}
/// <summary>
/// Delta entry within the change trace predicate.
/// </summary>
public sealed record ChangeTraceDeltaEntry
{
/// <summary>
/// Package URL (PURL) of the changed package.
/// </summary>
[JsonPropertyName("purl")]
public required string Purl { get; init; }
/// <summary>
/// Version before the change.
/// </summary>
[JsonPropertyName("fromVersion")]
public required string FromVersion { get; init; }
/// <summary>
/// Version after the change.
/// </summary>
[JsonPropertyName("toVersion")]
public required string ToVersion { get; init; }
/// <summary>
/// Type of change (Added, Removed, Modified, Upgraded, Downgraded, Rebuilt).
/// </summary>
[JsonPropertyName("changeType")]
public required string ChangeType { get; init; }
/// <summary>
/// Explanation of the change reason.
/// </summary>
[JsonPropertyName("explain")]
public required string Explain { get; init; }
/// <summary>
/// Number of symbols changed in this package.
/// </summary>
[JsonPropertyName("symbolsChanged")]
public int SymbolsChanged { get; init; }
/// <summary>
/// Total bytes changed in this package.
/// </summary>
[JsonPropertyName("bytesChanged")]
public long BytesChanged { get; init; }
/// <summary>
/// Confidence score for the change classification (0.0-1.0).
/// </summary>
[JsonPropertyName("confidence")]
public double Confidence { get; init; }
/// <summary>
/// Trust delta score for this specific package change.
/// </summary>
[JsonPropertyName("trustDeltaScore")]
public double TrustDeltaScore { get; init; }
/// <summary>
/// CVE identifiers addressed by this change.
/// </summary>
[JsonPropertyName("cveIds")]
public ImmutableArray<string> CveIds { get; init; } = [];
/// <summary>
/// Function names affected by this change.
/// </summary>
[JsonPropertyName("functions")]
public ImmutableArray<string> Functions { get; init; } = [];
}
/// <summary>
/// Summary within the change trace predicate.
/// </summary>
public sealed record ChangeTracePredicateSummary
{
/// <summary>
/// Total number of packages with changes.
/// </summary>
[JsonPropertyName("changedPackages")]
public required int ChangedPackages { get; init; }
/// <summary>
/// Total number of symbols with changes.
/// </summary>
[JsonPropertyName("changedSymbols")]
public required int ChangedSymbols { get; init; }
/// <summary>
/// Total bytes changed across all packages.
/// </summary>
[JsonPropertyName("changedBytes")]
public required long ChangedBytes { get; init; }
/// <summary>
/// Aggregated risk delta score.
/// </summary>
[JsonPropertyName("riskDelta")]
public required double RiskDelta { get; init; }
/// <summary>
/// Overall verdict (risk_down, neutral, risk_up, inconclusive).
/// </summary>
[JsonPropertyName("verdict")]
public required string Verdict { get; init; }
}
/// <summary>
/// Trust delta record within the predicate.
/// </summary>
public sealed record TrustDeltaRecord
{
/// <summary>
/// Overall trust delta score (-1.0 to +1.0).
/// </summary>
[JsonPropertyName("score")]
public required double Score { get; init; }
/// <summary>
/// Trust score before the change.
/// </summary>
[JsonPropertyName("beforeScore")]
public double? BeforeScore { get; init; }
/// <summary>
/// Trust score after the change.
/// </summary>
[JsonPropertyName("afterScore")]
public double? AfterScore { get; init; }
/// <summary>
/// Impact on code reachability (unchanged, reduced, increased, eliminated, introduced).
/// </summary>
[JsonPropertyName("reachabilityImpact")]
public required string ReachabilityImpact { get; init; }
/// <summary>
/// Impact on exploitability (unchanged, down, up, eliminated, introduced).
/// </summary>
[JsonPropertyName("exploitabilityImpact")]
public required string ExploitabilityImpact { get; init; }
}

View File

@@ -0,0 +1,27 @@
// -----------------------------------------------------------------------------
// ChangeTraceStatement.cs
// Sprint: SPRINT_20260112_200_005_ATTEST_predicate
// Description: In-toto statement for change trace attestations.
// -----------------------------------------------------------------------------
using System.Text.Json.Serialization;
using StellaOps.Attestor.ProofChain.Predicates;
namespace StellaOps.Attestor.ProofChain.Statements;
/// <summary>
/// In-toto statement for change trace attestations.
/// Predicate type: stella.ops/changetrace@v1
/// </summary>
public sealed record ChangeTraceStatement : InTotoStatement
{
/// <inheritdoc />
[JsonPropertyName("predicateType")]
public override string PredicateType => ChangeTracePredicate.PredicateTypeUri;
/// <summary>
/// The change trace predicate payload.
/// </summary>
[JsonPropertyName("predicate")]
public required ChangeTracePredicate Predicate { get; init; }
}

View File

@@ -17,6 +17,7 @@
<ProjectReference Include="..\..\..\Feedser\StellaOps.Feedser.Core\StellaOps.Feedser.Core.csproj" />
<ProjectReference Include="..\..\..\Feedser\StellaOps.Feedser.BinaryAnalysis\StellaOps.Feedser.BinaryAnalysis.csproj" />
<ProjectReference Include="..\..\..\Concelier\__Libraries\StellaOps.Concelier.SourceIntel\StellaOps.Concelier.SourceIntel.csproj" />
<ProjectReference Include="..\..\..\Scanner\__Libraries\StellaOps.Scanner.ChangeTrace\StellaOps.Scanner.ChangeTrace.csproj" />
</ItemGroup>
</Project>