From ee2182d9dfd4fbe446bf718ce8c1f4c8b7614dc4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 21 Aug 2023 18:15:39 +0200 Subject: [PATCH 1/2] MergeCommand: add options to --validate-output(-relaxed) and optionally avoid writing an invalid document Signed-off-by: Jim Klimov --- README.md | 2 + src/cyclonedx/Commands/MergeCommand.cs | 43 ++++++++++++++++++- src/cyclonedx/Commands/MergeCommandOptions.cs | 2 + 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd59de2..b9652c7 100755 --- a/README.md +++ b/README.md @@ -200,6 +200,8 @@ Options: --group Provide the group of software the merged BOM describes. --name Provide the name of software the merged BOM describes (required for hierarchical merging). --version Provide the version of software the merged BOM describes (required for hierarchical merging). + --validate-output Perform validation of the resulting document before writing it, and do not write if it fails. + --validate-output-relaxed Perform validation of the resulting document, and still write the file for troubleshooting if it fails. ``` Note: To perform a hierarchical merge all BOMs need the subject of the BOM diff --git a/src/cyclonedx/Commands/MergeCommand.cs b/src/cyclonedx/Commands/MergeCommand.cs index 5c150d5..231a8a0 100644 --- a/src/cyclonedx/Commands/MergeCommand.cs +++ b/src/cyclonedx/Commands/MergeCommand.cs @@ -39,6 +39,8 @@ public static void Configure(RootCommand rootCommand) subCommand.Add(new Option("--group", "Provide the group of software the merged BOM describes.")); subCommand.Add(new Option("--name", "Provide the name of software the merged BOM describes (required for hierarchical merging).")); subCommand.Add(new Option("--version", "Provide the version of software the merged BOM describes (required for hierarchical merging).")); + subCommand.Add(new Option("--validate-output", "Perform validation of the resulting document before writing it, and do not write if it fails.")); + subCommand.Add(new Option("--validate-output-relaxed", "Perform validation of the resulting document, and still write the file for troubleshooting if it fails.")); subCommand.Handler = CommandHandler.Create(Merge); rootCommand.Add(subCommand); } @@ -104,13 +106,52 @@ public static async Task Merge(MergeCommandOptions options) outputBom.Version = 1; outputBom.SerialNumber = "urn:uuid:" + System.Guid.NewGuid().ToString(); + ValidationResult validationResult = null; + if (options.ValidateOutput || options.ValidateOutputRelaxed) + { + Console.WriteLine("Validating merged BOM..."); + + // TOTHINK: let it pick versions (no arg) if current does not cut it... + // else SpecificationVersionHelpers.CurrentVersion ? + validationResult = Json.Validator.Validate(Json.Serializer.Serialize(outputBom), outputBom.SpecVersion); + + if (validationResult.Messages != null) + { + foreach (var message in validationResult.Messages) + { + Console.WriteLine(message); + } + } + + if (validationResult.Valid) + { + Console.WriteLine("Merged BOM validated successfully."); + } + else + { + Console.WriteLine("Merged BOM is not valid."); + if (options.ValidateOutput) + { + // Not-relaxed mode: abort! + Console.WriteLine($" Total {outputBom.Components?.Count ?? 0} components"); + return (int)ExitCode.SignatureFailedVerification; + } + } + } + if (!outputToConsole) { Console.WriteLine("Writing output file..."); Console.WriteLine($" Total {outputBom.Components?.Count ?? 0} components"); } - return await CliUtils.OutputBomHelper(outputBom, options.OutputFormat, options.OutputFile).ConfigureAwait(false); + int res = await CliUtils.OutputBomHelper(outputBom, options.OutputFormat, options.OutputFile).ConfigureAwait(false); + if (validationResult != null && (!validationResult.Valid)) + { + // Relaxed mode: abort after writing the file! + return (int)ExitCode.SignatureFailedVerification; + } + return res; } private static async Task> InputBoms(IEnumerable inputFilenames, CycloneDXBomFormat inputFormat, bool outputToConsole) diff --git a/src/cyclonedx/Commands/MergeCommandOptions.cs b/src/cyclonedx/Commands/MergeCommandOptions.cs index f3078c4..8e586a2 100644 --- a/src/cyclonedx/Commands/MergeCommandOptions.cs +++ b/src/cyclonedx/Commands/MergeCommandOptions.cs @@ -28,5 +28,7 @@ public class MergeCommandOptions public string Group { get; set; } public string Name { get; set; } public string Version { get; set; } + public bool ValidateOutput { get; set; } + public bool ValidateOutputRelaxed { get; set; } } } \ No newline at end of file From ed8e30b34a2d652ae8347db1474eff455620a558 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Aug 2023 09:11:06 +0200 Subject: [PATCH 2/2] MergeCommand.cs: fix handling of "--validate-output-relaxed" flag to do write the file for troubleshooting Signed-off-by: Jim Klimov --- src/cyclonedx/Commands/MergeCommand.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cyclonedx/Commands/MergeCommand.cs b/src/cyclonedx/Commands/MergeCommand.cs index 231a8a0..8f81865 100644 --- a/src/cyclonedx/Commands/MergeCommand.cs +++ b/src/cyclonedx/Commands/MergeCommand.cs @@ -109,6 +109,8 @@ public static async Task Merge(MergeCommandOptions options) ValidationResult validationResult = null; if (options.ValidateOutput || options.ValidateOutputRelaxed) { + // Note that C# CLI args parser seems to set both booleans + // for one "--validate-output-relaxed" flag Console.WriteLine("Validating merged BOM..."); // TOTHINK: let it pick versions (no arg) if current does not cut it... @@ -130,9 +132,10 @@ public static async Task Merge(MergeCommandOptions options) else { Console.WriteLine("Merged BOM is not valid."); - if (options.ValidateOutput) + if (!(options.ValidateOutputRelaxed)) { // Not-relaxed mode: abort! + Console.WriteLine("NOT writing output file..."); Console.WriteLine($" Total {outputBom.Components?.Count ?? 0} components"); return (int)ExitCode.SignatureFailedVerification; }