diff --git a/cyclonedx/model/service.py b/cyclonedx/model/service.py index 10597370..9f78b69f 100644 --- a/cyclonedx/model/service.py +++ b/cyclonedx/model/service.py @@ -67,6 +67,7 @@ def __init__( endpoints: Optional[Iterable[XsUri]] = None, authenticated: Optional[bool] = None, x_trust_boundary: Optional[bool] = None, + trust_zone: Optional[str] = None, data: Optional[Iterable[DataClassification]] = None, licenses: Optional[Iterable[License]] = None, external_references: Optional[Iterable[ExternalReference]] = None, @@ -83,6 +84,7 @@ def __init__( self.endpoints = endpoints or [] self.authenticated = authenticated self.x_trust_boundary = x_trust_boundary + self.trust_zone = trust_zone self.data = data or [] self.licenses = licenses or [] self.external_references = external_references or [] @@ -239,16 +241,25 @@ def x_trust_boundary(self) -> Optional[bool]: def x_trust_boundary(self, x_trust_boundary: Optional[bool]) -> None: self._x_trust_boundary = x_trust_boundary - # @property - # ... - # @serializable.view(SchemaVersion1Dot5) - # @serializable.xml_sequence(9) - # def trust_zone(self) -> ...: - # ... # since CDX1.5 - # - # @trust_zone.setter - # def trust_zone(self, ...) -> None: - # ... # since CDX1.5 + @property + @serializable.view(SchemaVersion1Dot5) + @serializable.view(SchemaVersion1Dot6) + @serializable.view(SchemaVersion1Dot7) + @serializable.xml_sequence(9) + def trust_zone(self) -> Optional[str]: + """ + The name of the trust zone the service resides in. + + Supported from CycloneDX v1.5 onwards. + + Returns: + `str` if set else `None` + """ + return self._trust_zone + + @trust_zone.setter + def trust_zone(self, trust_zone: Optional[str]) -> None: + self._trust_zone = trust_zone @property @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'classification') @@ -369,7 +380,7 @@ def __comparable_tuple(self) -> _ComparableTuple: self.authenticated, _ComparableTuple(self.data), _ComparableTuple(self.endpoints), _ComparableTuple(self.external_references), _ComparableTuple(self.licenses), _ComparableTuple(self.properties), self.release_notes, _ComparableTuple(self.services), - self.x_trust_boundary + self.x_trust_boundary, self.trust_zone )) def __eq__(self, other: object) -> bool: diff --git a/tests/_data/models.py b/tests/_data/models.py index 565f56ec..3138bd06 100644 --- a/tests/_data/models.py +++ b/tests/_data/models.py @@ -620,7 +620,7 @@ def get_bom_with_services_complex() -> Bom: XsUri('/api/thing/1'), XsUri('/api/thing/2') ], - authenticated=False, x_trust_boundary=True, data=[ + authenticated=False, x_trust_boundary=True, trust_zone='internal\tvpc', data=[ DataClassification(flow=DataFlow.OUTBOUND, classification='public') ], licenses=[DisjunctiveLicense(name='Commercial')], diff --git a/tests/_data/snapshots/get_bom_with_services_complex-1.5.json.bin b/tests/_data/snapshots/get_bom_with_services_complex-1.5.json.bin index 7672db57..cb6d3173 100644 --- a/tests/_data/snapshots/get_bom_with_services_complex-1.5.json.bin +++ b/tests/_data/snapshots/get_bom_with_services_complex-1.5.json.bin @@ -154,6 +154,7 @@ "title": "Release Notes Title", "type": "major" }, + "trustZone": "internal\tvpc", "version": "1.2.3", "x-trust-boundary": true }, diff --git a/tests/_data/snapshots/get_bom_with_services_complex-1.5.xml.bin b/tests/_data/snapshots/get_bom_with_services_complex-1.5.xml.bin index 7fb7fc50..bb929035 100644 --- a/tests/_data/snapshots/get_bom_with_services_complex-1.5.xml.bin +++ b/tests/_data/snapshots/get_bom_with_services_complex-1.5.xml.bin @@ -33,6 +33,7 @@ false true + internal vpc public diff --git a/tests/_data/snapshots/get_bom_with_services_complex-1.6.json.bin b/tests/_data/snapshots/get_bom_with_services_complex-1.6.json.bin index 45b78218..14ee1b0c 100644 --- a/tests/_data/snapshots/get_bom_with_services_complex-1.6.json.bin +++ b/tests/_data/snapshots/get_bom_with_services_complex-1.6.json.bin @@ -160,6 +160,7 @@ "title": "Release Notes Title", "type": "major" }, + "trustZone": "internal\tvpc", "version": "1.2.3", "x-trust-boundary": true }, diff --git a/tests/_data/snapshots/get_bom_with_services_complex-1.6.xml.bin b/tests/_data/snapshots/get_bom_with_services_complex-1.6.xml.bin index 7a054cfa..8920017d 100644 --- a/tests/_data/snapshots/get_bom_with_services_complex-1.6.xml.bin +++ b/tests/_data/snapshots/get_bom_with_services_complex-1.6.xml.bin @@ -39,6 +39,7 @@ false true + internal vpc public diff --git a/tests/_data/snapshots/get_bom_with_services_complex-1.7.json.bin b/tests/_data/snapshots/get_bom_with_services_complex-1.7.json.bin index 9aa33fa2..3c58418f 100644 --- a/tests/_data/snapshots/get_bom_with_services_complex-1.7.json.bin +++ b/tests/_data/snapshots/get_bom_with_services_complex-1.7.json.bin @@ -160,6 +160,7 @@ "title": "Release Notes Title", "type": "major" }, + "trustZone": "internal\tvpc", "version": "1.2.3", "x-trust-boundary": true }, diff --git a/tests/_data/snapshots/get_bom_with_services_complex-1.7.xml.bin b/tests/_data/snapshots/get_bom_with_services_complex-1.7.xml.bin index 770f7a84..642dd969 100644 --- a/tests/_data/snapshots/get_bom_with_services_complex-1.7.xml.bin +++ b/tests/_data/snapshots/get_bom_with_services_complex-1.7.xml.bin @@ -39,6 +39,7 @@ false true + internal vpc public diff --git a/tests/test_model_service.py b/tests/test_model_service.py index c66c2521..94f87da5 100644 --- a/tests/test_model_service.py +++ b/tests/test_model_service.py @@ -18,6 +18,8 @@ from unittest import TestCase +from sortedcontainers import SortedSet + from cyclonedx.model.service import Service from tests import reorder @@ -35,6 +37,7 @@ def test_minimal_service(self) -> None: self.assertFalse(s.endpoints) self.assertIsNone(s.authenticated) self.assertIsNone(s.x_trust_boundary) + self.assertIsNone(s.trust_zone) self.assertFalse(s.data) self.assertFalse(s.licenses) self.assertFalse(s.external_references) @@ -57,6 +60,7 @@ def test_service_with_services(self) -> None: self.assertFalse(parent_service.endpoints) self.assertIsNone(parent_service.authenticated) self.assertIsNone(parent_service.x_trust_boundary) + self.assertIsNone(parent_service.trust_zone) self.assertFalse(parent_service.data) self.assertFalse(parent_service.licenses) self.assertFalse(parent_service.external_references) @@ -80,3 +84,19 @@ def test_sort(self) -> None: sorted_services = sorted(services) expected_services = reorder(services, expected_order) self.assertListEqual(sorted_services, expected_services) + + def test_trust_zone_default(self) -> None: + s = Service(name='my-test-service') + self.assertIsNone(s.trust_zone) + + def test_trust_zone_setter(self) -> None: + s = Service(name='my-test-service', trust_zone='internal-vpc') + self.assertEqual('internal-vpc', s.trust_zone) + s.trust_zone = 'public-internet' + self.assertEqual('public-internet', s.trust_zone) + + def test_trust_zone_affects_equality_and_sorted_set_membership(self) -> None: + s1 = Service(name='my-test-service', trust_zone='internal-vpc') + s2 = Service(name='my-test-service', trust_zone='public-internet') + self.assertNotEqual(s1, s2) + self.assertEqual(2, len(SortedSet((s1, s2))))