license switch agpl -> busl1, sprints work, new product advisories
This commit is contained in:
@@ -3,13 +3,12 @@
|
||||
// Sprint: SPRINT_20260119_004_BinaryIndex_deltasig_extensions
|
||||
// Task: DSIG-006 - CLI Updates
|
||||
// Description: CLI commands for DeltaSig v2 predicate operations.
|
||||
// Uses System.CommandLine 2.0.1 API with SetAction pattern.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using StellaOps.BinaryIndex.DeltaSig;
|
||||
using StellaOps.BinaryIndex.DeltaSig.Attestation;
|
||||
|
||||
namespace StellaOps.Cli.Plugins.DeltaSig;
|
||||
@@ -28,7 +27,7 @@ public static class DeltaSigCliCommands
|
||||
public static Command BuildDeltaSigCommand(Option<bool> verboseOption)
|
||||
{
|
||||
var deltasig = new Command("deltasig", "DeltaSig predicate generation and verification.");
|
||||
deltasig.AddAlias("dsig");
|
||||
deltasig.Aliases.Add("dsig");
|
||||
|
||||
// Add subcommands
|
||||
deltasig.Add(BuildInspectCommand(verboseOption));
|
||||
@@ -45,34 +44,47 @@ public static class DeltaSigCliCommands
|
||||
Description = "Path to DeltaSig predicate JSON file"
|
||||
};
|
||||
|
||||
var formatOption = new Option<string>("--format", () => "summary")
|
||||
var formatOption = new Option<string>("--format", "-f")
|
||||
{
|
||||
Description = "Output format: summary, json, detailed"
|
||||
Description = "Output format: summary, json, detailed",
|
||||
DefaultValueFactory = _ => "summary"
|
||||
};
|
||||
|
||||
var showEvidenceOption = new Option<bool>("--show-evidence", "-e")
|
||||
{
|
||||
Description = "Show provenance and IR diff evidence details"
|
||||
};
|
||||
|
||||
var outputV2Option = new Option<bool>("--v2", "--output-v2")
|
||||
{
|
||||
Description = "Force v2 format output when using json format"
|
||||
};
|
||||
formatOption.AddAlias("-f");
|
||||
|
||||
var inspect = new Command("inspect", "Inspect a DeltaSig predicate file.")
|
||||
{
|
||||
pathArg,
|
||||
formatOption,
|
||||
showEvidenceOption,
|
||||
outputV2Option,
|
||||
verboseOption
|
||||
};
|
||||
|
||||
inspect.SetHandler(async (InvocationContext context) =>
|
||||
inspect.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var file = context.ParseResult.GetValueForArgument(pathArg);
|
||||
var format = context.ParseResult.GetValueForOption(formatOption) ?? "summary";
|
||||
var file = parseResult.GetValue(pathArg)!;
|
||||
var format = parseResult.GetValue(formatOption) ?? "summary";
|
||||
var showEvidence = parseResult.GetValue(showEvidenceOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
if (!file.Exists)
|
||||
{
|
||||
Console.Error.WriteLine($"File not found: {file.FullName}");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = await File.ReadAllTextAsync(file.FullName);
|
||||
var json = await File.ReadAllTextAsync(file.FullName, ct);
|
||||
var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
@@ -86,7 +98,7 @@ public static class DeltaSigCliCommands
|
||||
if (format == "json")
|
||||
{
|
||||
Console.WriteLine(json);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Console.WriteLine($"DeltaSig Predicate: {(isV2 ? "v2" : "v1")}");
|
||||
@@ -97,7 +109,7 @@ public static class DeltaSigCliCommands
|
||||
var v2 = JsonSerializer.Deserialize<DeltaSigPredicateV2>(json, JsonOptions);
|
||||
if (v2 != null)
|
||||
{
|
||||
PrintV2Summary(v2, format == "detailed");
|
||||
PrintV2Summary(v2, format == "detailed", showEvidence);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -109,12 +121,12 @@ public static class DeltaSigCliCommands
|
||||
}
|
||||
}
|
||||
|
||||
context.ExitCode = 0;
|
||||
return 0;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to parse predicate: {ex.Message}");
|
||||
context.ExitCode = 1;
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -128,11 +140,10 @@ public static class DeltaSigCliCommands
|
||||
Description = "Path to source predicate JSON file"
|
||||
};
|
||||
|
||||
var outputOption = new Option<FileInfo?>("--output")
|
||||
var outputOption = new Option<FileInfo?>("--output", "-o")
|
||||
{
|
||||
Description = "Output file path (default: stdout)"
|
||||
};
|
||||
outputOption.AddAlias("-o");
|
||||
|
||||
var toV2Option = new Option<bool>("--to-v2")
|
||||
{
|
||||
@@ -153,31 +164,29 @@ public static class DeltaSigCliCommands
|
||||
verboseOption
|
||||
};
|
||||
|
||||
convert.SetHandler(async (InvocationContext context) =>
|
||||
convert.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var file = context.ParseResult.GetValueForArgument(inputArg);
|
||||
var output = context.ParseResult.GetValueForOption(outputOption);
|
||||
var toV2 = context.ParseResult.GetValueForOption(toV2Option);
|
||||
var toV1 = context.ParseResult.GetValueForOption(toV1Option);
|
||||
var verbose = context.ParseResult.GetValueForOption(verboseOption);
|
||||
var file = parseResult.GetValue(inputArg)!;
|
||||
var output = parseResult.GetValue(outputOption);
|
||||
var toV2 = parseResult.GetValue(toV2Option);
|
||||
var toV1 = parseResult.GetValue(toV1Option);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
if (toV2 == toV1)
|
||||
{
|
||||
Console.Error.WriteLine("Specify exactly one of --to-v1 or --to-v2");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!file.Exists)
|
||||
{
|
||||
Console.Error.WriteLine($"File not found: {file.FullName}");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = await File.ReadAllTextAsync(file.FullName);
|
||||
var json = await File.ReadAllTextAsync(file.FullName, ct);
|
||||
string resultJson;
|
||||
|
||||
if (toV2)
|
||||
@@ -186,8 +195,7 @@ public static class DeltaSigCliCommands
|
||||
if (v1 == null)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to parse v1 predicate");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
var v2 = DeltaSigPredicateConverter.ToV2(v1);
|
||||
@@ -204,8 +212,7 @@ public static class DeltaSigCliCommands
|
||||
if (v2 == null)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to parse v2 predicate");
|
||||
context.ExitCode = 1;
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
var v1 = DeltaSigPredicateConverter.ToV1(v2);
|
||||
@@ -219,7 +226,7 @@ public static class DeltaSigCliCommands
|
||||
|
||||
if (output != null)
|
||||
{
|
||||
await File.WriteAllTextAsync(output.FullName, resultJson);
|
||||
await File.WriteAllTextAsync(output.FullName, resultJson, ct);
|
||||
Console.WriteLine($"Written to {output.FullName}");
|
||||
}
|
||||
else
|
||||
@@ -227,12 +234,12 @@ public static class DeltaSigCliCommands
|
||||
Console.WriteLine(resultJson);
|
||||
}
|
||||
|
||||
context.ExitCode = 0;
|
||||
return 0;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to parse predicate: {ex.Message}");
|
||||
context.ExitCode = 1;
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -243,7 +250,7 @@ public static class DeltaSigCliCommands
|
||||
{
|
||||
var version = new Command("version", "Show DeltaSig schema version information.");
|
||||
|
||||
version.SetHandler((InvocationContext context) =>
|
||||
version.SetAction((_, _) =>
|
||||
{
|
||||
Console.WriteLine("DeltaSig Schema Versions:");
|
||||
Console.WriteLine($" v1: {DeltaSigPredicate.PredicateType}");
|
||||
@@ -255,7 +262,7 @@ public static class DeltaSigCliCommands
|
||||
Console.WriteLine(" - Explicit verdict and confidence scores");
|
||||
Console.WriteLine(" - Function-level match states (vulnerable/patched/modified)");
|
||||
Console.WriteLine(" - Enhanced tooling metadata");
|
||||
context.ExitCode = 0;
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
|
||||
return version;
|
||||
@@ -284,7 +291,7 @@ public static class DeltaSigCliCommands
|
||||
Console.WriteLine("Deltas:");
|
||||
foreach (var delta in v1.Delta.Take(10))
|
||||
{
|
||||
Console.WriteLine($" {delta.FunctionId}: {delta.State}");
|
||||
Console.WriteLine($" {delta.FunctionId}: {delta.ChangeType}");
|
||||
}
|
||||
if (v1.Delta.Count > 10)
|
||||
{
|
||||
@@ -293,7 +300,7 @@ public static class DeltaSigCliCommands
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintV2Summary(DeltaSigPredicateV2 v2, bool detailed)
|
||||
private static void PrintV2Summary(DeltaSigPredicateV2 v2, bool detailed, bool showEvidence)
|
||||
{
|
||||
Console.WriteLine($"PURL: {v2.Subject.Purl}");
|
||||
Console.WriteLine($"Verdict: {v2.Verdict}");
|
||||
@@ -323,7 +330,7 @@ public static class DeltaSigCliCommands
|
||||
Console.WriteLine($" Match Algorithm:{v2.Tooling.MatchAlgorithm}");
|
||||
}
|
||||
|
||||
if (detailed)
|
||||
if (detailed || showEvidence)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Function Matches:");
|
||||
@@ -332,6 +339,20 @@ public static class DeltaSigCliCommands
|
||||
var provenance = match.SymbolProvenance != null ? $"[{match.SymbolProvenance.SourceId}]" : "";
|
||||
var irDiff = match.IrDiff != null ? "[IR]" : "";
|
||||
Console.WriteLine($" {match.Name}: {match.MatchState} ({match.MatchScore:P0}) {provenance}{irDiff}");
|
||||
|
||||
if (showEvidence)
|
||||
{
|
||||
if (match.SymbolProvenance != null)
|
||||
{
|
||||
Console.WriteLine($" Provenance: {match.SymbolProvenance.SourceId} @ {match.SymbolProvenance.FetchedAt:u}");
|
||||
Console.WriteLine($" Observation: {match.SymbolProvenance.ObservationId}");
|
||||
}
|
||||
if (match.IrDiff != null)
|
||||
{
|
||||
Console.WriteLine($" IR Diff: {match.IrDiff.CasDigest}");
|
||||
Console.WriteLine($" Changes: +{match.IrDiff.AddedBlocks} -{match.IrDiff.RemovedBlocks} ~{match.IrDiff.ChangedInstructions}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v2.FunctionMatches.Count > 10)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user