up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled

This commit is contained in:
master
2025-11-27 15:05:48 +02:00
parent 4831c7fcb0
commit e950474a77
278 changed files with 81498 additions and 672 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -7810,4 +7810,198 @@ internal static class CommandHandlers
}
private sealed record ProviderInfo(string Name, string Type, IReadOnlyList<CryptoProviderKeyDescriptor> Keys);
#region Risk Profile Commands
public static async Task HandleRiskProfileValidateAsync(
string inputPath,
string format,
string? outputPath,
bool strict,
bool verbose)
{
_ = verbose;
using var activity = CliActivitySource.Instance.StartActivity("cli.riskprofile.validate", ActivityKind.Client);
using var duration = CliMetrics.MeasureCommandDuration("risk-profile validate");
try
{
if (!File.Exists(inputPath))
{
AnsiConsole.MarkupLine("[red]Error:[/] Input file not found: {0}", Markup.Escape(inputPath));
Environment.ExitCode = 1;
return;
}
var profileJson = await File.ReadAllTextAsync(inputPath).ConfigureAwait(false);
var schema = StellaOps.Policy.RiskProfile.Schema.RiskProfileSchemaProvider.GetSchema();
var schemaVersion = StellaOps.Policy.RiskProfile.Schema.RiskProfileSchemaProvider.GetSchemaVersion();
JsonNode? profileNode;
try
{
profileNode = JsonNode.Parse(profileJson);
if (profileNode is null)
{
throw new InvalidOperationException("Parsed JSON is null.");
}
}
catch (JsonException ex)
{
AnsiConsole.MarkupLine("[red]Error:[/] Invalid JSON: {0}", Markup.Escape(ex.Message));
Environment.ExitCode = 1;
return;
}
var result = schema.Evaluate(profileNode);
var issues = new List<RiskProfileValidationIssue>();
if (!result.IsValid)
{
CollectValidationIssues(result, issues);
}
var report = new RiskProfileValidationReport(
FilePath: inputPath,
IsValid: result.IsValid,
SchemaVersion: schemaVersion,
Issues: issues);
if (format.Equals("json", StringComparison.OrdinalIgnoreCase))
{
var reportJson = JsonSerializer.Serialize(report, new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
});
if (!string.IsNullOrEmpty(outputPath))
{
await File.WriteAllTextAsync(outputPath, reportJson).ConfigureAwait(false);
AnsiConsole.MarkupLine("Validation report written to [cyan]{0}[/]", Markup.Escape(outputPath));
}
else
{
Console.WriteLine(reportJson);
}
}
else
{
if (result.IsValid)
{
AnsiConsole.MarkupLine("[green]✓[/] Profile is valid (schema v{0})", schemaVersion);
}
else
{
AnsiConsole.MarkupLine("[red]✗[/] Profile is invalid (schema v{0})", schemaVersion);
AnsiConsole.WriteLine();
var table = new Table();
table.AddColumn("Path");
table.AddColumn("Error");
table.AddColumn("Message");
foreach (var issue in issues)
{
table.AddRow(
Markup.Escape(issue.Path),
Markup.Escape(issue.Error),
Markup.Escape(issue.Message));
}
AnsiConsole.Write(table);
}
if (!string.IsNullOrEmpty(outputPath))
{
var reportJson = JsonSerializer.Serialize(report, new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
});
await File.WriteAllTextAsync(outputPath, reportJson).ConfigureAwait(false);
AnsiConsole.MarkupLine("Validation report written to [cyan]{0}[/]", Markup.Escape(outputPath));
}
}
Environment.ExitCode = result.IsValid ? 0 : (strict ? 1 : 0);
if (!result.IsValid && !strict)
{
Environment.ExitCode = 1;
}
}
catch (Exception ex)
{
AnsiConsole.MarkupLine("[red]Error:[/] {0}", Markup.Escape(ex.Message));
Environment.ExitCode = 1;
}
await Task.CompletedTask.ConfigureAwait(false);
}
public static async Task HandleRiskProfileSchemaAsync(string? outputPath, bool verbose)
{
_ = verbose;
using var activity = CliActivitySource.Instance.StartActivity("cli.riskprofile.schema", ActivityKind.Client);
using var duration = CliMetrics.MeasureCommandDuration("risk-profile schema");
try
{
var schemaText = StellaOps.Policy.RiskProfile.Schema.RiskProfileSchemaProvider.GetSchemaText();
var schemaVersion = StellaOps.Policy.RiskProfile.Schema.RiskProfileSchemaProvider.GetSchemaVersion();
if (!string.IsNullOrEmpty(outputPath))
{
await File.WriteAllTextAsync(outputPath, schemaText).ConfigureAwait(false);
AnsiConsole.MarkupLine("Risk profile schema v{0} written to [cyan]{1}[/]", schemaVersion, Markup.Escape(outputPath));
}
else
{
Console.WriteLine(schemaText);
}
Environment.ExitCode = 0;
}
catch (Exception ex)
{
AnsiConsole.MarkupLine("[red]Error:[/] {0}", Markup.Escape(ex.Message));
Environment.ExitCode = 1;
}
}
private static void CollectValidationIssues(
Json.Schema.EvaluationResults results,
List<RiskProfileValidationIssue> issues,
string path = "")
{
if (results.Errors is not null)
{
foreach (var (key, message) in results.Errors)
{
var instancePath = results.InstanceLocation?.ToString() ?? path;
issues.Add(new RiskProfileValidationIssue(instancePath, key, message));
}
}
if (results.Details is not null)
{
foreach (var detail in results.Details)
{
if (!detail.IsValid)
{
CollectValidationIssues(detail, issues, detail.InstanceLocation?.ToString() ?? path);
}
}
}
}
private sealed record RiskProfileValidationReport(
string FilePath,
bool IsValid,
string SchemaVersion,
IReadOnlyList<RiskProfileValidationIssue> Issues);
private sealed record RiskProfileValidationIssue(string Path, string Error, string Message);
#endregion
}

View File

@@ -54,6 +54,7 @@
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java/StellaOps.Scanner.Analyzers.Lang.Java.csproj" />
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Surface.Env/StellaOps.Scanner.Surface.Env.csproj" />
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Surface.Validation/StellaOps.Scanner.Surface.Validation.csproj" />
<ProjectReference Include="../../Policy/StellaOps.Policy.RiskProfile/StellaOps.Policy.RiskProfile.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(StellaOpsEnableCryptoPro)' == 'true'">