Add Authority Advisory AI and API Lifecycle Configuration

- Introduced AuthorityAdvisoryAiOptions and related classes for managing advisory AI configurations, including remote inference options and tenant-specific settings.
- Added AuthorityApiLifecycleOptions to control API lifecycle settings, including legacy OAuth endpoint configurations.
- Implemented validation and normalization methods for both advisory AI and API lifecycle options to ensure proper configuration.
- Created AuthorityNotificationsOptions and its related classes for managing notification settings, including ack tokens, webhooks, and escalation options.
- Developed IssuerDirectoryClient and related models for interacting with the issuer directory service, including caching mechanisms and HTTP client configurations.
- Added support for dependency injection through ServiceCollectionExtensions for the Issuer Directory Client.
- Updated project file to include necessary package references for the new Issuer Directory Client library.
This commit is contained in:
master
2025-11-02 13:40:38 +02:00
parent 66cb6c4b8a
commit f98cea3bcf
516 changed files with 68157 additions and 24754 deletions

View File

@@ -1,13 +1,13 @@
using System;
using System.CommandLine;
using System;
using System.CommandLine;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Plugins;
namespace StellaOps.Cli.Commands;
namespace StellaOps.Cli.Commands;
internal static class CommandFactory
{
public static RootCommand Create(
@@ -22,11 +22,11 @@ internal static class CommandFactory
{
Description = "Enable verbose logging output."
};
var root = new RootCommand("StellaOps command-line interface")
{
TreatUnmatchedTokensAsErrors = true
};
var root = new RootCommand("StellaOps command-line interface")
{
TreatUnmatchedTokensAsErrors = true
};
root.Add(verboseOption);
root.Add(BuildScannerCommand(services, verboseOption, cancellationToken));
@@ -47,108 +47,132 @@ internal static class CommandFactory
return root;
}
private static Command BuildScannerCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var scanner = new Command("scanner", "Manage scanner artifacts and lifecycle.");
var download = new Command("download", "Download the latest scanner bundle.");
var channelOption = new Option<string>("--channel", new[] { "-c" })
{
Description = "Scanner channel (stable, beta, nightly)."
};
var outputOption = new Option<string?>("--output")
{
Description = "Optional output path for the downloaded bundle."
};
var overwriteOption = new Option<bool>("--overwrite")
{
Description = "Overwrite existing bundle if present."
};
var noInstallOption = new Option<bool>("--no-install")
{
Description = "Skip installing the scanner container after download."
};
download.Add(channelOption);
download.Add(outputOption);
download.Add(overwriteOption);
download.Add(noInstallOption);
download.SetAction((parseResult, _) =>
{
var channel = parseResult.GetValue(channelOption) ?? "stable";
var output = parseResult.GetValue(outputOption);
var overwrite = parseResult.GetValue(overwriteOption);
var install = !parseResult.GetValue(noInstallOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleScannerDownloadAsync(services, channel, output, overwrite, install, verbose, cancellationToken);
});
scanner.Add(download);
return scanner;
}
private static Command BuildScannerCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var scanner = new Command("scanner", "Manage scanner artifacts and lifecycle.");
var download = new Command("download", "Download the latest scanner bundle.");
var channelOption = new Option<string>("--channel", new[] { "-c" })
{
Description = "Scanner channel (stable, beta, nightly)."
};
var outputOption = new Option<string?>("--output")
{
Description = "Optional output path for the downloaded bundle."
};
var overwriteOption = new Option<bool>("--overwrite")
{
Description = "Overwrite existing bundle if present."
};
var noInstallOption = new Option<bool>("--no-install")
{
Description = "Skip installing the scanner container after download."
};
download.Add(channelOption);
download.Add(outputOption);
download.Add(overwriteOption);
download.Add(noInstallOption);
download.SetAction((parseResult, _) =>
{
var channel = parseResult.GetValue(channelOption) ?? "stable";
var output = parseResult.GetValue(outputOption);
var overwrite = parseResult.GetValue(overwriteOption);
var install = !parseResult.GetValue(noInstallOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleScannerDownloadAsync(services, channel, output, overwrite, install, verbose, cancellationToken);
});
scanner.Add(download);
return scanner;
}
private static Command BuildScanCommand(IServiceProvider services, StellaOpsCliOptions options, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var scan = new Command("scan", "Execute scanners and manage scan outputs.");
var run = new Command("run", "Execute a scanner bundle with the configured runner.");
var runnerOption = new Option<string>("--runner")
{
Description = "Execution runtime (dotnet, self, docker)."
};
var entryOption = new Option<string>("--entry")
{
Description = "Path to the scanner entrypoint or Docker image.",
Required = true
};
var targetOption = new Option<string>("--target")
{
Description = "Directory to scan.",
Required = true
};
var argsArgument = new Argument<string[]>("scanner-args")
{
Arity = ArgumentArity.ZeroOrMore
};
run.Add(runnerOption);
run.Add(entryOption);
run.Add(targetOption);
run.Add(argsArgument);
run.SetAction((parseResult, _) =>
{
var runner = parseResult.GetValue(runnerOption) ?? options.DefaultRunner;
var entry = parseResult.GetValue(entryOption) ?? string.Empty;
var target = parseResult.GetValue(targetOption) ?? string.Empty;
var forwardedArgs = parseResult.GetValue(argsArgument) ?? Array.Empty<string>();
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleScannerRunAsync(services, runner, entry, target, forwardedArgs, verbose, cancellationToken);
});
var upload = new Command("upload", "Upload completed scan results to the backend.");
var fileOption = new Option<string>("--file")
{
Description = "Path to the scan result artifact.",
Required = true
};
upload.Add(fileOption);
upload.SetAction((parseResult, _) =>
{
var file = parseResult.GetValue(fileOption) ?? string.Empty;
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleScanUploadAsync(services, file, verbose, cancellationToken);
});
scan.Add(run);
var run = new Command("run", "Execute a scanner bundle with the configured runner.");
var runnerOption = new Option<string>("--runner")
{
Description = "Execution runtime (dotnet, self, docker)."
};
var entryOption = new Option<string>("--entry")
{
Description = "Path to the scanner entrypoint or Docker image.",
Required = true
};
var targetOption = new Option<string>("--target")
{
Description = "Directory to scan.",
Required = true
};
var argsArgument = new Argument<string[]>("scanner-args")
{
Arity = ArgumentArity.ZeroOrMore
};
run.Add(runnerOption);
run.Add(entryOption);
run.Add(targetOption);
run.Add(argsArgument);
run.SetAction((parseResult, _) =>
{
var runner = parseResult.GetValue(runnerOption) ?? options.DefaultRunner;
var entry = parseResult.GetValue(entryOption) ?? string.Empty;
var target = parseResult.GetValue(targetOption) ?? string.Empty;
var forwardedArgs = parseResult.GetValue(argsArgument) ?? Array.Empty<string>();
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleScannerRunAsync(services, runner, entry, target, forwardedArgs, verbose, cancellationToken);
});
var upload = new Command("upload", "Upload completed scan results to the backend.");
var fileOption = new Option<string>("--file")
{
Description = "Path to the scan result artifact.",
Required = true
};
upload.Add(fileOption);
upload.SetAction((parseResult, _) =>
{
var file = parseResult.GetValue(fileOption) ?? string.Empty;
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleScanUploadAsync(services, file, verbose, cancellationToken);
});
var entryTrace = new Command("entrytrace", "Show entry trace summary for a scan.");
var scanIdOption = new Option<string>("--scan-id")
{
Description = "Scan identifier.",
Required = true
};
var includeNdjsonOption = new Option<bool>("--include-ndjson")
{
Description = "Include raw NDJSON output."
};
entryTrace.Add(scanIdOption);
entryTrace.Add(includeNdjsonOption);
entryTrace.SetAction((parseResult, _) =>
{
var id = parseResult.GetValue(scanIdOption) ?? string.Empty;
var includeNdjson = parseResult.GetValue(includeNdjsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleScanEntryTraceAsync(services, id, includeNdjson, verbose, cancellationToken);
});
scan.Add(entryTrace);
scan.Add(run);
scan.Add(upload);
return scan;
}
@@ -272,43 +296,43 @@ internal static class CommandFactory
private static Command BuildDatabaseCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var db = new Command("db", "Trigger Concelier database operations via backend jobs.");
var fetch = new Command("fetch", "Trigger connector fetch/parse/map stages.");
var sourceOption = new Option<string>("--source")
{
Description = "Connector source identifier (e.g. redhat, osv, vmware).",
Required = true
};
var stageOption = new Option<string>("--stage")
{
Description = "Stage to trigger: fetch, parse, or map."
};
var modeOption = new Option<string?>("--mode")
{
Description = "Optional connector-specific mode (init, resume, cursor)."
};
fetch.Add(sourceOption);
fetch.Add(stageOption);
fetch.Add(modeOption);
fetch.SetAction((parseResult, _) =>
{
var source = parseResult.GetValue(sourceOption) ?? string.Empty;
var stage = parseResult.GetValue(stageOption) ?? "fetch";
var mode = parseResult.GetValue(modeOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleConnectorJobAsync(services, source, stage, mode, verbose, cancellationToken);
});
var merge = new Command("merge", "Run canonical merge reconciliation.");
merge.SetAction((parseResult, _) =>
{
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleMergeJobAsync(services, verbose, cancellationToken);
});
var export = new Command("export", "Run Concelier export jobs.");
var fetch = new Command("fetch", "Trigger connector fetch/parse/map stages.");
var sourceOption = new Option<string>("--source")
{
Description = "Connector source identifier (e.g. redhat, osv, vmware).",
Required = true
};
var stageOption = new Option<string>("--stage")
{
Description = "Stage to trigger: fetch, parse, or map."
};
var modeOption = new Option<string?>("--mode")
{
Description = "Optional connector-specific mode (init, resume, cursor)."
};
fetch.Add(sourceOption);
fetch.Add(stageOption);
fetch.Add(modeOption);
fetch.SetAction((parseResult, _) =>
{
var source = parseResult.GetValue(sourceOption) ?? string.Empty;
var stage = parseResult.GetValue(stageOption) ?? "fetch";
var mode = parseResult.GetValue(modeOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleConnectorJobAsync(services, source, stage, mode, verbose, cancellationToken);
});
var merge = new Command("merge", "Run canonical merge reconciliation.");
merge.SetAction((parseResult, _) =>
{
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleMergeJobAsync(services, verbose, cancellationToken);
});
var export = new Command("export", "Run Concelier export jobs.");
var formatOption = new Option<string>("--format")
{
Description = "Export format: json or trivy-db."
@@ -351,7 +375,7 @@ internal static class CommandFactory
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleExportJobAsync(services, format, delta, publishFull, publishDelta, includeFull, includeDelta, verbose, cancellationToken);
});
db.Add(fetch);
db.Add(merge);
db.Add(export);
@@ -1087,25 +1111,25 @@ internal static class CommandFactory
return Task.CompletedTask;
});
config.Add(show);
return config;
}
private static string MaskIfEmpty(string value)
=> string.IsNullOrWhiteSpace(value) ? "<not configured>" : value;
private static string DescribeSecret(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return "<not configured>";
}
return value.Length switch
{
<= 4 => "****",
_ => $"{value[..2]}***{value[^2..]}"
};
}
}
config.Add(show);
return config;
}
private static string MaskIfEmpty(string value)
=> string.IsNullOrWhiteSpace(value) ? "<not configured>" : value;
private static string DescribeSecret(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return "<not configured>";
}
return value.Length switch
{
<= 4 => "****",
_ => $"{value[..2]}***{value[^2..]}"
};
}
}