This commit is contained in:
master
2025-10-15 19:20:13 +03:00
parent 3a80a11648
commit 0dab35fbf9
125 changed files with 9383 additions and 3306 deletions

View File

@@ -105,12 +105,15 @@ internal static class Program
services.AddSingleton<IScannerExecutor, ScannerExecutor>();
services.AddSingleton<IScannerInstaller, ScannerInstaller>();
await using var serviceProvider = services.BuildServiceProvider();
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, eventArgs) =>
{
eventArgs.Cancel = true;
cts.Cancel();
await using var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
var startupLogger = loggerFactory.CreateLogger("StellaOps.Cli.Startup");
AuthorityDiagnosticsReporter.Emit(configuration, startupLogger);
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, eventArgs) =>
{
eventArgs.Cancel = true;
cts.Cancel();
};
var rootCommand = CommandFactory.Create(serviceProvider, options, cts.Token);

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using StellaOps.Authority.Plugins.Abstractions;
using StellaOps.Configuration;
namespace StellaOps.Cli.Services;
/// <summary>
/// Emits Authority configuration diagnostics discovered during CLI startup.
/// </summary>
internal static class AuthorityDiagnosticsReporter
{
public static void Emit(IConfiguration configuration, ILogger logger)
{
ArgumentNullException.ThrowIfNull(configuration);
ArgumentNullException.ThrowIfNull(logger);
var basePath = Directory.GetCurrentDirectory();
EmitInternal(configuration, logger, basePath);
}
internal static void Emit(IConfiguration configuration, ILogger logger, string basePath)
{
ArgumentNullException.ThrowIfNull(configuration);
ArgumentNullException.ThrowIfNull(logger);
ArgumentNullException.ThrowIfNull(basePath);
EmitInternal(configuration, logger, basePath);
}
private static void EmitInternal(IConfiguration configuration, ILogger logger, string basePath)
{
if (string.IsNullOrWhiteSpace(basePath))
{
basePath = Directory.GetCurrentDirectory();
}
var authoritySection = configuration.GetSection("Authority");
if (!authoritySection.Exists())
{
return;
}
var authorityOptions = new StellaOpsAuthorityOptions();
authoritySection.Bind(authorityOptions);
if (authorityOptions.Plugins.Descriptors.Count == 0)
{
return;
}
var resolvedBasePath = Path.GetFullPath(basePath);
IReadOnlyList<AuthorityPluginContext> contexts;
try
{
contexts = AuthorityPluginConfigurationLoader.Load(authorityOptions, resolvedBasePath);
}
catch (Exception ex)
{
logger.LogDebug(ex, "Failed to load Authority plug-in configuration for diagnostics.");
return;
}
if (contexts.Count == 0)
{
return;
}
IReadOnlyList<AuthorityConfigurationDiagnostic> diagnostics;
try
{
diagnostics = AuthorityPluginConfigurationAnalyzer.Analyze(contexts);
}
catch (Exception ex)
{
logger.LogDebug(ex, "Failed to analyze Authority plug-in configuration for diagnostics.");
return;
}
if (diagnostics.Count == 0)
{
return;
}
var contextLookup = new Dictionary<string, AuthorityPluginContext>(StringComparer.OrdinalIgnoreCase);
foreach (var context in contexts)
{
if (context?.Manifest?.Name is { Length: > 0 } name && !contextLookup.ContainsKey(name))
{
contextLookup[name] = context;
}
}
foreach (var diagnostic in diagnostics)
{
var level = diagnostic.Severity switch
{
AuthorityConfigurationDiagnosticSeverity.Error => LogLevel.Error,
AuthorityConfigurationDiagnosticSeverity.Warning => LogLevel.Warning,
_ => LogLevel.Information
};
if (!logger.IsEnabled(level))
{
continue;
}
if (contextLookup.TryGetValue(diagnostic.PluginName, out var context) &&
context?.Manifest?.ConfigPath is { Length: > 0 } configPath)
{
logger.Log(level, "{DiagnosticMessage} (config: {ConfigPath})", diagnostic.Message, configPath);
}
else
{
logger.Log(level, "{DiagnosticMessage}", diagnostic.Message);
}
}
}
}

View File

