Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
849c7a4
feat: add support for component's evidences according to spec
OxPirates May 3, 2025
f92dcc5
Merge branch 'main' into main
jkowalleck May 26, 2025
a65cc0e
prep extraxtion of component_evidence
jkowalleck May 27, 2025
63c1cc5
prep extraxtion of component_evidence
jkowalleck May 27, 2025
7b30eff
refactor: compoennt evidence
jkowalleck May 27, 2025
45abefe
prep extraxtion of component_evidence
jkowalleck May 27, 2025
392e61d
prep extraxtion of component_evidence
jkowalleck May 27, 2025
cbb7370
refactor: compoennt evidence
jkowalleck May 27, 2025
ec89ae7
wip
jkowalleck May 27, 2025
da433b6
tests
jkowalleck May 27, 2025
564b3e6
wip
jkowalleck May 27, 2025
23fdd94
wip
jkowalleck May 27, 2025
88bcc20
wip
jkowalleck May 27, 2025
9382ad4
Fix for pipeline failures, improvement for Identity, spec v1.5, v1.6
OxPirates May 29, 2025
1a7b41f
tests: bring backtest case with multiple evidence identities
jkowalleck May 31, 2025
95df588
modified `_IdentityToolRepositorySerializationHelper`
jkowalleck May 31, 2025
b66ecc3
reverted callstack frames as lists
jkowalleck May 31, 2025
47a1ef4
clean `_IdentityRepositorySerializationHelper`
jkowalleck May 31, 2025
5de7307
fix component evidence serialization
jkowalleck May 31, 2025
0ee583e
tidy
jkowalleck May 31, 2025
78fd4db
cleanup
jkowalleck May 31, 2025
7aef6e1
typing for serialization lib
jkowalleck May 31, 2025
de6d628
remove TODO
jkowalleck May 31, 2025
57625da
Merge remote-tracking branch 'origin/main' into OxPirates_main
jkowalleck Jun 2, 2025
c4a0218
style: remove no longer used typehint-ignores
jkowalleck Jun 2, 2025
bb111c3
Merge remote-tracking branch 'origin/main' into OxPirates_main
jkowalleck Jun 2, 2025
d2f93aa
Merge branch 'main' into main
jkowalleck Jun 5, 2025
8aac643
style: upgrade code style
jkowalleck Jun 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions cyclonedx/model/component_evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
from collections.abc import Iterable
from decimal import Decimal
from enum import Enum
from json import loads as json_loads
from typing import Any, Optional, Union
from xml.etree.ElementTree import Element as XmlElement
from xml.etree.ElementTree import Element as XmlElement # nosec B405

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

def __init__(
self, *,
frames: Optional[Iterable[CallStackFrame]] = None,
frames: Optional[SortedSet[CallStackFrame]] = None,
) -> None:
self.frames = frames or [] # type:ignore[assignment]

@property
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'frame')
def frames(self) -> 'list[CallStackFrame]':
@serializable.xml_sequence(1)
def frames(self) -> 'SortedSet[CallStackFrame]':
"""
Array of stack frames
"""
return self._frames

@frames.setter
def frames(self, frames: Iterable[CallStackFrame]) -> None:
self._frames = list(frames)
def frames(self, frames: SortedSet[CallStackFrame]) -> None:
self._frames = frames

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


class _IdentitySerializationHelper(serializable.helpers.BaseHelper):
"""THIS CLASS IS NON-PUBLIC API"""

@classmethod
def json_normalize(cls, o: SortedSet[Identity], *,
view: Optional[type[serializable.ViewType]],
**__: Any) -> Any:
if not o:
return None

# For Schema 1.5 JSON, return first identity as a single object
if view and issubclass(view, SchemaVersion1Dot5):
first_identity = next(iter(o))
return json_loads(first_identity.as_json(view_=view)) # type: ignore[attr-defined]

# For Schema 1.6 and others, return array of all identities
return [json_loads(identity.as_json(view_=view)) for identity in o] # type: ignore[attr-defined]

@classmethod
def json_denormalize(cls, o: Any, **__: Any) -> SortedSet[Identity]:
if isinstance(o, dict): # Single Identity object (Schema 1.5)
return SortedSet([Identity.from_json(o)]) # type: ignore[attr-defined]
elif isinstance(o, (list, tuple)): # Array of Identity objects (Schema 1.6)
return SortedSet(Identity.from_json(i) for i in o) # type: ignore[attr-defined]
return SortedSet()


@serializable.serializable_class
class ComponentEvidence:
"""
Expand Down Expand Up @@ -649,10 +678,11 @@ def __init__(
@property
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'identity')
@serializable.xml_sequence(1)
@serializable.type_mapping(_IdentitySerializationHelper)
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'identity')
# TODO: CDX 1.5 knows only one identity, all versions later known multiple ...
# TODO: need to fix the serializatoin/normlaization
# TODO: need to fix the serialization/normalization
def identity(self) -> 'SortedSet[Identity]':
"""
Provides a way to identify components via various methods.
Expand Down
13 changes: 0 additions & 13 deletions tests/_data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,19 +782,6 @@ def get_component_evidence_basic(tools: Iterable[Component]) -> ComponentEvidenc
"""
return ComponentEvidence(
identity=[
Identity(
field=IdentityField.NAME,
confidence=Decimal('0.9'),
concluded_value='example-component',
methods=[
Method(
technique=AnalysisTechnique.SOURCE_CODE_ANALYSIS,
confidence=Decimal('0.8'),
value='analysis-tool'
),
],
tools=(tool.bom_ref for tool in tools)
),
Identity(
field=IdentityField.HASH,
confidence=Decimal('0.1'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,41 @@
</licenses>
<purl>pkg:pypi/[email protected]?extension=tar.gz</purl>
<evidence>
<identity>
<field>hash</field>
<confidence>0.1</confidence>
<methods>
<method>
<technique>attestation</technique>
<confidence>0.1</confidence>
<value>analysis-tool</value>
</method>
</methods>
<tools>
<tool ref="cbom:generator"/>
</tools>
</identity>
<occurrences>
<occurrence>
<location>path/to/file</location>
</occurrence>
</occurrences>
<callstack>
<frames>
<frame>
<package>example.package</package>
<module>example.module</module>
<function>example_function</function>
<parameters>
<parameter>param1</parameter>
<parameter>param2</parameter>
</parameters>
<line>10</line>
<column>5</column>
<fullFilename>path/to/file</fullFilename>
</frame>
</frames>
</callstack>
<licenses>
<license>
<id>MIT</id>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,6 @@
"tools": [
"cbom:generator"
]
},
{
"concludedValue": "example-component",
"confidence": 0.9,
"field": "name",
"methods": [
{
"confidence": 0.8,
"technique": "source-code-analysis",
"value": "analysis-tool"
}
],
"tools": [
"cbom:generator"
]
}
],
"licenses": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,6 @@
<tool ref="cbom:generator"/>
</tools>
</identity>
<identity>
<field>name</field>
<confidence>0.9</confidence>
<concludedValue>example-component</concludedValue>
<methods>
<method>
<technique>source-code-analysis</technique>
<confidence>0.8</confidence>
<value>analysis-tool</value>
</method>
</methods>
<tools>
<tool ref="cbom:generator"/>
</tools>
</identity>
<occurrences>
<occurrence>
<location>path/to/file</location>
Expand Down