Add post-quantum cryptography support with PqSoftCryptoProvider
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
wine-csp-build / Build Wine CSP Image (push) Has been cancelled

- Implemented PqSoftCryptoProvider for software-only post-quantum algorithms (Dilithium3, Falcon512) using BouncyCastle.
- Added PqSoftProviderOptions and PqSoftKeyOptions for configuration.
- Created unit tests for Dilithium3 and Falcon512 signing and verification.
- Introduced EcdsaPolicyCryptoProvider for compliance profiles (FIPS/eIDAS) with explicit allow-lists.
- Added KcmvpHashOnlyProvider for KCMVP baseline compliance.
- Updated project files and dependencies for new libraries and testing frameworks.
This commit is contained in:
StellaOps Bot
2025-12-07 15:04:19 +02:00
parent 862bb6ed80
commit 98e6b76584
119 changed files with 11436 additions and 1732 deletions

View File

@@ -64,18 +64,18 @@ internal static class CommandFactory
root.Add(BuildPromotionCommand(services, verboseOption, cancellationToken));
root.Add(BuildDetscoreCommand(services, verboseOption, cancellationToken));
root.Add(BuildObsCommand(services, verboseOption, cancellationToken));
root.Add(BuildPackCommand(services, verboseOption, cancellationToken));
root.Add(BuildExceptionsCommand(services, verboseOption, cancellationToken));
root.Add(BuildOrchCommand(services, verboseOption, cancellationToken));
root.Add(BuildSbomCommand(services, verboseOption, cancellationToken));
root.Add(BuildNotifyCommand(services, verboseOption, cancellationToken));
root.Add(BuildSbomerCommand(services, verboseOption, cancellationToken));
root.Add(BuildCvssCommand(services, verboseOption, cancellationToken));
root.Add(BuildRiskCommand(services, verboseOption, cancellationToken));
root.Add(BuildReachabilityCommand(services, verboseOption, cancellationToken));
root.Add(BuildApiCommand(services, verboseOption, cancellationToken));
root.Add(BuildSdkCommand(services, verboseOption, cancellationToken));
root.Add(BuildMirrorCommand(services, verboseOption, cancellationToken));
root.Add(BuildPackCommand(services, verboseOption, cancellationToken));
root.Add(BuildExceptionsCommand(services, verboseOption, cancellationToken));
root.Add(BuildOrchCommand(services, verboseOption, cancellationToken));
root.Add(BuildSbomCommand(services, verboseOption, cancellationToken));
root.Add(BuildNotifyCommand(services, verboseOption, cancellationToken));
root.Add(BuildSbomerCommand(services, verboseOption, cancellationToken));
root.Add(BuildCvssCommand(services, verboseOption, cancellationToken));
root.Add(BuildRiskCommand(services, verboseOption, cancellationToken));
root.Add(BuildReachabilityCommand(services, verboseOption, cancellationToken));
root.Add(BuildApiCommand(services, verboseOption, cancellationToken));
root.Add(BuildSdkCommand(services, verboseOption, cancellationToken));
root.Add(BuildMirrorCommand(services, verboseOption, cancellationToken));
root.Add(BuildAirgapCommand(services, verboseOption, cancellationToken));
root.Add(SystemCommandBuilder.BuildSystemCommand(services, verboseOption, cancellationToken));
@@ -127,79 +127,79 @@ internal static class CommandFactory
return CommandHandlers.HandleScannerDownloadAsync(services, channel, output, overwrite, install, verbose, cancellationToken);
});
scanner.Add(download);
return scanner;
}
private static Command BuildCvssCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var cvss = new Command("cvss", "CVSS v4.0 receipt operations (score, show, history, export)." );
var score = new Command("score", "Create a CVSS v4 receipt for a vulnerability.");
var vulnOption = new Option<string>("--vuln") { Description = "Vulnerability identifier (e.g., CVE).", IsRequired = true };
var policyFileOption = new Option<string>("--policy-file") { Description = "Path to CvssPolicy JSON file.", IsRequired = true };
var vectorOption = new Option<string>("--vector") { Description = "CVSS:4.0 vector string.", IsRequired = true };
var jsonOption = new Option<bool>("--json") { Description = "Emit JSON output." };
score.Add(vulnOption);
score.Add(policyFileOption);
score.Add(vectorOption);
score.Add(jsonOption);
score.SetAction((parseResult, _) =>
{
var vuln = parseResult.GetValue(vulnOption) ?? string.Empty;
var policyPath = parseResult.GetValue(policyFileOption) ?? string.Empty;
var vector = parseResult.GetValue(vectorOption) ?? string.Empty;
var json = parseResult.GetValue(jsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssScoreAsync(services, vuln, policyPath, vector, json, verbose, cancellationToken);
});
var show = new Command("show", "Fetch a CVSS receipt by ID.");
var receiptArg = new Argument<string>("receipt-id") { Description = "Receipt identifier." };
show.Add(receiptArg);
var showJsonOption = new Option<bool>("--json") { Description = "Emit JSON output." };
show.Add(showJsonOption);
show.SetAction((parseResult, _) =>
{
var receiptId = parseResult.GetValue(receiptArg) ?? string.Empty;
var json = parseResult.GetValue(showJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssShowAsync(services, receiptId, json, verbose, cancellationToken);
});
var history = new Command("history", "Show receipt amendment history.");
history.Add(receiptArg);
var historyJsonOption = new Option<bool>("--json") { Description = "Emit JSON output." };
history.Add(historyJsonOption);
history.SetAction((parseResult, _) =>
{
var receiptId = parseResult.GetValue(receiptArg) ?? string.Empty;
var json = parseResult.GetValue(historyJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssHistoryAsync(services, receiptId, json, verbose, cancellationToken);
});
var export = new Command("export", "Export a CVSS receipt to JSON (pdf not yet supported).");
export.Add(receiptArg);
var formatOption = new Option<string>("--format") { Description = "json|pdf (json default)." };
var outOption = new Option<string>("--out") { Description = "Output file path." };
export.Add(formatOption);
export.Add(outOption);
export.SetAction((parseResult, _) =>
{
var receiptId = parseResult.GetValue(receiptArg) ?? string.Empty;
var format = parseResult.GetValue(formatOption) ?? "json";
var output = parseResult.GetValue(outOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssExportAsync(services, receiptId, format, output, verbose, cancellationToken);
});
cvss.Add(score);
cvss.Add(show);
cvss.Add(history);
cvss.Add(export);
return cvss;
}
scanner.Add(download);
return scanner;
}
private static Command BuildCvssCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var cvss = new Command("cvss", "CVSS v4.0 receipt operations (score, show, history, export)." );
var score = new Command("score", "Create a CVSS v4 receipt for a vulnerability.");
var vulnOption = new Option<string>("--vuln") { Description = "Vulnerability identifier (e.g., CVE).", Required = true };
var policyFileOption = new Option<string>("--policy-file") { Description = "Path to CvssPolicy JSON file.", Required = true };
var vectorOption = new Option<string>("--vector") { Description = "CVSS:4.0 vector string.", Required = true };
var jsonOption = new Option<bool>("--json") { Description = "Emit JSON output." };
score.Add(vulnOption);
score.Add(policyFileOption);
score.Add(vectorOption);
score.Add(jsonOption);
score.SetAction((parseResult, _) =>
{
var vuln = parseResult.GetValue(vulnOption) ?? string.Empty;
var policyPath = parseResult.GetValue(policyFileOption) ?? string.Empty;
var vector = parseResult.GetValue(vectorOption) ?? string.Empty;
var json = parseResult.GetValue(jsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssScoreAsync(services, vuln, policyPath, vector, json, verbose, cancellationToken);
});
var show = new Command("show", "Fetch a CVSS receipt by ID.");
var receiptArg = new Argument<string>("receipt-id") { Description = "Receipt identifier." };
show.Add(receiptArg);
var showJsonOption = new Option<bool>("--json") { Description = "Emit JSON output." };
show.Add(showJsonOption);
show.SetAction((parseResult, _) =>
{
var receiptId = parseResult.GetValue(receiptArg) ?? string.Empty;
var json = parseResult.GetValue(showJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssShowAsync(services, receiptId, json, verbose, cancellationToken);
});
var history = new Command("history", "Show receipt amendment history.");
history.Add(receiptArg);
var historyJsonOption = new Option<bool>("--json") { Description = "Emit JSON output." };
history.Add(historyJsonOption);
history.SetAction((parseResult, _) =>
{
var receiptId = parseResult.GetValue(receiptArg) ?? string.Empty;
var json = parseResult.GetValue(historyJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssHistoryAsync(services, receiptId, json, verbose, cancellationToken);
});
var export = new Command("export", "Export a CVSS receipt to JSON (pdf not yet supported).");
export.Add(receiptArg);
var formatOption = new Option<string>("--format") { Description = "json|pdf (json default)." };
var outOption = new Option<string>("--out") { Description = "Output file path." };
export.Add(formatOption);
export.Add(outOption);
export.SetAction((parseResult, _) =>
{
var receiptId = parseResult.GetValue(receiptArg) ?? string.Empty;
var format = parseResult.GetValue(formatOption) ?? "json";
var output = parseResult.GetValue(outOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleCvssExportAsync(services, receiptId, format, output, verbose, cancellationToken);
});
cvss.Add(score);
cvss.Add(show);
cvss.Add(history);
cvss.Add(export);
return cvss;
}
private static Command BuildScanCommand(IServiceProvider services, StellaOpsCliOptions options, Option<bool> verboseOption, CancellationToken cancellationToken)
{
@@ -3672,24 +3672,20 @@ internal static class CommandFactory
};
var expFormatOption = new Option<string>("--format")
{
Description = "Export format (ndjson, json).",
DefaultValueFactory = _ => "ndjson"
};
Description = "Export format (ndjson, json)."
}.SetDefaultValue("ndjson");
var expIncludeEvidenceOption = new Option<bool>("--include-evidence")
{
Description = "Include evidence data in export (default: true).",
DefaultValueFactory = _ => true
};
Description = "Include evidence data in export (default: true)."
}.SetDefaultValue(true);
var expIncludeLedgerOption = new Option<bool>("--include-ledger")
{
Description = "Include workflow ledger in export (default: true).",
DefaultValueFactory = _ => true
};
Description = "Include workflow ledger in export (default: true)."
}.SetDefaultValue(true);
var expSignedOption = new Option<bool>("--signed")
{
Description = "Request signed export bundle (default: true).",
DefaultValueFactory = _ => true
};
Description = "Request signed export bundle (default: true)."
}.SetDefaultValue(true);
var expOutputOption = new Option<string>("--output")
{
Description = "Output file path for the export bundle.",
@@ -10637,3 +10633,4 @@ internal static class CommandFactory
return airgap;
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace StellaOps.Cli.Commands;
internal sealed class CommandLineException : Exception
{
public CommandLineException(string message) : base(message)
{
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Cli.Services;
using StellaOps.Cli.Extensions;
using StellaOps.Infrastructure.Postgres.Migrations;
namespace StellaOps.Cli.Commands;
@@ -32,30 +33,38 @@ internal static class SystemCommandBuilder
Option<bool> verboseOption,
CancellationToken cancellationToken)
{
var moduleOption = new Option<string?>(
"--module",
description: "Module name (Authority, Scheduler, Concelier, Policy, Notify, Excititor, all)");
var categoryOption = new Option<string?>(
"--category",
description: "Migration category (startup, release, seed, data)");
var dryRunOption = new Option<bool>("--dry-run", description: "List migrations without executing");
var connectionOption = new Option<string?>(
"--connection",
description: "PostgreSQL connection string override (otherwise uses STELLAOPS_POSTGRES_* env vars)");
var timeoutOption = new Option<int?>(
"--timeout",
description: "Command timeout in seconds for each migration (default 300).");
var forceOption = new Option<bool>(
"--force",
description: "Allow execution of release migrations without --dry-run.");
var moduleOption = new Option<string?>("--module")
{
Description = "Module name (Authority, Scheduler, Concelier, Policy, Notify, Excititor, all)"
};
var categoryOption = new Option<string?>("--category")
{
Description = "Migration category (startup, release, seed, data)"
};
var dryRunOption = new Option<bool>("--dry-run")
{
Description = "List migrations without executing"
};
var connectionOption = new Option<string?>("--connection")
{
Description = "PostgreSQL connection string override (otherwise uses STELLAOPS_POSTGRES_* env vars)"
};
var timeoutOption = new Option<int?>("--timeout")
{
Description = "Command timeout in seconds for each migration (default 300)."
};
var forceOption = new Option<bool>("--force")
{
Description = "Allow execution of release migrations without --dry-run."
};
var run = new Command("migrations-run", "Run migrations for the selected module(s).");
run.AddOption(moduleOption);
run.AddOption(categoryOption);
run.AddOption(dryRunOption);
run.AddOption(connectionOption);
run.AddOption(timeoutOption);
run.AddOption(forceOption);
run.Add(moduleOption);
run.Add(categoryOption);
run.Add(dryRunOption);
run.Add(connectionOption);
run.Add(timeoutOption);
run.Add(forceOption);
run.SetAction(async parseResult =>
{
var modules = MigrationModuleRegistry.GetModules(parseResult.GetValue(moduleOption)).ToList();
@@ -91,8 +100,8 @@ internal static class SystemCommandBuilder
});
var status = new Command("migrations-status", "Show migration status for the selected module(s).");
status.AddOption(moduleOption);
status.AddOption(connectionOption);
status.Add(moduleOption);
status.Add(connectionOption);
status.SetAction(async parseResult =>
{
var modules = MigrationModuleRegistry.GetModules(parseResult.GetValue(moduleOption)).ToList();
@@ -117,8 +126,8 @@ internal static class SystemCommandBuilder
});
var verify = new Command("migrations-verify", "Verify migration checksums for the selected module(s).");
verify.AddOption(moduleOption);
verify.AddOption(connectionOption);
verify.Add(moduleOption);
verify.Add(connectionOption);
verify.SetAction(async parseResult =>
{
var modules = MigrationModuleRegistry.GetModules(parseResult.GetValue(moduleOption)).ToList();

View File

@@ -1,4 +1,5 @@
using System.CommandLine;
using StellaOps.Cli.Extensions;
using StellaOps.Cli.Output;
namespace StellaOps.Cli.Configuration;
@@ -54,23 +55,23 @@ public sealed class GlobalOptions
/// </summary>
public static IEnumerable<Option> CreateGlobalOptions()
{
yield return new Option<string?>("--profile", "-p")
yield return new Option<string?>("--profile", new[] { "-p" })
{
Description = "Profile name to use for this invocation"
};
yield return new Option<OutputFormat>("--output", "-o")
var outputOption = new Option<OutputFormat>("--output", new[] { "-o" })
{
Description = "Output format (table, json, yaml)",
DefaultValueFactory = _ => OutputFormat.Table
};
Description = "Output format (table, json, yaml)"
}.SetDefaultValue(OutputFormat.Table);
yield return outputOption;
yield return new Option<bool>("--verbose", "-v")
yield return new Option<bool>("--verbose", new[] { "-v" })
{
Description = "Enable verbose output"
};
yield return new Option<bool>("--quiet", "-q")
yield return new Option<bool>("--quiet", new[] { "-q" })
{
Description = "Quiet mode - suppress non-error output"
};

View File

@@ -1,32 +1,44 @@
using System;
using System.CommandLine;
namespace StellaOps.Cli.Extensions;
/// <summary>
/// Compatibility extensions for System.CommandLine 2.0.0-beta5+ API changes.
/// These restore the older extension method patterns that were used in earlier versions.
/// See: https://learn.microsoft.com/en-us/dotnet/standard/commandline/migration-guide-2.0.0-beta5
/// These restore the older helper methods the codebase relied on.
/// </summary>
public static class CommandLineExtensions
{
/// <summary>
/// Sets the default value for an option (compatibility shim for older API).
/// In beta5+, this maps to DefaultValueFactory.
/// Set a default value for an option.
/// </summary>
public static Option<T> SetDefaultValue<T>(this Option<T> option, T defaultValue)
{
ArgumentNullException.ThrowIfNull(option);
option.DefaultValueFactory = _ => defaultValue;
return option;
}
/// <summary>
/// Restricts the option to accept only the specified values (compatibility shim).
/// Works for both Option&lt;string&gt; and Option&lt;string?&gt;.
/// Restrict the option to a fixed set of values and add completions.
/// </summary>
public static Option<T> FromAmong<T>(this Option<T> option, params string[] allowedValues)
where T : class?
{
option.AcceptOnlyFromAmong(allowedValues);
ArgumentNullException.ThrowIfNull(option);
if (allowedValues is { Length: > 0 })
{
option.AcceptOnlyFromAmong(allowedValues);
}
return option;
}
/// <summary>
/// Mark the option as required (compatibility shim for the old Required property).
/// </summary>
public static Option<T> Required<T>(this Option<T> option, bool isRequired = true)
{
ArgumentNullException.ThrowIfNull(option);
option.Required = isRequired;
return option;
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System.Net;
using System.Threading;
@@ -250,11 +251,11 @@ internal static class Program
};
var rootCommand = CommandFactory.Create(serviceProvider, options, cts.Token, loggerFactory);
var commandConfiguration = new CommandLineConfiguration(rootCommand);
int commandExit;
try
{
commandExit = await commandConfiguration.InvokeAsync(args, cts.Token).ConfigureAwait(false);
var parseResult = rootCommand.Parse(args);
commandExit = await parseResult.InvokeAsync(cts.Token).ConfigureAwait(false);
}
catch (AirGapEgressBlockedException ex)
{

View File

@@ -9,6 +9,8 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StellaOps.Auth.Client;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Services.Models;
using StellaOps.Cli.Extensions;
using StellaOps.Policy.Scoring;
namespace StellaOps.Cli.Services;

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Cli.Services.Models;
using StellaOps.Policy.Scoring;
namespace StellaOps.Cli.Services;

View File

@@ -2,10 +2,10 @@ using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using StellaOps.AirGap.Importer.Contracts;
using StellaOps.AirGap.Importer.Models;
using StellaOps.AirGap.Importer.Repositories;
using StellaOps.AirGap.Importer.Validation;
using StellaOps.Cli.Services.Models;
using ImportModels = StellaOps.AirGap.Importer.Models;
namespace StellaOps.Cli.Services;
@@ -238,10 +238,10 @@ public sealed class MirrorBundleImportService : IMirrorBundleImportService
try
{
var envelopeJson = await File.ReadAllTextAsync(dsseFile, cancellationToken).ConfigureAwait(false);
var envelope = DsseEnvelope.Parse(envelopeJson);
var envelope = ImportModels.DsseEnvelope.Parse(envelopeJson);
// Load trust roots if provided
TrustRootConfig trustRoots;
ImportModels.TrustRootConfig trustRoots;
if (!string.IsNullOrWhiteSpace(trustRootsPath) && File.Exists(trustRootsPath))
{
trustRoots = await LoadTrustRootsAsync(trustRootsPath, cancellationToken).ConfigureAwait(false);
@@ -287,7 +287,7 @@ public sealed class MirrorBundleImportService : IMirrorBundleImportService
}
}
private static async Task<TrustRootConfig> LoadTrustRootsAsync(string path, CancellationToken cancellationToken)
private static async Task<ImportModels.TrustRootConfig> LoadTrustRootsAsync(string path, CancellationToken cancellationToken)
{
var json = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false);
var doc = JsonDocument.Parse(json);
@@ -324,7 +324,7 @@ public sealed class MirrorBundleImportService : IMirrorBundleImportService
}
}
return new TrustRootConfig(path, fingerprints, algorithms, null, null, publicKeys);
return new ImportModels.TrustRootConfig(path, fingerprints, algorithms, null, null, publicKeys);
}
private async Task<List<string>> CopyArtifactsAsync(string bundleDir, string dataStorePath, MirrorBundle manifest, CancellationToken cancellationToken)