From 9f5b308f44a73a7622b3ed3da988f5b3f967e611 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck <jan.kowalleck@gmail.com> Date: Mon, 20 Jan 2025 16:38:51 +0100 Subject: [PATCH 1/2] feat: licenses allow mix of multiple SPDX expressions AND/OR multiple named/spdx licenses Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com> --- schema/bom-1.7.proto | 8 +- schema/bom-1.7.schema.json | 30 +++---- schema/bom-1.7.xsd | 9 ++- ...id-license-declared-concluded-mix-1.6.json | 79 +++++++++++++++++++ ...lid-license-declared-concluded-mix-1.6.xml | 46 +++++++++++ ...id-license-declared-concluded-mix-1.7.json | 79 +++++++++++++++++++ ...cense-declared-concluded-mix-1.7.textproto | 73 +++++++++++++++++ ...lid-license-declared-concluded-mix-1.7.xml | 46 +++++++++++ 8 files changed, 343 insertions(+), 27 deletions(-) create mode 100644 tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.json create mode 100644 tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.xml create mode 100644 tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.json create mode 100644 tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.textproto create mode 100644 tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.xml diff --git a/schema/bom-1.7.proto b/schema/bom-1.7.proto index 4677eb5b..ec5aaf6a 100644 --- a/schema/bom-1.7.proto +++ b/schema/bom-1.7.proto @@ -114,7 +114,7 @@ message Component { optional Scope scope = 11; // The hashes of the component. repeated Hash hashes = 12; - // EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression) + // A list of SPDX licenses and/or named licenses and/or SPDX License Expression. repeated LicenseChoice licenses = 13; // An optional copyright notice informing users of the underlying claims to copyright ownership in a published work. optional string copyright = 14; @@ -506,7 +506,7 @@ message Metadata { // The organization that supplied the component that the BOM describes. The supplier may often be the manufacture, but may also be a distributor or repackager. optional OrganizationalEntity supplier = 6; // The license information for the BOM document. This may be different from the license(s) of the component(s) that the BOM describes. - // EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression) + // A list of SPDX licenses and/or named licenses and/or SPDX License Expression. repeated LicenseChoice licenses = 7; // Specifies optional, custom, properties repeated Property properties = 8; @@ -641,7 +641,7 @@ message Service { optional bool x_trust_boundary = 9; // Specifies information about the data including the directional flow of data and the data classification. repeated DataFlow data = 10; - // EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression) + // A list of SPDX licenses and/or named licenses and/or SPDX License Expression. repeated LicenseChoice licenses = 11; // Provides the ability to document external references related to the service. repeated ExternalReference external_references = 12; @@ -745,7 +745,7 @@ message EvidenceCopyright { // Provides the ability to document evidence collected through various forms of extraction or analysis. message Evidence { - // EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression) + // A list of SPDX licenses and/or named licenses and/or SPDX License Expression. repeated LicenseChoice licenses = 1; // Copyright evidence captures intellectual property assertions, providing evidence of possible ownership and legal protection. repeated EvidenceCopyright copyright = 2; diff --git a/schema/bom-1.7.schema.json b/schema/bom-1.7.schema.json index 2a8e13ab..fdbd78ae 100644 --- a/schema/bom-1.7.schema.json +++ b/schema/bom-1.7.schema.json @@ -1454,14 +1454,11 @@ }, "licenseChoice": { "title": "License Choice", - "description": "EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression)", + "description": "A list of SPDX licenses and/or named licenses and/or SPDX License Expression.", "type": "array", - "oneOf": [ - { - "title": "Multiple licenses", - "description": "A list of SPDX licenses and/or named licenses.", - "type": "array", - "items": { + "items": { + "oneOf": [ + { "type": "object", "title": "License", "required": ["license"], @@ -1469,17 +1466,10 @@ "properties": { "license": {"$ref": "#/definitions/license"} } - } - }, - { - "title": "SPDX License Expression", - "description": "A tuple of exactly one SPDX License Expression.", - "type": "array", - "additionalItems": false, - "minItems": 1, - "maxItems": 1, - "items": [{ + }, + { "type": "object", + "title": "SPDX License Expression", "additionalProperties": false, "required": ["expression"], "properties": { @@ -1501,9 +1491,9 @@ "description": "An optional identifier which can be used to reference the license elsewhere in the BOM. Every bom-ref must be unique within the BOM.\nValue SHOULD not start with the BOM-Link intro 'urn:cdx:' to avoid conflicts with BOM-Links." } } - }] - } - ] + } + ] + } }, "commit": { "type": "object", diff --git a/schema/bom-1.7.xsd b/schema/bom-1.7.xsd index 173634c9..6b95e54f 100644 --- a/schema/bom-1.7.xsd +++ b/schema/bom-1.7.xsd @@ -2297,9 +2297,12 @@ limitations under the License. </xs:simpleType> <xs:complexType name="licenseChoiceType"> - <xs:choice> - <xs:element name="license" type="bom:licenseType" minOccurs="0" maxOccurs="unbounded"/> - <xs:element name="expression" minOccurs="0" maxOccurs="1"> + <xs:annotation> + <xs:documentation>A list of SPDX licenses and/or named licenses and/or SPDX License Expression.</xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="license" type="bom:licenseType"/> + <xs:element name="expression"> <xs:annotation> <xs:documentation>A valid SPDX license expression. Refer to https://spdx.org/specifications for syntax requirements diff --git a/tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.json b/tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.json new file mode 100644 index 00000000..7c6813c1 --- /dev/null +++ b/tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.json @@ -0,0 +1,79 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:df628836-6b9b-41c9-a724-b44743c54d42", + "version": 1, + "metadata": { + "lifecycles": [{"phase": "design"}] + }, + "components": [ + { + "type": "library", + "group": "com.example", + "name": "situation-A", + "version": "1", + "description": "Multiple licenses: declared ids/names, and a concluded expression", + "licenses": [ + { + "license": { + "id": "MIT", + "acknowledgement": "declared" + } + }, + { + "license": { + "id": "PostgreSQL", + "acknowledgement": "declared" + } + }, + { + "license": { + "name": "Apache Software License", + "acknowledgement": "declared" + } + }, + { + "expression": "(MIT OR PostgreSQL OR Apache-2.0)", + "acknowledgement": "concluded" + } + ] + }, + { + "type": "library", + "group": "com.example", + "name": "situation-B", + "version": "1", + "description": "Multiple license expressions: one declared, one concluded", + "licenses": [ + { + "expression": "MIT OR (GPL-3.0 OR GPL-2.0)", + "acknowledgement": "declared" + }, + { + "expression": "(GPL-3.0-only AND LGPL-2.0-only)", + "acknowledgement": "concluded" + } + ] + }, + { + "type": "library", + "group": "com.example", + "name": "situation-C", + "version": "1", + "description": "Multiple license: one declared expression, one concluded id", + "licenses": [ + { + "expression": "GPL-3.0-or-later OR GPL-2.0", + "acknowledgement": "declared" + }, + { + "license": { + "id": "GPL-3.0-only", + "acknowledgement": "concluded" + } + } + ] + } + ] +} diff --git a/tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.xml b/tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.xml new file mode 100644 index 00000000..849ad97b --- /dev/null +++ b/tools/src/test/resources/1.6/invalid-license-declared-concluded-mix-1.6.xml @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<bom xmlns="http://cyclonedx.org/schema/bom/1.6" + serialNumber="urn:uuid:df628836-6b9b-41c9-a724-b44743c54d42" +> + <!-- + All license posture in here is for show-case ony. + This is not a real law-case! + --> + <metadata> + <lifecycles><lifecycle><phase>design</phase></lifecycle></lifecycles> + </metadata> + <components> + <component type="library"> + <group>com.example</group> + <name>situation-A</name> + <version>1</version> + <description>Multiple licenses: declared ids/names, and a concluded expression</description> + <licenses> + <license acknowledgement="declared"><id>MIT</id></license> + <license acknowledgement="declared"><id>PostgreSQL</id></license> + <license acknowledgement="declared"><name>Apache Software License</name></license> + <expression acknowledgement="concluded">(MIT OR PostgreSQL OR Apache-2.0)</expression> + </licenses> + </component> + <component type="library"> + <group>com.example</group> + <name>situation-B</name> + <version>1</version> + <description>Multiple license expressions: one declared, one concluded</description> + <licenses> + <expression acknowledgement="declared">MIT OR (GPL-3.0 OR GPL-2.0)</expression> + <expression acknowledgement="concluded">(GPL-3.0-only AND LGPL-2.0-only)</expression> + </licenses> + </component> + <component type="library"> + <group>com.example</group> + <name>situation-C</name> + <version>1</version> + <description>Multiple license: one declared expression, one concluded id</description> + <licenses> + <expression acknowledgement="declared">GPL-3.0-or-later OR GPL-2.0</expression> + <license acknowledgement="concluded"><id>GPL-3.0-only</id></license> + </licenses> + </component> + </components> +</bom> diff --git a/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.json b/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.json new file mode 100644 index 00000000..82af9bf1 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.json @@ -0,0 +1,79 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "serialNumber": "urn:uuid:df628836-6b9b-41c9-a724-b44743c54d42", + "version": 1, + "metadata": { + "lifecycles": [{"phase": "design"}] + }, + "components": [ + { + "type": "library", + "group": "com.example", + "name": "situation-A", + "version": "1", + "description": "Multiple licenses: declared ids/names, and a concluded expression", + "licenses": [ + { + "license": { + "id": "MIT", + "acknowledgement": "declared" + } + }, + { + "license": { + "id": "PostgreSQL", + "acknowledgement": "declared" + } + }, + { + "license": { + "name": "Apache Software License", + "acknowledgement": "declared" + } + }, + { + "expression": "(MIT OR PostgreSQL OR Apache-2.0)", + "acknowledgement": "concluded" + } + ] + }, + { + "type": "library", + "group": "com.example", + "name": "situation-B", + "version": "1", + "description": "Multiple license expressions: one declared, one concluded", + "licenses": [ + { + "expression": "MIT OR (GPL-3.0 OR GPL-2.0)", + "acknowledgement": "declared" + }, + { + "expression": "(GPL-3.0-only AND LGPL-2.0-only)", + "acknowledgement": "concluded" + } + ] + }, + { + "type": "library", + "group": "com.example", + "name": "situation-C", + "version": "1", + "description": "Multiple license: one declared expression, one concluded id", + "licenses": [ + { + "expression": "GPL-3.0-or-later OR GPL-2.0", + "acknowledgement": "declared" + }, + { + "license": { + "id": "GPL-3.0-only", + "acknowledgement": "concluded" + } + } + ] + } + ] +} diff --git a/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.textproto b/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.textproto new file mode 100644 index 00000000..fb0d57b8 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.textproto @@ -0,0 +1,73 @@ +# proto-file: schema/bom-1.7.proto +# proto-message: Bom + +# All license posture in here is for show-case ony. +# This is not a real law-case! + +spec_version: "1.7" +version: 1 +serial_number: "urn:uuid:df628836-6b9b-41c9-a724-b44743c54d42" +metadata: { + lifecycles { phase: LIFECYCLE_PHASE_DESIGN } +} +components { + type: CLASSIFICATION_LIBRARY + group: "com.example" + name: "situation-A" + version: "1" + description: "Multiple licenses: declared ids/names, and a concluded expression" + licenses { + license { + id: "MIT" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_DECLARED + } + } + licenses { + license { + id: "PostgreSQL" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_DECLARED + } + } + licenses { + license { + name: "Apache Software License" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_DECLARED + } + } + licenses { + expression: "(MIT OR PostgreSQL OR Apache-2.0)" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_CONCLUDED + } +} +components { + type: CLASSIFICATION_LIBRARY + group: "com.example" + name: "situation-B" + version: "1" + description: "Multiple license expressions: one declared, one concluded" + licenses { + expression: "MIT OR (GPL-3.0 OR GPL-2.0)" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_DECLARED + } + licenses { + expression: "(GPL-3.0-only AND LGPL-2.0-only)" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_CONCLUDED + } +} +components { + type: CLASSIFICATION_LIBRARY + group: "com.example" + name: "situation-C" + version: "1" + description: "Multiple license: one declared expression, one concluded id" + licenses { + expression: "GPL-3.0-or-later OR GPL-2.0" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_DECLARED + } + licenses { + license { + id: "GPL-3.0-only" + acknowledgement: LICENSE_ACKNOWLEDGEMENT_ENUMERATION_CONCLUDED + } + } +} diff --git a/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.xml b/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.xml new file mode 100644 index 00000000..47c16c52 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-license-declared-concluded-mix-1.7.xml @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<bom xmlns="http://cyclonedx.org/schema/bom/1.7" + serialNumber="urn:uuid:df628836-6b9b-41c9-a724-b44743c54d42" +> + <!-- + All license posture in here is for show-case ony. + This is not a real law-case! + --> + <metadata> + <lifecycles><lifecycle><phase>design</phase></lifecycle></lifecycles> + </metadata> + <components> + <component type="library"> + <group>com.example</group> + <name>situation-A</name> + <version>1</version> + <description>Multiple licenses: declared ids/names, and a concluded expression</description> + <licenses> + <license acknowledgement="declared"><id>MIT</id></license> + <license acknowledgement="declared"><id>PostgreSQL</id></license> + <license acknowledgement="declared"><name>Apache Software License</name></license> + <expression acknowledgement="concluded">(MIT OR PostgreSQL OR Apache-2.0)</expression> + </licenses> + </component> + <component type="library"> + <group>com.example</group> + <name>situation-B</name> + <version>1</version> + <description>Multiple license expressions: one declared, one concluded</description> + <licenses> + <expression acknowledgement="declared">MIT OR (GPL-3.0 OR GPL-2.0)</expression> + <expression acknowledgement="concluded">(GPL-3.0-only AND LGPL-2.0-only)</expression> + </licenses> + </component> + <component type="library"> + <group>com.example</group> + <name>situation-C</name> + <version>1</version> + <description>Multiple license: one declared expression, one concluded id</description> + <licenses> + <expression acknowledgement="declared">GPL-3.0-or-later OR GPL-2.0</expression> + <license acknowledgement="concluded"><id>GPL-3.0-only</id></license> + </licenses> + </component> + </components> +</bom> From 4abbe2fce89f4f492ecc96da45cdbbac873f30c3 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck <jan.kowalleck@gmail.com> Date: Wed, 22 Jan 2025 10:37:51 +0100 Subject: [PATCH 2/2] tests Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com> --- .../1.7/valid-license-choice-1.7.json | 36 +++++++++++++++++++ .../1.7/valid-license-choice-1.7.textproto | 34 ++++++++++++++++++ ...e-1.7.xml => valid-license-choice-1.7.xml} | 15 ++++---- 3 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 tools/src/test/resources/1.7/valid-license-choice-1.7.json create mode 100644 tools/src/test/resources/1.7/valid-license-choice-1.7.textproto rename tools/src/test/resources/1.7/{invalid-license-choice-1.7.xml => valid-license-choice-1.7.xml} (53%) diff --git a/tools/src/test/resources/1.7/valid-license-choice-1.7.json b/tools/src/test/resources/1.7/valid-license-choice-1.7.json new file mode 100644 index 00000000..a7026109 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-license-choice-1.7.json @@ -0,0 +1,36 @@ +{ + "$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": "application", + "publisher": "Acme Inc", + "group": "com.acme", + "name": "tomcat-catalina", + "version": "9.0.14", + "description": "Modified version of Apache Catalina", + "scope": "required", + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + }, + { + "expression": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0" + }, + { + "license": { + "name": "My Own License", + "text": { + "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + } + } + } + ] + } + ] +} diff --git a/tools/src/test/resources/1.7/valid-license-choice-1.7.textproto b/tools/src/test/resources/1.7/valid-license-choice-1.7.textproto new file mode 100644 index 00000000..7daa3d30 --- /dev/null +++ b/tools/src/test/resources/1.7/valid-license-choice-1.7.textproto @@ -0,0 +1,34 @@ +# proto-file: schema/bom-1.7.proto +# proto-message: Bom + +# All license posture in here is for show-case ony. +# This is not a real law-case! + +spec_version: "1.7" +serial_number: "urn:uuid:b1ef52c6-7cd8-43d5-9e42-5e69044bbe9e" +version: 1 +components { + type: CLASSIFICATION_APPLICATION + publisher: "Acme Inc" + group: "com.acme" + name: "tomcat-catalina" + version: "9.0.14" + description: "Modified version of Apache Catalina" + scope: SCOPE_REQUIRED + licenses { + license: { + id: "Apache-2.0" + } + } + licenses { + expression: "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0" + } + licenses { + license: { + name: "My Own License" + text: { + value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + } + } + } +} diff --git a/tools/src/test/resources/1.7/invalid-license-choice-1.7.xml b/tools/src/test/resources/1.7/valid-license-choice-1.7.xml similarity index 53% rename from tools/src/test/resources/1.7/invalid-license-choice-1.7.xml rename to tools/src/test/resources/1.7/valid-license-choice-1.7.xml index fec014d5..1947c9a2 100644 --- a/tools/src/test/resources/1.7/invalid-license-choice-1.7.xml +++ b/tools/src/test/resources/1.7/valid-license-choice-1.7.xml @@ -1,5 +1,8 @@ <?xml version="1.0"?> -<bom serialNumber="urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79" version="1" xmlns="http://cyclonedx.org/schema/bom/1.7"> +<bom xmlns="http://cyclonedx.org/schema/bom/1.7" + serialNumber="urn:uuid:b1ef52c6-7cd8-43d5-9e42-5e69044bbe9e" + version="1" +> <components> <component type="application"> <publisher>Acme Inc</publisher> @@ -8,17 +11,15 @@ <version>9.0.14</version> <description>Modified version of Apache Catalina</description> <scope>required</scope> - <hashes> - <hash alg="MD5">3942447fac867ae5cdb3229b658f4d48</hash> - <hash alg="SHA-1">e6b1000b94e835ffd37f4c6dcbdad43f4b48a02a</hash> - <hash alg="SHA-256">f498a8ff2dd007e29c2074f5e4b01a9a01775c3ff3aeaf6906ea503bc5791b7b</hash> - <hash alg="SHA-512">e8f33e424f3f4ed6db76a482fde1a5298970e442c531729119e37991884bdffab4f9426b7ee11fccd074eeda0634d71697d6f88a460dce0ac8d627a29f7d1282</hash> - </hashes> <licenses> <license> <id>Apache-2.0</id> </license> <expression>EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0</expression> + <license> + <name>My Own License</name> + <text><![CDATA[Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.]]></text> + </license> </licenses> <purl>pkg:maven/com.acme/tomcat-catalina@9.0.14?packaging=jar</purl> </component>