Skip to content

Commit 1d1c45a

Browse files
authored
feat: support bom.properties for CycloneDX v1.5+ (#585)
Signed-off-by: Paul Horton <paul.horton@owasp.org>
1 parent d230e67 commit 1d1c45a

File tree

151 files changed

+1074
-44
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+1074
-44
lines changed

cyclonedx/model/bom.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,8 @@ def __init__(self, *, components: Optional[Iterable[Component]] = None,
310310
serial_number: Optional[UUID] = None, version: int = 1,
311311
metadata: Optional[BomMetaData] = None,
312312
dependencies: Optional[Iterable[Dependency]] = None,
313-
vulnerabilities: Optional[Iterable[Vulnerability]] = None) -> None:
313+
vulnerabilities: Optional[Iterable[Vulnerability]] = None,
314+
properties: Optional[Iterable[Property]] = None) -> None:
314315
"""
315316
Create a new Bom that you can manually/programmatically add data to later.
316317
@@ -325,6 +326,7 @@ def __init__(self, *, components: Optional[Iterable[Component]] = None,
325326
self.external_references = external_references or [] # type:ignore[assignment]
326327
self.vulnerabilities = vulnerabilities or [] # type:ignore[assignment]
327328
self.dependencies = dependencies or [] # type:ignore[assignment]
329+
self.properties = properties or [] # type:ignore[assignment]
328330

329331
@property
330332
@serializable.type_mapping(UrnUuidHelper)
@@ -364,7 +366,7 @@ def version(self, version: int) -> None:
364366
@serializable.view(SchemaVersion1Dot4)
365367
@serializable.view(SchemaVersion1Dot5)
366368
@serializable.view(SchemaVersion1Dot6)
367-
@serializable.xml_sequence(1)
369+
@serializable.xml_sequence(10)
368370
def metadata(self) -> BomMetaData:
369371
"""
370372
Get our internal metadata object for this Bom.
@@ -385,7 +387,7 @@ def metadata(self, metadata: BomMetaData) -> None:
385387
@serializable.include_none(SchemaVersion1Dot0)
386388
@serializable.include_none(SchemaVersion1Dot1)
387389
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'component')
388-
@serializable.xml_sequence(2)
390+
@serializable.xml_sequence(20)
389391
def components(self) -> 'SortedSet[Component]':
390392
"""
391393
Get all the Components currently in this Bom.
@@ -406,7 +408,7 @@ def components(self, components: Iterable[Component]) -> None:
406408
@serializable.view(SchemaVersion1Dot5)
407409
@serializable.view(SchemaVersion1Dot6)
408410
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'service')
409-
@serializable.xml_sequence(3)
411+
@serializable.xml_sequence(30)
410412
def services(self) -> 'SortedSet[Service]':
411413
"""
412414
Get all the Services currently in this Bom.
@@ -428,7 +430,7 @@ def services(self, services: Iterable[Service]) -> None:
428430
@serializable.view(SchemaVersion1Dot5)
429431
@serializable.view(SchemaVersion1Dot6)
430432
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
431-
@serializable.xml_sequence(4)
433+
@serializable.xml_sequence(40)
432434
def external_references(self) -> 'SortedSet[ExternalReference]':
433435
"""
434436
Provides the ability to document external references related to the BOM or to the project the BOM describes.
@@ -449,7 +451,7 @@ def external_references(self, external_references: Iterable[ExternalReference])
449451
@serializable.view(SchemaVersion1Dot5)
450452
@serializable.view(SchemaVersion1Dot6)
451453
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'dependency')
452-
@serializable.xml_sequence(5)
454+
@serializable.xml_sequence(50)
453455
def dependencies(self) -> 'SortedSet[Dependency]':
454456
return self._dependencies
455457

@@ -470,25 +472,35 @@ def dependencies(self, dependencies: Iterable[Dependency]) -> None:
470472
# def compositions(self, ...) -> None:
471473
# ... # TODO Since CDX 1.3
472474

473-
# @property
474-
# ...
475-
# @serializable.view(SchemaVersion1Dot3)
476-
# @serializable.view(SchemaVersion1Dot4)
477-
# @serializable.view(SchemaVersion1Dot5)
478-
# @serializable.xml_sequence(7)
479-
# def properties(self) -> ...:
480-
# ... # TODO Since CDX 1.3
481-
#
482-
# @properties.setter
483-
# def properties(self, ...) -> None:
484-
# ... # TODO Since CDX 1.3
475+
@property
476+
# @serializable.view(SchemaVersion1Dot3) @todo: Update py-serializable to support view by OutputFormat filtering
477+
# @serializable.view(SchemaVersion1Dot4) @todo: Update py-serializable to support view by OutputFormat filtering
478+
@serializable.view(SchemaVersion1Dot5)
479+
@serializable.view(SchemaVersion1Dot6)
480+
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
481+
@serializable.xml_sequence(70)
482+
def properties(self) -> 'SortedSet[Property]':
483+
"""
484+
Provides the ability to document properties in a name/value store. This provides flexibility to include data
485+
not officially supported in the standard without having to use additional namespaces or create extensions.
486+
Property names of interest to the general public are encouraged to be registered in the CycloneDX Property
487+
Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. Formal registration is OPTIONAL.
488+
489+
Return:
490+
Set of `Property`
491+
"""
492+
return self._properties
493+
494+
@properties.setter
495+
def properties(self, properties: Iterable[Property]) -> None:
496+
self._properties = SortedSet(properties)
485497

486498
@property
487499
@serializable.view(SchemaVersion1Dot4)
488500
@serializable.view(SchemaVersion1Dot5)
489501
@serializable.view(SchemaVersion1Dot6)
490502
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'vulnerability')
491-
@serializable.xml_sequence(8)
503+
@serializable.xml_sequence(80)
492504
def vulnerabilities(self) -> 'SortedSet[Vulnerability]':
493505
"""
494506
Get all the Vulnerabilities in this BOM.
@@ -682,7 +694,8 @@ def __eq__(self, other: object) -> bool:
682694
def __hash__(self) -> int:
683695
return hash((
684696
self.serial_number, self.version, self.metadata, tuple(self.components), tuple(self.services),
685-
tuple(self.external_references), tuple(self.vulnerabilities), tuple(self.dependencies)
697+
tuple(self.external_references), tuple(self.dependencies), tuple(self.properties),
698+
tuple(self.vulnerabilities),
686699
))
687700

