From af7c9f8b253b385f4759d0cfac3ecde679ed0585 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Mon, 3 Jun 2024 17:13:26 -0400 Subject: [PATCH 1/2] Support the --normalize flag on the Patch command Signed-off-by: Matt Rutkowski --- README.md | 2 +- cmd/patch.go | 11 +++++++++++ cmd/root.go | 2 +- cmd/trim.go | 1 - 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 379e50d9..7bbf71e7 100644 --- a/README.md +++ b/README.md @@ -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.

**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.* diff --git a/cmd/patch.go b/cmd/patch.go index 0d5ba935..13f618f4 100644 --- a/cmd/patch.go +++ b/cmd/patch.go @@ -197,6 +197,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) diff --git a/cmd/root.go b/cmd/root.go index e0fe488d..5ef5d468 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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 ( diff --git a/cmd/trim.go b/cmd/trim.go index b2741cfb..0c89884b 100644 --- a/cmd/trim.go +++ b/cmd/trim.go @@ -32,7 +32,6 @@ import ( const ( FLAG_TRIM_FROM_PATHS = "from" FLAG_TRIM_MAP_KEYS = "keys" - FLAG_TRIM_NORMALIZE = "normalize" ) // flag help (translate) From ee90a0bcc191bceff87bccd561e436127b9696d0 Mon Sep 17 00:00:00 2001 From: Matt Rutkowski Date: Thu, 6 Jun 2024 12:03:12 -0500 Subject: [PATCH 2/2] Document normalize flag and provide an example Signed-off-by: Matt Rutkowski --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++------- cmd/patch.go | 1 + 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7bbf71e7..a29a6750 100644 --- a/README.md +++ b/README.md @@ -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. @@ -1024,17 +1024,26 @@ The patch command operates on a JSON BOM input file (see [`--input-file` flag](# The `--patch-file ` 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` @@ -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. diff --git a/cmd/patch.go b/cmd/patch.go index 13f618f4..cf6c33fe 100644 --- a/cmd/patch.go +++ b/cmd/patch.go @@ -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)