diff --git a/cyclonedx/exception/serialization.py b/cyclonedx/exception/serialization.py index 565b36c8..3ff7b3d8 100644 --- a/cyclonedx/exception/serialization.py +++ b/cyclonedx/exception/serialization.py @@ -35,6 +35,15 @@ class CycloneDxDeserializationException(CycloneDxException): pass +class SerializationOfUnsupportedAggregateTypeException(CycloneDxSerializationException): + """ + Raised when attempting serializing/normalizing a :py:class:`cyclonedx.model.bom.Bom` + to a :py:class:`cyclonedx.schema.schema.BaseSchemaVersion` + which does not support that :py:class:`cyclonedx.model.composition.AggregateType` + . + """ + + class SerializationOfUnsupportedComponentTypeException(CycloneDxSerializationException): """ Raised when attempting serializing/normalizing a :py:class:`cyclonedx.model.component.Component` diff --git a/cyclonedx/model/bom.py b/cyclonedx/model/bom.py index aaf61fe7..79e1e08c 100644 --- a/cyclonedx/model/bom.py +++ b/cyclonedx/model/bom.py @@ -40,6 +40,7 @@ from . import ExternalReference, Property, ThisTool, Tool from .bom_ref import BomRef from .component import Component +from .composition import Composition from .contact import OrganizationalContact, OrganizationalEntity from .dependency import Dependable, Dependency from .license import License, LicenseExpression, LicenseRepository @@ -310,6 +311,7 @@ def __init__(self, *, components: Optional[Iterable[Component]] = None, serial_number: Optional[UUID] = None, version: int = 1, metadata: Optional[BomMetaData] = None, dependencies: Optional[Iterable[Dependency]] = None, + compositions: Optional[Iterable[Composition]] = None, vulnerabilities: Optional[Iterable[Vulnerability]] = None, properties: Optional[Iterable[Property]] = None) -> None: """ @@ -324,8 +326,9 @@ def __init__(self, *, components: Optional[Iterable[Component]] = None, self.components = components or [] # type:ignore[assignment] self.services = services or [] # type:ignore[assignment] self.external_references = external_references or [] # type:ignore[assignment] - self.vulnerabilities = vulnerabilities or [] # type:ignore[assignment] + self.compositions = compositions or [] # type:ignore[assignment] self.dependencies = dependencies or [] # type:ignore[assignment] + self.vulnerabilities = vulnerabilities or [] # type:ignore[assignment] self.properties = properties or [] # type:ignore[assignment] @property @@ -453,24 +456,38 @@ def external_references(self, external_references: Iterable[ExternalReference]) @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'dependency') @serializable.xml_sequence(50) def dependencies(self) -> 'SortedSet[Dependency]': + """ + Provides the ability to document dependency relationships. + + Returns: + Set of `Dependency` + """ return self._dependencies @dependencies.setter def dependencies(self, dependencies: Iterable[Dependency]) -> None: self._dependencies = SortedSet(dependencies) - # @property - # ... - # @serializable.view(SchemaVersion1Dot3) - # @serializable.view(SchemaVersion1Dot4) - # @serializable.view(SchemaVersion1Dot5) - # @serializable.xml_sequence(6) - # def compositions(self) -> ...: - # ... # TODO Since CDX 1.3 - # - # @compositions.setter - # def compositions(self, ...) -> None: - # ... # TODO Since CDX 1.3 + @property + @serializable.view(SchemaVersion1Dot3) + @serializable.view(SchemaVersion1Dot4) + @serializable.view(SchemaVersion1Dot5) + @serializable.view(SchemaVersion1Dot6) + @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'composition') + @serializable.xml_sequence(60) + def compositions(self) -> 'SortedSet[Composition]': + """ + Compositions describe constituent parts (including components, services, and dependency relationships) and + their completeness. + + Returns: + `SortedSet[Composition]` + """ + return self._compositions + + @compositions.setter + def compositions(self, compositions: Optional[Iterable[Composition]]) -> None: + self._compositions = SortedSet(compositions) @property # @serializable.view(SchemaVersion1Dot3) @todo: Update py-serializable to support view by OutputFormat filtering @@ -694,7 +711,7 @@ def __eq__(self, other: object) -> bool: def __hash__(self) -> int: return hash(( self.serial_number, self.version, self.metadata, tuple(self.components), tuple(self.services), - tuple(self.external_references), tuple(self.dependencies), tuple(self.properties), + tuple(self.external_references), tuple(self.dependencies), tuple(self.compositions), tuple(self.properties), tuple(self.vulnerabilities), )) diff --git a/cyclonedx/model/composition.py b/cyclonedx/model/composition.py new file mode 100644 index 00000000..5cc4bd1a --- /dev/null +++ b/cyclonedx/model/composition.py @@ -0,0 +1,295 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) OWASP Foundation. All Rights Reserved. +from enum import Enum +from typing import Any, Dict, FrozenSet, Iterable, Optional, Type + +import serializable +from sortedcontainers import SortedSet + +from .._internal.compare import ComparableTuple as _ComparableTuple +from ..exception.serialization import SerializationOfUnsupportedAggregateTypeException +from ..schema.schema import ( + SchemaVersion1Dot0, + SchemaVersion1Dot1, + SchemaVersion1Dot2, + SchemaVersion1Dot3, + SchemaVersion1Dot4, + SchemaVersion1Dot5, + SchemaVersion1Dot6, +) +from ..serialization import BomRefHelper +from .bom_ref import BomRef + + +@serializable.serializable_enum +class AggregateType(str, Enum): + """ + This is our internal representation of the composition.aggregate ENUM type within the CycloneDX standard. + + .. note:: + Introduced in CycloneDX v1.4 + + .. note:: + See the CycloneDX Schema for hashType: https://cyclonedx.org/docs/1.4/xml/#type_aggregateType + """ + + # See `_AggregateTypeSerializationHelper.__CASES` for view/case map + + """ + The relationship is complete. No further relationships including constituent components, services, or dependencies + are known to exist. + """ + COMPLETE = 'complete' + + """ + The relationship is incomplete. Additional relationships exist and may include constituent components, services, or + dependencies. + """ + INCOMPLETE = 'incomplete' + + """ + The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are + represented. + """ + INCOMPLETE_FIRST_PARTY_ONLY = 'incomplete_first_party_only' + + """ + The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are + represented, limited specifically to those that are proprietary. + """ + INCOMPLETE_FIRST_PARTY_PROPRIETARY_ONLY = 'incomplete_first_party_proprietary_only' + + """ + The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are + represented, limited specifically to those that are opensource. + """ + INCOMPLETE_FIRST_PARTY_OPENSOURCE_ONLY = 'incomplete_first_party_opensource_only' + + """ + The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are + represented. + """ + INCOMPLETE_THIRD_PARTY_ONLY = 'incomplete_third_party_only' + + """ + The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are + represented, limited specifically to those that are proprietary. + """ + INCOMPLETE_THIRD_PARTY_PROPRIETARY_ONLY = 'incomplete_third_party_proprietary_only' + + """ + The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are + represented, limited specifically to those that are opensource. + """ + INCOMPLETE_THIRD_PARTY_OPENSOURCE_ONLY = 'incomplete_third_party_opensource_only' + + """ + The relationship may be complete or incomplete. This usually signifies a 'best-effort' to obtain constituent + components, services, or dependencies but the completeness is inconclusive. + """ + UNKNOWN = 'unknown' + + """ + The relationship completeness is not specified. + """ + NOT_SPECIFIED = 'not_specified' + + +class _AggregateTypeSerializationHelper(serializable.helpers.BaseHelper): + """ THIS CLASS IS NON-PUBLIC API """ + + __CASES: Dict[Type[serializable.ViewType], FrozenSet[AggregateType]] = dict() + __CASES[SchemaVersion1Dot0] = frozenset({}) + __CASES[SchemaVersion1Dot1] = __CASES[SchemaVersion1Dot0] + __CASES[SchemaVersion1Dot2] = __CASES[SchemaVersion1Dot1] + __CASES[SchemaVersion1Dot3] = __CASES[SchemaVersion1Dot2] | { + AggregateType.COMPLETE, + AggregateType.INCOMPLETE, + AggregateType.INCOMPLETE_FIRST_PARTY_ONLY, + AggregateType.INCOMPLETE_THIRD_PARTY_ONLY, + AggregateType.UNKNOWN, + AggregateType.NOT_SPECIFIED + } + __CASES[SchemaVersion1Dot4] = __CASES[SchemaVersion1Dot3] + __CASES[SchemaVersion1Dot5] = __CASES[SchemaVersion1Dot4] | { + AggregateType.INCOMPLETE_FIRST_PARTY_PROPRIETARY_ONLY, + AggregateType.INCOMPLETE_FIRST_PARTY_OPENSOURCE_ONLY, + AggregateType.INCOMPLETE_THIRD_PARTY_PROPRIETARY_ONLY, + AggregateType.INCOMPLETE_THIRD_PARTY_OPENSOURCE_ONLY + } + __CASES[SchemaVersion1Dot6] = __CASES[SchemaVersion1Dot5] + + @classmethod + def __normalize(cls, at: AggregateType, view: Type[serializable.ViewType]) -> Optional[str]: + print(f'Normalising {at!r} for {view!r}') + if at in cls.__CASES.get(view, ()): + return at.value + raise SerializationOfUnsupportedAggregateTypeException(f'unsupported {at!r} for view {view!r}') + + @classmethod + def json_normalize(cls, o: Any, *, + view: Optional[Type[serializable.ViewType]], + **__: Any) -> Optional[str]: + assert view is not None + return cls.__normalize(o, view) + + @classmethod + def xml_normalize(cls, o: Any, *, + view: Optional[Type[serializable.ViewType]], + **__: Any) -> Optional[str]: + assert view is not None + return cls.__normalize(o, view) + + @classmethod + def deserialize(cls, o: Any) -> AggregateType: + return AggregateType(o) + + +@serializable.serializable_class +class CompositionReference: + """ + Models a reference for an assembly or dependency in a Composition. + + .. note:: + See https://cyclonedx.org/docs/1.4/xml/#type_compositionType + """ + + def __init__(self, *, ref: BomRef) -> None: + self.ref = ref + + @property + @serializable.json_name('.') + @serializable.type_mapping(BomRefHelper) + @serializable.xml_attribute() + def ref(self) -> BomRef: + """ + References a component or service by its bom-ref attribute. + + Returns: + `BomRef` + """ + return self._ref + + @ref.setter + def ref(self, ref: BomRef) -> None: + self._ref = ref + + def __eq__(self, other: object) -> bool: + if isinstance(other, CompositionReference): + return hash(other) == hash(self) + return False + + def __lt__(self, other: Any) -> bool: + if isinstance(other, CompositionReference): + return self.ref < other.ref + return NotImplemented + + def __hash__(self) -> int: + return hash(self.ref) + + def __repr__(self) -> str: + return f'' + + +@serializable.serializable_class +class Composition: + """ + This is our internal representation of the `compositionType` type within the CycloneDX standard. + + .. note:: + Introduced in CycloneDX v1.4 + + .. note:: + See the CycloneDX Schema for hashType: https://cyclonedx.org/docs/1.4/xml/#type_compositionType + """ + + def __init__(self, *, aggregate: AggregateType = AggregateType.NOT_SPECIFIED, + assemblies: Optional[Iterable[CompositionReference]] = None, + dependencies: Optional[Iterable[CompositionReference]] = None) -> None: + self.aggregate = aggregate + self.assemblies = assemblies or [] # type:ignore[assignment] + self.dependencies = dependencies or [] # type:ignore[assignment] + + @property + @serializable.type_mapping(_AggregateTypeSerializationHelper) + @serializable.xml_sequence(10) + def aggregate(self) -> AggregateType: + """ + Specifies an aggregate type that describe how complete a relationship is. + + Returns: + `AggregateType` + """ + return self._aggregate + + @aggregate.setter + def aggregate(self, aggregate: AggregateType) -> None: + self._aggregate = aggregate + + @property + @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'assembly') + @serializable.xml_sequence(20) + def assemblies(self) -> 'SortedSet[CompositionReference]': + """ + The bom-ref identifiers of the components or services being described. Assemblies refer to nested relationships + whereby a constituent part may include other constituent parts. References do not cascade to child parts. + References are explicit for the specified constituent part only. + + Returns: + 'SortedSet[CompositionReference]` + """ + return self._assemblies + + @assemblies.setter + def assemblies(self, assemblies: Optional[Iterable[CompositionReference]]) -> None: + self._assemblies = SortedSet(assemblies) + + @property + @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'dependency') + @serializable.xml_sequence(30) + def dependencies(self) -> 'SortedSet[CompositionReference]': + """ + The bom-ref identifiers of the components or services being described. Dependencies refer to a relationship + whereby an independent constituent part requires another independent constituent part. References do not + cascade to transitive dependencies. References are explicit for the specified dependency only. + + Returns: + 'SortedSet[CompositionReference]` + """ + return self._dependencies + + @dependencies.setter + def dependencies(self, dependencies: Optional[Iterable[CompositionReference]]) -> None: + self._dependencies = SortedSet(dependencies) + + def __eq__(self, other: object) -> bool: + if isinstance(other, Composition): + return hash(other) == hash(self) + return False + + def __lt__(self, other: Any) -> bool: + if isinstance(other, Composition): + return _ComparableTuple(( + self.aggregate, _ComparableTuple(self.assemblies), _ComparableTuple(self.dependencies) + )) < _ComparableTuple(( + other.aggregate, _ComparableTuple(other.assemblies), _ComparableTuple(other.dependencies) + )) + return NotImplemented + + def __hash__(self) -> int: + return hash((self.aggregate, tuple(self.assemblies), tuple(self.dependencies))) + + def __repr__(self) -> str: + return f'' diff --git a/docs/schema-support.rst b/docs/schema-support.rst index 57b4cdb5..1001e888 100644 --- a/docs/schema-support.rst +++ b/docs/schema-support.rst @@ -41,7 +41,7 @@ Root Level Schema Support +----------------------------+---------------+---------------------------------------------------------------------------------------------------+ | ``bom.dependencies`` | Yes | Since ``2.3.0`` | +----------------------------+---------------+---------------------------------------------------------------------------------------------------+ -| ``bom.compositions`` | No | | +| ``bom.compositions`` | Yes | Since ``7.4.0`` | +----------------------------+---------------+---------------------------------------------------------------------------------------------------+ | ``bom.properties`` | Yes | Supported when outputting to Schema Version >= 1.5. See `schema specification bug 130`_ | +----------------------------+---------------+---------------------------------------------------------------------------------------------------+ diff --git a/tests/_data/models.py b/tests/_data/models.py index 198ea9a7..a8b06e2f 100644 --- a/tests/_data/models.py +++ b/tests/_data/models.py @@ -57,6 +57,7 @@ Swhid, Swid, ) +from cyclonedx.model.composition import AggregateType, Composition, CompositionReference from cyclonedx.model.contact import OrganizationalContact, OrganizationalEntity, PostalAddress from cyclonedx.model.crypto import ( AlgorithmProperties, @@ -391,6 +392,24 @@ def get_bom_with_component_setuptools_with_release_notes() -> Bom: return _make_bom(components=[component]) +def get_bom_with_compositions_migrate() -> Bom: + c1 = get_component_setuptools_simple() + c2 = get_component_toml_with_hashes_with_references() + bom = _make_bom(components=[c1, c2]) + bom.compositions = [ + Composition( + aggregate=AggregateType.COMPLETE, + assemblies=[ + CompositionReference(ref=c1.bom_ref) + ], + dependencies=[ + CompositionReference(ref=c2.bom_ref) + ] + ) + ] + return bom + + def get_bom_with_dependencies_valid() -> Bom: c1 = get_component_setuptools_simple() c2 = get_component_toml_with_hashes_with_references() diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.3.json.bin b/tests/_data/snapshots/enum_TestAggregateType-1.3.json.bin new file mode 100644 index 00000000..287e636f --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.3.json.bin @@ -0,0 +1,37 @@ +{ + "compositions": [ + { + "aggregate": "complete" + }, + { + "aggregate": "incomplete" + }, + { + "aggregate": "incomplete_first_party_only" + }, + { + "aggregate": "incomplete_third_party_only" + }, + { + "aggregate": "not_specified" + }, + { + "aggregate": "unknown" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.3" +} \ No newline at end of file diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.3.xml.bin b/tests/_data/snapshots/enum_TestAggregateType-1.3.xml.bin new file mode 100644 index 00000000..831ccab1 --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.3.xml.bin @@ -0,0 +1,33 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + + + + complete + + + incomplete + + + incomplete_first_party_only + + + incomplete_third_party_only + + + not_specified + + + unknown + + + diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.4.json.bin b/tests/_data/snapshots/enum_TestAggregateType-1.4.json.bin new file mode 100644 index 00000000..8ce27605 --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.4.json.bin @@ -0,0 +1,71 @@ +{ + "compositions": [ + { + "aggregate": "complete" + }, + { + "aggregate": "incomplete" + }, + { + "aggregate": "incomplete_first_party_only" + }, + { + "aggregate": "incomplete_third_party_only" + }, + { + "aggregate": "not_specified" + }, + { + "aggregate": "unknown" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.4" +} \ No newline at end of file diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.4.xml.bin b/tests/_data/snapshots/enum_TestAggregateType-1.4.xml.bin new file mode 100644 index 00000000..e69de219 --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.4.xml.bin @@ -0,0 +1,59 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + complete + + + incomplete + + + incomplete_first_party_only + + + incomplete_third_party_only + + + not_specified + + + unknown + + + diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.5.json.bin b/tests/_data/snapshots/enum_TestAggregateType-1.5.json.bin new file mode 100644 index 00000000..404f87d7 --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.5.json.bin @@ -0,0 +1,93 @@ +{ + "compositions": [ + { + "aggregate": "complete" + }, + { + "aggregate": "incomplete" + }, + { + "aggregate": "incomplete_first_party_only" + }, + { + "aggregate": "incomplete_first_party_opensource_only" + }, + { + "aggregate": "incomplete_first_party_proprietary_only" + }, + { + "aggregate": "incomplete_third_party_only" + }, + { + "aggregate": "incomplete_third_party_opensource_only" + }, + { + "aggregate": "incomplete_third_party_proprietary_only" + }, + { + "aggregate": "not_specified" + }, + { + "aggregate": "unknown" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "properties": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ], + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5" +} \ No newline at end of file diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.5.xml.bin b/tests/_data/snapshots/enum_TestAggregateType-1.5.xml.bin new file mode 100644 index 00000000..9e0241f5 --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.5.xml.bin @@ -0,0 +1,75 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + complete + + + incomplete + + + incomplete_first_party_only + + + incomplete_first_party_opensource_only + + + incomplete_first_party_proprietary_only + + + incomplete_third_party_only + + + incomplete_third_party_opensource_only + + + incomplete_third_party_proprietary_only + + + not_specified + + + unknown + + + + val1 + val2 + + diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.6.json.bin b/tests/_data/snapshots/enum_TestAggregateType-1.6.json.bin new file mode 100644 index 00000000..432d1e87 --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.6.json.bin @@ -0,0 +1,93 @@ +{ + "compositions": [ + { + "aggregate": "complete" + }, + { + "aggregate": "incomplete" + }, + { + "aggregate": "incomplete_first_party_only" + }, + { + "aggregate": "incomplete_first_party_opensource_only" + }, + { + "aggregate": "incomplete_first_party_proprietary_only" + }, + { + "aggregate": "incomplete_third_party_only" + }, + { + "aggregate": "incomplete_third_party_opensource_only" + }, + { + "aggregate": "incomplete_third_party_proprietary_only" + }, + { + "aggregate": "not_specified" + }, + { + "aggregate": "unknown" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "properties": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ], + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6" +} \ No newline at end of file diff --git a/tests/_data/snapshots/enum_TestAggregateType-1.6.xml.bin b/tests/_data/snapshots/enum_TestAggregateType-1.6.xml.bin new file mode 100644 index 00000000..98096791 --- /dev/null +++ b/tests/_data/snapshots/enum_TestAggregateType-1.6.xml.bin @@ -0,0 +1,75 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + complete + + + incomplete + + + incomplete_first_party_only + + + incomplete_first_party_opensource_only + + + incomplete_first_party_proprietary_only + + + incomplete_third_party_only + + + incomplete_third_party_opensource_only + + + incomplete_third_party_proprietary_only + + + not_specified + + + unknown + + + + val1 + val2 + + diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.0.xml.bin b/tests/_data/snapshots/get_bom_with_compositions-1.0.xml.bin new file mode 100644 index 00000000..9d0e55d4 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.0.xml.bin @@ -0,0 +1,20 @@ + + + + + setuptools + 50.3.2 + pkg:pypi/setuptools@50.3.2?extension=tar.gz + false + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + false + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.1.xml.bin b/tests/_data/snapshots/get_bom_with_compositions-1.1.xml.bin new file mode 100644 index 00000000..9d446f67 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.1.xml.bin @@ -0,0 +1,29 @@ + + + + + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.2.json.bin b/tests/_data/snapshots/get_bom_with_compositions-1.2.json.bin new file mode 100644 index 00000000..fa9162f5 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.2.json.bin @@ -0,0 +1,62 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.2" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.2.xml.bin b/tests/_data/snapshots/get_bom_with_compositions-1.2.xml.bin new file mode 100644 index 00000000..29fc1fb0 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.2.xml.bin @@ -0,0 +1,44 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + + + + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.3.json.bin b/tests/_data/snapshots/get_bom_with_compositions-1.3.json.bin new file mode 100644 index 00000000..4bb50222 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.3.json.bin @@ -0,0 +1,68 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.3" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.3.xml.bin b/tests/_data/snapshots/get_bom_with_compositions-1.3.xml.bin new file mode 100644 index 00000000..954cbcea --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.3.xml.bin @@ -0,0 +1,47 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.4.json.bin b/tests/_data/snapshots/get_bom_with_compositions-1.4.json.bin new file mode 100644 index 00000000..adfacfcc --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.4.json.bin @@ -0,0 +1,113 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "compositions": [ + { + "aggregate": "complete", + "assemblies": [ + "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + ], + "dependencies": [ + "pkg:pypi/toml@0.10.2?extension=tar.gz" + ] + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.4" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.4.xml.bin b/tests/_data/snapshots/get_bom_with_compositions-1.4.xml.bin new file mode 100644 index 00000000..8cc1dd74 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.4.xml.bin @@ -0,0 +1,84 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + + + complete + + + + + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.5.json.bin b/tests/_data/snapshots/get_bom_with_compositions-1.5.json.bin new file mode 100644 index 00000000..5d041291 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.5.json.bin @@ -0,0 +1,123 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "compositions": [ + { + "aggregate": "complete", + "assemblies": [ + "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + ], + "dependencies": [ + "pkg:pypi/toml@0.10.2?extension=tar.gz" + ] + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "properties": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ], + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.5.xml.bin b/tests/_data/snapshots/get_bom_with_compositions-1.5.xml.bin new file mode 100644 index 00000000..a5a4816a --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.5.xml.bin @@ -0,0 +1,88 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + + + complete + + + + + + + + + + val1 + val2 + + diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.6.json.bin b/tests/_data/snapshots/get_bom_with_compositions-1.6.json.bin new file mode 100644 index 00000000..34b09e94 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.6.json.bin @@ -0,0 +1,123 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "compositions": [ + { + "aggregate": "complete", + "assemblies": [ + "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + ], + "dependencies": [ + "pkg:pypi/toml@0.10.2?extension=tar.gz" + ] + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "properties": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ], + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions-1.6.xml.bin b/tests/_data/snapshots/get_bom_with_compositions-1.6.xml.bin new file mode 100644 index 00000000..4992400e --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions-1.6.xml.bin @@ -0,0 +1,88 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + + + complete + + + + + + + + + + val1 + val2 + + diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.0.xml.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.0.xml.bin new file mode 100644 index 00000000..9d0e55d4 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.0.xml.bin @@ -0,0 +1,20 @@ + + + + + setuptools + 50.3.2 + pkg:pypi/setuptools@50.3.2?extension=tar.gz + false + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + false + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.1.xml.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.1.xml.bin new file mode 100644 index 00000000..9d446f67 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.1.xml.bin @@ -0,0 +1,29 @@ + + + + + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.2.json.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.2.json.bin new file mode 100644 index 00000000..fa9162f5 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.2.json.bin @@ -0,0 +1,62 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.2" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.2.xml.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.2.xml.bin new file mode 100644 index 00000000..29fc1fb0 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.2.xml.bin @@ -0,0 +1,44 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + + + + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.3.json.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.3.json.bin new file mode 100644 index 00000000..2a4c692a --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.3.json.bin @@ -0,0 +1,79 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "compositions": [ + { + "aggregate": "complete", + "assemblies": [ + "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + ], + "dependencies": [ + "pkg:pypi/toml@0.10.2?extension=tar.gz" + ] + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.3" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.3.xml.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.3.xml.bin new file mode 100644 index 00000000..75222c7f --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.3.xml.bin @@ -0,0 +1,58 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + + + complete + + + + + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.4.json.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.4.json.bin new file mode 100644 index 00000000..adfacfcc --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.4.json.bin @@ -0,0 +1,113 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "compositions": [ + { + "aggregate": "complete", + "assemblies": [ + "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + ], + "dependencies": [ + "pkg:pypi/toml@0.10.2?extension=tar.gz" + ] + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.4" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.4.xml.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.4.xml.bin new file mode 100644 index 00000000..8cc1dd74 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.4.xml.bin @@ -0,0 +1,84 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + + + complete + + + + + + + + + diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.5.json.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.5.json.bin new file mode 100644 index 00000000..5d041291 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.5.json.bin @@ -0,0 +1,123 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "compositions": [ + { + "aggregate": "complete", + "assemblies": [ + "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + ], + "dependencies": [ + "pkg:pypi/toml@0.10.2?extension=tar.gz" + ] + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "properties": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ], + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.5.xml.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.5.xml.bin new file mode 100644 index 00000000..a5a4816a --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.5.xml.bin @@ -0,0 +1,88 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + + + complete + + + + + + + + + + val1 + val2 + + diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.6.json.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.6.json.bin new file mode 100644 index 00000000..34b09e94 --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.6.json.bin @@ -0,0 +1,123 @@ +{ + "components": [ + { + "author": "Test Author", + "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "name": "setuptools", + "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz", + "type": "library", + "version": "50.3.2" + }, + { + "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "externalReferences": [ + { + "comment": "No comment", + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "type": "distribution", + "url": "https://cyclonedx.org" + } + ], + "hashes": [ + { + "alg": "SHA-256", + "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b" + } + ], + "name": "toml", + "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz", + "type": "library", + "version": "0.10.2" + } + ], + "compositions": [ + { + "aggregate": "complete", + "assemblies": [ + "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + ], + "dependencies": [ + "pkg:pypi/toml@0.10.2?extension=tar.gz" + ] + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz" + }, + { + "ref": "pkg:pypi/toml@0.10.2?extension=tar.gz" + } + ], + "metadata": { + "timestamp": "2023-01-07T13:44:32.312678+00:00", + "tools": [ + { + "externalReferences": [ + { + "type": "build-system", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" + }, + { + "type": "distribution", + "url": "https://pypi.org/project/cyclonedx-python-lib/" + }, + { + "type": "documentation", + "url": "https://cyclonedx-python-library.readthedocs.io/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" + }, + { + "type": "license", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" + }, + { + "type": "release-notes", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" + } + ], + "name": "cyclonedx-python-lib", + "vendor": "CycloneDX", + "version": "TESTING" + } + ] + }, + "properties": [ + { + "name": "key1", + "value": "val1" + }, + { + "name": "key2", + "value": "val2" + } + ], + "serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac", + "version": 1, + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6" +} \ No newline at end of file diff --git a/tests/_data/snapshots/get_bom_with_compositions_migrate-1.6.xml.bin b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.6.xml.bin new file mode 100644 index 00000000..4992400e --- /dev/null +++ b/tests/_data/snapshots/get_bom_with_compositions_migrate-1.6.xml.bin @@ -0,0 +1,88 @@ + + + + 2023-01-07T13:44:32.312678+00:00 + + + CycloneDX + cyclonedx-python-lib + TESTING + + + https://github.com/CycloneDX/cyclonedx-python-lib/actions + + + https://pypi.org/project/cyclonedx-python-lib/ + + + https://cyclonedx-python-library.readthedocs.io/ + + + https://github.com/CycloneDX/cyclonedx-python-lib/issues + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE + + + https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md + + + https://github.com/CycloneDX/cyclonedx-python-lib + + + https://github.com/CycloneDX/cyclonedx-python-lib/#readme + + + + + + + + Test Author + setuptools + 50.3.2 + + + MIT + + + pkg:pypi/setuptools@50.3.2?extension=tar.gz + + + toml + 0.10.2 + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + pkg:pypi/toml@0.10.2?extension=tar.gz + + + https://cyclonedx.org + No comment + + 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b + + + + + + + + + + + + complete + + + + + + + + + + val1 + val2 + + diff --git a/tests/test_enums.py b/tests/test_enums.py index ba5c6c5f..4c709e7b 100644 --- a/tests/test_enums.py +++ b/tests/test_enums.py @@ -23,13 +23,17 @@ from warnings import warn from xml.etree.ElementTree import parse as xml_parse # nosec B405 -from ddt import ddt, idata, named_data +from ddt import ddt, idata, named_data, unpack from cyclonedx.exception import MissingOptionalDependencyException -from cyclonedx.exception.serialization import SerializationOfUnsupportedComponentTypeException +from cyclonedx.exception.serialization import ( + SerializationOfUnsupportedAggregateTypeException, + SerializationOfUnsupportedComponentTypeException, +) from cyclonedx.model import AttachedText, ExternalReference, HashType, XsUri from cyclonedx.model.bom import Bom from cyclonedx.model.component import Component, Patch, Pedigree +from cyclonedx.model.composition import AggregateType, Composition from cyclonedx.model.issue import IssueType from cyclonedx.model.license import DisjunctiveLicense from cyclonedx.model.service import DataClassification, Service @@ -111,19 +115,46 @@ def dp_cases_from_json_schemas(*jsonpointer: str) -> Generator[str, None, None]: yield from dp_cases_from_json_schema(sf, jsonpointer) -UNSUPPORTED_OF_SV = frozenset([ +_OF_SV_JSON_SUPPORT_EXCLUSIONS = frozenset([ + (OutputFormat.JSON, SchemaVersion.V1_1), + (OutputFormat.JSON, SchemaVersion.V1_0), +]) + +_OF_SV_DOES_NOT_SUPPORT_COMPOSITIONS = frozenset([ + (OutputFormat.JSON, SchemaVersion.V1_2), (OutputFormat.JSON, SchemaVersion.V1_1), (OutputFormat.JSON, SchemaVersion.V1_0), + (OutputFormat.XML, SchemaVersion.V1_2), + (OutputFormat.XML, SchemaVersion.V1_1), + (OutputFormat.XML, SchemaVersion.V1_0), ]) -NAMED_OF_SV = tuple( +NAMED_OF_SV_1_2_ONWARDS = tuple( (f'{of.name}-{sv.to_version()}', of, sv) for of in OutputFormat for sv in SchemaVersion - if (of, sv) not in UNSUPPORTED_OF_SV + if (of, sv) not in _OF_SV_JSON_SUPPORT_EXCLUSIONS ) +def _get_named_of_sv_supported( + exclusions: Iterable[Tuple[OutputFormat, SchemaVersion]] +) -> Generator[str, OutputFormat, SchemaVersion]: + for of in OutputFormat: + for sv in SchemaVersion: + if (of, sv) not in exclusions: + yield f'{of.name}-{sv.to_version()}', of, sv + + +def _get_named_of_sv_unsupported( + unsupported: Iterable[Tuple[OutputFormat, SchemaVersion]] +) -> Generator[Tuple[str, OutputFormat, SchemaVersion], None, None]: + for of in OutputFormat: + for sv in SchemaVersion: + if (of, sv) in unsupported: + yield (f'{of.name}-{sv.to_version()}', of, sv), None, None + + class _EnumTestCase(TestCase, SnapshotMixin): def _test_knows_value(self, enum: Type[Enum], value: str) -> None: @@ -162,7 +193,7 @@ class TestEnumDataFlow(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(DataFlow, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(services=[Service(name='dummy', bom_ref='dummy', data=( @@ -182,7 +213,7 @@ class TestEnumEncoding(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(Encoding, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(components=[Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', licenses=( @@ -203,7 +234,7 @@ class TestEnumExternalReferenceType(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(ExternalReferenceType, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(components=[ @@ -225,7 +256,7 @@ class TestEnumHashAlgorithm(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(HashAlgorithm, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(components=[Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', hashes=( @@ -245,7 +276,7 @@ class TestEnumComponentScope(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(ComponentScope, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(components=( @@ -262,7 +293,7 @@ class _DP_ComponentType(): # noqa: N801 @classmethod def unsupported_cases(cls) -> Generator[Tuple[str, OutputFormat, SchemaVersion, ComponentType], None, None]: - for name, of, sv in NAMED_OF_SV: + for name, of, sv in NAMED_OF_SV_1_2_ONWARDS: if OutputFormat.XML is of: schema_cases = set(dp_cases_from_xml_schema(SCHEMA_XML[sv], cls.XML_SCHEMA_XPATH)) elif OutputFormat.JSON is of: @@ -284,7 +315,7 @@ class TestEnumComponentType(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(ComponentType, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: if OutputFormat.XML is of: @@ -321,7 +352,7 @@ class TestEnumPatchClassification(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(PatchClassification, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(components=[ @@ -343,7 +374,7 @@ class TestEnumImpactAnalysisAffectedStatus(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(ImpactAnalysisAffectedStatus, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(vulnerabilities=[Vulnerability( @@ -365,7 +396,7 @@ class TestEnumImpactAnalysisJustification(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(ImpactAnalysisJustification, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(vulnerabilities=( @@ -388,7 +419,7 @@ class TestEnumImpactAnalysisResponse(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(ImpactAnalysisResponse, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(vulnerabilities=[Vulnerability( @@ -410,7 +441,7 @@ class TestEnumImpactAnalysisState(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(ImpactAnalysisState, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(vulnerabilities=( @@ -432,7 +463,7 @@ class TestEnumIssueClassification(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(IssueClassification, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(components=[ @@ -456,7 +487,7 @@ class TestEnumVulnerabilityScoreSource(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(VulnerabilityScoreSource, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(vulnerabilities=[Vulnerability(bom_ref='dummy', id='dummy', ratings=( @@ -476,7 +507,7 @@ class TestEnumVulnerabilitySeverity(_EnumTestCase): def test_knows_value(self, value: str) -> None: super()._test_knows_value(VulnerabilitySeverity, value) - @named_data(*NAMED_OF_SV) + @named_data(*NAMED_OF_SV_1_2_ONWARDS) @patch('cyclonedx.model.ThisTool._version', 'TESTING') def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: bom = _make_bom(vulnerabilities=[Vulnerability(bom_ref='dummy', id='dummy', ratings=( @@ -484,3 +515,55 @@ def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, for vs in VulnerabilitySeverity ))]) super()._test_cases_render(bom, of, sv) + + +class _DP_AggregateType(): # noqa: N801 + XML_SCHEMA_XPATH = f"./{SCHEMA_NS}simpleType[@name='aggregateType']" + JSON_SCHEMA_POINTER = ('definitions', 'aggregateType') + + @classmethod + def unsupported_cases(cls) -> Generator[Tuple[str, OutputFormat, SchemaVersion, AggregateType], None, None]: + for (name, of, sv), _a, _b in _get_named_of_sv_unsupported( + unsupported=_OF_SV_DOES_NOT_SUPPORT_COMPOSITIONS + ): + if sv > SchemaVersion.V1_2: + for at in AggregateType: + yield f'{name}-{at.name}', of, sv, at + + +@ddt +class TestAggregateType(_EnumTestCase): + + @idata(set(chain( + dp_cases_from_xml_schemas(_DP_AggregateType.XML_SCHEMA_XPATH), + dp_cases_from_json_schemas(*_DP_AggregateType.JSON_SCHEMA_POINTER), + ))) + def test_knows_value(self, value: str) -> None: + super()._test_knows_value(AggregateType, value) + + @idata(_get_named_of_sv_supported(_OF_SV_DOES_NOT_SUPPORT_COMPOSITIONS)) + @unpack + @patch('cyclonedx.model.ThisTool._version', 'TESTING') + def test_cases_render_valid(self, n: str, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None: + if OutputFormat.XML is of: + schema_cases = set(dp_cases_from_xml_schema(SCHEMA_XML[sv], _DP_AggregateType.XML_SCHEMA_XPATH)) + elif OutputFormat.JSON is of: + schema_cases = set(dp_cases_from_json_schema(SCHEMA_JSON[sv], _DP_AggregateType.JSON_SCHEMA_POINTER)) + else: + raise ValueError(f'unexpected of: {of!r}') + bom = _make_bom(compositions=( + Composition(aggregate=at) + for at in AggregateType + if at.value in schema_cases + )) + super()._test_cases_render(bom, of, sv) + + @idata(_DP_AggregateType.unsupported_cases()) + @unpack + @patch('cyclonedx.model.ThisTool._version', 'TESTING') + def test_cases_render_raises_on_unsupported(self, name: str, of: OutputFormat, sv: SchemaVersion, + at: AggregateType, + *_: Any, **__: Any) -> None: + bom = _make_bom(compositions=[Composition(aggregate=at)]) + with self.assertRaises(SerializationOfUnsupportedAggregateTypeException): + super()._test_cases_render(bom, of, sv)