set

This command sets properties on specified components to specified values. If a component in an SBOM is missing a particular property or the property is present but has a wrong value, this command can be used to modify just the affected properties without changing the rest of the SBOM.

For this command to work, three bits of information must be provided by the user: The target component(s) to modify as well as the name and new value of each property to set on the target component.

This data can either be passed directly on the command-line — in this case only a single update can be performed per invocation — or in a JSON file — this allows performing an unlimited number of updates in a single invocation.

usage: cdx-ev set [-h] [--output <file>] [--force] (--from-file <file> | <target> --key <key> --value <value>) <input>

Positional Arguments

<input>

Path to the input SBOM file.

Named Arguments

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

--from-file

Read the target components and updated fields from a file or URL. Cannot be used in conjunction with a target option or the --key and --value options.

--key

Name of the component field to set or update. May not be combined with --from-file.

--value

The new value for the field. If the target field is an array or object, you’ll have to format your value as a valid JSON object or array, too. May not be combined with --from-file.

--force, -f

Quietly overwrite existing information without asking.

Default: False

--ignore-existing

Quietly skip existing information without asking.

Default: False

--allow-protected

Allow writing protected fields, e.g., identifiers.

Default: False

--ignore-missing

Suppress warnings that a component is not present when using ‘--from-file’

Default: False

target

Identifies the target component. If the --from-file option is not used, exactly one of these options must be specified. --name, --version and --group act together to identify a component. If the --from-file option is used, none of these options may be present.

--cpe

CPE of target component.

--purl

PURL of target component.

--swid

SWID of target component.

--name

Name of target component. Can be combined with group and version.

--group

Group of target component. If specified, name must also be specified.

--version

Version of target component. If specified, name must also be specified.

--version-range

Version range of target components. If specified, name must also be specified.

Target components

The target component can be identified through any of the identifiable properties defined by CycloneDX, specifically: cpe, purl, swid or the combination of name, group and/or version (collectively called coordinates).

If coordinates are used to identify the target, they must match the component fully. In other words, if only name is given, it will only match components with that name which do not contain version or group fields.

In coordinates it is also possible to provide a range of versions using the version-range parameter instead of version following the PURL specification as referenced by CycloneDX.

The version range has the format:

vers:<versioning-scheme>/<version-constraint>|<version-constraint>|...

beginning with the vers identifier. Following this the versioning scheme is specified, in the case of semantic versioning this would be semver or generic. Following this a list of constraints divided by an | can be provided, to specify which versions are in scope. A few examples:

To target all versions higher than and not including 2.0.0 the version range to provide would be:

vers:generic/>2.0.0

To target all versions higher than and not including 2.0.0 that are also smaller than and including 4.5.0 the version range to provide would be:

vers:generic/>2.0.0|<=4.5.0

To target all versions higher than and not including 2.0.0 that are also smaller than and including 4.5.0 except the single version 4.1.1 the version range to provide would be:

vers:generic/>2.0.0|!=4.1.1|<=4.5.0

To target all versions to target all versions higher than and not including 2.0.0 that are also smaller than and including 4.5.0 as well as the additional version 5.0.0 the version range to provide would be:

vers:generic/>2.0.0|<=4.5.0|5.0.0

Note that instead of specific version constraints it is possible to provide a wildcard * to allow all versions. So to target all versions the provided version range would be:

vers:generic/*

Further information on the supported versions can be found here univers documentation.

If the target component isn’t found in the SBOM, the program aborts with an error by default. This error can be downgraded to a warning using the --ignore-missing flag.

Protected fields

Some fields are protected and cannot be set by default. The full list of protected properties is:

  • cpe

  • purl

  • swid

  • name

  • group

  • version

  • components

To set any of these fields, use the --allow-protected command-line switch.

Values

The value must be given as a valid JSON value. That means command-line usage can be a little strange, when setting a simple string value. To be valid JSON, the string must be surrounded by double quotes. Since double quotes hold a special meaning in most shells, they will likely have to be escaped. An example in the bash shell:

# Set a simple string property, such as copyright in bash
cdx-ev set bom.json --cpe <target-cpe> --key copyright --value '"2022 Acme Inc"'

# Set the copyright for all versions of the given component
cdx-ev set bom.json --group=org.acme --name=my_program --version-range vers:generic/* --key copyright --value '"Copyright 2024 Acme"'

Conflicts

Conflicts arise when a target component already has a value for the specified property. When this happens, the command follows the following rules to determine how to proceed:

  1. If the new value is null, delete the existing property. The tool assumes that a user who sets null is aware that the property exists and wants to delete it.

  2. If the property is an array, the new value is appended to the old value.

  3. If the --ignore-existing command-line option is set, the old value will not be overwritten.

  4. If the --force command-line option is set, the old value is overwritten with the new.

  5. If the tool is running in an interactive terminal, the user is prompted to decide whether to overwrite the old value.

  6. If none of the above applies, an error is thrown.

Hawk-eyed readers will have spotted a little stumbling block in these rules. What if an array should be overwritten? A little trickery is needed here. The property must first be explicitly deleted by setting it to null, then re-added with the new value. On the command-line this can be done in two subsequent invocations:

# Overwrite an array-valued property
cdx-ev set bom.json --cpe <target_cpe> --key licenses --value null
cdx-ev set bom.json --cpe <target_cpe> --key licenses --value '[{"license": {"id": "MIT"}}]'

When passing the set list in a file, two separate updates must be specified for the same target component.

Set list file format

When passing the targets, names and values in a file, the file must conform to this format:

[
    {
        "id": {
            # Could be any one of the identifying properties in CycloneDX.
            # Multiple identifiers are not allowed (with the special exception of name,
            # group and version/version-range which are only valid together)
            "cpe": "CPE of target component goes here"
        },
        "set": {
            # Sets a simple property
            "copyright": "2022 Acme Inc",
            # Deletes a property
            "author": null,
            # Sets an array array-valued property. If the property already exists on the target,
            # the new value will be appended to the existing one.
            "licenses": [
                {
                    "license": {
                        "id": "MIT"
                    }
                }
            ]
        }
    },
    ...
]

Example for the use of version ranges:

[
    {
        "id": {
            "name": "web-framework",
            "group": "org.acme",
            # It is possible to provide a version range
            # the format must comply with the PURL specification for version ranges
            "version-range": "vers:generic/>=1.0.2|<2.0.0",
        },
            "set": {"copyright": "1990 Acme Inc"},
    },
    {
        "id": {
            "name": "firmware-framework",
            "group": "org.acme",
            # It is also possible to provide a wildcard for the version
            # if the version is set to "*" all versions of the specified schema are passed
            "version-range": "vers:generic/*",
        },
            "set": {"copyright": "1990 Acme Inc"},
    },
    ...
]

The above provided example would set the copyright in the component

{
    "name": "web-framework"
    "group": "org.acme",
    "version":"1.5.0"
}

while it would leave the component

{
    "name": "web-framework"
    "group": "org.acme",
    "version":"2.0.0"
}

unchanged.

This file can then be applied as the following example shows:

# Perform several operations on properties using set-command
cdx-ev set bom.json --from-file mysetfile.json