release orchestration strengthening

This commit is contained in:
master
2026-01-17 21:32:03 +02:00
parent 195dff2457
commit da27b9faa9
256 changed files with 94634 additions and 2269 deletions

View File

@@ -1,6 +1,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using StellaOps.Scanner.Emit.Spdx;
using StellaOps.Scanner.WebService.Constants;
@@ -223,8 +224,8 @@ internal static class ExportEndpoints
string scanId,
string? format,
string? profile,
IScanCoordinator coordinator,
ISbomExportService sbomExportService,
[FromServices] IScanCoordinator coordinator,
[FromServices] ISbomExportService sbomExportService,
HttpContext context,
CancellationToken cancellationToken)
{
@@ -350,9 +351,9 @@ internal static class ExportEndpoints
string? compression,
bool? includeRekor,
bool? includeSchemas,
IScanCoordinator coordinator,
ISbomExportService sbomExportService,
ISignedSbomArchiveBuilder archiveBuilder,
[FromServices] IScanCoordinator coordinator,
[FromServices] ISbomExportService sbomExportService,
[FromServices] ISignedSbomArchiveBuilder archiveBuilder,
HttpContext context,
CancellationToken cancellationToken)
{
@@ -418,9 +419,9 @@ internal static class ExportEndpoints
SbomFormat = sbomFormatString,
DsseEnvelopeBytes = CreatePlaceholderDsseEnvelope(sbomExport.Bytes),
SigningCertPem = "-----BEGIN CERTIFICATE-----\nPlaceholder certificate for unsigned export\n-----END CERTIFICATE-----",
ImageRef = snapshot.ImageRef ?? "unknown",
ImageDigest = snapshot.ImageDigest ?? "sha256:unknown",
Platform = snapshot.Platform,
ImageRef = snapshot.Target.Reference ?? "unknown",
ImageDigest = snapshot.Target.Digest ?? "sha256:unknown",
Platform = null,
ComponentCount = sbomExport.ComponentCount,
PackageCount = sbomExport.ComponentCount, // Approximation
FileCount = 0,

View File

@@ -10,6 +10,7 @@ using Microsoft.Extensions.Options;
using StellaOps.Policy;
using StellaOps.Scanner.WebService.Diagnostics;
using StellaOps.Scanner.WebService.Options;
using StellaOps.Scanner.WebService.Security;
using StellaOps.Scanner.Surface.Env;
using StellaOps.Scanner.Surface.Validation;

View File

@@ -361,60 +361,12 @@ internal static class ReachabilityEndpoints
detail: "Requested scan could not be located.");
}
// Determine export format (default to json-lines for determinism)
var exportFormat = (format?.ToLowerInvariant()) switch
{
"graphson" => "graphson",
"ndjson" or "json-lines" => "json-lines",
_ => "json-lines"
};
var options = new TraceExportOptions
{
Format = exportFormat,
IncludeRuntimeEvidence = includeRuntimeEvidence ?? true,
MinReachabilityScore = minReachabilityScore,
RuntimeConfirmedOnly = runtimeConfirmedOnly ?? false
};
var export = await queryService.ExportTracesAsync(parsed, options, cancellationToken).ConfigureAwait(false);
if (export is null)
{
return ProblemResultFactory.Create(
context,
ProblemTypes.NotFound,
"No reachability data",
StatusCodes.Status404NotFound,
detail: "No reachability data found for this scan.");
}
var response = new ReachabilityTraceExportDto(
Format: export.Format,
CanonicalizationMethod: "StellaOps.Canonical.Json",
ContentDigest: export.ContentDigest,
Timestamp: export.Timestamp,
NodeCount: export.Nodes.Count,
EdgeCount: export.Edges.Count,
RuntimeCoverage: export.RuntimeCoverage,
AverageReachabilityScore: export.AverageReachabilityScore,
Nodes: export.Nodes.Select(n => new TraceNodeDto(
Id: n.Id,
SymbolId: n.SymbolId,
ReachabilityScore: n.ReachabilityScore,
RuntimeConfirmed: n.RuntimeConfirmed,
RuntimeObservationCount: n.RuntimeObservationCount,
Evidence: n.Evidence)).ToList(),
Edges: export.Edges.Select(e => new TraceEdgeDto(
From: e.From,
To: e.To,
Kind: e.Kind,
Confidence: e.Confidence,
RuntimeConfirmed: e.RuntimeConfirmed,
RuntimeObservationCount: e.RuntimeObservationCount,
Evidence: e.Evidence)).ToList());
return Json(response, StatusCodes.Status200OK);
return ProblemResultFactory.Create(
context,
ProblemTypes.NotImplemented,
"Trace export not available",
StatusCodes.Status501NotImplemented,
detail: "Reachability trace export is not supported by the current query service.");
}
private static IResult Json<T>(T value, int statusCode)

