up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Reachability Corpus Validation / validate-corpus (push) Has been cancelled
Reachability Corpus Validation / validate-ground-truths (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Reachability Corpus Validation / determinism-check (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-14 15:50:38 +02:00
parent f1a39c4ce3
commit 233873f620
249 changed files with 29746 additions and 154 deletions

View File

@@ -56,6 +56,7 @@ internal static class CommandFactory
root.Add(BuildKmsCommand(services, verboseOption, cancellationToken));
root.Add(BuildVulnCommand(services, verboseOption, cancellationToken));
root.Add(BuildVexCommand(services, options, verboseOption, cancellationToken));
root.Add(BuildDecisionCommand(services, verboseOption, cancellationToken));
root.Add(BuildCryptoCommand(services, verboseOption, cancellationToken));
root.Add(BuildExportCommand(services, verboseOption, cancellationToken));
root.Add(BuildAttestCommand(services, verboseOption, cancellationToken));
@@ -74,11 +75,13 @@ internal static class CommandFactory
root.Add(BuildCvssCommand(services, verboseOption, cancellationToken));
root.Add(BuildRiskCommand(services, verboseOption, cancellationToken));
root.Add(BuildReachabilityCommand(services, verboseOption, cancellationToken));
root.Add(BuildGraphCommand(services, verboseOption, cancellationToken));
root.Add(BuildApiCommand(services, verboseOption, cancellationToken));
root.Add(BuildSdkCommand(services, verboseOption, cancellationToken));
root.Add(BuildMirrorCommand(services, verboseOption, cancellationToken));
root.Add(BuildAirgapCommand(services, verboseOption, cancellationToken));
root.Add(BuildDevPortalCommand(services, verboseOption, cancellationToken));
root.Add(BuildSymbolsCommand(services, verboseOption, cancellationToken));
root.Add(SystemCommandBuilder.BuildSystemCommand(services, verboseOption, cancellationToken));
var pluginLogger = loggerFactory.CreateLogger<CliCommandModuleLoader>();
@@ -3868,11 +3871,32 @@ internal static class CommandFactory
{
Description = "Emit raw JSON payload instead of formatted output."
};
// GAP-VEX-006: Evidence display options
var showCallPathsOption = new Option<bool>("--call-paths")
{
Description = "Include reachability call paths in the output."
};
var showGraphHashOption = new Option<bool>("--graph-hash")
{
Description = "Include call graph hash and CAS URI in the output."
};
var showRuntimeHitsOption = new Option<bool>("--runtime-hits")
{
Description = "Include runtime execution hits from probes."
};
var showFullEvidenceOption = new Option<bool>("--full-evidence")
{
Description = "Include all evidence types (call paths, graph hash, runtime hits, DSSE pointers)."
};
show.Add(showVulnIdArg);
show.Add(showProductKeyArg);
show.Add(showTenantOption);
show.Add(showJsonOption);
show.Add(showCallPathsOption);
show.Add(showGraphHashOption);
show.Add(showRuntimeHitsOption);
show.Add(showFullEvidenceOption);
show.SetAction((parseResult, _) =>
{
@@ -3880,14 +3904,29 @@ internal static class CommandFactory
var productKey = parseResult.GetValue(showProductKeyArg) ?? string.Empty;
var tenant = parseResult.GetValue(showTenantOption);
var emitJson = parseResult.GetValue(showJsonOption);
var includeCallPaths = parseResult.GetValue(showCallPathsOption);
var includeGraphHash = parseResult.GetValue(showGraphHashOption);
var includeRuntimeHits = parseResult.GetValue(showRuntimeHitsOption);
var fullEvidence = parseResult.GetValue(showFullEvidenceOption);
var verbose = parseResult.GetValue(verboseOption);
// Full evidence enables all flags
if (fullEvidence)
{
includeCallPaths = true;
includeGraphHash = true;
includeRuntimeHits = true;
}
return CommandHandlers.HandleVexConsensusShowAsync(
services,
vulnId,
productKey,
tenant,
emitJson,
includeCallPaths,
includeGraphHash,
includeRuntimeHits,
verbose,
cancellationToken);
});
@@ -4269,9 +4308,336 @@ internal static class CommandFactory
obs.Add(linkset);
vex.Add(obs);
// UI-VEX-401-032: VEX explain command for comprehensive decision explanation
var explain = new Command("explain", "Explain a VEX decision with full reachability evidence and verification status.");
var explainVulnIdArg = new Argument<string>("vulnerability-id")
{
Description = "Vulnerability identifier (e.g., CVE-2024-1234)."
};
var explainProductKeyOption = new Option<string>("--product-key", new[] { "-p" })
{
Description = "Product key for the decision.",
Required = true
};
var explainTenantOption = new Option<string?>("--tenant", new[] { "-t" })
{
Description = "Tenant identifier."
};
var explainCallPathsOption = new Option<bool>("--call-paths")
{
Description = "Include call path evidence with full frame details."
};
explainCallPathsOption.SetDefaultValue(true);
var explainRuntimeHitsOption = new Option<bool>("--runtime-hits")
{
Description = "Include runtime execution hit evidence."
};
explainRuntimeHitsOption.SetDefaultValue(true);
var explainGraphOption = new Option<bool>("--graph")
{
Description = "Include reachability graph metadata."
};
explainGraphOption.SetDefaultValue(true);
var explainDsseOption = new Option<bool>("--dsse")
{
Description = "Include DSSE envelope details."
};
var explainRekorOption = new Option<bool>("--rekor")
{
Description = "Include Rekor transparency log entry details."
};
var explainVerifyOption = new Option<bool>("--verify")
{
Description = "Verify attestation signatures and Rekor inclusion proofs."
};
var explainOfflineOption = new Option<bool>("--offline")
{
Description = "Perform verification using embedded proofs only (air-gapped mode)."
};
var explainJsonOption = new Option<bool>("--json")
{
Description = "Output as JSON for machine processing."
};
explain.Add(explainVulnIdArg);
explain.Add(explainProductKeyOption);
explain.Add(explainTenantOption);
explain.Add(explainCallPathsOption);
explain.Add(explainRuntimeHitsOption);
explain.Add(explainGraphOption);
explain.Add(explainDsseOption);
explain.Add(explainRekorOption);
explain.Add(explainVerifyOption);
explain.Add(explainOfflineOption);
explain.Add(explainJsonOption);
explain.Add(verboseOption);
explain.SetAction((parseResult, _) =>
{
var vulnId = parseResult.GetValue(explainVulnIdArg) ?? string.Empty;
var productKey = parseResult.GetValue(explainProductKeyOption) ?? string.Empty;
var tenant = parseResult.GetValue(explainTenantOption);
var includeCallPaths = parseResult.GetValue(explainCallPathsOption);
var includeRuntimeHits = parseResult.GetValue(explainRuntimeHitsOption);
var includeGraph = parseResult.GetValue(explainGraphOption);
var includeDsse = parseResult.GetValue(explainDsseOption);
var includeRekor = parseResult.GetValue(explainRekorOption);
var verify = parseResult.GetValue(explainVerifyOption);
var offline = parseResult.GetValue(explainOfflineOption);
var emitJson = parseResult.GetValue(explainJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleVexExplainAsync(
services,
vulnId,
productKey,
tenant,
includeCallPaths,
includeRuntimeHits,
includeGraph,
includeDsse,
includeRekor,
verify,
offline,
emitJson,
verbose,
cancellationToken);
});
vex.Add(explain);
return vex;
}
// CLI-VEX-401-011: VEX decision commands with DSSE/Rekor integration
private static Command BuildDecisionCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var decision = new Command("decision", "Manage VEX decisions with DSSE signing and Rekor transparency.");
// decision export
var export = new Command("export", "Export VEX decisions as OpenVEX documents with optional DSSE signing.");
var expTenantOption = new Option<string>("--tenant", new[] { "-t" })
{
Description = "Tenant identifier.",
Required = true
};
var expScanIdOption = new Option<string?>("--scan-id")
{
Description = "Filter by scan identifier."
};
var expVulnIdsOption = new Option<string[]>("--vuln-id")
{
Description = "Filter by vulnerability identifiers (repeatable).",
Arity = ArgumentArity.ZeroOrMore
};
var expPurlsOption = new Option<string[]>("--purl")
{
Description = "Filter by Package URLs (repeatable).",
Arity = ArgumentArity.ZeroOrMore
};
var expStatusesOption = new Option<string[]>("--status")
{
Description = "Filter by VEX status (not_affected, affected, fixed, under_investigation). Repeatable.",
Arity = ArgumentArity.ZeroOrMore
};
var expOutputOption = new Option<string>("--output", new[] { "-o" })
{
Description = "Output file path for the OpenVEX document.",
Required = true
};
var expFormatOption = new Option<string>("--format", new[] { "-f" })
{
Description = "Output format (openvex, dsse, ndjson). Default: openvex."
};
expFormatOption.SetDefaultValue("openvex");
var expSignOption = new Option<bool>("--sign", new[] { "-s" })
{
Description = "Sign the output with DSSE envelope."
};
var expRekorOption = new Option<bool>("--rekor")
{
Description = "Submit DSSE envelope to Rekor transparency log."
};
var expIncludeEvidenceOption = new Option<bool>("--include-evidence")
{
Description = "Include reachability evidence blocks in output."
};
expIncludeEvidenceOption.SetDefaultValue(true);
var expJsonOption = new Option<bool>("--json")
{
Description = "Output metadata as JSON to stdout."
};
export.Add(expTenantOption);
export.Add(expScanIdOption);
export.Add(expVulnIdsOption);
export.Add(expPurlsOption);
export.Add(expStatusesOption);
export.Add(expOutputOption);
export.Add(expFormatOption);
export.Add(expSignOption);
export.Add(expRekorOption);
export.Add(expIncludeEvidenceOption);
export.Add(expJsonOption);
export.SetAction((parseResult, _) =>
{
var tenant = parseResult.GetValue(expTenantOption) ?? string.Empty;
var scanId = parseResult.GetValue(expScanIdOption);
var vulnIds = parseResult.GetValue(expVulnIdsOption) ?? Array.Empty<string>();
var purls = parseResult.GetValue(expPurlsOption) ?? Array.Empty<string>();
var statuses = parseResult.GetValue(expStatusesOption) ?? Array.Empty<string>();
var output = parseResult.GetValue(expOutputOption) ?? string.Empty;
var format = parseResult.GetValue(expFormatOption) ?? "openvex";
var sign = parseResult.GetValue(expSignOption);
var rekor = parseResult.GetValue(expRekorOption);
var includeEvidence = parseResult.GetValue(expIncludeEvidenceOption);
var emitJson = parseResult.GetValue(expJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleDecisionExportAsync(
services,
tenant,
scanId,
vulnIds,
purls,
statuses,
output,
format,
sign,
rekor,
includeEvidence,
emitJson,
verbose,
cancellationToken);
});
decision.Add(export);
// decision verify
var verify = new Command("verify", "Verify DSSE signature and optional Rekor inclusion proof of a VEX decision document.");
var verifyFileArg = new Argument<string>("file")
{
Description = "Path to the VEX document or DSSE envelope to verify."
};
var verifyDigestOption = new Option<string?>("--digest")
{
Description = "Expected payload digest (sha256:...) to verify."
};
var verifyRekorOption = new Option<bool>("--rekor")
{
Description = "Verify Rekor inclusion proof."
};
var verifyRekorUuidOption = new Option<string?>("--rekor-uuid")
{
Description = "Rekor entry UUID for inclusion verification."
};
var verifyPublicKeyOption = new Option<string?>("--public-key")
{
Description = "Path to public key file for offline signature verification."
};
var verifyJsonOption = new Option<bool>("--json")
{
Description = "Output verification result as JSON."
};
verify.Add(verifyFileArg);
verify.Add(verifyDigestOption);
verify.Add(verifyRekorOption);
verify.Add(verifyRekorUuidOption);
verify.Add(verifyPublicKeyOption);
verify.Add(verifyJsonOption);
verify.SetAction((parseResult, _) =>
{
var file = parseResult.GetValue(verifyFileArg) ?? string.Empty;
var digest = parseResult.GetValue(verifyDigestOption);
var verifyRekor = parseResult.GetValue(verifyRekorOption);
var rekorUuid = parseResult.GetValue(verifyRekorUuidOption);
var publicKey = parseResult.GetValue(verifyPublicKeyOption);
var emitJson = parseResult.GetValue(verifyJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleDecisionVerifyAsync(
services,
file,
digest,
verifyRekor,
rekorUuid,
publicKey,
emitJson,
verbose,
cancellationToken);
});
decision.Add(verify);
// decision compare
var compare = new Command("compare", "Compare two VEX decision documents and show differences.");
var compareBaseArg = new Argument<string>("base")
{
Description = "Path to the base VEX document."
};
var compareTargetArg = new Argument<string>("target")
{
Description = "Path to the target VEX document to compare against base."
};
var compareOutputOption = new Option<string?>("--output", new[] { "-o" })
{
Description = "Output file path for the diff report."
};
var compareFormatOption = new Option<string>("--format", new[] { "-f" })
{
Description = "Output format (text, json, markdown). Default: text."
};
compareFormatOption.SetDefaultValue("text");
var compareShowUnchangedOption = new Option<bool>("--show-unchanged")
{
Description = "Include unchanged statements in output."
};
var compareSummaryOnlyOption = new Option<bool>("--summary-only")
{
Description = "Show only summary counts, not detailed diffs."
};
compare.Add(compareBaseArg);
compare.Add(compareTargetArg);
compare.Add(compareOutputOption);
compare.Add(compareFormatOption);
compare.Add(compareShowUnchangedOption);
compare.Add(compareSummaryOnlyOption);
compare.SetAction((parseResult, _) =>
{
var basePath = parseResult.GetValue(compareBaseArg) ?? string.Empty;
var targetPath = parseResult.GetValue(compareTargetArg) ?? string.Empty;
var output = parseResult.GetValue(compareOutputOption);
var format = parseResult.GetValue(compareFormatOption) ?? "text";
var showUnchanged = parseResult.GetValue(compareShowUnchangedOption);
var summaryOnly = parseResult.GetValue(compareSummaryOnlyOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleDecisionCompareAsync(
services,
basePath,
targetPath,
output,
format,
showUnchanged,
summaryOnly,
verbose,
cancellationToken);
});
decision.Add(compare);
return decision;
}
private static Command BuildConfigCommand(StellaOpsCliOptions options)
{
var config = new Command("config", "Inspect CLI configuration state.");
@@ -10458,6 +10824,120 @@ internal static class CommandFactory
return reachability;
}
// UI-CLI-401-007: stella graph command with DSSE pointers, runtime hits, predicates, counterfactuals
private static Command BuildGraphCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var graph = new Command("graph", "Call graph evidence commands.");
var tenantOption = new Option<string?>("--tenant", "-t")
{
Description = "Tenant context for the operation."
};
var jsonOption = new Option<bool>("--json")
{
Description = "Output in JSON format."
};
// stella graph explain
var explain = new Command("explain", "Explain call graph reachability with signed evidence.");
var graphIdOption = new Option<string>("--graph-id", "-g")
{
Description = "Call graph identifier.",
Required = true
};
var vulnerabilityIdOption = new Option<string?>("--vuln-id", "-v")
{
Description = "Vulnerability identifier to explain."
};
var packagePurlOption = new Option<string?>("--purl")
{
Description = "Package URL to explain."
};
var includeCallPathsOption = new Option<bool>("--call-paths")
{
Description = "Include detailed signed call paths in the explanation."
};
var includeRuntimeHitsOption = new Option<bool>("--runtime-hits")
{
Description = "Include runtime execution hits from instrumentation probes."
};
var includePredicatesOption = new Option<bool>("--predicates")
{
Description = "Include semantic predicates attached to evidence."
};
var includeDsseOption = new Option<bool>("--dsse")
{
Description = "Include DSSE envelope pointers and Rekor log entries."
};
var includeCounterfactualsOption = new Option<bool>("--counterfactuals")
{
Description = "Include counterfactual controls showing what-if scenarios."
};
var fullEvidenceOption = new Option<bool>("--full-evidence")
{
Description = "Include all evidence types (call paths, runtime hits, predicates, DSSE, counterfactuals)."
};
explain.Add(tenantOption);
explain.Add(graphIdOption);
explain.Add(vulnerabilityIdOption);
explain.Add(packagePurlOption);
explain.Add(includeCallPathsOption);
explain.Add(includeRuntimeHitsOption);
explain.Add(includePredicatesOption);
explain.Add(includeDsseOption);
explain.Add(includeCounterfactualsOption);
explain.Add(fullEvidenceOption);
explain.Add(jsonOption);
explain.Add(verboseOption);
explain.SetAction((parseResult, _) =>
{
var tenant = parseResult.GetValue(tenantOption);
var graphId = parseResult.GetValue(graphIdOption) ?? string.Empty;
var vulnerabilityId = parseResult.GetValue(vulnerabilityIdOption);
var packagePurl = parseResult.GetValue(packagePurlOption);
var includeCallPaths = parseResult.GetValue(includeCallPathsOption);
var includeRuntimeHits = parseResult.GetValue(includeRuntimeHitsOption);
var includePredicates = parseResult.GetValue(includePredicatesOption);
var includeDsse = parseResult.GetValue(includeDsseOption);
var includeCounterfactuals = parseResult.GetValue(includeCounterfactualsOption);
var fullEvidence = parseResult.GetValue(fullEvidenceOption);
var emitJson = parseResult.GetValue(jsonOption);
var verbose = parseResult.GetValue(verboseOption);
// Full evidence enables all flags
if (fullEvidence)
{
includeCallPaths = true;
includeRuntimeHits = true;
includePredicates = true;
includeDsse = true;
includeCounterfactuals = true;
}
return CommandHandlers.HandleGraphExplainAsync(
services,
tenant,
graphId,
vulnerabilityId,
packagePurl,
includeCallPaths,
includeRuntimeHits,
includePredicates,
includeDsse,
includeCounterfactuals,
emitJson,
verbose,
cancellationToken);
});
graph.Add(explain);
return graph;
}
// CLI-SDK-63-001: stella api command
private static Command BuildApiCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
@@ -11071,4 +11551,316 @@ internal static class CommandFactory
return devportal;
}
// SYMS-BUNDLE-401-014: Symbol bundle commands for air-gapped installations
private static Command BuildSymbolsCommand(IServiceProvider services, Option<bool> verboseOption, CancellationToken cancellationToken)
{
var symbols = new Command("symbols", "Manage symbol bundles for air-gapped installations.");
// symbols bundle build
var bundleBuild = new Command("bundle", "Build a deterministic symbol bundle.");
var bundleNameOption = new Option<string>("--name", new[] { "-n" })
{
Description = "Bundle name.",
Required = true
};
var bundleVersionOption = new Option<string>("--version")
{
Description = "Bundle version (SemVer).",
Required = true
};
var bundleSourceOption = new Option<string>("--source", new[] { "-s" })
{
Description = "Source directory containing symbol manifests.",
Required = true
};
var bundleOutputOption = new Option<string>("--output", new[] { "-o" })
{
Description = "Output directory for bundle archive.",
Required = true
};
var bundlePlatformOption = new Option<string?>("--platform")
{
Description = "Filter symbols by platform (e.g., linux-x64, win-x64)."
};
var bundleTenantOption = new Option<string?>("--tenant")
{
Description = "Filter symbols by tenant ID."
};
var bundleSignOption = new Option<bool>("--sign")
{
Description = "Sign the bundle with DSSE."
};
var bundleKeyPathOption = new Option<string?>("--key")
{
Description = "Path to signing key (PEM-encoded private key)."
};
var bundleKeyIdOption = new Option<string?>("--key-id")
{
Description = "Key ID for DSSE signature."
};
var bundleAlgorithmOption = new Option<string>("--algorithm")
{
Description = "Signing algorithm (ecdsa-p256, ed25519, rsa-pss-sha256)."
};
bundleAlgorithmOption.SetDefaultValue("ecdsa-p256");
var bundleRekorOption = new Option<bool>("--rekor")
{
Description = "Submit to Rekor transparency log."
};
var bundleRekorUrlOption = new Option<string>("--rekor-url")
{
Description = "Rekor server URL."
};
bundleRekorUrlOption.SetDefaultValue("https://rekor.sigstore.dev");
var bundleFormatOption = new Option<string>("--format")
{
Description = "Bundle format (zip, tar.gz)."
};
bundleFormatOption.SetDefaultValue("zip");
var bundleCompressionOption = new Option<int>("--compression")
{
Description = "Compression level (0-9)."
};
bundleCompressionOption.SetDefaultValue(6);
var bundleJsonOption = new Option<bool>("--json")
{
Description = "Output result as JSON."
};
bundleBuild.Add(bundleNameOption);
bundleBuild.Add(bundleVersionOption);
bundleBuild.Add(bundleSourceOption);
bundleBuild.Add(bundleOutputOption);
bundleBuild.Add(bundlePlatformOption);
bundleBuild.Add(bundleTenantOption);
bundleBuild.Add(bundleSignOption);
bundleBuild.Add(bundleKeyPathOption);
bundleBuild.Add(bundleKeyIdOption);
bundleBuild.Add(bundleAlgorithmOption);
bundleBuild.Add(bundleRekorOption);
bundleBuild.Add(bundleRekorUrlOption);
bundleBuild.Add(bundleFormatOption);
bundleBuild.Add(bundleCompressionOption);
bundleBuild.Add(bundleJsonOption);
bundleBuild.Add(verboseOption);
bundleBuild.SetAction((parseResult, _) =>
{
var name = parseResult.GetValue(bundleNameOption)!;
var version = parseResult.GetValue(bundleVersionOption)!;
var source = parseResult.GetValue(bundleSourceOption)!;
var output = parseResult.GetValue(bundleOutputOption)!;
var platform = parseResult.GetValue(bundlePlatformOption);
var tenant = parseResult.GetValue(bundleTenantOption);
var sign = parseResult.GetValue(bundleSignOption);
var keyPath = parseResult.GetValue(bundleKeyPathOption);
var keyId = parseResult.GetValue(bundleKeyIdOption);
var algorithm = parseResult.GetValue(bundleAlgorithmOption)!;
var rekor = parseResult.GetValue(bundleRekorOption);
var rekorUrl = parseResult.GetValue(bundleRekorUrlOption)!;
var format = parseResult.GetValue(bundleFormatOption)!;
var compression = parseResult.GetValue(bundleCompressionOption);
var json = parseResult.GetValue(bundleJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleSymbolBundleBuildAsync(
services,
name,
version,
source,
output,
platform,
tenant,
sign,
keyPath,
keyId,
algorithm,
rekor,
rekorUrl,
format,
compression,
json,
verbose,
cancellationToken);
});
symbols.Add(bundleBuild);
// symbols verify
var verify = new Command("verify", "Verify a symbol bundle's integrity and signatures.");
var verifyBundleOption = new Option<string>("--bundle", new[] { "-b" })
{
Description = "Path to bundle archive.",
Required = true
};
var verifyPublicKeyOption = new Option<string?>("--public-key")
{
Description = "Path to public key for signature verification."
};
var verifyRekorOfflineOption = new Option<bool>("--rekor-offline")
{
Description = "Verify Rekor inclusion proof offline."
};
verifyRekorOfflineOption.SetDefaultValue(true);
var verifyRekorKeyOption = new Option<string?>("--rekor-key")
{
Description = "Path to Rekor public key for offline verification."
};
var verifyHashesOption = new Option<bool>("--verify-hashes")
{
Description = "Verify all blob hashes."
};
verifyHashesOption.SetDefaultValue(true);
var verifyJsonOption = new Option<bool>("--json")
{
Description = "Output result as JSON."
};
verify.Add(verifyBundleOption);
verify.Add(verifyPublicKeyOption);
verify.Add(verifyRekorOfflineOption);
verify.Add(verifyRekorKeyOption);
verify.Add(verifyHashesOption);
verify.Add(verifyJsonOption);
verify.Add(verboseOption);
verify.SetAction((parseResult, _) =>
{
var bundlePath = parseResult.GetValue(verifyBundleOption)!;
var publicKeyPath = parseResult.GetValue(verifyPublicKeyOption);
var rekorOffline = parseResult.GetValue(verifyRekorOfflineOption);
var rekorKeyPath = parseResult.GetValue(verifyRekorKeyOption);
var verifyHashes = parseResult.GetValue(verifyHashesOption);
var json = parseResult.GetValue(verifyJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleSymbolBundleVerifyAsync(
services,
bundlePath,
publicKeyPath,
rekorOffline,
rekorKeyPath,
verifyHashes,
json,
verbose,
cancellationToken);
});
symbols.Add(verify);
// symbols extract
var extract = new Command("extract", "Extract symbols from a bundle.");
var extractBundleOption = new Option<string>("--bundle", new[] { "-b" })
{
Description = "Path to bundle archive.",
Required = true
};
var extractOutputOption = new Option<string>("--output", new[] { "-o" })
{
Description = "Output directory.",
Required = true
};
var extractVerifyOption = new Option<bool>("--verify")
{
Description = "Verify bundle before extraction."
};
extractVerifyOption.SetDefaultValue(true);
var extractPlatformOption = new Option<string?>("--platform")
{
Description = "Extract only symbols for this platform."
};
var extractOverwriteOption = new Option<bool>("--overwrite")
{
Description = "Overwrite existing files."
};
var extractManifestsOnlyOption = new Option<bool>("--manifests-only")
{
Description = "Extract only manifest files (not blobs)."
};
var extractJsonOption = new Option<bool>("--json")
{
Description = "Output result as JSON."
};
extract.Add(extractBundleOption);
extract.Add(extractOutputOption);
extract.Add(extractVerifyOption);
extract.Add(extractPlatformOption);
extract.Add(extractOverwriteOption);
extract.Add(extractManifestsOnlyOption);
extract.Add(extractJsonOption);
extract.Add(verboseOption);
extract.SetAction((parseResult, _) =>
{
var bundlePath = parseResult.GetValue(extractBundleOption)!;
var outputDir = parseResult.GetValue(extractOutputOption)!;
var verifyFirst = parseResult.GetValue(extractVerifyOption);
var platform = parseResult.GetValue(extractPlatformOption);
var overwrite = parseResult.GetValue(extractOverwriteOption);
var manifestsOnly = parseResult.GetValue(extractManifestsOnlyOption);
var json = parseResult.GetValue(extractJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleSymbolBundleExtractAsync(
services,
bundlePath,
outputDir,
verifyFirst,
platform,
overwrite,
manifestsOnly,
json,
verbose,
cancellationToken);
});
symbols.Add(extract);
// symbols inspect
var inspect = new Command("inspect", "Inspect bundle contents without extracting.");
var inspectBundleOption = new Option<string>("--bundle", new[] { "-b" })
{
Description = "Path to bundle archive.",
Required = true
};
var inspectEntriesOption = new Option<bool>("--entries")
{
Description = "List all entries in the bundle."
};
var inspectJsonOption = new Option<bool>("--json")
{
Description = "Output result as JSON."
};
inspect.Add(inspectBundleOption);
inspect.Add(inspectEntriesOption);
inspect.Add(inspectJsonOption);
inspect.Add(verboseOption);
inspect.SetAction((parseResult, _) =>
{
var bundlePath = parseResult.GetValue(inspectBundleOption)!;
var showEntries = parseResult.GetValue(inspectEntriesOption);
var json = parseResult.GetValue(inspectJsonOption);
var verbose = parseResult.GetValue(verboseOption);
return CommandHandlers.HandleSymbolBundleInspectAsync(
services,
bundlePath,
showEntries,
json,
verbose,
cancellationToken);
});
symbols.Add(inspect);
return symbols;
}
}