From 712c750be684542d1783a69d3311a9a45f3aee97 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Fri, 11 Oct 2024 04:27:07 +0200 Subject: [PATCH] rework tools xml deserializer Signed-off-by: Jan Kowalleck --- cyclonedx/model/tool.py | 36 +++++++++++------------- tests/_data/own/xml/1.5/invalid-tool.xml | 8 ------ tests/test_deserialize_xml.py | 11 ++------ 3 files changed, 18 insertions(+), 37 deletions(-) delete mode 100644 tests/_data/own/xml/1.5/invalid-tool.xml diff --git a/cyclonedx/model/tool.py b/cyclonedx/model/tool.py index bb639bbe..4b056519 100644 --- a/cyclonedx/model/tool.py +++ b/cyclonedx/model/tool.py @@ -26,7 +26,6 @@ from sortedcontainers import SortedSet from .._internal.compare import ComparableTuple as _ComparableTuple -from ..exception.serialization import CycloneDxDeserializationException from ..schema import SchemaVersion from ..schema.schema import SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6 from . import ExternalReference, HashType, _HashTypeRepositorySerializationHelper @@ -308,7 +307,8 @@ def json_denormalize(cls, o: Union[List[Dict[str, Any]], Dict[str, Any]], services = map(lambda s: Service.from_json( # type:ignore[attr-defined] s), o.get('services', ())) elif isinstance(o, Iterable): - tools = map(lambda t: Tool.from_json(t), o) # type:ignore[attr-defined] + tools = map(lambda t: Tool.from_json( # type:ignore[attr-defined] + t), o) return ToolRepository(components=components, services=services, tools=tools) @classmethod @@ -349,23 +349,19 @@ def xml_denormalize(cls, o: Element, *, prop_info: 'ObjectMetadataLibrary.SerializableProperty', ctx: Type[Any], **kwargs: Any) -> ToolRepository: - tools = [] + ns_map = {'bom': default_ns or ''} + # Do not iterate over `o` and do not check for expected `.tag` of items. + # This check could have been done by schema validators before even deserializing. + tools = None components = None services = None - for e in o: - tag = e.tag if default_ns is None else e.tag.replace(f'{{{default_ns}}}', '') - if tag == 'tool': - tools.append(Tool.from_xml( # type:ignore[attr-defined] - e, default_ns)) - elif tag == 'components': - components = map(lambda s: Component.from_xml( # type:ignore[attr-defined] - s, default_ns), e) - elif tag == 'services': - services = map(lambda s: Service.from_xml( # type:ignore[attr-defined] - s, default_ns), e) - else: - raise CycloneDxDeserializationException(f'unexpected: {e!r}') - return ToolRepository( - tools=tools, - components=components, - services=services) + ts = o.findall('bom:tool', ns_map) + if len(ts) > 0: + tools = map(lambda t: Tool.from_xml( # type:ignore[attr-defined] + t, default_ns), ts) + else: + components = map(lambda c: Component.from_xml( # type:ignore[attr-defined] + c, default_ns), o.iterfind('./bom:components/bom:component', ns_map)) + services = map(lambda s: Service.from_xml( # type:ignore[attr-defined] + s, default_ns), o.iterfind('./bom:services/bom:service', ns_map)) + return ToolRepository(components=components, services=services, tools=tools) diff --git a/tests/_data/own/xml/1.5/invalid-tool.xml b/tests/_data/own/xml/1.5/invalid-tool.xml deleted file mode 100644 index f57259b9..00000000 --- a/tests/_data/own/xml/1.5/invalid-tool.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/tests/test_deserialize_xml.py b/tests/test_deserialize_xml.py index 5ebbc64d..26cc34ec 100644 --- a/tests/test_deserialize_xml.py +++ b/tests/test_deserialize_xml.py @@ -14,17 +14,16 @@ # # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. -from os.path import join + from typing import Any, Callable from unittest import TestCase from unittest.mock import patch from ddt import ddt, named_data -from cyclonedx.exception.serialization import CycloneDxDeserializationException from cyclonedx.model.bom import Bom from cyclonedx.schema import OutputFormat, SchemaVersion -from tests import OWN_DATA_DIRECTORY, DeepCompareMixin, SnapshotMixin, mksname +from tests import DeepCompareMixin, SnapshotMixin, mksname from tests._data.models import ( all_get_bom_funct_valid_immut, all_get_bom_funct_valid_reversible_migrate, @@ -46,9 +45,3 @@ def test_prepared(self, get_bom: Callable[[], Bom], *_: Any, **__: Any) -> None: bom = Bom.from_xml(s) self.assertBomDeepEqual(expected, bom, fuzzy_deps=get_bom in all_get_bom_funct_with_incomplete_deps) - - def test_unexpected_toolrepository_item(self) -> None: - with open(join(OWN_DATA_DIRECTORY, 'xml', '1.5', 'invalid-tool.xml')) as input_xml: - with self.assertRaisesRegex(CycloneDxDeserializationException, - r"^unexpected: $"): - Bom.from_xml(input_xml)