Skip to content

Commit 2c0d5ba

Browse files
committed
Added XML serializations decorators, enum test cases
1 parent 072f4f5 commit 2c0d5ba

16 files changed

+561
-132
lines changed

cyclonedx/model/component.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ class ComponentIdentityEvidenceField(str, Enum):
212212
VERSION = 'version'
213213
PURL = 'purl'
214214
CPE = 'cpe'
215-
OMNIBOR_ID = 'omniborId'
216-
SWHID = 'swhid'
215+
OMNIBOR_ID = 'omniborId' # Only supported in >= 1.6
216+
SWHID = 'swhid' # Only supported in >= 1.6
217217
SWID = 'swid'
218218
HASH = 'hash'
219219

@@ -307,9 +307,11 @@ def __normalize(
307307
cs: ComponentIdentityEvidenceMethodTechnique,
308308
view: Type[serializable.ViewType]
309309
) -> Optional[str]:
310-
return cs.value \
311-
if cs in cls.__CASES.get(view, ()) \
312-
else None
310+
return (
311+
cs
312+
if cs in cls.__CASES.get(view, ())
313+
else ComponentIdentityEvidenceMethodTechnique.OTHER
314+
).value
313315

314316
@classmethod
315317
def json_normalize(cls, o: Any, *,
@@ -354,6 +356,7 @@ def __init__(
354356

355357
@property
356358
@serializable.type_mapping(_ComponentIdentityEvidenceMethodTechniqueSerializationHelper)
359+
@serializable.xml_sequence(1)
357360
def technique(self) -> Optional[ComponentIdentityEvidenceMethodTechnique]:
358361
"""
359362
The evidence technique used by the method of the component which the evidence describes.
@@ -368,6 +371,7 @@ def technique(self, technique: Optional[ComponentIdentityEvidenceMethodTechnique
368371
self._technique = technique
369372

370373
@property
374+
@serializable.xml_sequence(2)
371375
def confidence(self) -> float:
372376
"""
373377
The overall confidence of the evidence from 0 - 1, where 1 is 100% confidence.
@@ -389,6 +393,7 @@ def confidence(self, confidence: float) -> None:
389393
self._confidence = confidence
390394

391395
@property
396+
@serializable.xml_sequence(3)
392397
def value(self) -> Optional[str]:
393398
"""
394399
The value or contents of the evidence.
@@ -452,6 +457,7 @@ def __init__(
452457

453458
@property
454459
@serializable.type_mapping(_ComponentIdentityEvidenceFieldSerializationHelper)
460+
@serializable.xml_sequence(1)
455461
def field(self) -> Optional[ComponentIdentityEvidenceField]:
456462
"""
457463
The identity field of the component which the evidence describes.
@@ -466,6 +472,7 @@ def field(self, field: Optional[ComponentIdentityEvidenceField]) -> None:
466472
self._field = field
467473

468474
@property
475+
@serializable.xml_sequence(2)
469476
def confidence(self) -> Optional[float]:
470477
"""
471478
The overall confidence of the evidence from 0 - 1, where 1 is 100% confidence.
@@ -486,6 +493,7 @@ def confidence(self, confidence: Optional[float]) -> None:
486493

487494
@property
488495
@serializable.type_mapping(ComponentIdentityEvidenceMethod)
496+
@serializable.xml_sequence(3)
489497
def methods(self) -> 'SortedSet[ComponentIdentityEvidenceMethod]':
490498
"""
491499
Optional list of methods used to extract and/or analyze the evidence.
@@ -500,6 +508,7 @@ def methods(self, methods: Iterable[ComponentIdentityEvidenceMethod]) -> None:
500508
self._methods = SortedSet(methods)
501509

502510
@property
511+
@serializable.xml_sequence(4)
503512
def tools(self) -> 'SortedSet[str]':
504513
"""
505514
Optional list of tools used to extract and/or analyze the evidence.
@@ -547,36 +556,36 @@ class ComponentEvidence:
547556

548557
def __init__(
549558
self, *,
550-
identity: Optional[ComponentIdentityEvidence] = None,
559+
identity: Optional[Iterable[ComponentIdentityEvidence]] = None,
551560
licenses: Optional[Iterable[License]] = None,
552561
copyright: Optional[Iterable[Copyright]] = None,
553562
) -> None:
554563
if not licenses and not copyright and not identity:
555564
raise NoPropertiesProvidedException(
556-
'At least one of `licenses` or `copyright` must be supplied for a `ComponentEvidence`.'
565+
'At least one of `licenses`, `copyright` or `identity` must be supplied for a `ComponentEvidence`.'
557566
)
558567

559-
self.identity = identity
568+
self.identity = identity or [] # type:ignore[assignment]
560569
self.licenses = licenses or [] # type:ignore[assignment]
561570
self.copyright = copyright or [] # type:ignore[assignment]
562571

563572
@property
564573
@serializable.view(SchemaVersion1Dot5)
565574
@serializable.view(SchemaVersion1Dot6)
575+
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'identity')
566576
@serializable.xml_sequence(1)
567-
def identity(self) -> Optional[ComponentIdentityEvidence]:
577+
def identity(self) -> 'SortedSet[ComponentIdentityEvidence]':
568578
"""
569579
Optional list of evidence that substantiates the identity of a component.
570580
571581
Returns:
572-
`ComponentIdentityEvidence` or `None`
582+
Set of `ComponentIdentityEvidence`
573583
"""
574-
575584
return self._identity
576585

577586
@identity.setter
578-
def identity(self, identity: Optional[ComponentIdentityEvidence]) -> None:
579-
self._identity = identity
587+
def identity(self, identity: Iterable[ComponentIdentityEvidence]) -> None:
588+
self._identity = SortedSet(identity)
580589

581590
# @property
582591
# ...
@@ -647,7 +656,7 @@ def __lt__(self, other: Any) -> bool:
647656
return NotImplemented
648657

649658
def __hash__(self) -> int:
650-
return hash((self.identity, tuple(self.licenses), tuple(self.copyright)))
659+
return hash((tuple(self.identity), tuple(self.licenses), tuple(self.copyright)))
651660

652661
def __repr__(self) -> str:
653662
return f'<ComponentEvidence id={id(self)}>'

tests/_data/models.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@
4747
Commit,
4848
Component,
4949
ComponentEvidence,
50-
ComponentIdentityEvidence,
51-
ComponentIdentityEvidenceField,
52-
ComponentIdentityEvidenceMethod,
53-
ComponentIdentityEvidenceMethodTechnique,
5450
ComponentScope,
5551
ComponentType,
5652
Diff,
@@ -934,15 +930,6 @@ def get_swid_2() -> Swid:
934930
)
935931

936932

937-
def get_evidence_identity() -> ComponentIdentityEvidence:
938-
return ComponentIdentityEvidence(
939-
field=ComponentIdentityEvidenceField.NAME, confidence=0.5, methods=[
940-
ComponentIdentityEvidenceMethod(technique=ComponentIdentityEvidenceMethodTechnique.FILENAME, confidence=0.5)
941-
],
942-
tools=['cyclonedx-python-lib']
943-
)
944-
945-
946933
def get_vulnerability_source_nvd() -> VulnerabilitySource:
947934
return VulnerabilitySource(name='NVD', url=XsUri('https://nvd.nist.gov/vuln/detail/CVE-2018-7489'))
948935

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.0" version="1">
3+
<components/>
4+
</bom>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.1" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<components/>
4+
</bom>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata": {
3+
"timestamp": "2023-01-07T13:44:32.312678+00:00",
4+
"tools": [
5+
{
6+
"name": "cyclonedx-python-lib",
7+
"vendor": "CycloneDX",
8+
"version": "TESTING"
9+
}
10+
]
11+
},
12+
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
13+
"version": 1,
14+
"$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json",
15+
"bomFormat": "CycloneDX",
16+
"specVersion": "1.2"
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<metadata>
4+
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
5+
<tools>
6+
<tool>
7+
<vendor>CycloneDX</vendor>
8+
<name>cyclonedx-python-lib</name>
9+
<version>TESTING</version>
10+
</tool>
11+
</tools>
12+
</metadata>
13+
</bom>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata": {
3+
"timestamp": "2023-01-07T13:44:32.312678+00:00",
4+
"tools": [
5+
{
6+
"name": "cyclonedx-python-lib",
7+
"vendor": "CycloneDX",
8+
"version": "TESTING"
9+
}
10+
]
11+
},
12+
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
13+
"version": 1,
14+
"$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json",
15+
"bomFormat": "CycloneDX",
16+
"specVersion": "1.3"
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<metadata>
4+
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
5+
<tools>
6+
<tool>
7+
<vendor>CycloneDX</vendor>
8+
<name>cyclonedx-python-lib</name>
9+
<version>TESTING</version>
10+
</tool>
11+
</tools>
12+
</metadata>
13+
</bom>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata": {
3+
"timestamp": "2023-01-07T13:44:32.312678+00:00",
4+
"tools": [
5+
{
6+
"name": "cyclonedx-python-lib",
7+
"vendor": "CycloneDX",
8+
"version": "TESTING"
9+
}
10+
]
11+
},
12+
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
13+
"version": 1,
14+
"$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
15+
"bomFormat": "CycloneDX",
16+
"specVersion": "1.4"
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
<?xml version="1.0" ?>
3+
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
4+
<metadata>
5+
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
6+
<tools>
7+
<tool>
8+
<vendor>CycloneDX</vendor>
9+
<name>cyclonedx-python-lib</name>
10+
<version>TESTING</version>
11+
</tool>
12+
</tools>
13+
</metadata>
14+
</bom>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"components": [
3+
{
4+
"bom-ref": "dummy",
5+
"name": "dummy",
6+
"type": "application",
7+
"evidence": {
8+
"identity": {
9+
"field": "group"
10+
}
11+
}
12+
}
13+
],
14+
"metadata": {
15+
"timestamp": "2023-01-07T13:44:32.312678+00:00",
16+
"tools": [
17+
{
18+
"externalReferences": [
19+
{
20+
"type": "build-system",
21+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions"
22+
},
23+
{
24+
"type": "distribution",
25+
"url": "https://pypi.org/project/cyclonedx-python-lib/"
26+
},
27+
{
28+
"type": "documentation",
29+
"url": "https://cyclonedx-python-library.readthedocs.io/"
30+
},
31+
{
32+
"type": "issue-tracker",
33+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues"
34+
},
35+
{
36+
"type": "license",
37+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE"
38+
},
39+
{
40+
"type": "release-notes",
41+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md"
42+
},
43+
{
44+
"type": "vcs",
45+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib"
46+
},
47+
{
48+
"type": "website",
49+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme"
50+
}
51+
],
52+
"name": "cyclonedx-python-lib",
53+
"vendor": "CycloneDX",
54+
"version": "TESTING"
55+
}
56+
]
57+
},
58+
"properties": [
59+
{
60+
"name": "key1",
61+
"value": "val1"
62+
},
63+
{
64+
"name": "key2",
65+
"value": "val2"
66+
}
67+
],
68+
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
69+
"version": 1,
70+
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
71+
"bomFormat": "CycloneDX",
72+
"specVersion": "1.5"
73+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" ?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.5" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
3+
<metadata>
4+
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
5+
<tools>
6+
<tool>
7+
<vendor>CycloneDX</vendor>
8+
<name>cyclonedx-python-lib</name>
9+
<version>TESTING</version>
10+
<externalReferences>
11+
<reference type="build-system">
12+
<url>https://github.com/CycloneDX/cyclonedx-python-lib/actions</url>
13+
</reference>
14+
<reference type="distribution">
15+
<url>https://pypi.org/project/cyclonedx-python-lib/</url>
16+
</reference>
17+
<reference type="documentation">
18+
<url>https://cyclonedx-python-library.readthedocs.io/</url>
19+
</reference>
20+
<reference type="issue-tracker">
21+
<url>https://github.com/CycloneDX/cyclonedx-python-lib/issues</url>
22+
</reference>
23+
<reference type="license">
24+
<url>https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE</url>
25+
</reference>
26+
<reference type="release-notes">
27+
<url>https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md</url>
28+
</reference>
29+
<reference type="vcs">
30+
<url>https://github.com/CycloneDX/cyclonedx-python-lib</url>
31+
</reference>
32+
<reference type="website">
33+
<url>https://github.com/CycloneDX/cyclonedx-python-lib/#readme</url>
34+
</reference>
35+
</externalReferences>
36+
</tool>
37+
</tools>
38+
</metadata>
39+
<components>
40+
<component type="application" bom-ref="scoped">
41+
<name>dummy</name>
42+
<evidence>
43+
<identity>
44+
<field>group</field>
45+
</identity>
46+
</evidence>
47+
</component>
48+
</components>
49+
<properties>
50+
<property name="key1">val1</property>
51+
<property name="key2">val2</property>
52+
</properties>
53+
</bom>

0 commit comments

Comments
 (0)