@@ -1,10 +1,11 @@
# TASKS
| Task | Owner(s) | Depends on | Notes |
|---|---|---|---|
|Bootstrap configuration fallback (env → appsettings{{.json/.yaml}})|DevEx/CLI|Core|**DONE** CLI loads `API_KEY`/`STELLAOPS_BACKEND_URL` from environment or local settings, defaulting to empty strings when unset.|
|Introduce command host & routing skeleton|DevEx/CLI|Configuration|**DONE** System.CommandLine (v2.0.0-beta5) router stitched with `scanner`, `scan`, `db`, and `config` verbs.|
|Scanner artifact download/install commands|Ops Integrator|Backend contracts|**DONE** `scanner download` caches bundles, validates SHA-256 (plus optional RSA signature), installs via `docker load`, persists metadata, and retries with exponential backoff.|
|Scan execution & result upload workflow|Ops Integrator, QA|Scanner cmd|**DONE** `scan run` drives container scans against directories, emits artefacts in `ResultsDirectory`, auto-uploads on success, and `scan upload` covers manual retries.|
If you are working on this file you need to read docs/ARCHITECTURE_VEXER.md and ./AGENTS.md).
# TASKS
| Task | Owner(s) | Depends on | Notes |
|---|---|---|---|
|Bootstrap configuration fallback (env → appsettings{{.json/.yaml}})|DevEx/CLI|Core|**DONE** CLI loads `API_KEY`/`STELLAOPS_BACKEND_URL` from environment or local settings, defaulting to empty strings when unset.|
|Introduce command host & routing skeleton|DevEx/CLI|Configuration|**DONE** System.CommandLine (v2.0.0-beta5) router stitched with `scanner`, `scan`, `db`, and `config` verbs.|
|Scanner artifact download/install commands|Ops Integrator|Backend contracts|**DONE** `scanner download` caches bundles, validates SHA-256 (plus optional RSA signature), installs via `docker load`, persists metadata, and retries with exponential backoff.|
|Scan execution & result upload workflow|Ops Integrator, QA|Scanner cmd|**DONE** `scan run` drives container scans against directories, emits artefacts in `ResultsDirectory`, auto-uploads on success, and `scan upload` covers manual retries.|
|Feedser DB operations passthrough|DevEx/CLI|Backend, Feedser APIs|**DONE** `db fetch|merge|export` trigger `/jobs/*` endpoints with parameter binding and consistent exit codes.|
|CLI observability & tests|QA|Command host|**DONE** Added console logging defaults & configuration bootstrap tests; future metrics hooks tracked separately.|
|Authority auth commands|DevEx/CLI|Auth libraries|**DONE** `auth login/logout/status` wrap the shared auth client, manage token cache, and surface status messages.|
@@ -12,4 +13,7 @@
|Authority whoami command|DevEx/CLI|Authority auth commands|**DONE (2025-10-10)** Added `auth whoami` verb that displays subject/audience/expiry from cached tokens and handles opaque tokens gracefully.|
|Expose auth client resilience settings|DevEx/CLI|Auth libraries LIB5|**DONE (2025-10-10)** CLI options now bind resilience knobs, `AddStellaOpsAuthClient` honours them, and tests cover env overrides.|
|Document advanced Authority tuning|Docs/CLI|Expose auth client resilience settings|**DONE (2025-10-10)** docs/09 and docs/10 describe retry/offline settings with env examples and point to the integration guide.|
|Surface password policy diagnostics in CLI output|DevEx/CLI, Security Guild|AUTHSEC-CRYPTO-02-004|**TODO** Bubble analyzer warnings during CLI startup (plugin load) and add tests/docs guiding operators to remediate weakened policies.|
|Surface password policy diagnostics in CLI output|DevEx/CLI, Security Guild|AUTHSEC-CRYPTO-02-004|**DONE (2025-10-15)** CLI startup runs the Authority plug-in analyzer, logs weakened password policy warnings with manifest paths, added unit tests (`dotnet test src/StellaOps.Cli.Tests`) and updated docs/09 with remediation guidance.|
|VEXER-CLI-01-001 Add `vexer` command group|DevEx/CLI|VEXER-WEB-01-001|TODO Introduce `vexer` verb hierarchy (init/pull/resume/list-providers/export/verify/reconcile) forwarding to WebService with token auth and consistent exit codes.|
|VEXER-CLI-01-002 Export download & attestation UX|DevEx/CLI|VEXER-CLI-01-001, VEXER-EXPORT-01-001|TODO Display export metadata (sha256, size, Rekor link), support optional artifact download path, and handle cache hits gracefully.|
|VEXER-CLI-01-003 CLI docs & examples for Vexer|Docs/CLI|VEXER-CLI-01-001|TODO Update docs/09_API_CLI_REFERENCE.md and quickstart snippets to cover Vexer verbs, offline guidance, and attestation verification workflow.|