Skip to content

Commit 9425c67

Browse files
qkaiserjkowalleck
andauthored
feat: deserialize single ComponentEvidence.identity (#900)
--------- Signed-off-by: Quentin Kaiser <quentin.kaiser@onekey.com> Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com> Co-authored-by: Jan Kowalleck <jan.kowalleck@gmail.com>
1 parent 0f17348 commit 9425c67

File tree

8 files changed

+185
-7
lines changed

8 files changed

+185
-7
lines changed

cyclonedx/model/component_evidence.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,10 @@ def __repr__(self) -> str:
752752

753753

754754
class _ComponentEvidenceSerializationHelper(serializable.helpers.BaseHelper):
755-
"""THIS CLASS IS NON-PUBLIC API"""
755+
"""THIS CLASS IS NON-PUBLIC API
756+
757+
This helper takes care of :attr:`ComponentEvidence.identity`.
758+
"""
756759

757760
@classmethod
758761
def json_normalize(cls, o: ComponentEvidence, *,
@@ -761,13 +764,16 @@ def json_normalize(cls, o: ComponentEvidence, *,
761764
data: dict[str, Any] = json_loads(o.as_json(view)) # type:ignore[attr-defined]
762765
if view is SchemaVersion1Dot5:
763766
identities = data.get('identity', [])
764-
if il := len(identities) > 1:
765-
warn(f'CycloneDX 1.5 does not support multiple identity items; dropping {il - 1} items.')
767+
if identities:
768+
if (il := len(identities)) > 1:
769+
warn(f'CycloneDX 1.5 does not support multiple identity items; dropping {il - 1} items.')
766770
data['identity'] = identities[0]
767771
return data
768772

769773
@classmethod
770774
def json_denormalize(cls, o: dict[str, Any], **__: Any) -> Any:
775+
if isinstance(identity := o.get('identity'), dict):
776+
o = {**o, 'identity': [identity]}
771777
return ComponentEvidence.from_json(o) # type:ignore[attr-defined]
772778

773779
@classmethod
@@ -779,7 +785,7 @@ def xml_normalize(cls, o: ComponentEvidence, *,
779785
normalized: 'XmlElement' = o.as_xml(view, False, element_name, xmlns) # type:ignore[attr-defined]
780786
if view is SchemaVersion1Dot5:
781787
identities = normalized.findall(f'./{{{xmlns}}}identity' if xmlns else './identity')
782-
if il := len(identities) > 1:
788+
if (il := len(identities)) > 1:
783789
warn(f'CycloneDX 1.5 does not support multiple identity items; dropping {il - 1} items.')
784790
for i in identities[1:]:
785791
normalized.remove(i)

tests/_data/own/json/1.5/component_evidence_identity.json

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/_data/own/json/1.6/component_evidence_identity.json

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/_data/own/xml/1.5/component_evidence_identity.xml

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/_data/own/xml/1.6/component_evidence_identity.xml

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test_deserialize_json.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,12 @@ def test_regression_issue690(self) -> None:
127127
json = json_loads(f.read())
128128
bom: Bom = Bom.from_json(json) # <<< is expected to not crash
129129
self.assertIsNotNone(bom)
130+
131+
def test_component_evidence_identity(self) -> None:
132+
json_file = join(OWN_DATA_DIRECTORY, 'json',
133+
SchemaVersion.V1_6.to_version(),
134+
'component_evidence_identity.json')
135+
with open(json_file) as f:
136+
json = json_loads(f.read())
137+
bom: Bom = Bom.from_json(json) # <<< is expected to not crash
138+
self.assertIsNotNone(bom)

tests/test_deserialize_xml.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# Copyright (c) OWASP Foundation. All Rights Reserved.
1717

1818
from collections.abc import Callable
19+
from os.path import join
1920
from typing import Any
2021
from unittest import TestCase
2122
from unittest.mock import patch
@@ -24,7 +25,7 @@
2425

2526
from cyclonedx.model.bom import Bom
2627
from cyclonedx.schema import OutputFormat, SchemaVersion
27-
from tests import DeepCompareMixin, SnapshotMixin, mksname
28+
from tests import OWN_DATA_DIRECTORY, DeepCompareMixin, SnapshotMixin, mksname
2829
from tests._data.models import (
2930
all_get_bom_funct_valid_immut,
3031
all_get_bom_funct_valid_reversible_migrate,
@@ -46,3 +47,11 @@ def test_prepared(self, get_bom: Callable[[], Bom], *_: Any, **__: Any) -> None:
4647
bom = Bom.from_xml(s)
4748
self.assertBomDeepEqual(expected, bom,
4849
fuzzy_deps=get_bom in all_get_bom_funct_with_incomplete_deps)
50+
51+
def test_component_evidence_identity(self) -> None:
52+
xml_file = join(OWN_DATA_DIRECTORY, 'xml',
53+
SchemaVersion.V1_6.to_version(),
54+
'component_evidence_identity.xml')
55+
with open(xml_file) as f:
56+
bom: Bom = Bom.from_xml(f) # <<< is expected to not crash
57+
self.assertIsNotNone(bom)

tests/test_model_component_evidence.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ class TestModelComponentEvidence(TestCase):
3737
def test_no_params(self) -> None:
3838
ComponentEvidence() # Does not raise `NoPropertiesProvidedException`
3939

40-
def test_identity(self) -> None:
40+
def test_identity_single(self) -> None:
4141
identity = Identity(field=IdentityField.NAME, confidence=Decimal('1'), concluded_value='test')
42-
ce = ComponentEvidence(identity=[identity])
42+
ce = ComponentEvidence(identity=identity)
4343
self.assertEqual(len(ce.identity), 1)
4444
self.assertEqual(ce.identity.pop().field, 'name')
4545

0 commit comments

Comments
 (0)