amend

The amend command splits its functionality into several operations. You can select which operations run using the --operation option. If you don’t, operations marked [default] will run. The following operations are available:

add-bom-ref [default]:

Adds a ‘bom-ref’ to components which don’t have one yet.

add-license-text:

Adds user-provided license texts to licenses with a specific name (not id).

compositions [default]:

Declares all component compositions as ‘unknown’.

default-author [default]:

Sets author of the SBOM in metadata to ‘automated’, if missing.

delete-ambiguous-licenses:

Deletes license claims which are solely identified by the ‘name’ property.

hierarchical-bom-refs:

Prepends parent bom-refs to the bom-refs of nested components.

infer-supplier [default]:

Attempts to infer component supplier from other fields.

license-name-to-id [default]:

Attempts to infer SPDX ids from license names.

usage: cdx-ev amend [-h] [--operation <operation>]
                    [--help-operation <operation>] [--license-dir LICENSE_DIR]
                    [--separator SEPARATOR] [--output <file>]
                    [<input>]

Positional Arguments

<input>

Path to the SBOM file.

Named Arguments

--operation

Possible choices: add-bom-ref, add-license-text, compositions, default-author, delete-ambiguous-licenses, hierarchical-bom-refs, infer-supplier, license-name-to-id

Select an operation to run. Can be provided more than once to run multiple operations in one run.

--help-operation

Possible choices: add-bom-ref, add-license-text, compositions, default-author, delete-ambiguous-licenses, hierarchical-bom-refs, infer-supplier, license-name-to-id

Displays details about an operation and exits afterwards.

--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.

Options for ‘add-license-text’

--license-dir

Path to a folder with files containing license texts.

Options for ‘hierarchical-bom-refs’

--separator

String to place between parent and child bom-refs.

Default: '/'

Note

Operations run in the order specified with --operation. If multiple operations are selected, place operations that provide input to another operation first.

Examples

# Run all default operations on an SBOM.
cdx-ev amend bom.json

# Run only the default-author and add-bom-ref operations.
cdx-ev amend --operation default-author --operation add-bom-ref bom.json

# Run the add-license-text operation. License texts are stored in a directory named 'license_texts'.
# Afterwards, run the delete-ambiguous-licenses operation.
cdx-ev amend --operation add-license-text --license-dir ./license_texts bom.json --output bom.json
cdx-ev amend --operation delete-ambiguous-licenses bom.json

# Build hierarchical bom-refs from nested components.
cdx-ev amend --operation hierarchical-bom-refs bom.json

# Add missing bom-refs first, then build the hierarchy in the same run.
cdx-ev amend --operation add-bom-ref --operation hierarchical-bom-refs bom.json

Operation details

add-bom-ref

Adds a bom-ref to components which don’t have one yet.

This operation generates bom-refs comprising a single UUIDv4 for any component which doesn’t have an SBOM.

add-license-text

The operation add-license-text can be used to insert known full license texts for licenses identified by name. You can use this, for instance, in workflows where SBOMs are created or edited by hand - so a clutter-free JSON is preferred - then, in a last step, full texts are inserted using this operation.

License texts are inserted only if:

  • The license has a name field.

  • The license has no id field.

  • The license has no or an empty text.content field.

  • A matching file is found.

You must provide one file per license text in a flat directory. The stem of the filename, that is everything up to the extension (i.e., up to but not including the last period), must match the license name specified in the SBOM.

Example

Given this license in the input SBOM:

{
    "license": {
        "name": "My license"
    }
}

the operation would search the full license text in any file named My license, My license.txt, My license.md, or any other extension. However, the file My license.2.txt would be disregarded, because its stem (My license.2) doesn’t match the license name.

compositions

Declares all component compositions as unknown.

Any existing entries in compositions are replaced by a single entry that marks all components in the SBOM as unknown. This serves two goals:

This excludes the metadata component because any SBOM supplier should be able to state the level of completeness of its first-level components.

default-author

Sets author of the SBOM in metadata to automated, if missing.

delete-ambiguous-licenses

Deletes license claims which are solely identified by the name property.

Licenses that contain only a name property but no URL or text for context provide little informational value beyond the fact that some form of license is present. In certain cases it can therefore be beneficial to remove such clutter from an SBOM.

