Skip to content

Commit 375d209

Browse files
authored
feat: add properties for licenses according to CycloneDX 1.5 (#947)
This change adds properties to DisjunctiveLicense according to CycloneDX v1.5 Related to / implements a part of issue #578 ---- ### AI Tool Disclosure - [x] My contribution does not include any AI-generated content - [ ] My contribution includes AI-generated content, as disclosed below: - AI Tools: `[e.g. GitHub CoPilot, ChatGPT, JetBrains Junie etc.]` - LLMs and versions: `[e.g. GPT-4.1, Claude Haiku 4.5, Gemini 2.5 Pro etc.]` - Prompts: `[Summarize the key prompts or instructions given to the AI tools]` ### Affirmation - [x] My code follows the [CONTRIBUTING.md](https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CONTRIBUTING.md) guidelines Signed-off-by: Peter Schuster <p.schuster@pilz.de>
1 parent 71edacf commit 375d209

17 files changed

+333
-13
lines changed

cyclonedx/model/license.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
License related things
2121
"""
2222

23+
from collections.abc import Iterable
2324
from enum import Enum
2425
from json import loads as json_loads
2526
from typing import TYPE_CHECKING, Any, Optional, Union
@@ -34,7 +35,7 @@
3435
from ..exception.model import MutuallyExclusivePropertiesException
3536
from ..exception.serialization import CycloneDxDeserializationException
3637
from ..schema.schema import SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7
37-
from . import AttachedText, XsUri
38+
from . import AttachedText, Property, XsUri
3839
from .bom_ref import BomRef
3940

4041

@@ -85,6 +86,7 @@ def __init__(
8586
id: Optional[str] = None, name: Optional[str] = None,
8687
text: Optional[AttachedText] = None, url: Optional[XsUri] = None,
8788
acknowledgement: Optional[LicenseAcknowledgement] = None,
89+
properties: Optional[Iterable[Property]] = None,
8890
) -> None:
8991
if not id and not name:
9092
raise MutuallyExclusivePropertiesException('Either `id` or `name` MUST be supplied')
@@ -99,6 +101,7 @@ def __init__(
99101
self._text = text
100102
self._url = url
101103
self._acknowledgement = acknowledgement
104+
self._properties = SortedSet(properties or [])
102105

103106
@property
104107
@serializable.view(SchemaVersion1Dot5)
@@ -200,17 +203,25 @@ def url(self, url: Optional[XsUri]) -> None:
200203
# def licensing(self, ...) -> None:
201204
# ... # TODO since CDX1.5
202205

203-
# @property
204-
# ...
205-
# @serializable.view(SchemaVersion1Dot5)
206-
# @serializable.view(SchemaVersion1Dot6)
207-
# @serializable.xml_sequence(6)
208-
# def properties(self) -> ...:
209-
# ... # TODO since CDX1.5
210-
#
211-
# @licensing.setter
212-
# def properties(self, ...) -> None:
213-
# ... # TODO since CDX1.5
206+
@property
207+
@serializable.view(SchemaVersion1Dot5)
208+
@serializable.view(SchemaVersion1Dot6)
209+
@serializable.view(SchemaVersion1Dot7)
210+
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
211+
@serializable.xml_sequence(6)
212+
def properties(self) -> 'SortedSet[Property]':
213+
"""
214+
Provides the ability to document properties in a key/value store. This provides flexibility to include data not
215+
officially supported in the standard without having to use additional namespaces or create extensions.
216+
217+
Return:
218+
Set of `Property`
219+
"""
220+
return self._properties
221+
222+
@properties.setter
223+
def properties(self, properties: Iterable[Property]) -> None:
224+
self._properties = SortedSet(properties)
214225

215226
@property
216227
@serializable.view(SchemaVersion1Dot6)
@@ -245,6 +256,7 @@ def __comparable_tuple(self) -> _ComparableTuple:
245256
self._url,
246257
self._text,
247258
self._bom_ref.value,
259+
_ComparableTuple(self._properties),
248260
))
249261

250262
def __eq__(self, other: object) -> bool:

tests/_data/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,14 @@ def get_bom_with_licenses() -> Bom:
10821082
DisjunctiveLicense(name='some additional',
10831083
text=AttachedText(content='this is additional license text')),
10841084
]),
1085+
Component(name='c-with-license-properties', type=ComponentType.LIBRARY, bom_ref='C4',
1086+
licenses=[
1087+
DisjunctiveLicense(id='Apache-2.0',
1088+
properties=[Property(name='key1', value='val1'),
1089+
Property(name='key2', value='val2')]),
1090+
DisjunctiveLicense(name='some other license',
1091+
properties=[Property(name='myname', value='proprietary')]),
1092+
]),
10851093
],
10861094
services=[
10871095
Service(name='s-with-expression', bom_ref='S1',

tests/_data/snapshots/get_bom_with_licenses-1.0.xml.bin

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
<version/>
1212
<modified>false</modified>
1313
</component>
14+
<component type="library">
15+
<name>c-with-license-properties</name>
16+
<version/>
17+
<modified>false</modified>
18+
</component>
1419
<component type="library">
1520
<name>c-with-name</name>
1621
<version/>

tests/_data/snapshots/get_bom_with_licenses-1.1.xml.bin

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@
1818
<expression>Apache-2.0 OR MIT</expression>
1919
</licenses>
2020
</component>
21+
<component type="library" bom-ref="C4">
22+
<name>c-with-license-properties</name>
23+
<version/>
24+
<licenses>
25+
<license>
26+
<id>Apache-2.0</id>
27+
</license>
28+
<license>
29+
<name>some other license</name>
30+
</license>
31+
</licenses>
32+
</component>
2133
<component type="library" bom-ref="C3">
2234
<name>c-with-name</name>
2335
<version/>

tests/_data/snapshots/get_bom_with_licenses-1.2.json.bin

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@
2525
"type": "library",
2626
"version": ""
2727
},
28+
{
29+
"bom-ref": "C4",
30+
"licenses": [
31+
{
32+
"license": {
33+
"id": "Apache-2.0"
34+
}
35+
},
36+
{
37+
"license": {
38+
"name": "some other license"
39+
}
40+
}
41+
],
42+
"name": "c-with-license-properties",
43+
"type": "library",
44+
"version": ""
45+
},
2846
{
2947
"bom-ref": "C3",
3048
"licenses": [
@@ -62,6 +80,9 @@
6280
{
6381
"ref": "C3"
6482
},
83+
{
84+
"ref": "C4"
85+
},
6586
{
6687
"ref": "S1"
6788
},

tests/_data/snapshots/get_bom_with_licenses-1.2.xml.bin

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@
3030
<expression>Apache-2.0 OR MIT</expression>
3131
</licenses>
3232
</component>
33+
<component type="library" bom-ref="C4">
34+
<name>c-with-license-properties</name>
35+
<version/>
36+
<licenses>
37+
<license>
38+
<id>Apache-2.0</id>
39+
</license>
40+
<license>
41+
<name>some other license</name>
42+
</license>
43+
</licenses>
44+
</component>
3345
<component type="library" bom-ref="C3">
3446
<name>c-with-name</name>
3547
<version/>
@@ -79,6 +91,7 @@
7991
<dependency ref="C1"/>
8092
<dependency ref="C2"/>
8193
<dependency ref="C3"/>
94+
<dependency ref="C4"/>
8295
<dependency ref="S1"/>
8396
<dependency ref="S2"/>
8497
<dependency ref="S3"/>

tests/_data/snapshots/get_bom_with_licenses-1.3.json.bin

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@
2525
"type": "library",
2626
"version": ""
2727
},
28+
{
29+
"bom-ref": "C4",
30+
"licenses": [
31+
{
32+
"license": {
33+
"id": "Apache-2.0"
34+
}
35+
},
36+
{
37+
"license": {
38+
"name": "some other license"
39+
}
40+
}
41+
],
42+
"name": "c-with-license-properties",
43+
"type": "library",
44+
"version": ""
45+
},
2846
{
2947
"bom-ref": "C3",
3048
"licenses": [
@@ -62,6 +80,9 @@
6280
{
6381
"ref": "C3"
6482
},
83+
{
84+
"ref": "C4"
85+
},
6586
{
6687
"ref": "S1"
6788
},

tests/_data/snapshots/get_bom_with_licenses-1.3.xml.bin

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@
3535
<expression>Apache-2.0 OR MIT</expression>
3636
</licenses>
3737
</component>
38+
<component type="library" bom-ref="C4">
39+
<name>c-with-license-properties</name>
40+
<version/>
41+
<licenses>
42+
<license>
43+
<id>Apache-2.0</id>
44+
</license>
45+
<license>
46+
<name>some other license</name>
47+
</license>
48+
</licenses>
49+
</component>
3850
<component type="library" bom-ref="C3">
3951
<name>c-with-name</name>
4052
<version/>
@@ -84,6 +96,7 @@
8496
<dependency ref="C1"/>
8597
<dependency ref="C2"/>
8698
<dependency ref="C3"/>
99+
<dependency ref="C4"/>
87100
<dependency ref="S1"/>
88101
<dependency ref="S2"/>
89102
<dependency ref="S3"/>

tests/_data/snapshots/get_bom_with_licenses-1.4.json.bin

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@
2323
"name": "c-with-expression",
2424
"type": "library"
2525
},
26+
{
27+
"bom-ref": "C4",
28+
"licenses": [
29+
{
30+
"license": {
31+
"id": "Apache-2.0"
32+
}
33+
},
34+
{
35+
"license": {
36+
"name": "some other license"
37+
}
38+
}
39+
],
40+
"name": "c-with-license-properties",
41+
"type": "library"
42+
},
2643
{
2744
"bom-ref": "C3",
2845
"licenses": [
@@ -59,6 +76,9 @@
5976
{
6077
"ref": "C3"
6178
},
79+
{
80+
"ref": "C4"
81+
},
6282
{
6383
"ref": "S1"
6484
},

tests/_data/snapshots/get_bom_with_licenses-1.4.xml.bin

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
<expression>Apache-2.0 OR MIT</expression>
3333
</licenses>
3434
</component>
35+
<component type="library" bom-ref="C4">
36+
<name>c-with-license-properties</name>
37+
<licenses>
38+
<license>
39+
<id>Apache-2.0</id>
40+
</license>
41+
<license>
42+
<name>some other license</name>
43+
</license>
44+
</licenses>
45+
</component>
3546
<component type="library" bom-ref="C3">
3647
<name>c-with-name</name>
3748
<licenses>
@@ -80,6 +91,7 @@
8091
<dependency ref="C1"/>
8192
<dependency ref="C2"/>
8293
<dependency ref="C3"/>
94+
<dependency ref="C4"/>
8395
<dependency ref="S1"/>
8496
<dependency ref="S2"/>
8597
<dependency ref="S3"/>

0 commit comments

Comments
 (0)