Skip to content

Commit f43675e

Browse files
authored
Introduce "cyclonedx rename-entity" command (#346)
Primarily written 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. Relies on CycloneDX/cyclonedx-dotnet-library#245 for the bulk of work (BomEntity base-class and interface family, etc.) and CycloneDX/cyclonedx-dotnet-library#256 for metadata update of the output document. --------- Signed-off-by: Jim Klimov <[email protected]>
1 parent 86f7a19 commit f43675e

File tree

4 files changed

+181
-0
lines changed

4 files changed

+181
-0
lines changed

README.md

+49
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,55 @@ Merge two XML formatted BOMs:
214214
Merging two BOMs and piping output to additional tools:
215215
`cyclonedx-cli merge --input-files sbom1.xml sbom2.xml --output-format json | grep "something"`
216216

217+
## Rename Entity command
218+
219+
Rename an entity identified by "bom-ref" (formally a "refType") in the document
220+
and/or back-references to such entity (formally a "refLinkType", typically as
221+
a "ref" property; or certain lists' items).
222+
223+
```
224+
rename-entity
225+
Rename an entity identified by a "bom-ref" (including back-references to it) in the BOM document
226+
227+
Usage:
228+
cyclonedx [options] rename-entity
229+
230+
Options:
231+
--input-file <input-file> Input BOM filename.
232+
--output-file <output-file> Output BOM filename, will write to stdout if no value provided.
233+
--old-ref <old-ref> Old value of "bom-ref" entity identifier (or "ref" values or certain list items pointing to it).
234+
--new-ref <new-ref> New value of "bom-ref" entity identifier (or "ref" values or certain list items pointing to it).
235+
--input-format <autodetect|json|protobuf|xml> Specify input file format.
236+
--output-format <autodetect|json|protobuf|xml> Specify output file format.
237+
```
238+
239+
Keep in mind that these identifiers are arbitrary strings that have a meaning
240+
within the Bom document (and should uniquely identify one entity in its scope).
241+
While in some cases these identifiers are meaningful (e.g. "purl" values used
242+
as "bom-ref" by the cyclonedx-maven-plugin), they may also validly be random
243+
UUIDs or collision-prone strings like "1", "2", "3"...
244+
245+
They may be opportunistically used as anchors for cross-document references,
246+
so in some cases a back-reference may point to a string for which there is no
247+
"bom-ref" in the same document (see relevant CycloneDX specification version
248+
for details).
249+
250+
This renaming operation also modifies the output document metadata, to reflect
251+
the modification compared to the input document.
252+
253+
Basic error-checking, such as attempt to re-use an already existing identifier,
254+
is performed.
255+
256+
### Examples
257+
258+
Rename an entity:
259+
```
260+
cyclonedx rename-entity --input-file sbom.json --output-format xml \
261+
--oldref "pkg:maven/org.yaml/[email protected]?type=jar" \
262+
--newref "thirdpartylibs:org.yaml:snakeyaml:1.33:jar" \
263+
| grep "thirdparty"
264+
```
265+
217266
## Sign Command
218267

219268
Sign a BOM or file
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// This file is part of CycloneDX CLI Tool
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the “License”);
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an “AS IS” BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// SPDX-License-Identifier: Apache-2.0
16+
// Copyright (c) OWASP Foundation. All Rights Reserved.
17+
using System;
18+
using System.Collections.Generic;
19+
using System.Diagnostics.Contracts;
20+
using System.CommandLine;
21+
using System.CommandLine.Invocation;
22+
using System.Threading.Tasks;
23+
using CycloneDX.Models;
24+
using CycloneDX.Utils;
25+
using System.IO;
26+
using System.Collections.Immutable;
27+
28+
namespace CycloneDX.Cli.Commands
29+
{
30+
public static class RenameEntityCommand
31+
{
32+
public static void Configure(RootCommand rootCommand)
33+
{
34+
Contract.Requires(rootCommand != null);
35+
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");
36+
subCommand.Add(new Option<string>("--input-file", "Input BOM filename."));
37+
subCommand.Add(new Option<string>("--output-file", "Output BOM filename, will write to stdout if no value provided."));
38+
subCommand.Add(new Option<string>("--old-ref", "Old value of \"bom-ref\" entity identifier (or \"ref\" values or certain list items pointing to it)."));
39+
subCommand.Add(new Option<string>("--new-ref", "New value of \"bom-ref\" entity identifier (or \"ref\" values or certain list items pointing to it)."));
40+
subCommand.Add(new Option<CycloneDXBomFormat>("--input-format", "Specify input file format."));
41+
subCommand.Add(new Option<CycloneDXBomFormat>("--output-format", "Specify output file format."));
42+
subCommand.Handler = CommandHandler.Create<RenameEntityCommandOptions>(RenameEntity);
43+
rootCommand.Add(subCommand);
44+
}
45+
46+
public static async Task<int> RenameEntity(RenameEntityCommandOptions options)
47+
{
48+
Contract.Requires(options != null);
49+
var outputToConsole = string.IsNullOrEmpty(options.OutputFile);
50+
51+
if (options.OutputFormat == CycloneDXBomFormat.autodetect)
52+
{
53+
options.OutputFormat = CliUtils.AutoDetectBomFormat(options.OutputFile);
54+
if (options.OutputFormat == CycloneDXBomFormat.autodetect)
55+
{
56+
Console.WriteLine($"Unable to auto-detect output format");
57+
return (int)ExitCode.ParameterValidationError;
58+
}
59+
}
60+
61+
Console.WriteLine($"Loading input document...");
62+
if (!outputToConsole) Console.WriteLine($"Processing input file {options.InputFile}");
63+
var bom = await CliUtils.InputBomHelper(options.InputFile, options.InputFormat).ConfigureAwait(false);
64+
65+
if (bom is null)
66+
{
67+
Console.WriteLine($"Empty or absent input document");
68+
return (int)ExitCode.ParameterValidationError;
69+
}
70+
71+
Console.WriteLine($"Beginning Bom walk to discover all identifiers (this can take a while)");
72+
BomWalkResult bwr = bom.WalkThis();
73+
74+
Console.WriteLine($"Beginning Bom walk rename processing (this can take a while)");
75+
if (bom.RenameBomRef(options.OldRef, options.NewRef, bwr))
76+
{
77+
Console.WriteLine($"Did not encounter any issues during the rename operation");
78+
}
79+
else
80+
{
81+
Console.WriteLine($"Rename operation failed non-fatally (e.g. old ref name not mentioned in the Bom document)");
82+
}
83+
84+
// Ensure that the modified document has its own identity
85+
// (new SerialNumber, Version=1, Timestamp...) and its Tools
86+
// collection refers to this library and the program/tool
87+
// like cyclonedx-cli which consumes it:
88+
bom.BomMetadataUpdate(true);
89+
bom.BomMetadataReferThisToolkit();
90+
91+
if (!outputToConsole)
92+
{
93+
Console.WriteLine("Writing output file...");
94+
Console.WriteLine($" Total {bom.Components?.Count ?? 0} components, {bom.Dependencies?.Count ?? 0} dependencies");
95+
}
96+
97+
int res = await CliUtils.OutputBomHelper(bom, options.OutputFormat, options.OutputFile).ConfigureAwait(false);
98+
return res;
99+
}
100+
}
101+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// This file is part of CycloneDX CLI Tool
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the “License”);
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an “AS IS” BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// SPDX-License-Identifier: Apache-2.0
16+
// Copyright (c) OWASP Foundation. All Rights Reserved.
17+
using System.Collections.Generic;
18+
19+
namespace CycloneDX.Cli.Commands
20+
{
21+
public class RenameEntityCommandOptions
22+
{
23+
public string InputFile { get; set; }
24+
public string OutputFile { get; set; }
25+
public string OldRef { get; set; }
26+
public string NewRef { get; set; }
27+
public CycloneDXBomFormat InputFormat { get; set; }
28+
public CycloneDXBomFormat OutputFormat { get; set; }
29+
}
30+
}

src/cyclonedx/Program.cs

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public static async Task<int> Main(string[] args)
4747
DiffCommand.Configure(rootCommand);
4848
KeyGenCommand.Configure(rootCommand);
4949
MergeCommand.Configure(rootCommand);
50+
RenameEntityCommand.Configure(rootCommand);
5051
SignCommand.Configure(rootCommand);
5152
ValidateCommand.Configure(rootCommand);
5253
VerifyCommand.Configure(rootCommand);

0 commit comments

Comments
 (0)