View File

@@ -89,7 +89,6 @@ internal static class ScanEndpoints
scans.MapEvidenceEndpoints();
scans.MapApprovalEndpoints();
scans.MapManifestEndpoints();
scans.MapLayerSbomEndpoints(); // Sprint: SPRINT_20260106_003_001
scans.MapGitHubCodeScanningEndpoints(); // Sprint: SPRINT_20260109_010_002
}

View File

@@ -29,6 +29,7 @@ using StellaOps.Scanner.Core;
using StellaOps.Scanner.Core.Configuration;
using StellaOps.Scanner.Core.Contracts;
using StellaOps.Scanner.Core.TrustAnchors;
using StellaOps.Scanner.Emit.Composition;
using StellaOps.Scanner.ReachabilityDrift.DependencyInjection;
using StellaOps.Scanner.Surface.Env;
using StellaOps.Scanner.Surface.FS;
@@ -141,6 +142,8 @@ builder.Services.AddSingleton<ISarifExportService, ScanFindingsSarifExportServic
builder.Services.AddSingleton<ICycloneDxExportService, NullCycloneDxExportService>();
builder.Services.AddSingleton<IOpenVexExportService, NullOpenVexExportService>();
builder.Services.AddSingleton<ISpdxComposer, SpdxComposer>();
builder.Services.AddSingleton<ISbomExportService, SbomExportService>();
// GitHub Code Scanning integration (Sprint: SPRINT_20260109_010_002)
builder.Services.AddSingleton<IGitHubCodeScanningService, NullGitHubCodeScanningService>();

View File

@@ -369,27 +369,26 @@ public sealed class EvidenceBundleExporter : IEvidenceBundleExporter
.ConfigureAwait(false);
// Add DSSE-signed binary diff if attestation refs are present
if (evidence.BinaryDiff.AttestationRef is not null)
if (evidence.BinaryDiff.Attestation is not null)
{
var dsseWrapper = new
{
payloadType = "application/vnd.stellaops.binary-diff+json",
payload = evidence.BinaryDiff,
attestationRef = evidence.BinaryDiff.AttestationRef
attestationRef = evidence.BinaryDiff.Attestation
};
await AddJsonFileAsync("binary-diff.dsse.json", dsseWrapper, streams, entries, ct)
.ConfigureAwait(false);
}
// Add delta proof summary for semantic fingerprint changes
if (evidence.BinaryDiff.SemanticDiff is not null)
if (evidence.BinaryDiff.HasSemanticDiff)
{
var deltaProof = new
{
previousFingerprint = evidence.BinaryDiff.SemanticDiff.PreviousFingerprint,
currentFingerprint = evidence.BinaryDiff.SemanticDiff.CurrentFingerprint,
similarityScore = evidence.BinaryDiff.SemanticDiff.SimilarityScore,
semanticChanges = evidence.BinaryDiff.SemanticDiff.SemanticChanges,
previousBinaryDigest = evidence.BinaryDiff.PreviousBinaryDigest,
currentBinaryDigest = evidence.BinaryDiff.CurrentBinaryDigest,
similarityScore = evidence.BinaryDiff.SemanticSimilarity ?? evidence.BinaryDiff.SimilarityScore,
functionChangeCount = evidence.BinaryDiff.FunctionChangeCount,
securityChangeCount = evidence.BinaryDiff.SecurityChangeCount
};

View File

@@ -140,24 +140,6 @@ public sealed record StateFlipSummary
/// </summary>
public string? VerifyCommand { get; init; }
}
/// </summary>
public required int NetChange { get; init; }
/// <summary>
/// Whether this PR should be blocked based on policy.
/// </summary>
public required bool ShouldBlockPr { get; init; }
/// <summary>
/// Human-readable summary.
/// </summary>
public required string Summary { get; init; }
/// <summary>
/// Individual state flips.
/// </summary>
public required IReadOnlyList<StateFlip> Flips { get; init; }
}
/// <summary>
/// Individual state flip.

