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

Support the --normalize flag on the Patch command #102

Merged
merged 2 commits into from
Jun 6, 2024
Merged
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
68 changes: 59 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ The following commands, which operate against input BOMs and their data, are off
| **[license](#license)** **[`policy`](#license-policy-subcommand)** | Produces filterable listings of software and data license information and associated license usage policies as defined a `license.json` configuration file. |
| **[resource `list`](#resource)** | Produces filterable listings of resources (i.e., components and services) declared in the BOM. |
| **[schema `list`](#schema)** | Produces filterable listings of schema formats, versions and variants supported by the `validation` command.</br></br> **Note**: Customized JSON schemas can also be permanently configured as named schema "variants" within the utility's configuration file (see the `schema` command's [adding schemas](#adding-schemas) section). |
| **[vulnerability `list`](#vulnerability)** | produces filterable listings of vulnerabilities declared in the BOM (i.e., CycloneDX Vulnerability Exploitability eXchange (**VEX**)) data or independently stored CycloneDX Vulnerability Disclosure Report (**VDR**) data stored in the BOM format. |
| **[vulnerability `list`](#vulnerability)** | Produces filterable listings of vulnerabilities declared in the BOM (i.e., CycloneDX Vulnerability Exploitability eXchange (**VEX**)) data or independently stored CycloneDX Vulnerability Disclosure Report (**VDR**) data stored in the BOM format. |

> **Experimental commands**:
*Testing, feedback and helpful suggestions and code commits are appreciated on experimental commands.*
Expand Down Expand Up @@ -626,7 +626,7 @@ A comma-separated list of JSON document paths using the same syntax as the [quer

A flag that normalizes the BOM data after trimming and prior to output.

This flag has custom code that sorts all components, services, licenses, vulnerabilities, properties, external references, hashes and *most* other BOM data using custom comparators.
This flag invokes custom code that sorts all components, services, licenses, vulnerabilities, properties, external references, hashes and *most* other BOM data using custom comparators.

Each comparator uses `required` fields and other identifying fields to create *"composite keys"* for each unique data structure.

Expand Down Expand Up @@ -1024,17 +1024,26 @@ The patch command operates on a JSON BOM input file (see [`--input-file` flag](#

The `--patch-file <filename>` flag is used to provide the relative path to the IETF RFC6902 patch file to applied to the BOM input file.

##### Patch `--normalize` flag

A flag that normalizes the BOM data after patching and prior to output.

This flag invokes custom code that sorts all components, services, licenses, vulnerabilities, properties, external references, hashes and *most* other BOM data using custom comparators.

Each comparator uses `required` fields and other identifying fields to create *"composite keys"* for each unique data structure.

#### Patch examples

This section contains examples of all supported patch operations (i.e., add, replace, test) including values that are primitives (i.e., `numbers`, `strings`) as well as JSON `objects` and may be indexed JSON `array` elements.

- ["add" BOM `serialNumber`](#patch-example-1-add-bom-serialnumber)
- ["add" (update) BOM `version`](#patch-example-2-add-update-bom-version)
- ["add" `supplier` object to `metadata`](#patch-example-3-add-supplier-object-to-metadata-object)
- ["add" `property` objects to `metadata.properties` array](#patch-example-4-add-property-objects-to-metadataproperties-array)
- ["replace" `version` and `timestamp` values](#patch-example-5-replace-bom-version-and-timestamp)
- ["remove" `property` from the `metadata.properties` array](#patch-example-6-remove-property-from-the-metadataproperties-array)
- ["test" if a `property` exists in the `metadata.properties` array](#patch-example-7-test-property-exists-in-the-metadataproperties-array)
- [Example 1: "add" BOM `serialNumber`](#patch-example-1-add-bom-serialnumber)
- [Example 2: "add" (update) BOM `version`](#patch-example-2-add-update-bom-version)
- [Example 3: "add" `supplier` object to `metadata`](#patch-example-3-add-supplier-object-to-metadata-object)
- [Example 4:"add" `property` objects to `metadata.properties` array](#patch-example-4-add-property-objects-to-metadataproperties-array)
- [Example 4a: Normalize `metadata.properties` after patching](#patch-example-4a---normalize-properties-after-patching)
- [Example 5: "replace" `version` and `timestamp` values](#patch-example-5-replace-bom-version-and-timestamp)
- [Example 6: "remove" `property` from the `metadata.properties` array](#patch-example-6-remove-property-from-the-metadataproperties-array)
- [Example 7: "test" if a `property` exists in the `metadata.properties` array](#patch-example-7-test-property-exists-in-the-metadataproperties-array)

##### Patch example 1: "add" BOM `serialNumber`

Expand Down Expand Up @@ -1262,6 +1271,47 @@ The patched, output BOM has the two new properties at the specified indices:
}
```

##### Patch example 4a: `--normalize` properties after patching

This variant of the previous example also normalizes the output BOM arrays; in this case, normalizing the existing and added properties.

```bash
./sbom-utility patch --input-file test/patch/cdx-1-5-simplest-base.json --patch-file test/patch/cdx-patch-example-add-metadata-properties.json --normalize -q
```

The patched and **normalized** properties appear as follows:

```shell
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"timestamp": "2023-10-12T19:07:00Z",
"properties": [
{
"name": "Property 1",
"value": "Value 1"
},
{
"name": "Property 2",
"value": "Value 2"
},
{
"name": "foo",
"value": "bar"
},
{
"name": "rush",
"value": "yyz"
}
]
}
}
```

```json

##### Patch example 5: "replace" BOM `version` and `timestamp`

This example shows how the patch's "replace" operation can be used to update the BOM document's `version` and `timestamp` values.
Expand Down
12 changes: 12 additions & 0 deletions cmd/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func initCommandPatchFlags(command *cobra.Command) (err error) {
command.PersistentFlags().StringVar(&utils.GlobalFlags.PatchFlags.OutputFormat, FLAG_OUTPUT_FORMAT, FORMAT_JSON,
MSG_FLAG_OUTPUT_FORMAT+PATCH_OUTPUT_SUPPORTED_FORMATS)
command.Flags().StringVarP(&utils.GlobalFlags.PatchFlags.PatchFile, FLAG_PATCH_FILE, "", "", MSG_PATCH_FILE)
command.PersistentFlags().BoolVar(&utils.GlobalFlags.PersistentFlags.OutputNormalize, FLAG_OUTPUT_NORMALIZE, false, MSG_FLAG_OUTPUT_NORMALIZE)
err = command.MarkFlagRequired(FLAG_PATCH_FILE)
if err != nil {
err = getLogger().Errorf("unable to mark flag `%s` as required: %s", FLAG_PATCH_FILE, err)
Expand Down Expand Up @@ -197,6 +198,17 @@ func Patch(writer io.Writer, persistentFlags utils.PersistentCommandFlags, patch
return
}

// Sort slices of BOM if "sort" flag set to true
if persistentFlags.OutputNormalize {
// Sort the slices of structures
if document.GetCdxBom() != nil {
bom := document.GetCdxBom()
if schema.NormalizeSupported(bom) {
document.GetCdxBom().Normalize()
}
}
}

// Output the "patched" version of the Input BOM
format := persistentFlags.OutputFormat
getLogger().Infof("Writing patched BOM (`%s` format)...", format)
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ const (
FLAG_QUIET_MODE = "quiet"
FLAG_QUIET_MODE_SHORT = "q"
FLAG_OUTPUT_INDENT = "indent"
FLAG_OUTPUT_NORMALIZE = "normalize"
FLAG_LOG_OUTPUT_INDENT = "log-indent"
FLAG_FILE_OUTPUT_FORMAT = "format"
FLAG_COLORIZE_OUTPUT = "colorize"
FLAG_OUTPUT_NORMALIZE = "normalize"
)

const (
Expand Down
1 change: 0 additions & 1 deletion cmd/trim.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import (
const (
FLAG_TRIM_FROM_PATHS = "from"
FLAG_TRIM_MAP_KEYS = "keys"
FLAG_TRIM_NORMALIZE = "normalize"
)

// flag help (translate)
Expand Down