diff --git a/schema/bom-1.7.proto b/schema/bom-1.7.proto index 2f1aaf0d..7b784f07 100644 --- a/schema/bom-1.7.proto +++ b/schema/bom-1.7.proto @@ -106,8 +106,16 @@ message Component { optional string group = 7; // The name of the component. This will often be a shortened, single name of the component. Examples: commons-lang3 and jquery string name = 8; - // The component version. The version should ideally comply with semantic versioning but is not enforced. Version was made optional in v1.4 of the spec. For backward compatibility, it is recommended to use an empty string to represent components without version information. + // The component version. The version should ideally comply with semantic versioning but is not enforced. + // Version was made optional in v1.4 of the spec. + // For backward compatibility, it is recommended to use an empty string to represent components without version information. + // Must be used exclusively, either 'version' or 'versionRange', but not both. string version = 9; + // For an external component, this specifies the accepted version range. + // The value must adhere to the Package URL Version Range syntax (vers), as defined at https://github.com/package-url/purl-spec/blob/master/VERSION-RANGE-SPEC.rst. + // May only be used if `isExternal` is set to `true`. + // Must be used exclusively, either 'version' or 'versionRange', but not both. + optional string versionRange = 33; // Specifies a description for the component optional string description = 10; // Specifies the scope of the component. If a scope is not specified, SCOPE_REQUIRED scope should be assumed by the consumer of the BOM @@ -154,6 +162,10 @@ message Component { repeated string omniborId = 31; // Specifies the Software Heritage persistent identifier (SWHID). The SWHID, if specified, must be valid and conform to the specification defined at: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html repeated string swhid = 32; + // Determine whether this component is external. + // An external component is one that is not part of an assembly, but is expected to be provided by the environment, regardless of the component's `scope`. This setting can be useful for distinguishing which components are bundled with the product and which can be relied upon to be present in the deployment environment. + // This may be set to `true` for runtime components only. For `Bom.metadata.component`, it must be set to `false`. + optional bool isExternal = 34; // implicit defaults to `false` } // Specifies the data flow. diff --git a/schema/bom-1.7.schema.json b/schema/bom-1.7.schema.json index e1dd5b1d..9bcae697 100644 --- a/schema/bom-1.7.schema.json +++ b/schema/bom-1.7.schema.json @@ -923,7 +923,18 @@ "version": { "$ref": "#/definitions/version", "title": "Component Version", - "description": "The component version. The version should ideally comply with semantic versioning but is not enforced." + "description": "The component version. The version should ideally comply with semantic versioning but is not enforced.\nMust be used exclusively, either 'version' or 'versionRange', but not both." + }, + "versionRange": { + "$ref": "#/definitions/versionRange", + "title": "Component Version Range", + "description": "For an external component, this specifies the accepted version range.\nThe value must adhere to the Package URL Version Range syntax (vers), as defined at .\nMay only be used if `.isExternal` is set to `true`.\nMust be used exclusively, either 'version' or 'versionRange', but not both." + }, + "isExternal": { + "type": "boolean", + "title": "Component Is External", + "description": "Determine whether this component is external.\nAn external component is one that is not part of an assembly, but is expected to be provided by the environment, regardless of the component's `.scope`. This setting can be useful for distinguishing which components are bundled with the product and which can be relied upon to be present in the deployment environment.\nThis may be set to `true` for runtime components only. For `$.metadata.component`, it must be set to `false`.", + "default": false }, "description": { "type": "string", @@ -1096,7 +1107,25 @@ "title": "Signature", "description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)." } - } + }, + "allOf": [ + { + "description": "Requirement: ensure that `version` and `versionRange` are not present simultaneously.", + "not": { + "required": ["version", "versionRange"] + } + }, + { + "description": "Requirement: 'versionRange' must not be present when 'isExternal' is `false`.", + "if": { + "properties": { "isExternal": { "const": false } } + }, + "then": { + "not": { "required": ["versionRange"] } + }, + "else": true + } + ] }, "swid": { "type": "object", diff --git a/schema/bom-1.7.xsd b/schema/bom-1.7.xsd index ae468665..c17c1ff3 100644 --- a/schema/bom-1.7.xsd +++ b/schema/bom-1.7.xsd @@ -554,12 +554,26 @@ limitations under the License. of the component. Examples: commons-lang3 and jquery - + - The component version. The version should ideally comply with semantic versioning - but is not enforced. + Must be used exclusively, either 'version' or 'versionRange', but not both. - + + + The component version. The version should ideally comply with semantic versioning + but is not enforced. + + + + + + + + Specifies a description for the component @@ -745,6 +759,15 @@ limitations under the License. + + + + Determine whether this component is external. + An external component is one that is not part of an assembly, but is expected to be provided by the environment, regardless of the component's `@scope`. This setting can be useful for distinguishing which components are bundled with the product and which can be relied upon to be present in the deployment environment. + This may be set to `true` for runtime components only. For `/metadata/component`, it must be set to `false`. + + + @@ -759,6 +782,14 @@ limitations under the License. do not have the same name as an existing attribute used by the schema. + diff --git a/tools/src/test/resources/1.7/informal-invalid-component-versionRange-non-external-explicit.xml b/tools/src/test/resources/1.7/informal-invalid-component-versionRange-non-external-explicit.xml new file mode 100644 index 00000000..66127f86 --- /dev/null +++ b/tools/src/test/resources/1.7/informal-invalid-component-versionRange-non-external-explicit.xml @@ -0,0 +1,16 @@ + + + + + + InvalidVersions + + versionRange may only exist on extraneous components, set `isExternal` explicit + + + diff --git a/tools/src/test/resources/1.7/informal-invalid-component-versionRange-non-external-implicit.xml b/tools/src/test/resources/1.7/informal-invalid-component-versionRange-non-external-implicit.xml new file mode 100644 index 00000000..f2cb83b2 --- /dev/null +++ b/tools/src/test/resources/1.7/informal-invalid-component-versionRange-non-external-implicit.xml @@ -0,0 +1,17 @@ + + + + + + + InvalidVersions + + versionRange may only exist on extraneous components, set `isExternal` implicit by default value + + + diff --git a/tools/src/test/resources/1.7/invalid-component-external-version-and-range.json b/tools/src/test/resources/1.7/invalid-component-external-version-and-range.json new file mode 100644 index 00000000..e1bc979e --- /dev/null +++ b/tools/src/test/resources/1.7/invalid-component-external-version-and-range.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "library", + "name": "InvalidVersions", + "description": "may have `version` or `versionRange`, not both. This one does - it is invalid", + "version": "9.0.14", + "versionRange": "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1", + "isExternal": true + } + ] +} diff --git a/tools/src/test/resources/1.7/invalid-component-external-version-and-range.xml b/tools/src/test/resources/1.7/invalid-component-external-version-and-range.xml new file mode 100644 index 00000000..6cf3f228 --- /dev/null +++ b/tools/src/test/resources/1.7/invalid-component-external-version-and-range.xml @@ -0,0 +1,13 @@ + + + + + InvalidVersions + 9.0.14 + + may have `version` or `versionRange`, not both. This one does - it is invalid + + + diff --git a/tools/src/test/resources/1.7/invalid-component-versionRange-non-external-explicit.json b/tools/src/test/resources/1.7/invalid-component-versionRange-non-external-explicit.json new file mode 100644 index 00000000..fd8fea18 --- /dev/null +++ b/tools/src/test/resources/1.7/invalid-component-versionRange-non-external-explicit.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "library", + "name": "InvalidVersions", + "description": "versionRange may only exist on extraneous components; set `.isExternal` explicit", + "isExternal": false, + "versionRange": "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1" + } + ] +} diff --git a/tools/src/test/resources/1.7/invalid-component-versionRange-non-external-implicit.json b/tools/src/test/resources/1.7/invalid-component-versionRange-non-external-implicit.json new file mode 100644 index 00000000..1d08d7b8 --- /dev/null +++ b/tools/src/test/resources/1.7/invalid-component-versionRange-non-external-implicit.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "library", + "name": "InvalidVersions", + "description": "versionRange may only exist on extraneous components; set `.isExternal` implicit by default value", + "versionRange": "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1" + } + ] +} diff --git a/tools/src/test/resources/1.7/valid-component-external-with-version.json b/tools/src/test/resources/1.7/valid-component-external-with-version.json new file mode 100644 index 00000000..82604759 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-with-version.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:7dc07ac8-7fb8-4782-bf89-4762394e189d", + "version": 1, + "metadata": { + "component": { + "bom-ref": "my-app", + "type": "application", + "name": "My Application" + } + }, + "components": [ + { + "bom-ref": "os", + "type": "operating-system", + "name": "Ubuntu", + "version": "24.04", + "description": "Ubuntu 24.04", + "isExternal": true + } + ], + "dependencies": [ + { + "ref": "my-app", + "dependsOn": [ + "os" + ] + }, + { + "ref": "os" + } + ] +} diff --git a/tools/src/test/resources/1.7/valid-component-external-with-version.textproto b/tools/src/test/resources/1.7/valid-component-external-with-version.textproto new file mode 100644 index 00000000..7ff68ce8 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-with-version.textproto @@ -0,0 +1,30 @@ +# proto-file: schema/bom-1.7.proto +# proto-message: Bom + +spec_version: "1.7" +version: 1 +serial_number: "urn:uuid:7dc07ac8-7fb8-4782-bf89-4762394e189d" +metadata { + component { + bom_ref: "my-app" + type: CLASSIFICATION_APPLICATION + name: "My Application" + } +} +components { + bom_ref: "os" + type: CLASSIFICATION_OPERATING_SYSTEM + name: "Ubuntu" + version: "24.04" + description: "Ubuntu 24.04" + isExternal: true +} +dependencies { + ref: "my-app" + dependencies { + ref: "os" + } +} +dependencies { + ref: "os" +} diff --git a/tools/src/test/resources/1.7/valid-component-external-with-version.xml b/tools/src/test/resources/1.7/valid-component-external-with-version.xml new file mode 100644 index 00000000..97d61c84 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-with-version.xml @@ -0,0 +1,25 @@ + + + + + My Application + + + + + Ubuntu + 24H2 + Windows 11 version 21H2 + + + + + + + + + diff --git a/tools/src/test/resources/1.7/valid-component-external-with-versionRange.json b/tools/src/test/resources/1.7/valid-component-external-with-versionRange.json new file mode 100644 index 00000000..183b2606 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-with-versionRange.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:bdd25550-f1c1-4cb4-b406-0c8d05ad6382", + "version": 1, + "metadata": { + "component": { + "bom-ref": "my-app", + "type": "application", + "name": "My Application" + } + }, + "components": [ + { + "bom-ref": "libcurl", + "type": "library", + "name": "libcurl", + "versionRange": "vers:generic/>=8.7.1|<9.0.0", + "description": "libcurl ^8.7.1", + "isExternal": true + } + ], + "dependencies": [ + { + "ref": "my-app", + "dependsOn": [ + "libcurl" + ] + }, + { + "ref": "libcurl" + } + ] +} diff --git a/tools/src/test/resources/1.7/valid-component-external-with-versionRange.textproto b/tools/src/test/resources/1.7/valid-component-external-with-versionRange.textproto new file mode 100644 index 00000000..fc07c684 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-with-versionRange.textproto @@ -0,0 +1,30 @@ +# proto-file: schema/bom-1.7.proto +# proto-message: Bom + +spec_version: "1.7" +version: 1 +serial_number: "urn:uuid:bdd25550-f1c1-4cb4-b406-0c8d05ad6382" +metadata { + component { + bom_ref: "my-app" + type: CLASSIFICATION_APPLICATION + name: "My Application" + } +} +components { + bom_ref: "libcurl" + type: CLASSIFICATION_LIBRARY + name: "libcurl" + versionRange: "vers:generic/>=8.7.1|<9.0.0" + description: "libcurl ^8.7.1" + isExternal: true +} +dependencies { + ref: "my-app" + dependencies { + ref: "libcurl" + } +} +dependencies { + ref: "libcurl" +} diff --git a/tools/src/test/resources/1.7/valid-component-external-with-versionRange.xml b/tools/src/test/resources/1.7/valid-component-external-with-versionRange.xml new file mode 100644 index 00000000..02557848 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-with-versionRange.xml @@ -0,0 +1,25 @@ + + + + + My Application + + + + + libcurl + =8.7.1|<9.0.0]]> + libcurl ^8.7.1 + + + + + + + + + diff --git a/tools/src/test/resources/1.7/valid-component-external-without-version.json b/tools/src/test/resources/1.7/valid-component-external-without-version.json new file mode 100644 index 00000000..4933e6fb --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-without-version.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "metadata": { + "component": { + "bom-ref": "my-app", + "type": "application", + "name": "My Application" + } + }, + "components": [ + { + "bom-ref": "os", + "type": "operating-system", + "name": "Windows 11 (64bit)", + "description": "any Windows 11", + "isExternal": true + } + ], + "dependencies": [ + { + "ref": "my-app", + "dependsOn": [ + "os" + ] + }, + { + "ref": "os" + } + ] +} diff --git a/tools/src/test/resources/1.7/valid-component-external-without-version.textproto b/tools/src/test/resources/1.7/valid-component-external-without-version.textproto new file mode 100644 index 00000000..abfa45ec --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-without-version.textproto @@ -0,0 +1,29 @@ +# proto-file: schema/bom-1.7.proto +# proto-message: Bom + +spec_version: "1.7" +version: 1 +serial_number: "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79" +metadata { + component { + bom_ref: "my-app" + type: CLASSIFICATION_APPLICATION + name: "My Application" + } +} +components { + bom_ref: "os" + type: CLASSIFICATION_OPERATING_SYSTEM + name: "Windows 11 (64bit)" + description: "any Windows 11" + isExternal: true +} +dependencies { + ref: "my-app" + dependencies { + ref: "os" + } +} +dependencies { + ref: "os" +} diff --git a/tools/src/test/resources/1.7/valid-component-external-without-version.xml b/tools/src/test/resources/1.7/valid-component-external-without-version.xml new file mode 100644 index 00000000..994cad06 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-component-external-without-version.xml @@ -0,0 +1,24 @@ + + + + + My Application + + + + + Windows 11 (64bit) + any Windows 11 + + + + + + + + +