Skip to content

Commit 9382ad4

Browse files
committed
Fix for pipeline failures, improvement for Identity, spec v1.5, v1.6
Signed-off-by: Arun <[email protected]>
1 parent 88bcc20 commit 9382ad4

File tree

5 files changed

+72
-50
lines changed

5 files changed

+72
-50
lines changed

cyclonedx/model/component_evidence.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
from collections.abc import Iterable
2020
from decimal import Decimal
2121
from enum import Enum
22+
from json import loads as json_loads
2223
from typing import Any, Optional, Union
23-
from xml.etree.ElementTree import Element as XmlElement
24+
from xml.etree.ElementTree import Element as XmlElement # nosec B405
2425

2526
# See https://github.com/package-url/packageurl-python/issues/65
2627
import py_serializable as serializable
@@ -579,21 +580,22 @@ class CallStack:
579580

580581
def __init__(
581582
self, *,
582-
frames: Optional[Iterable[CallStackFrame]] = None,
583+
frames: Optional[SortedSet[CallStackFrame]] = None,
583584
) -> None:
584585
self.frames = frames or [] # type:ignore[assignment]
585586

586587
@property
587588
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'frame')
588-
def frames(self) -> 'list[CallStackFrame]':
589+
@serializable.xml_sequence(1)
590+
def frames(self) -> 'SortedSet[CallStackFrame]':
589591
"""
590592
Array of stack frames
591593
"""
592594
return self._frames
593595

594596
@frames.setter
595-
def frames(self, frames: Iterable[CallStackFrame]) -> None:
596-
self._frames = list(frames)
597+
def frames(self, frames: SortedSet[CallStackFrame]) -> None:
598+
self._frames = frames
597599

598600
def __comparable_tuple(self) -> _ComparableTuple:
599601
return _ComparableTuple((
@@ -621,6 +623,33 @@ def __repr__(self) -> str:
621623
return f'<CallStack frames={len(self.frames)}>'
622624

623625

626+
class _IdentitySerializationHelper(serializable.helpers.BaseHelper):
627+
"""THIS CLASS IS NON-PUBLIC API"""
628+
629+
@classmethod
630+
def json_normalize(cls, o: SortedSet[Identity], *,
631+
view: Optional[type[serializable.ViewType]],
632+
**__: Any) -> Any:
633+
if not o:
634+
return None
635+
636+
# For Schema 1.5 JSON, return first identity as a single object
637+
if view and issubclass(view, SchemaVersion1Dot5):
638+
first_identity = next(iter(o))
639+
return json_loads(first_identity.as_json(view_=view)) # type: ignore[attr-defined]
640+
641+
# For Schema 1.6 and others, return array of all identities
642+
return [json_loads(identity.as_json(view_=view)) for identity in o] # type: ignore[attr-defined]
643+
644+
@classmethod
645+
def json_denormalize(cls, o: Any, **__: Any) -> SortedSet[Identity]:
646+
if isinstance(o, dict): # Single Identity object (Schema 1.5)
647+
return SortedSet([Identity.from_json(o)]) # type: ignore[attr-defined]
648+
elif isinstance(o, (list, tuple)): # Array of Identity objects (Schema 1.6)
649+
return SortedSet(Identity.from_json(i) for i in o) # type: ignore[attr-defined]
650+
return SortedSet()
651+
652+
624653
@serializable.serializable_class
625654
class ComponentEvidence:
626655
"""
@@ -649,10 +678,11 @@ def __init__(
649678
@property
650679
@serializable.view(SchemaVersion1Dot5)
651680
@serializable.view(SchemaVersion1Dot6)
652-
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'identity')
653681
@serializable.xml_sequence(1)
682+
@serializable.type_mapping(_IdentitySerializationHelper)
683+
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'identity')
654684
# TODO: CDX 1.5 knows only one identity, all versions later known multiple ...
655-
# TODO: need to fix the serializatoin/normlaization
685+
# TODO: need to fix the serialization/normalization
656686
def identity(self) -> 'SortedSet[Identity]':
657687
"""
658688
Provides a way to identify components via various methods.

tests/_data/models.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -782,19 +782,6 @@ def get_component_evidence_basic(tools: Iterable[Component]) -> ComponentEvidenc
782782
"""
783783
return ComponentEvidence(
784784
identity=[
785-
Identity(
786-
field=IdentityField.NAME,
787-
confidence=Decimal('0.9'),
788-
concluded_value='example-component',
789-
methods=[
790-
Method(
791-
technique=AnalysisTechnique.SOURCE_CODE_ANALYSIS,
792-
confidence=Decimal('0.8'),
793-
value='analysis-tool'
794-
),
795-
],
796-
tools=(tool.bom_ref for tool in tools)
797-
),
798785
Identity(
799786
field=IdentityField.HASH,
800787
confidence=Decimal('0.1'),

tests/_data/snapshots/get_bom_with_component_evidence-1.5.xml.bin

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,41 @@
3030
</licenses>
3131
<purl>pkg:pypi/[email protected]?extension=tar.gz</purl>
3232
<evidence>
33+
<identity>
34+
<field>hash</field>
35+
<confidence>0.1</confidence>
36+
<methods>
37+
<method>
38+
<technique>attestation</technique>
39+
<confidence>0.1</confidence>
40+
<value>analysis-tool</value>
41+
</method>
42+
</methods>
43+
<tools>
44+
<tool ref="cbom:generator"/>
45+
</tools>
46+
</identity>
47+
<occurrences>
48+
<occurrence>
49+
<location>path/to/file</location>
50+
</occurrence>
51+
</occurrences>
52+
<callstack>
53+
<frames>
54+
<frame>
55+
<package>example.package</package>
56+
<module>example.module</module>
57+
<function>example_function</function>
58+
<parameters>
59+
<parameter>param1</parameter>
60+
<parameter>param2</parameter>
61+
</parameters>
62+
<line>10</line>
63+
<column>5</column>
64+
<fullFilename>path/to/file</fullFilename>
65+
</frame>
66+
</frames>
67+
</callstack>
3368
<licenses>
3469
<license>
3570
<id>MIT</id>

tests/_data/snapshots/get_bom_with_component_evidence-1.6.json.bin

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,6 @@
4343
"tools": [
4444
"cbom:generator"
4545
]
46-
},
47-
{
48-
"concludedValue": "example-component",
49-
"confidence": 0.9,
50-
"field": "name",
51-
"methods": [
52-
{
53-
"confidence": 0.8,
54-
"technique": "source-code-analysis",
55-
"value": "analysis-tool"
56-
}
57-
],
58-
"tools": [
59-
"cbom:generator"
60-
]
6146
}
6247
],
6348
"licenses": [

tests/_data/snapshots/get_bom_with_component_evidence-1.6.xml.bin

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,6 @@
4545
<tool ref="cbom:generator"/>
4646
</tools>
4747
</identity>
48-
<identity>
49-
<field>name</field>
50-
<confidence>0.9</confidence>
51-
<concludedValue>example-component</concludedValue>
52-
<methods>
53-
<method>
54-
<technique>source-code-analysis</technique>
55-
<confidence>0.8</confidence>
56-
<value>analysis-tool</value>
57-
</method>
58-
</methods>
59-
<tools>
60-
<tool ref="cbom:generator"/>
61-
</tools>
62-
</identity>
6348
<occurrences>
6449
<occurrence>
6550
<location>path/to/file</location>

0 commit comments

Comments
 (0)