Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
// Licensed to StellaOps under the AGPL-3.0-or-later license.
|
||||
|
||||
using System.CommandLine;
|
||||
|
||||
namespace StellaOps.Cli.Commands.ReachGraph;
|
||||
|
||||
/// <summary>
|
||||
/// CLI command group for reachability graph operations.
|
||||
/// stella reachgraph [slice|replay|verify]
|
||||
/// </summary>
|
||||
public static class ReachGraphCommandGroup
|
||||
{
|
||||
public static Command Build()
|
||||
{
|
||||
var command = new Command("reachgraph", "Reachability graph operations");
|
||||
|
||||
command.Add(BuildSliceCommand());
|
||||
command.Add(BuildReplayCommand());
|
||||
command.Add(BuildVerifyCommand());
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private static Command BuildSliceCommand()
|
||||
{
|
||||
var digestOption = new Option<string>("--digest", "-d")
|
||||
{
|
||||
Description = "BLAKE3 digest of the graph",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var cveOption = new Option<string?>("--cve")
|
||||
{
|
||||
Description = "CVE identifier to slice by"
|
||||
};
|
||||
|
||||
var purlOption = new Option<string?>("--purl", "-p")
|
||||
{
|
||||
Description = "Package PURL pattern to slice by"
|
||||
};
|
||||
|
||||
var entrypointOption = new Option<string?>("--entrypoint", "-e")
|
||||
{
|
||||
Description = "Entrypoint path or symbol pattern"
|
||||
};
|
||||
|
||||
var fileOption = new Option<string?>("--file", "-f")
|
||||
{
|
||||
Description = "File path pattern (glob) to slice by"
|
||||
};
|
||||
|
||||
var depthOption = new Option<int>("--depth")
|
||||
{
|
||||
Description = "Max traversal depth"
|
||||
}.SetDefaultValue(3);
|
||||
|
||||
var outputOption = new Option<string>("--output", "-o")
|
||||
{
|
||||
Description = "Output format: json, table, or dot (GraphViz)"
|
||||
}.SetDefaultValue("table");
|
||||
|
||||
var apiUrlOption = new Option<string>("--api-url")
|
||||
{
|
||||
Description = "ReachGraph Store API URL"
|
||||
}.SetDefaultValue("http://localhost:5000");
|
||||
|
||||
var command = new Command("slice", "Query a slice of a reachability graph")
|
||||
{
|
||||
digestOption,
|
||||
cveOption,
|
||||
purlOption,
|
||||
entrypointOption,
|
||||
fileOption,
|
||||
depthOption,
|
||||
outputOption,
|
||||
apiUrlOption
|
||||
};
|
||||
|
||||
command.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var digest = parseResult.GetValue(digestOption) ?? string.Empty;
|
||||
var cve = parseResult.GetValue(cveOption);
|
||||
var purl = parseResult.GetValue(purlOption);
|
||||
var entrypoint = parseResult.GetValue(entrypointOption);
|
||||
var file = parseResult.GetValue(fileOption);
|
||||
var depth = parseResult.GetValue(depthOption);
|
||||
var output = parseResult.GetValue(outputOption) ?? "table";
|
||||
var apiUrl = parseResult.GetValue(apiUrlOption) ?? "http://localhost:5000";
|
||||
|
||||
await ReachGraphCommandHandlers.HandleSliceAsync(
|
||||
digest, cve, purl, entrypoint, file, depth, output, apiUrl);
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private static Command BuildReplayCommand()
|
||||
{
|
||||
var inputsOption = new Option<string>("--inputs", "-i")
|
||||
{
|
||||
Description = "Comma-separated input files (sbom.json,vex.json,callgraph.json)",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var expectedOption = new Option<string>("--expected", "-e")
|
||||
{
|
||||
Description = "Expected BLAKE3 digest",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var outputFileOption = new Option<string?>("--output-file", "-o")
|
||||
{
|
||||
Description = "Write computed graph to file"
|
||||
};
|
||||
|
||||
var verboseOption = new Option<bool>("--verbose", "-v")
|
||||
{
|
||||
Description = "Show verbose output"
|
||||
};
|
||||
|
||||
var apiUrlOption = new Option<string>("--api-url")
|
||||
{
|
||||
Description = "ReachGraph Store API URL"
|
||||
}.SetDefaultValue("http://localhost:5000");
|
||||
|
||||
var command = new Command("replay", "Verify deterministic replay of a graph")
|
||||
{
|
||||
inputsOption,
|
||||
expectedOption,
|
||||
outputFileOption,
|
||||
verboseOption,
|
||||
apiUrlOption
|
||||
};
|
||||
|
||||
command.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var inputs = parseResult.GetValue(inputsOption) ?? string.Empty;
|
||||
var expected = parseResult.GetValue(expectedOption) ?? string.Empty;
|
||||
var outputFile = parseResult.GetValue(outputFileOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
var apiUrl = parseResult.GetValue(apiUrlOption) ?? "http://localhost:5000";
|
||||
|
||||
await ReachGraphCommandHandlers.HandleReplayAsync(
|
||||
inputs, expected, outputFile, verbose, apiUrl);
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private static Command BuildVerifyCommand()
|
||||
{
|
||||
var digestOption = new Option<string>("--digest", "-d")
|
||||
{
|
||||
Description = "BLAKE3 digest of the graph to verify",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var apiUrlOption = new Option<string>("--api-url")
|
||||
{
|
||||
Description = "ReachGraph Store API URL"
|
||||
}.SetDefaultValue("http://localhost:5000");
|
||||
|
||||
var command = new Command("verify", "Verify signatures on a reachability graph")
|
||||
{
|
||||
digestOption,
|
||||
apiUrlOption
|
||||
};
|
||||
|
||||
command.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var digest = parseResult.GetValue(digestOption) ?? string.Empty;
|
||||
var apiUrl = parseResult.GetValue(apiUrlOption) ?? "http://localhost:5000";
|
||||
|
||||
await ReachGraphCommandHandlers.HandleVerifyAsync(digest, apiUrl);
|
||||
});
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user