Implement MongoDB-based storage for Pack Run approval, artifact, log, and state management
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Added MongoPackRunApprovalStore for managing approval states with MongoDB. - Introduced MongoPackRunArtifactUploader for uploading and storing artifacts. - Created MongoPackRunLogStore to handle logging of pack run events. - Developed MongoPackRunStateStore for persisting and retrieving pack run states. - Implemented unit tests for MongoDB stores to ensure correct functionality. - Added MongoTaskRunnerTestContext for setting up MongoDB test environment. - Enhanced PackRunStateFactory to correctly initialize state with gate reasons.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Scanner.Surface.Env;
|
||||
@@ -28,6 +29,13 @@ internal sealed class ScannerSurfaceSecretConfigurator : IConfigureOptions<Scann
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
var tenant = _surfaceEnvironment.Settings.Secrets.Tenant;
|
||||
ApplyCasAccessSecret(options, tenant);
|
||||
ApplyRegistrySecret(options, tenant);
|
||||
ApplyAttestationSecret(options, tenant);
|
||||
}
|
||||
|
||||
private void ApplyCasAccessSecret(ScannerWebServiceOptions options, string tenant)
|
||||
{
|
||||
var request = new SurfaceSecretRequest(
|
||||
Tenant: tenant,
|
||||
Component: ComponentName,
|
||||
@@ -56,6 +64,120 @@ internal sealed class ScannerSurfaceSecretConfigurator : IConfigureOptions<Scann
|
||||
ApplySecret(options.ArtifactStore ??= new ScannerWebServiceOptions.ArtifactStoreOptions(), secret);
|
||||
}
|
||||
|
||||
private void ApplyRegistrySecret(ScannerWebServiceOptions options, string tenant)
|
||||
{
|
||||
var request = new SurfaceSecretRequest(
|
||||
Tenant: tenant,
|
||||
Component: ComponentName,
|
||||
SecretType: "registry");
|
||||
|
||||
RegistryAccessSecret? secret = null;
|
||||
try
|
||||
{
|
||||
using var handle = _secretProvider.GetAsync(request).AsTask().GetAwaiter().GetResult();
|
||||
secret = SurfaceSecretParser.ParseRegistryAccessSecret(handle);
|
||||
}
|
||||
catch (SurfaceSecretNotFoundException)
|
||||
{
|
||||
_logger.LogDebug("Surface secret 'registry' not found for {Component}; leaving registry credentials unchanged.", ComponentName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to resolve surface secret 'registry' for {Component}.", ComponentName);
|
||||
}
|
||||
|
||||
if (secret is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
options.Registry ??= new ScannerWebServiceOptions.RegistryOptions();
|
||||
options.Registry.DefaultRegistry = secret.DefaultRegistry;
|
||||
options.Registry.Credentials = new List<ScannerWebServiceOptions.RegistryCredentialOptions>();
|
||||
|
||||
foreach (var entry in secret.Entries)
|
||||
{
|
||||
var credential = new ScannerWebServiceOptions.RegistryCredentialOptions
|
||||
{
|
||||
Registry = entry.Registry,
|
||||
Username = entry.Username,
|
||||
Password = entry.Password,
|
||||
IdentityToken = entry.IdentityToken,
|
||||
RegistryToken = entry.RegistryToken,
|
||||
RefreshToken = entry.RefreshToken,
|
||||
ExpiresAt = entry.ExpiresAt,
|
||||
AllowInsecureTls = entry.AllowInsecureTls,
|
||||
Email = entry.Email
|
||||
};
|
||||
|
||||
if (entry.Scopes.Count > 0)
|
||||
{
|
||||
credential.Scopes = new List<string>(entry.Scopes);
|
||||
}
|
||||
|
||||
if (entry.Headers.Count > 0)
|
||||
{
|
||||
foreach (var (key, value) in entry.Headers)
|
||||
{
|
||||
credential.Headers[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
options.Registry.Credentials.Add(credential);
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"Surface secret 'registry' applied for {Component} (default: {DefaultRegistry}, entries: {Count}).",
|
||||
ComponentName,
|
||||
options.Registry.DefaultRegistry ?? "(none)",
|
||||
options.Registry.Credentials.Count);
|
||||
}
|
||||
|
||||
private void ApplyAttestationSecret(ScannerWebServiceOptions options, string tenant)
|
||||
{
|
||||
var request = new SurfaceSecretRequest(
|
||||
Tenant: tenant,
|
||||
Component: ComponentName,
|
||||
SecretType: "attestation");
|
||||
|
||||
AttestationSecret? secret = null;
|
||||
try
|
||||
{
|
||||
using var handle = _secretProvider.GetAsync(request).AsTask().GetAwaiter().GetResult();
|
||||
secret = SurfaceSecretParser.ParseAttestationSecret(handle);
|
||||
}
|
||||
catch (SurfaceSecretNotFoundException)
|
||||
{
|
||||
_logger.LogDebug("Surface secret 'attestation' not found for {Component}; retaining signing configuration.", ComponentName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to resolve surface secret 'attestation' for {Component}.", ComponentName);
|
||||
}
|
||||
|
||||
if (secret is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
options.Signing ??= new ScannerWebServiceOptions.SigningOptions();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(secret.KeyPem))
|
||||
{
|
||||
options.Signing.KeyPem = secret.KeyPem;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(secret.CertificatePem))
|
||||
{
|
||||
options.Signing.CertificatePem = secret.CertificatePem;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(secret.CertificateChainPem))
|
||||
{
|
||||
options.Signing.CertificateChainPem = secret.CertificateChainPem;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySecret(ScannerWebServiceOptions.ArtifactStoreOptions artifactStore, CasAccessSecret secret)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(secret.Driver))
|
||||
|
||||
@@ -26,10 +26,15 @@ public sealed class ScannerWebServiceOptions
|
||||
/// </summary>
|
||||
public QueueOptions Queue { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Object store configuration for SBOM artefacts.
|
||||
/// </summary>
|
||||
public ArtifactStoreOptions ArtifactStore { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Object store configuration for SBOM artefacts.
|
||||
/// </summary>
|
||||
public ArtifactStoreOptions ArtifactStore { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Registry credential configuration for report/export operations.
|
||||
/// </summary>
|
||||
public RegistryOptions Registry { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Feature flags toggling optional behaviours.
|
||||
@@ -144,11 +149,11 @@ public sealed class ScannerWebServiceOptions
|
||||
public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public sealed class FeatureFlagOptions
|
||||
{
|
||||
public bool AllowAnonymousScanSubmission { get; set; }
|
||||
|
||||
public bool EnableSignedReports { get; set; } = true;
|
||||
public sealed class FeatureFlagOptions
|
||||
{
|
||||
public bool AllowAnonymousScanSubmission { get; set; }
|
||||
|
||||
public bool EnableSignedReports { get; set; } = true;
|
||||
|
||||
public bool EnablePolicyPreview { get; set; } = true;
|
||||
|
||||
@@ -233,11 +238,11 @@ public sealed class ScannerWebServiceOptions
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SigningOptions
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
public string KeyId { get; set; } = string.Empty;
|
||||
public sealed class SigningOptions
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
public string KeyId { get; set; } = string.Empty;
|
||||
|
||||
public string Algorithm { get; set; } = "ed25519";
|
||||
|
||||
@@ -251,12 +256,44 @@ public sealed class ScannerWebServiceOptions
|
||||
|
||||
public string? CertificatePemFile { get; set; }
|
||||
|
||||
public string? CertificateChainPem { get; set; }
|
||||
|
||||
public string? CertificateChainPemFile { get; set; }
|
||||
|
||||
public int EnvelopeTtlSeconds { get; set; } = 600;
|
||||
}
|
||||
public string? CertificateChainPem { get; set; }
|
||||
|
||||
public string? CertificateChainPemFile { get; set; }
|
||||
|
||||
public int EnvelopeTtlSeconds { get; set; } = 600;
|
||||
}
|
||||
|
||||
public sealed class RegistryOptions
|
||||
{
|
||||
public string? DefaultRegistry { get; set; }
|
||||
|
||||
public IList<RegistryCredentialOptions> Credentials { get; set; } = new List<RegistryCredentialOptions>();
|
||||
}
|
||||
|
||||
public sealed class RegistryCredentialOptions
|
||||
{
|
||||
public string Registry { get; set; } = string.Empty;
|
||||
|
||||
public string? Username { get; set; }
|
||||
|
||||
public string? Password { get; set; }
|
||||
|
||||
public string? IdentityToken { get; set; }
|
||||
|
||||
public string? RegistryToken { get; set; }
|
||||
|
||||
public string? RefreshToken { get; set; }
|
||||
|
||||
public DateTimeOffset? ExpiresAt { get; set; }
|
||||
|
||||
public IList<string> Scopes { get; set; } = new List<string>();
|
||||
|
||||
public bool? AllowInsecureTls { get; set; }
|
||||
|
||||
public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public string? Email { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ApiOptions
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user