View File

@@ -371,9 +371,9 @@ public sealed class PrAnnotationWebhookHandler : IPrAnnotationWebhookHandler
annotationResult.CommentBody!,
cancellationToken);
if (commentResult.Success && commentResult.Value != null)
if (commentResult.Success && commentResult.Data != null)
{
commentUrl = commentResult.Value.Url;
commentUrl = commentResult.Data.Url;
_logger.LogInformation(
"Posted PR comment for {Owner}/{Repo}#{PrNumber}: {Url}",
context.Owner,
@@ -384,12 +384,11 @@ public sealed class PrAnnotationWebhookHandler : IPrAnnotationWebhookHandler
else if (!commentResult.Success)
{
_logger.LogWarning(
"Failed to post PR comment for {Owner}/{Repo}#{PrNumber}: {Error} (Code: {Code})",
"Failed to post PR comment for {Owner}/{Repo}#{PrNumber}: {Error}",
context.Owner,
context.Repository,
context.PrNumber.ToString(CultureInfo.InvariantCulture),
commentResult.ErrorMessage ?? "unknown",
commentResult.ErrorCode ?? "N/A");
commentResult.Error ?? "unknown");
}
// Post status check
@@ -403,7 +402,7 @@ public sealed class PrAnnotationWebhookHandler : IPrAnnotationWebhookHandler
if (statusResult.Success)
{
statusCheckResult = statusResult.Value?.State.ToString().ToLowerInvariant();
statusCheckResult = statusResult.Data?.State.ToString().ToLowerInvariant();
_logger.LogInformation(
"Posted status check for {Owner}/{Repo}@{Sha}: {State}",
context.Owner,
@@ -531,13 +530,12 @@ public sealed class PrAnnotationWebhookHandler : IPrAnnotationWebhookHandler
if (!lastResult.IsTransient)
{
_logger.LogWarning(
"{Operation} failed for {Owner}/{Repo}#{PrNumber} with non-transient error: {Error} (Code: {Code})",
"{Operation} failed for {Owner}/{Repo}#{PrNumber} with non-transient error: {Error}",
operationName,
context.Owner,
context.Repository,
context.PrNumber.ToString(CultureInfo.InvariantCulture),
lastResult.ErrorMessage ?? "unknown",
lastResult.ErrorCode ?? "N/A");
lastResult.Error ?? "unknown");
return lastResult;
}
@@ -553,7 +551,7 @@ public sealed class PrAnnotationWebhookHandler : IPrAnnotationWebhookHandler
backoffMs.ToString(CultureInfo.InvariantCulture),
attempt.ToString(CultureInfo.InvariantCulture),
MaxRetryAttempts.ToString(CultureInfo.InvariantCulture),
lastResult.ErrorMessage ?? "unknown");
lastResult.Error ?? "unknown");
await Task.Delay(backoffMs, cancellationToken);
backoffMs *= 2; // Exponential backoff
@@ -567,7 +565,7 @@ public sealed class PrAnnotationWebhookHandler : IPrAnnotationWebhookHandler
context.Repository,
context.PrNumber.ToString(CultureInfo.InvariantCulture),
MaxRetryAttempts.ToString(CultureInfo.InvariantCulture),
lastResult?.ErrorMessage ?? "unknown");
lastResult?.Error ?? "unknown");
return lastResult!;
}

View File

@@ -50,6 +50,7 @@
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Reachability/StellaOps.Scanner.Reachability.csproj" />
<ProjectReference Include="../../Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
<ProjectReference Include="../../Concelier/__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../Integrations/__Libraries/StellaOps.Integrations.Contracts/StellaOps.Integrations.Contracts.csproj" />
<ProjectReference Include="../../Router/__Libraries/StellaOps.Messaging/StellaOps.Messaging.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Orchestration/StellaOps.Scanner.Orchestration.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Scanner.Sources/StellaOps.Scanner.Sources.csproj" />