Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -62,18 +62,14 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
StellaOpsCliOptions options,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var cmd = new Command("auto-downgrade", "Auto-downgrade VEX based on runtime observations.");
|
||||
|
||||
var imageOption = new Option<string>("--image")
|
||||
var imageOption = new Option<string?>("--image")
|
||||
{
|
||||
Description = "Container image digest or reference to check",
|
||||
IsRequired = false
|
||||
Description = "Container image digest or reference to check"
|
||||
};
|
||||
|
||||
var checkOption = new Option<string>("--check")
|
||||
var checkOption = new Option<string?>("--check")
|
||||
{
|
||||
Description = "Image to check for hot vulnerable symbols",
|
||||
IsRequired = false
|
||||
Description = "Image to check for hot vulnerable symbols"
|
||||
};
|
||||
|
||||
var dryRunOption = new Option<bool>("--dry-run")
|
||||
@@ -84,20 +80,20 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
var minObservationsOption = new Option<int>("--min-observations")
|
||||
{
|
||||
Description = "Minimum observation count threshold",
|
||||
DefaultValueFactory = _ => 10
|
||||
};
|
||||
minObservationsOption.SetDefaultValue(10);
|
||||
|
||||
var minCpuOption = new Option<double>("--min-cpu")
|
||||
{
|
||||
Description = "Minimum CPU percentage threshold",
|
||||
DefaultValueFactory = _ => 1.0
|
||||
};
|
||||
minCpuOption.SetDefaultValue(1.0);
|
||||
|
||||
var minConfidenceOption = new Option<double>("--min-confidence")
|
||||
{
|
||||
Description = "Minimum confidence threshold (0.0-1.0)",
|
||||
DefaultValueFactory = _ => 0.7
|
||||
};
|
||||
minConfidenceOption.SetDefaultValue(0.7);
|
||||
|
||||
var outputOption = new Option<string?>("--output")
|
||||
{
|
||||
@@ -106,39 +102,40 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
|
||||
var formatOption = new Option<OutputFormat>("--format")
|
||||
{
|
||||
Description = "Output format"
|
||||
Description = "Output format",
|
||||
DefaultValueFactory = _ => OutputFormat.Table
|
||||
};
|
||||
formatOption.SetDefaultValue(OutputFormat.Table);
|
||||
|
||||
cmd.AddOption(imageOption);
|
||||
cmd.AddOption(checkOption);
|
||||
cmd.AddOption(dryRunOption);
|
||||
cmd.AddOption(minObservationsOption);
|
||||
cmd.AddOption(minCpuOption);
|
||||
cmd.AddOption(minConfidenceOption);
|
||||
cmd.AddOption(outputOption);
|
||||
cmd.AddOption(formatOption);
|
||||
cmd.AddOption(verboseOption);
|
||||
|
||||
cmd.SetHandler(async (context) =>
|
||||
var cmd = new Command("auto-downgrade", "Auto-downgrade VEX based on runtime observations.")
|
||||
{
|
||||
var image = context.ParseResult.GetValueForOption(imageOption);
|
||||
var check = context.ParseResult.GetValueForOption(checkOption);
|
||||
var dryRun = context.ParseResult.GetValueForOption(dryRunOption);
|
||||
var minObs = context.ParseResult.GetValueForOption(minObservationsOption);
|
||||
var minCpu = context.ParseResult.GetValueForOption(minCpuOption);
|
||||
var minConf = context.ParseResult.GetValueForOption(minConfidenceOption);
|
||||
var output = context.ParseResult.GetValueForOption(outputOption);
|
||||
var format = context.ParseResult.GetValueForOption(formatOption);
|
||||
var verbose = context.ParseResult.GetValueForOption(verboseOption);
|
||||
imageOption,
|
||||
checkOption,
|
||||
dryRunOption,
|
||||
minObservationsOption,
|
||||
minCpuOption,
|
||||
minConfidenceOption,
|
||||
outputOption,
|
||||
formatOption
|
||||
};
|
||||
|
||||
cmd.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var image = parseResult.GetValue(imageOption);
|
||||
var check = parseResult.GetValue(checkOption);
|
||||
var dryRun = parseResult.GetValue(dryRunOption);
|
||||
var minObs = parseResult.GetValue(minObservationsOption);
|
||||
var minCpu = parseResult.GetValue(minCpuOption);
|
||||
var minConf = parseResult.GetValue(minConfidenceOption);
|
||||
var output = parseResult.GetValue(outputOption);
|
||||
var format = parseResult.GetValue(formatOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
// Use --check if --image not provided
|
||||
var targetImage = image ?? check;
|
||||
if (string.IsNullOrWhiteSpace(targetImage))
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Error:[/] Either --image or --check must be specified.");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
var logger = services.GetService<ILogger<VexCliCommandModule>>();
|
||||
@@ -155,9 +152,9 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
format,
|
||||
verbose,
|
||||
options,
|
||||
cancellationToken);
|
||||
ct);
|
||||
|
||||
context.ExitCode = 0;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return cmd;
|
||||
@@ -322,8 +319,6 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
Option<bool> verboseOption,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var cmd = new Command("check", "Check VEX status for an image or CVE.");
|
||||
|
||||
var imageOption = new Option<string?>("--image")
|
||||
{
|
||||
Description = "Container image to check"
|
||||
@@ -334,25 +329,26 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
Description = "CVE identifier to check"
|
||||
};
|
||||
|
||||
cmd.AddOption(imageOption);
|
||||
cmd.AddOption(cveOption);
|
||||
cmd.AddOption(verboseOption);
|
||||
|
||||
cmd.SetHandler(async (context) =>
|
||||
var cmd = new Command("check", "Check VEX status for an image or CVE.")
|
||||
{
|
||||
var image = context.ParseResult.GetValueForOption(imageOption);
|
||||
var cve = context.ParseResult.GetValueForOption(cveOption);
|
||||
var verbose = context.ParseResult.GetValueForOption(verboseOption);
|
||||
imageOption,
|
||||
cveOption
|
||||
};
|
||||
|
||||
cmd.SetAction((parseResult, ct) =>
|
||||
{
|
||||
var image = parseResult.GetValue(imageOption);
|
||||
var cve = parseResult.GetValue(cveOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(image) && string.IsNullOrWhiteSpace(cve))
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Error:[/] Either --image or --cve must be specified.");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return Task.FromResult(1);
|
||||
}
|
||||
|
||||
AnsiConsole.MarkupLine("[grey]VEX check not yet implemented[/]");
|
||||
context.ExitCode = 0;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
|
||||
return cmd;
|
||||
@@ -363,8 +359,6 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
Option<bool> verboseOption,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var cmd = new Command("list", "List VEX statements.");
|
||||
|
||||
var productOption = new Option<string?>("--product")
|
||||
{
|
||||
Description = "Filter by product identifier"
|
||||
@@ -377,23 +371,25 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
|
||||
var limitOption = new Option<int>("--limit")
|
||||
{
|
||||
Description = "Maximum number of results"
|
||||
Description = "Maximum number of results",
|
||||
DefaultValueFactory = _ => 100
|
||||
};
|
||||
limitOption.SetDefaultValue(100);
|
||||
|
||||
cmd.AddOption(productOption);
|
||||
cmd.AddOption(statusOption);
|
||||
cmd.AddOption(limitOption);
|
||||
cmd.AddOption(verboseOption);
|
||||
|
||||
cmd.SetHandler(async (context) =>
|
||||
var cmd = new Command("list", "List VEX statements.")
|
||||
{
|
||||
var product = context.ParseResult.GetValueForOption(productOption);
|
||||
var status = context.ParseResult.GetValueForOption(statusOption);
|
||||
var limit = context.ParseResult.GetValueForOption(limitOption);
|
||||
productOption,
|
||||
statusOption,
|
||||
limitOption
|
||||
};
|
||||
|
||||
cmd.SetAction((parseResult, ct) =>
|
||||
{
|
||||
var product = parseResult.GetValue(productOption);
|
||||
var status = parseResult.GetValue(statusOption);
|
||||
var limit = parseResult.GetValue(limitOption);
|
||||
|
||||
AnsiConsole.MarkupLine("[grey]VEX list not yet implemented[/]");
|
||||
context.ExitCode = 0;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
|
||||
return cmd;
|
||||
@@ -405,25 +401,23 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
StellaOpsCliOptions options,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var cmd = new Command("not-reachable", "Generate VEX with not_reachable_at_runtime justification.");
|
||||
|
||||
var imageOption = new Option<string>("--image")
|
||||
{
|
||||
Description = "Container image to analyze",
|
||||
IsRequired = true
|
||||
Required = true
|
||||
};
|
||||
|
||||
var windowOption = new Option<int>("--window")
|
||||
{
|
||||
Description = "Observation window in hours"
|
||||
Description = "Observation window in hours",
|
||||
DefaultValueFactory = _ => 24
|
||||
};
|
||||
windowOption.SetDefaultValue(24);
|
||||
|
||||
var minConfidenceOption = new Option<double>("--min-confidence")
|
||||
{
|
||||
Description = "Minimum confidence threshold"
|
||||
Description = "Minimum confidence threshold",
|
||||
DefaultValueFactory = _ => 0.6
|
||||
};
|
||||
minConfidenceOption.SetDefaultValue(0.6);
|
||||
|
||||
var outputOption = new Option<string?>("--output")
|
||||
{
|
||||
@@ -435,27 +429,28 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
Description = "Dry run - analyze but don't generate VEX"
|
||||
};
|
||||
|
||||
cmd.AddOption(imageOption);
|
||||
cmd.AddOption(windowOption);
|
||||
cmd.AddOption(minConfidenceOption);
|
||||
cmd.AddOption(outputOption);
|
||||
cmd.AddOption(dryRunOption);
|
||||
cmd.AddOption(verboseOption);
|
||||
|
||||
cmd.SetHandler(async (context) =>
|
||||
var cmd = new Command("not-reachable", "Generate VEX with not_reachable_at_runtime justification.")
|
||||
{
|
||||
var image = context.ParseResult.GetValueForOption(imageOption);
|
||||
var window = context.ParseResult.GetValueForOption(windowOption);
|
||||
var minConf = context.ParseResult.GetValueForOption(minConfidenceOption);
|
||||
var output = context.ParseResult.GetValueForOption(outputOption);
|
||||
var dryRun = context.ParseResult.GetValueForOption(dryRunOption);
|
||||
var verbose = context.ParseResult.GetValueForOption(verboseOption);
|
||||
imageOption,
|
||||
windowOption,
|
||||
minConfidenceOption,
|
||||
outputOption,
|
||||
dryRunOption
|
||||
};
|
||||
|
||||
cmd.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var image = parseResult.GetValue(imageOption);
|
||||
var window = parseResult.GetValue(windowOption);
|
||||
var minConf = parseResult.GetValue(minConfidenceOption);
|
||||
var output = parseResult.GetValue(outputOption);
|
||||
var dryRun = parseResult.GetValue(dryRunOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(image))
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Error:[/] --image is required.");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
await RunNotReachableAnalysisAsync(
|
||||
@@ -467,9 +462,9 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
dryRun,
|
||||
verbose,
|
||||
options,
|
||||
cancellationToken);
|
||||
ct);
|
||||
|
||||
context.ExitCode = 0;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return cmd;
|
||||
@@ -584,9 +579,8 @@ public sealed class VexCliCommandModule : ICliCommandModule
|
||||
var httpClient = services.GetService<IHttpClientFactory>()?.CreateClient("autovex")
|
||||
?? new HttpClient();
|
||||
|
||||
var baseUrl = options.ExcititorApiBaseUrl
|
||||
?? Environment.GetEnvironmentVariable("STELLAOPS_EXCITITOR_URL")
|
||||
?? "http://localhost:5080";
|
||||
var baseUrl = Environment.GetEnvironmentVariable("STELLAOPS_EXCITITOR_URL")
|
||||
?? (string.IsNullOrEmpty(options.BackendUrl) ? "http://localhost:5080" : options.BackendUrl);
|
||||
|
||||
return new AutoVexHttpClient(httpClient, baseUrl);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user