Files
git.stella-ops.org/src/Cli/StellaOps.Cli/Program.cs
master 536f6249a6
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Add SBOM, symbols, traces, and VEX files for CVE-2022-21661 SQLi case
- Created CycloneDX and SPDX SBOM files for both reachable and unreachable images.
- Added symbols.json detailing function entry and sink points in the WordPress code.
- Included runtime traces for function calls in both reachable and unreachable scenarios.
- Developed OpenVEX files indicating vulnerability status and justification for both cases.
- Updated README for evaluator harness to guide integration with scanner output.
2025-11-08 20:53:45 +02:00

178 lines
7.0 KiB
C#

using System;
using System.CommandLine;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.Auth.Client;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Services;
using StellaOps.Cli.Telemetry;
using StellaOps.AirGap.Policy;
using StellaOps.Configuration;
namespace StellaOps.Cli;
internal static class Program
{
internal static async Task<int> Main(string[] args)
{
var (options, configuration) = CliBootstrapper.Build(args);
var services = new ServiceCollection();
services.AddSingleton(configuration);
services.AddSingleton(options);
services.AddOptions();
var verbosityState = new VerbosityState();
services.AddSingleton(verbosityState);
services.AddAirGapEgressPolicy(configuration);
services.AddStellaOpsCrypto(options.Crypto);
services.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddSimpleConsole(logOptions =>
{
logOptions.TimestampFormat = "HH:mm:ss ";
logOptions.SingleLine = true;
});
builder.AddFilter((category, level) => level >= verbosityState.MinimumLevel);
});
if (!string.IsNullOrWhiteSpace(options.Authority.Url))
{
services.AddStellaOpsAuthClient(clientOptions =>
{
clientOptions.Authority = options.Authority.Url;
clientOptions.ClientId = options.Authority.ClientId ?? string.Empty;
clientOptions.ClientSecret = options.Authority.ClientSecret;
clientOptions.DefaultScopes.Clear();
clientOptions.DefaultScopes.Add(string.IsNullOrWhiteSpace(options.Authority.Scope)
? StellaOps.Auth.Abstractions.StellaOpsScopes.ConcelierJobsTrigger
: options.Authority.Scope);
var resilience = options.Authority.Resilience ?? new StellaOpsCliAuthorityResilienceOptions();
clientOptions.EnableRetries = resilience.EnableRetries ?? true;
if (resilience.RetryDelays is { Count: > 0 })
{
clientOptions.RetryDelays.Clear();
foreach (var delay in resilience.RetryDelays)
{
if (delay > TimeSpan.Zero)
{
clientOptions.RetryDelays.Add(delay);
}
}
}
if (resilience.AllowOfflineCacheFallback.HasValue)
{
clientOptions.AllowOfflineCacheFallback = resilience.AllowOfflineCacheFallback.Value;
}
if (resilience.OfflineCacheTolerance.HasValue && resilience.OfflineCacheTolerance.Value >= TimeSpan.Zero)
{
clientOptions.OfflineCacheTolerance = resilience.OfflineCacheTolerance.Value;
}
});
var cacheDirectory = options.Authority.TokenCacheDirectory;
if (!string.IsNullOrWhiteSpace(cacheDirectory))
{
Directory.CreateDirectory(cacheDirectory);
services.AddStellaOpsFileTokenCache(cacheDirectory);
}
services.AddHttpClient<IAuthorityRevocationClient, AuthorityRevocationClient>(client =>
{
client.Timeout = TimeSpan.FromMinutes(2);
if (Uri.TryCreate(options.Authority.Url, UriKind.Absolute, out var authorityUri))
{
client.BaseAddress = authorityUri;
}
}).AddEgressPolicyGuard("stellaops-cli", "authority-revocation");
}
services.AddHttpClient<IBackendOperationsClient, BackendOperationsClient>(client =>
{
client.Timeout = TimeSpan.FromMinutes(5);
if (!string.IsNullOrWhiteSpace(options.BackendUrl) &&
Uri.TryCreate(options.BackendUrl, UriKind.Absolute, out var backendUri))
{
client.BaseAddress = backendUri;
}
}).AddEgressPolicyGuard("stellaops-cli", "backend-api");
services.AddHttpClient<IConcelierObservationsClient, ConcelierObservationsClient>(client =>
{
client.Timeout = TimeSpan.FromSeconds(30);
if (!string.IsNullOrWhiteSpace(options.ConcelierUrl) &&
Uri.TryCreate(options.ConcelierUrl, UriKind.Absolute, out var concelierUri))
{
client.BaseAddress = concelierUri;
}
}).AddEgressPolicyGuard("stellaops-cli", "concelier-api");
services.AddHttpClient("stellaops-cli.ingest-download")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.All
})
.AddEgressPolicyGuard("stellaops-cli", "sources-ingest");
services.AddSingleton<IScannerExecutor, ScannerExecutor>();
services.AddSingleton<IScannerInstaller, ScannerInstaller>();
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, loggerFactory);
var commandConfiguration = new CommandLineConfiguration(rootCommand);
int commandExit;
try
{
commandExit = await commandConfiguration.InvokeAsync(args, cts.Token).ConfigureAwait(false);
}
catch (AirGapEgressBlockedException ex)
{
var guardLogger = loggerFactory.CreateLogger("StellaOps.Cli.AirGap");
guardLogger.LogError("{ErrorCode}: {Reason} Remediation: {Remediation}", AirGapEgressBlockedException.ErrorCode, ex.Reason, ex.Remediation);
if (!string.IsNullOrWhiteSpace(ex.DocumentationUrl))
{
guardLogger.LogInformation("Documentation: {DocumentationUrl}", ex.DocumentationUrl);
}
if (!string.IsNullOrWhiteSpace(ex.SupportContact))
{
guardLogger.LogInformation("Support contact: {SupportContact}", ex.SupportContact);
}
Console.Error.WriteLine(ex.Message);
return 1;
}
var finalExit = Environment.ExitCode != 0 ? Environment.ExitCode : commandExit;
if (cts.IsCancellationRequested && finalExit == 0)
{
finalExit = 130; // Typical POSIX cancellation exit code
}
return finalExit;
}
}