UI work to fill SBOM sourcing management gap. UI planning remaining functionality exposure. Work on CI/Tests stabilization
Introduces CGS determinism test runs to CI workflows for Windows, macOS, Linux, Alpine, and Debian, fulfilling CGS-008 cross-platform requirements. Updates local-ci scripts to support new smoke steps, test timeouts, progress intervals, and project slicing for improved test isolation and diagnostics.
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// VerdictServiceCollectionExtensions.cs
|
||||
// Sprint: SPRINT_20251229_001_001_BE_cgs_infrastructure (CGS-007)
|
||||
// Task: Wire Fulcio keyless signing for VerdictBuilder
|
||||
// Description: DI extensions for VerdictBuilder with optional keyless signing
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Signer.Core;
|
||||
using StellaOps.Signer.Keyless;
|
||||
|
||||
namespace StellaOps.Verdict;
|
||||
|
||||
/// <summary>
|
||||
/// Dependency injection extensions for the Verdict Builder service.
|
||||
/// </summary>
|
||||
public static class VerdictServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the Verdict Builder service to the DI container.
|
||||
/// Signing mode is determined from configuration (VerdictBuilder:SigningMode).
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection</param>
|
||||
/// <param name="configuration">Configuration root</param>
|
||||
/// <returns>Service collection for chaining</returns>
|
||||
public static IServiceCollection AddVerdictBuilder(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
// Bind configuration
|
||||
services.Configure<VerdictBuilderOptions>(configuration.GetSection("VerdictBuilder"));
|
||||
|
||||
// Validate options on startup
|
||||
services.AddSingleton<IValidateOptions<VerdictBuilderOptions>, VerdictBuilderOptionsValidator>();
|
||||
|
||||
// Register VerdictBuilder with conditional signer based on mode
|
||||
services.AddSingleton<IVerdictBuilder>(sp =>
|
||||
{
|
||||
var options = sp.GetRequiredService<IOptions<VerdictBuilderOptions>>().Value;
|
||||
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<VerdictBuilderService>>();
|
||||
|
||||
IDsseSigner? signer = null;
|
||||
|
||||
if (options.SigningMode == VerdictSigningMode.Keyless)
|
||||
{
|
||||
logger.LogInformation(
|
||||
"VerdictBuilder configured for Keyless signing (Fulcio: {FulcioUrl}, OIDC: {OidcIssuer})",
|
||||
options.FulcioUrl,
|
||||
options.OidcIssuerUrl);
|
||||
|
||||
signer = sp.GetRequiredService<KeylessDsseSigner>();
|
||||
}
|
||||
else if (options.SigningMode == VerdictSigningMode.AirGap)
|
||||
{
|
||||
logger.LogInformation("VerdictBuilder configured for AirGap mode (unsigned verdicts)");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("VerdictBuilder SigningMode={Mode} not supported, falling back to AirGap", options.SigningMode);
|
||||
}
|
||||
|
||||
return new VerdictBuilderService(logger, signer);
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Verdict Builder service with keyless signing support.
|
||||
/// Requires Fulcio and OIDC configuration.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection</param>
|
||||
/// <param name="configuration">Configuration root</param>
|
||||
/// <returns>Service collection for chaining</returns>
|
||||
public static IServiceCollection AddVerdictBuilderWithKeylessSigning(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
// Add keyless signing infrastructure
|
||||
services.AddKeylessSigning(configuration);
|
||||
|
||||
// Add verdict builder
|
||||
services.AddVerdictBuilder(configuration);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Verdict Builder service for air-gapped deployments (no signing).
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection</param>
|
||||
/// <returns>Service collection for chaining</returns>
|
||||
public static IServiceCollection AddVerdictBuilderAirGap(this IServiceCollection services)
|
||||
{
|
||||
// Configure for air-gap mode
|
||||
services.Configure<VerdictBuilderOptions>(options =>
|
||||
{
|
||||
options.SigningMode = VerdictSigningMode.AirGap;
|
||||
});
|
||||
|
||||
// Register VerdictBuilder without signer
|
||||
services.AddSingleton<IVerdictBuilder>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<VerdictBuilderService>>();
|
||||
return new VerdictBuilderService(logger, signer: null);
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds keyless signing infrastructure (Fulcio client, OIDC token provider).
|
||||
/// </summary>
|
||||
private static IServiceCollection AddKeylessSigning(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
// Use existing Signer.Keyless infrastructure
|
||||
services.AddKeylessSigning(configuration.GetSection("VerdictBuilder:Keyless"));
|
||||
|
||||
// Register OIDC token provider for ambient tokens (CI runners, workload identity)
|
||||
// This is environment-specific and needs to be configured based on deployment
|
||||
services.AddSingleton<IOidcTokenProvider>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<AmbientOidcTokenProvider>>();
|
||||
|
||||
// Check for Gitea/GitHub Actions token
|
||||
var giteaToken = Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN");
|
||||
var githubToken = Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN");
|
||||
|
||||
// Check for Google Cloud workload identity
|
||||
var googleToken = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS");
|
||||
|
||||
// Default to file-based ambient token
|
||||
var tokenPath = Environment.GetEnvironmentVariable("OIDC_TOKEN_FILE")
|
||||
?? "/var/run/sigstore/cosign/oidc-token";
|
||||
|
||||
var config = new OidcAmbientConfig
|
||||
{
|
||||
Issuer = configuration["VerdictBuilder:OidcIssuerUrl"] ?? "https://token.actions.githubusercontent.com",
|
||||
TokenPath = tokenPath,
|
||||
WatchForChanges = true
|
||||
};
|
||||
|
||||
return new AmbientOidcTokenProvider(config, logger);
|
||||
});
|
||||
|
||||
// Register KeylessDsseSigner
|
||||
services.AddSingleton<KeylessDsseSigner>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates VerdictBuilderOptions on startup.
|
||||
/// </summary>
|
||||
internal sealed class VerdictBuilderOptionsValidator : IValidateOptions<VerdictBuilderOptions>
|
||||
{
|
||||
public ValidateOptionsResult Validate(string? name, VerdictBuilderOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
options.Validate();
|
||||
return ValidateOptionsResult.Success;
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return ValidateOptionsResult.Fail(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user