Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce "cyclonedx rename-entity" command #346

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-file> Input BOM filename.
--output-file <output-file> Output BOM filename, will write to stdout if no value provided.
--old-ref <old-ref> Old value of "bom-ref" entity identifier (or "ref" values or certain list items pointing to it).
--new-ref <new-ref> New value of "bom-ref" entity identifier (or "ref" values or certain list items pointing to it).
--input-format <autodetect|json|protobuf|xml> Specify input file format.
--output-format <autodetect|json|protobuf|xml> 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/[email protected]?type=jar" \
--newref "thirdpartylibs:org.yaml:snakeyaml:1.33:jar" \
| grep "thirdparty"
```

## Sign Command

Sign a BOM or file
Expand Down
101 changes: 101 additions & 0 deletions src/cyclonedx/Commands/RenameEntityCommand.cs
Original file line number Diff line number Diff line change
@@ -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<string>("--input-file", "Input BOM filename."));
subCommand.Add(new Option<string>("--output-file", "Output BOM filename, will write to stdout if no value provided."));
subCommand.Add(new Option<string>("--old-ref", "Old value of \"bom-ref\" entity identifier (or \"ref\" values or certain list items pointing to it)."));
subCommand.Add(new Option<string>("--new-ref", "New value of \"bom-ref\" entity identifier (or \"ref\" values or certain list items pointing to it)."));
subCommand.Add(new Option<CycloneDXBomFormat>("--input-format", "Specify input file format."));
subCommand.Add(new Option<CycloneDXBomFormat>("--output-format", "Specify output file format."));
subCommand.Handler = CommandHandler.Create<RenameEntityCommandOptions>(RenameEntity);
rootCommand.Add(subCommand);
}

public static async Task<int> 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;
}
}
}
30 changes: 30 additions & 0 deletions src/cyclonedx/Commands/RenameEntityCommandOptions.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
1 change: 1 addition & 0 deletions src/cyclonedx/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public static async Task<int> Main(string[] args)
DiffCommand.Configure(rootCommand);
KeyGenCommand.Configure(rootCommand);
MergeCommand.Configure(rootCommand);
RenameEntityCommand.Configure(rootCommand);
SignCommand.Configure(rootCommand);
ValidateCommand.Configure(rootCommand);
VerifyCommand.Configure(rootCommand);
Expand Down