Skip to content

Commit 70adb7c

Browse files
authored
feat: add support for DistributionConstraints in BOM metadata (#906)
part of #903 --------- Signed-off-by: Johannes Feichtner <[email protected]>
1 parent 5d42b55 commit 70adb7c

34 files changed

+584
-5
lines changed

cyclonedx/model/bom.py

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from collections.abc import Generator, Iterable
2020
from datetime import datetime
21+
from enum import Enum
2122
from itertools import chain
2223
from typing import TYPE_CHECKING, Optional, Union
2324
from uuid import UUID, uuid4
@@ -56,6 +57,81 @@
5657
from packageurl import PackageURL
5758

5859

60+
@serializable.serializable_enum
61+
class TlpClassification(str, Enum):
62+
"""
63+
Enum object that defines the Traffic Light Protocol (TLP) classification that controls the sharing and distribution
64+
of the data that the BOM describes.
65+
66+
.. note::
67+
Introduced in CycloneDX v1.7
68+
69+
.. note::
70+
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.7/xml/#type_tlpClassificationType
71+
"""
72+
73+
CLEAR = 'CLEAR'
74+
GREEN = 'GREEN'
75+
AMBER = 'AMBER'
76+
AMBER_AND_STRICT = 'AMBER_AND_STRICT'
77+
RED = 'RED'
78+
79+
80+
@serializable.serializable_class(ignore_unknown_during_deserialization=True)
81+
class DistributionConstraints:
82+
"""
83+
Our internal representation of the `distributionConstraints` complex type.
84+
Conditions and constraints governing the sharing and distribution of the data or components described by this BOM.
85+
86+
.. note::
87+
Introduced in CycloneDX v1.7
88+
89+
.. note::
90+
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.7/xml/#type_metadata
91+
"""
92+
93+
def __init__(
94+
self, *,
95+
tlp: Optional[TlpClassification] = None,
96+
) -> None:
97+
self.tlp = tlp or TlpClassification.CLEAR
98+
99+
@property
100+
@serializable.xml_sequence(0)
101+
def tlp(self) -> TlpClassification:
102+
"""
103+
The Traffic Light Protocol (TLP) classification that controls the sharing and distribution of the data that the
104+
BOM describes.
105+
106+
Returns:
107+
`TlpClassification` enum value
108+
"""
109+
return self._tlp
110+
111+
@tlp.setter
112+
def tlp(self, tlp: TlpClassification) -> None:
113+
self._tlp = tlp
114+
115+
def __comparable_tuple(self) -> _ComparableTuple:
116+
return _ComparableTuple(self.tlp)
117+
118+
def __eq__(self, other: object) -> bool:
119+
if isinstance(other, DistributionConstraints):
120+
return self.__comparable_tuple() == other.__comparable_tuple()
121+
return False
122+
123+
def __lt__(self, other: object) -> bool:
124+
if isinstance(other, DistributionConstraints):
125+
return self.__comparable_tuple() < other.__comparable_tuple()
126+
return NotImplemented
127+
128+
def __hash__(self) -> int:
129+
return hash(self.__comparable_tuple())
130+
131+
def __repr__(self) -> str:
132+
return f'<DistributionConstraints tlp={self.tlp}>'
133+
134+
59135
@serializable.serializable_class(ignore_unknown_during_deserialization=True)
60136
class BomMetaData:
61137
"""
@@ -76,6 +152,7 @@ def __init__(
76152
timestamp: Optional[datetime] = None,
77153
manufacturer: Optional[OrganizationalEntity] = None,
78154
lifecycles: Optional[Iterable[Lifecycle]] = None,
155+
distribution_constraints: Optional[DistributionConstraints] = None,
79156
# Deprecated as of v1.6
80157
manufacture: Optional[OrganizationalEntity] = None,
81158
) -> None:
@@ -88,6 +165,7 @@ def __init__(
88165
self.properties = properties or []
89166
self.manufacturer = manufacturer
90167
self.lifecycles = lifecycles or []
168+
self.distribution_constraints = distribution_constraints
91169
# deprecated properties below
92170
self.manufacture = manufacture
93171

@@ -301,10 +379,27 @@ def properties(self) -> 'SortedSet[Property]':
301379
def properties(self, properties: Iterable[Property]) -> None:
302380
self._properties = SortedSet(properties)
303381

382+
@property
383+
@serializable.view(SchemaVersion1Dot7)
384+
@serializable.xml_sequence(11)
385+
def distribution_constraints(self) -> Optional[DistributionConstraints]:
386+
"""
387+
Conditions and constraints governing the sharing and distribution of the data or components described by this
388+
BOM.
389+
390+
Returns:
391+
`DistributionConstraints` or `None`
392+
"""
393+
return self._distribution_constraints
394+
395+
@distribution_constraints.setter
396+
def distribution_constraints(self, distribution_constraints: Optional[DistributionConstraints]) -> None:
397+
self._distribution_constraints = distribution_constraints
398+
304399
def __comparable_tuple(self) -> _ComparableTuple:
305400
return _ComparableTuple((
306401
_ComparableTuple(self.authors), self.component, _ComparableTuple(self.licenses), self.manufacture,
307-
_ComparableTuple(self.properties),
402+
_ComparableTuple(self.properties), self.distribution_constraints,
308403
_ComparableTuple(self.lifecycles), self.supplier, self.timestamp, self.tools, self.manufacturer
309404
))
310405

tests/_data/models.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
Property,
4343
XsUri,
4444
)
45-
from cyclonedx.model.bom import Bom, BomMetaData
45+
from cyclonedx.model.bom import Bom, BomMetaData, DistributionConstraints, TlpClassification
4646
from cyclonedx.model.bom_ref import BomRef
4747
from cyclonedx.model.component import (
4848
Commit,
@@ -582,6 +582,7 @@ def get_bom_just_complete_metadata() -> Bom:
582582
)]
583583
bom.metadata.lifecycles = [PredefinedLifecycle(LifecyclePhase.BUILD)]
584584
bom.metadata.properties = get_properties_1()
585+
bom.metadata.distribution_constraints = DistributionConstraints(tlp=TlpClassification.GREEN)
585586
return bom
586587

587588

@@ -1403,6 +1404,17 @@ def get_bom_with_lifecycles() -> Bom:
14031404
)
14041405

14051406

1407+
def get_bom_with_distribution_constraints() -> Bom:
1408+
return _make_bom(
1409+
metadata=BomMetaData(
1410+
distribution_constraints=DistributionConstraints(
1411+
tlp=TlpClassification.AMBER_AND_STRICT
1412+
),
1413+
component=Component(name='app', type=ComponentType.APPLICATION, bom_ref='my-app'),
1414+
)
1415+
)
1416+
1417+
14061418
def get_bom_with_definitions_standards() -> Bom:
14071419
"""
14081420
Returns a BOM with definitions and standards only.
@@ -1584,6 +1596,7 @@ def get_bom_for_issue540_duplicate_components() -> Bom:
15841596
get_bom_with_component_setuptools_with_v16_fields,
15851597
get_bom_for_issue_630_empty_property,
15861598
get_bom_with_lifecycles,
1599+
get_bom_with_distribution_constraints,
15871600
get_bom_with_definitions_standards,
15881601
get_bom_with_definitions_and_detailed_standards,
15891602
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.0" version="1">
3+
<components/>
4+
</bom>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.1" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<components/>
4+
</bom>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"metadata": {
3+
"timestamp": "2023-01-07T13:44:32.312678+00:00"
4+
},
5+
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
6+
"version": 1,
7+
"$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json",
8+
"bomFormat": "CycloneDX",
9+
"specVersion": "1.2"
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<metadata>
4+
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
5+
</metadata>
6+
</bom>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"metadata": {
3+
"timestamp": "2023-01-07T13:44:32.312678+00:00"
4+
},
5+
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
6+
"version": 1,
7+
"$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json",
8+
"bomFormat": "CycloneDX",
9+
"specVersion": "1.3"
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<metadata>
4+
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
5+
</metadata>
6+
</bom>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"metadata": {
3+
"timestamp": "2023-01-07T13:44:32.312678+00:00"
4+
},
5+
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
6+
"version": 1,
7+
"$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
8+
"bomFormat": "CycloneDX",
9+
"specVersion": "1.4"
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<metadata>
4+
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
5+
</metadata>
6+
</bom>

0 commit comments

Comments
 (0)