688701
def __repr__(self) -> str:

docs/schema-support.rst

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,16 @@ The following sub-sections aim to explain what support this library provides and
2121
by calling out support for data as defined in the latest CycloneDX standard specification, regardless of whether it is
2222
supported in prior versions of the CycloneDX schema.
2323

24-
Root Level Schema Support
25-
-------------------------
26-
2724
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
2825
| Data Path | Supported? | Notes |
2926
+============================+===============+===================================================================================================+
3027
| ``bom[@version]`` | Yes | |
3128
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
3229
| ``bom[@serialNumber]`` | Yes | |
3330
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
34-
| ``bom.metadata`` | Yes | Not supported: ``lifecycles`` |
31+
| ``bom.metadata`` | Yes | |
3532
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
36-
| ``bom.components`` | Yes | Not supported: ``modified`` (as it is deprecated), ``modelCard``, ``data``, ``signature``. |
33+
| ``bom.components`` | Yes | Not supported: ``modified`` (as it is deprecated), ``signature``. |
3734
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
3835
| ``bom.services`` | Yes | Not supported: ``signature``. |
3936
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
@@ -43,6 +40,8 @@ Root Level Schema Support
4340
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
4441
| ``bom.compositions`` | No | |
4542
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
43+
| ``bom.properties`` | Yes | Supported when outputting to Schema Version >= 1.5. See `schema specification bug 130`_ |
44+
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
4645
| ``bom.vulnerabilities`` | Yes | Note: Prior to CycloneDX 1.4, these were present under ``bom.components`` via a schema extension. |
4746
| | | Note: As of ``cyclonedx-python-lib`` ``>3.0.0``, Vulnerability are modelled differently |
4847
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
@@ -54,27 +53,8 @@ Root Level Schema Support
5453
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
5554
| ``bom.definitions`` | No | |
5655
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
57-
| ``bom.properties`` | No | See `schema specification bug 130`_ |
58-
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
5956
| ``bom.signature`` | No | |
6057
+----------------------------+---------------+---------------------------------------------------------------------------------------------------+
6158

