Skip to content

feat: support multi license mix #582

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

Merged
merged 7 commits into from
Jul 24, 2025
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
8 changes: 4 additions & 4 deletions schema/bom-1.7.proto
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,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;
Expand Down Expand Up @@ -572,7 +572,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;
Expand Down Expand Up @@ -709,7 +709,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;
Expand Down Expand Up @@ -831,7 +831,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;
Expand Down
53 changes: 27 additions & 26 deletions schema/bom-1.7.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1525,36 +1525,31 @@
},
"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.",
Copy link
Member Author

@jkowalleck jkowalleck Apr 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pombredanne proposed to go with

EITHER (list of SPDX licenses and/or named licenses) OR (list of SPDX License Expressions)
but with the addons from #619

Copy link
Member Author

@jkowalleck jkowalleck Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont see how this proposal is a user-friendly solution.
if my intake is a list of declared SPDX licenses, and i am planning to add a single SPDX Expression for the concluded license, then I would have to migrate all the existing structures from SPDX licenses to SPDX license expressions.

INTAKE

<licenses>
  <license acknowledgement="declared"><id>Apache-2.0<id><license>
  <license acknowledgement="declared"><id>MIT<id><license>
  <license acknowledgement="declared"><name>My Custom Foo<name><license>
<licenses>

OUTPUT as proposed - causing migration

<licenses>
  <expression acknowledgement="declared">Apache-2.0</expression>
  <expression acknowledgement="declared">MIT</expression>
  <expression acknowledgement="declared">LicenseRef-My-Custom-Foo</expression>
  <expression acknowledgement="concluded">Apache-2.0 and MIT and LicenseRef-My-Custom-Foo</expression>
<licenses>

instead, i would prefer to not migrate any structures and still be able to add new data.

this is especially important when evidence collection happens - i want to be free with the types i record, i dont want ot be forces to use only one or the other.

"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"],
"required": [
"license"
],
"additionalProperties": false,
"properties": {
"license": {"$ref": "#/definitions/license"}
"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": [{
},
{
"title": "License Expression",
"description": "Specifies the details and attributes related to a software license.\nIt must be a valid SPDX license expression, along with additional properties such as license acknowledgment.",
"type": "object",
"additionalProperties": false,
"required": ["expression"],
"required": [
"expression"
],
"properties": {
"expression": {
"type": "string",
Expand Down Expand Up @@ -1600,7 +1595,9 @@
"type": "string",
"title": "License URL",
"description": "The URL to the license file. If specified, a 'license' externalReference should also be specified for completeness",
"examples": ["https://www.apache.org/licenses/LICENSE-2.0.txt"],
"examples": [
"https://www.apache.org/licenses/LICENSE-2.0.txt"
],
"format": "iri-reference"
}
},
Expand All @@ -1615,17 +1612,21 @@
"title": "BOM Reference",
"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."
},
"licensing": {"$ref": "#/definitions/licensing"},
"licensing": {
"$ref": "#/definitions/licensing"
},
"properties": {
"type": "array",
"title": "Properties",
"description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is optional.",
"items": {"$ref": "#/definitions/property"}
"items": {
"$ref": "#/definitions/property"
}
}
}
}]
}
]
}
]
}
},
"commit": {
"type": "object",
Expand Down
11 changes: 7 additions & 4 deletions schema/bom-1.7.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -2553,10 +2553,13 @@ 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" type="bom:licenseExpressionType" minOccurs="0" maxOccurs="1" />
<xs:element name="expression-detailed" type="bom:licenseExpressionDetailedType" 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" type="bom:licenseExpressionType"/>
<xs:element name="expression-detailed" type="bom:licenseExpressionDetailedType"/>
</xs:choice>
</xs:complexType>

Expand Down
Original file line number Diff line number Diff line change
@@ -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"
}
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -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>
45 changes: 45 additions & 0 deletions tools/src/test/resources/1.7/valid-license-choice-1.7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$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."
}
}
},
{
"expression": "LicenseRef-MIT-Style-2",
"expressionDetails": [
{
"licenseIdentifier": "LicenseRef-MIT-Style-2",
"url": "https://example.com/license"
}
]
}
]
}
]
}
43 changes: 43 additions & 0 deletions tools/src/test/resources/1.7/valid-license-choice-1.7.textproto
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 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."
}
}
}
licenses {
expression_detailed {
expression: "LicenseRef-MIT-Style-2"
details {
license_identifier: "LicenseRef-MIT-Style-2"
url: "https://example.com/license"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -8,17 +11,20 @@
<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>
<expression-detailed expression="LicenseRef-MIT-Style-2">
<details license-identifier="LicenseRef-MIT-Style-2">
<url>https://example.com/license</url>
</details>
</expression-detailed>
</licenses>
<purl>pkg:maven/com.acme/[email protected]?packaging=jar</purl>
</component>
Expand Down
Loading