From afe887b7d327e3b9a521baf26b7f63d499ef253c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 5 Oct 2023 16:45:06 +0200 Subject: [PATCH 1/2] Introduce "cyclonedx rename-entity" command Primarily as a practical test case for Bom.WalkThis() and Bom.RenameBomRef() methods introduced in the library, but may be useful to have exposed for end-users. Signed-off-by: Jim Klimov --- README.md | 49 +++++++++ src/cyclonedx/Commands/RenameEntityCommand.cs | 101 ++++++++++++++++++ .../Commands/RenameEntityCommandOptions.cs | 30 ++++++ src/cyclonedx/Program.cs | 1 + 4 files changed, 181 insertions(+) create mode 100644 src/cyclonedx/Commands/RenameEntityCommand.cs create mode 100644 src/cyclonedx/Commands/RenameEntityCommandOptions.cs diff --git a/README.md b/README.md index cd59de2..3fe6b4a 100755 --- a/README.md +++ b/README.md @@ -213,6 +213,55 @@ Merge two XML formatted BOMs: Merging two BOMs and piping output to additional tools: `cyclonedx-cli merge --input-files sbom1.xml sbom2.xml --output-format json | grep "something"` +## Rename Entity command + +Rename an entity identified by "bom-ref" (formally a "refType") in the document +and/or back-references to such entity (formally a "refLinkType", typically as +a "ref" property; or certain lists' items). + +``` +rename-entity + Rename an entity identified by a "bom-ref" (including back-references to it) in the BOM document + +Usage: + cyclonedx [options] rename-entity + +Options: + --input-file Input BOM filename. + --output-file Output BOM filename, will write to stdout if no value provided. + --old-ref Old value of "bom-ref" entity identifier (or "ref" values or certain list items pointing to it). + --new-ref New value of "bom-ref" entity identifier (or "ref" values or certain list items pointing to it). + --input-format Specify input file format. + --output-format Specify output file format. +``` + +Keep in mind that these identifiers are arbitrary strings that have a meaning +within the Bom document (and should uniquely identify one entity in its scope). +While in some cases these identifiers are meaningful (e.g. "purl" values used +as "bom-ref" by the cyclonedx-maven-plugin), they may also validly be random +UUIDs or collision-prone strings like "1", "2", "3"... + +They may be opportunistically used as anchors for cross-document references, +so in some cases a back-reference may point to a string for which there is no +"bom-ref" in the same document (see relevant CycloneDX specification version +for details). + +This renaming operation also modifies the output document metadata, to reflect +the modification compared to the input document. + +Basic error-checking, such as attempt to re-use an already existing identifier, +is performed. + +### Examples + +Rename an entity: +``` +cyclonedx rename-entity --input-file sbom.json --output-format xml \ + --oldref "pkg:maven/org.yaml/snakeyaml@1.33?type=jar" \ + --newref "thirdpartylibs:org.yaml:snakeyaml:1.33:jar" \ +| grep "thirdparty" +``` + ## Sign Command Sign a BOM or file diff --git a/src/cyclonedx/Commands/RenameEntityCommand.cs b/src/cyclonedx/Commands/RenameEntityCommand.cs new file mode 100644 index 0000000..4a53886 --- /dev/null +++ b/src/cyclonedx/Commands/RenameEntityCommand.cs @@ -0,0 +1,101 @@ +// This file is part of CycloneDX CLI Tool +// +// Licensed under the Apache License, Version 2.0 (the “License”); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an “AS IS” BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) OWASP Foundation. All Rights Reserved. +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Threading.Tasks; +using CycloneDX.Models; +using CycloneDX.Utils; +using System.IO; +using System.Collections.Immutable; + +namespace CycloneDX.Cli.Commands +{ + public static class RenameEntityCommand + { + public static void Configure(RootCommand rootCommand) + { + Contract.Requires(rootCommand != null); + var subCommand = new System.CommandLine.Command("rename-entity", "Rename an entity identified by a \"bom-ref\" (including back-references to it) in the BOM document"); + subCommand.Add(new Option("--input-file", "Input BOM filename.")); + subCommand.Add(new Option("--output-file", "Output BOM filename, will write to stdout if no value provided.")); + subCommand.Add(new Option("--old-ref", "Old value of \"bom-ref\" entity identifier (or \"ref\" values or certain list items pointing to it).")); + subCommand.Add(new Option("--new-ref", "New value of \"bom-ref\" entity identifier (or \"ref\" values or certain list items pointing to it).")); + subCommand.Add(new Option("--input-format", "Specify input file format.")); + subCommand.Add(new Option("--output-format", "Specify output file format.")); + subCommand.Handler = CommandHandler.Create(RenameEntity); + rootCommand.Add(subCommand); + } + + public static async Task RenameEntity(RenameEntityCommandOptions options) + { + Contract.Requires(options != null); + var outputToConsole = string.IsNullOrEmpty(options.OutputFile); + + if (options.OutputFormat == CycloneDXBomFormat.autodetect) + { + options.OutputFormat = CliUtils.AutoDetectBomFormat(options.OutputFile); + } + if (options.OutputFormat == CycloneDXBomFormat.autodetect) + { + Console.WriteLine($"Unable to auto-detect output format"); + return (int)ExitCode.ParameterValidationError; + } + + Console.WriteLine($"Loading input document..."); + if (!outputToConsole) Console.WriteLine($"Processing input file {options.InputFile}"); + var bom = await CliUtils.InputBomHelper(options.InputFile, options.InputFormat).ConfigureAwait(false); + + if (bom is null) + { + Console.WriteLine($"Empty or absent input document"); + return (int)ExitCode.ParameterValidationError; + } + + Console.WriteLine($"Beginning Bom walk to discover all identifiers (this can take a while)"); + BomWalkResult bwr = bom.WalkThis(); + + Console.WriteLine($"Beginning Bom walk rename processing (this can take a while)"); + if (bom.RenameBomRef(options.OldRef, options.NewRef, bwr)) + { + Console.WriteLine($"Did not encounter any issues during the rename operation"); + } + else + { + Console.WriteLine($"Rename operation failed non-fatally (e.g. old ref name not mentioned in the Bom document)"); + } + + // Ensure that the modified document has its own identity + // (new SerialNumber, Version=1, Timestamp...) and its Tools + // collection refers to this library and the program/tool + // like cyclonedx-cli which consumes it: + bom.BomMetadataUpdate(true); + bom.BomMetadataReferThisToolkit(); + + if (!outputToConsole) + { + Console.WriteLine("Writing output file..."); + Console.WriteLine($" Total {bom.Components?.Count ?? 0} components, {bom.Dependencies?.Count ?? 0} dependencies"); + } + + int res = await CliUtils.OutputBomHelper(bom, options.OutputFormat, options.OutputFile).ConfigureAwait(false); + return res; + } + } +} diff --git a/src/cyclonedx/Commands/RenameEntityCommandOptions.cs b/src/cyclonedx/Commands/RenameEntityCommandOptions.cs new file mode 100644 index 0000000..323cbc4 --- /dev/null +++ b/src/cyclonedx/Commands/RenameEntityCommandOptions.cs @@ -0,0 +1,30 @@ +// This file is part of CycloneDX CLI Tool +// +// Licensed under the Apache License, Version 2.0 (the “License”); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an “AS IS” BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) OWASP Foundation. All Rights Reserved. +using System.Collections.Generic; + +namespace CycloneDX.Cli.Commands +{ + public class RenameEntityCommandOptions + { + public string InputFile { get; set; } + public string OutputFile { get; set; } + public string OldRef { get; set; } + public string NewRef { get; set; } + public CycloneDXBomFormat InputFormat { get; set; } + public CycloneDXBomFormat OutputFormat { get; set; } + } +} diff --git a/src/cyclonedx/Program.cs b/src/cyclonedx/Program.cs index ce34561..a3101c5 100644 --- a/src/cyclonedx/Program.cs +++ b/src/cyclonedx/Program.cs @@ -47,6 +47,7 @@ public static async Task Main(string[] args) DiffCommand.Configure(rootCommand); KeyGenCommand.Configure(rootCommand); MergeCommand.Configure(rootCommand); + RenameEntityCommand.Configure(rootCommand); SignCommand.Configure(rootCommand); ValidateCommand.Configure(rootCommand); VerifyCommand.Configure(rootCommand); From 9564ac534a8ca0d9925103afdea7880c53820b4c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 6 Oct 2023 08:50:58 +0200 Subject: [PATCH 2/2] RenameEntityCommand.cs: address Codacy warnings Signed-off-by: Jim Klimov --- src/cyclonedx/Commands/RenameEntityCommand.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cyclonedx/Commands/RenameEntityCommand.cs b/src/cyclonedx/Commands/RenameEntityCommand.cs index 4a53886..5569cbc 100644 --- a/src/cyclonedx/Commands/RenameEntityCommand.cs +++ b/src/cyclonedx/Commands/RenameEntityCommand.cs @@ -51,11 +51,11 @@ public static async Task RenameEntity(RenameEntityCommandOptions options) if (options.OutputFormat == CycloneDXBomFormat.autodetect) { options.OutputFormat = CliUtils.AutoDetectBomFormat(options.OutputFile); - } - if (options.OutputFormat == CycloneDXBomFormat.autodetect) - { - Console.WriteLine($"Unable to auto-detect output format"); - return (int)ExitCode.ParameterValidationError; + if (options.OutputFormat == CycloneDXBomFormat.autodetect) + { + Console.WriteLine($"Unable to auto-detect output format"); + return (int)ExitCode.ParameterValidationError; + } } Console.WriteLine($"Loading input document...");