Because of the risk involved in accidentally removing important data, this operation is disabled by default.

hierarchical-bom-refs

Prepends parent bom-refs to the bom-refs of nested components.

Every nested component is rewritten to <parent bom-ref><separator><own bom-ref> recursively. Each bom-ref is treated as an opaque string. Top-level components remain unchanged. References to rewritten components are updated throughout the SBOM. Every bom-ref in the document is considered when detecting collisions, which are resolved with an incrementing numeric suffix. Components without a bom-ref, or whose parent has no bom-ref, remain unchanged and cause an informational log message.

When used together with AddBomRef, select add-bom-ref before this operation so that generated bom-refs can be included in the hierarchy.

This operation is not enabled by default because it changes existing bom-refs.

This operation follows structural containment expressed by a component’s components array. It does not infer hierarchy from dependencies and does not place ordinary top-level components beneath metadata.component.

For example, these nested component references:

{
    "bom-ref": "module-a",
    "components": [
        {
            "bom-ref": "library-b",
            "components": [
                {"bom-ref": "application-c"}
            ]
        }
    ]
}

become:

{
    "bom-ref": "module-a",
    "components": [
        {
            "bom-ref": "module-a/library-b",
            "components": [
                {"bom-ref": "module-a/library-b/application-c"}
            ]
        }
    ]
}

The operation has the following behavior:

  • Top-level component bom-refs remain unchanged.

  • Nesting is processed recursively at any depth in the top-level components tree.

    The metadata.component tree is skipped.

  • Every bom-ref is treated as an opaque string. Values such as 1, arbitrary words, UUIDs, and

    PURLs are preserved in full and are never parsed as path segments.

  • Running the operation repeatedly prepends the hierarchy repeatedly. The operation cannot infer

    from an opaque bom-ref whether a prefix was added by an earlier run.

  • Internal references defined by the supported CycloneDX 1.2 through 1.7 specifications are

    updated recursively. This includes dependency, composition, vulnerability, annotation, evidence, declaration, requirement, formulation, cryptography, patent, and citation links. Unrelated strings and external reference URLs are not changed.

  • Every bom-ref in the document participates in collision detection, not only component

    bom-refs. A generated path that conflicts with any existing bom-ref receives an incrementing suffix such as -1.

  • The separator between parent and child refs can be configured with --separator; it defaults

    to /.

  • When used together with add-bom-ref, place add-bom-ref first so generated refs are also

    included in the hierarchy.

  • A component without a bom-ref, or whose parent has no bom-ref, cannot be adjusted. The operation

    logs this at INFO level, leaves that relationship unchanged, and continues with the remaining component tree.

  • Run add-bom-ref in a separate amend invocation before this operation if missing bom-refs

    should be generated. Operation ordering within one amend invocation cannot provide this order.

The operation is not enabled by default because changing bom-refs can affect systems outside the SBOM that refer to the old values.

infer-supplier

Attempts to infer component supplier from other fields.

CycloneDX contains numerous attributes on components to attest some sort of responsibility for its creation or distribution with fine semantic differences between them. These include author, authors, manufacturer, supplier, or publisher and the list might grow in future versions. Unfortunately, the toolscape doesn’t work equally well with all of these. For instance, Dependency-Track ignores everything but author and supplier. However, SBOMs generated by many tools do not always expose this information for all components. Where it is missing, this operation attempts to infer a supplier from available data.

The algorithm sets the supplier.name to the first element found from the following list:

  • publisher

  • manufacturer.name

  • authors[].name

  • author

The supplier.url will be inferred from the following sources, in order of precedence:

  • externalReference of type website

  • externalReference of type issue-tracker

  • externalReference of type vcs

For all of the URLs there is the additional condition that they must utilize either the http or https scheme.

license-name-to-id

Attempts to infer SPDX ids from license names.

For any license on a component or the metadata component that is declared with a name but no id, this operation attempts to replace the name with a matching SPDX id. The operation contains a lookup table of common license names to SPDX ids largely sourced from https://github.com/CycloneDX/cyclonedx-core-java/ and https://spdx.org/licenses/.

Licenses that already have an id are skipped. If no corresponding id can be found, the license is also skipped.