62-
Internal Model Schema Support
63-
-----------------------------
64-
65-
+----------------------------+---------------+----------------------------------------------------------------------------------------------+
66-
| Internal Model | Supported? | Notes |
67-
+============================+===============+==============================================================================================+
68-
| ``ComponentEvidence`` |Yes | Not currently supported: ``callstack``, ``identity``, ``occurrences``. |
69-
+----------------------------+---------------+----------------------------------------------------------------------------------------------+
70-
| ``DisjunctiveLicense`` |Yes | Not currently supported: ``@bom-ref``, ``licensing``, ``properties``. |
71-
+----------------------------+---------------+----------------------------------------------------------------------------------------------+
72-
| ``LicenseExpression`` |Yes | Not currently supported: ``@bom-ref`` |
73-
+----------------------------+---------------+----------------------------------------------------------------------------------------------+
74-
| ``OrganizationalContact`` |Yes | Not currently supported: ``@bom-ref`` |
75-
+----------------------------+---------------+----------------------------------------------------------------------------------------------+
76-
| ``OrganizationalEntity`` |Yes | Not currently supported: ``@bom-ref`` |
77-
+----------------------------+---------------+----------------------------------------------------------------------------------------------+
7859

7960
.. _schema specification bug 130: https://github.com/CycloneDX/specification/issues/130
80-

tests/_data/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ def _make_bom(**kwargs: Any) -> Bom:
133133
bom = Bom(**kwargs)
134134
bom.serial_number = BOM_SERIAL_NUMBER
135135
bom.metadata.timestamp = BOM_TIMESTAMP
136+
bom.properties = get_properties_1()
136137
return bom
137138

138139

tests/_data/snapshots/enum_ComponentScope-1.5.json.bin

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@
7474
}
7575
]
7676
},
77+
"properties": [
78+
{
79+
"name": "key1",
80+
"value": "val1"
81+
},
82+
{
83+
"name": "key2",
84+
"value": "val2"
85+
}
86+
],
7787
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
7888
"version": 1,
7989
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",

tests/_data/snapshots/enum_ComponentScope-1.5.xml.bin

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,8 @@
5555
<dependency ref="scoped-OPTIONAL"/>
5656
<dependency ref="scoped-REQUIRED"/>
5757
</dependencies>
58+
<properties>
59+
<property name="key1">val1</property>
60+
<property name="key2">val2</property>
61+
</properties>
5862
</bom>

tests/_data/snapshots/enum_ComponentScope-1.6.json.bin

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@
7474
}
7575
]
7676
},
77+
"properties": [
78+
{
79+
"name": "key1",
80+
"value": "val1"
81+
},
82+
{
83+
"name": "key2",
84+
"value": "val2"
85+
}
86+
],
7787
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
7888
"version": 1,
7989
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",

tests/_data/snapshots/enum_ComponentScope-1.6.xml.bin

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,8 @@
5555
<dependency ref="scoped-OPTIONAL"/>
5656
<dependency ref="scoped-REQUIRED"/>
5757
</dependencies>
58+
<properties>
59+
<property name="key1">val1</property>
60+
<property name="key2">val2</property>
61+
</properties>
5862
</bom>

tests/_data/snapshots/enum_ComponentType-1.5.json.bin

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@
143143
}
144144
]
145145
},
146+
"properties": [
147+
{
148+
"name": "key1",
149+
"value": "val1"
150+
},
151+
{
152+
"name": "key2",
153+
"value": "val2"
154+
}
155+
],
146156
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
147157
"version": 1,
148158
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",

tests/_data/snapshots/enum_ComponentType-1.5.xml.bin

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,8 @@
8888
<dependency ref="typed-OPERATING_SYSTEM"/>
8989
<dependency ref="typed-PLATFORM"/>
9090
</dependencies>
91+
<properties>
92+
<property name="key1">val1</property>
93+
<property name="key2">val2</property>
94+
</properties>
9195
</bom>

tests/_data/snapshots/enum_ComponentType-1.6.json.bin

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@
151151
}
152152
]
153153
},
154+
"properties": [
155+
{
156+
"name": "key1",
157+
"value": "val1"
158+
},
159+
{
160+
"name": "key2",
161+
"value": "val2"
162+
}
163+
],
154164
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
155165
"version": 1,
156166
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",

0 commit comments

Comments
 (0)