merge
Merges two or more CycloneDX input files into one. Inputs can either be specified directly as positional arguments on the command-line or using the --from-folder option. Files specified as arguments are merged in the order they are given, files in the folder are merged in alphabetical order (see note below).
If both positional arguments and the --from-folder option are used, then the position arguments are merged first, followed by the files in the folder. The command will not merge the same file twice, if it is specified on the command-line and also part of the folder.
When using the --from-folder option, the program looks for files matching either of the recommended CycloneDX naming schemes: bom.json or *.cdx.json.
usage: cdx-ev merge [-h] [--from-folder <from-folder>] [--hierarchical]
[--output <file>]
[<input> ...]
Positional Arguments
- <input>
Paths to SBOM files to merge. You must specify at least two paths.
Named Arguments
- --from-folder
Path to a folder with SBOMs to be merged.
- --hierarchical
Flag to determine if the components should be merged hierarchical.
Default:
False- --output, -o
The path to where the output should be written. If this is a file, output is written there. If it’s a directory, output is written to a file with an auto-generated name inside that directory. If it’s not specified, output is written to stdout.
Details
Input files in the folder provided to the --from-folder option are sorted by name in a platform-specific way. In other words, they are merged in the same order they appear in your operating system’s file browser (e.g., Windows Explorer) when sorted by name.
The process runs iteratively, merging two SBOMs in each iteration. In the first round, the second submitted SBOM is merged into the first. In the second round the third would be merged into the result of the first round and so on.
In mathematical terms: \(output = (((input_1 * input_2) * input_3) * input_4 ...)\)
The merge is per default not hierarchical for the components field of a component (CycloneDX documentation). This means that components that were contained in the components of an already present component will just be added as new components under the SBOMs’ components sections.
The --hierarchical flag allows for hierarchical merges. This affects only the top level components of the merged SBOM. The structured of nested components is preserved in both cases (except the removal of already present components), as shown for “component 4” in the image below.
A few notes on the merge algorithm:
The
metadatafield is always retained from the first input and never changed through a merge with the exception of thetimestamp.The command merges the contents of the fields
components,dependencies,compositionsandvulnerabilities.Components are merged into the result in the order they first appear in the inputs. If any subsequent input specifies the same component (sameness in this case being defined as having identical identifying attributes such as
name,version,purl, etc.), the later instance of the component will be dropped with a warning. Itspropertiesare the exception: properties not already present on the retained component are appended in input order. Properties with the same name but different values are retained, while completely identical property objects are added only once. This applies iteratively to every input.The resulting dependency graph will reflect all dependencies from all inputs. Dependencies from later inputs are always added to the result, even if the component is dropped as a duplicate as described above.
Uniqueness of bom-refs will be ensured across all inputs in every merge mode. This applies to components, services, vulnerabilities, compositions, licenses, annotations, formulation, declarations, definitions and every other element that can carry a
bom-ref. Identical components (with the same identifying attributes) receive the samebom-ref; a different component that collides with an existing reference receives a new reference; other colliding elements from later inputs receive numeric suffixes such as-1and-2. All references to a renamed element within its input are updated.During
--hierarchicalmerges, when components from a later input are added under an already present matching parent, all relocated descendants have the new parentbom-refprepended. Existing parent prefixes are not duplicated, so relocating a subtree withappandapp/loggerunder parentsystemchanges thosebom-refvalues tosystem/appandsystem/app/logger; opaque refs such aspkg:npm/foo@1.0becomesystem/pkg:npm/foo@1.0. Prefix stripping only uses/as the separator. Only relocated components are rebased; components already present in the first input keep their refs, so the resulting refs are not necessarily full paths from the root. If rebasing would create a collision, suffixes such as-1and-2are appended.Because of these renames, output
bom-refvalues can differ from the inputs. External documents that point at input references, such as separate VEX files or BOM-Links, may need to be updated.The command is able to merge inputs containing only VEX information in the form of a
vulnerabilities. To ensure a sensible result, it should be ensured that bom-refs in the affects field reference components of the same SBOM.Vulnerabilities, like components, are merged into the result in the order they first appear in the inputs.
If a merged vulnerability contains additional entries in the
affectsfield, those will be added to the original vulnerability object (duplicates are possible if version ranges are used).