diff --git a/cyclonedx/exception/serialization.py b/cyclonedx/exception/serialization.py
index 565b36c8b..9c947344b 100644
--- a/cyclonedx/exception/serialization.py
+++ b/cyclonedx/exception/serialization.py
@@ -44,6 +44,15 @@ class SerializationOfUnsupportedComponentTypeException(CycloneDxSerializationExc
"""
+class SerializationOfUnsupportedComponentIdentityEvidenceFieldException(CycloneDxSerializationException):
+ """
+ Raised when attempting serializing/normalizing a :py:class:`cyclonedx.model.component.Component`
+ to a :py:class:`cyclonedx.schema.schema.BaseSchemaVersion`
+ which does not support that :py:class:`cyclonedx.model.component.ComponentIdentityEvidenceField`
+ .
+ """
+
+
class SerializationOfUnexpectedValueException(CycloneDxSerializationException, ValueError):
"""
Raised when attempting serializing/normalizing a type that is not expected there.
diff --git a/cyclonedx/model/component.py b/cyclonedx/model/component.py
index 6b106c312..682ad4c53 100644
--- a/cyclonedx/model/component.py
+++ b/cyclonedx/model/component.py
@@ -38,6 +38,7 @@
from ..exception.serialization import (
CycloneDxDeserializationException,
SerializationOfUnexpectedValueException,
+ SerializationOfUnsupportedComponentIdentityEvidenceFieldException,
SerializationOfUnsupportedComponentTypeException,
)
from ..schema.schema import (
@@ -237,10 +238,11 @@ class _ComponentIdentityEvidenceFieldSerializationHelper(serializable.helpers.Ba
}
@classmethod
- def __normalize(cls, cs: ComponentIdentityEvidenceField, view: Type[serializable.ViewType]) -> Optional[str]:
- return cs.value \
- if cs in cls.__CASES.get(view, ()) \
- else None
+ def __normalize(cls, cief: ComponentIdentityEvidenceField, view: Type[serializable.ViewType]) -> Optional[str]:
+ if cief in cls.__CASES.get(view, ()):
+ return cief.value
+ raise SerializationOfUnsupportedComponentIdentityEvidenceFieldException(
+ f'unsupported {cief!r} for view {view!r}')
@classmethod
def json_normalize(cls, o: Any, *,
@@ -492,7 +494,7 @@ def confidence(self, confidence: Optional[float]) -> None:
self._confidence = confidence
@property
- @serializable.type_mapping(ComponentIdentityEvidenceMethod)
+ @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'method')
@serializable.xml_sequence(3)
def methods(self) -> 'SortedSet[ComponentIdentityEvidenceMethod]':
"""
@@ -572,7 +574,7 @@ def __init__(
@property
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
- @serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'identity')
+ @serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'identity')
@serializable.xml_sequence(1)
def identity(self) -> 'SortedSet[ComponentIdentityEvidence]':
"""
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.0.xml.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.0.xml.bin
index acb066124..068b881e8 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.0.xml.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.0.xml.bin
@@ -1,4 +1,10 @@
-
+
+
+ dummy
+
+ false
+
+
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.1.xml.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.1.xml.bin
index 55ef5cda2..6212e7a1e 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.1.xml.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.1.xml.bin
@@ -1,4 +1,9 @@
-
+
+
+ dummy
+
+
+
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.json.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.json.bin
index fafe615c9..2161f0540 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.json.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.json.bin
@@ -1,4 +1,17 @@
{
+ "components": [
+ {
+ "bom-ref": "dummy",
+ "name": "dummy",
+ "type": "library",
+ "version": ""
+ }
+ ],
+ "dependencies": [
+ {
+ "ref": "dummy"
+ }
+ ],
"metadata": {
"timestamp": "2023-01-07T13:44:32.312678+00:00",
"tools": [
@@ -14,4 +27,4 @@
"$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.2"
-}
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.xml.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.xml.bin
index bc36ede08..9be884da4 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.xml.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.2.xml.bin
@@ -10,4 +10,13 @@
+
+
+ dummy
+
+
+
+
+
+
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.json.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.json.bin
index d2e65ac15..05a843a59 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.json.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.json.bin
@@ -1,4 +1,24 @@
{
+ "components": [
+ {
+ "bom-ref": "dummy",
+ "evidence": {
+ "copyright": [
+ {
+ "text": "Dummy"
+ }
+ ]
+ },
+ "name": "dummy",
+ "type": "library",
+ "version": ""
+ }
+ ],
+ "dependencies": [
+ {
+ "ref": "dummy"
+ }
+ ],
"metadata": {
"timestamp": "2023-01-07T13:44:32.312678+00:00",
"tools": [
@@ -14,4 +34,4 @@
"$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.3"
-}
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.xml.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.xml.bin
index 1ebd391f9..d8a52a530 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.xml.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.3.xml.bin
@@ -10,4 +10,18 @@
+
+
+ dummy
+
+
+
+ Dummy
+
+
+
+
+
+
+
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.json.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.json.bin
index 513c4f0f5..a3baa3997 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.json.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.json.bin
@@ -1,8 +1,61 @@
{
+ "components": [
+ {
+ "bom-ref": "dummy",
+ "evidence": {
+ "copyright": [
+ {
+ "text": "Dummy"
+ }
+ ]
+ },
+ "name": "dummy",
+ "type": "library"
+ }
+ ],
+ "dependencies": [
+ {
+ "ref": "dummy"
+ }
+ ],
"metadata": {
"timestamp": "2023-01-07T13:44:32.312678+00:00",
"tools": [
{
+ "externalReferences": [
+ {
+ "type": "build-system",
+ "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions"
+ },
+ {
+ "type": "distribution",
+ "url": "https://pypi.org/project/cyclonedx-python-lib/"
+ },
+ {
+ "type": "documentation",
+ "url": "https://cyclonedx-python-library.readthedocs.io/"
+ },
+ {
+ "type": "issue-tracker",
+ "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues"
+ },
+ {
+ "type": "license",
+ "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE"
+ },
+ {
+ "type": "release-notes",
+ "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md"
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/CycloneDX/cyclonedx-python-lib"
+ },
+ {
+ "type": "website",
+ "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme"
+ }
+ ],
"name": "cyclonedx-python-lib",
"vendor": "CycloneDX",
"version": "TESTING"
@@ -14,4 +67,4 @@
"$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.4"
-}
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.xml.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.xml.bin
index 21093ed67..e5530a752 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.xml.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.4.xml.bin
@@ -1,4 +1,3 @@
-
@@ -8,7 +7,46 @@
CycloneDX
cyclonedx-python-lib
TESTING
+
+
+ https://github.com/CycloneDX/cyclonedx-python-lib/actions
+
+
+ https://pypi.org/project/cyclonedx-python-lib/
+
+
+ https://cyclonedx-python-library.readthedocs.io/
+
+
+ https://github.com/CycloneDX/cyclonedx-python-lib/issues
+
+
+ https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE
+
+
+ https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md
+
+
+ https://github.com/CycloneDX/cyclonedx-python-lib
+
+
+ https://github.com/CycloneDX/cyclonedx-python-lib/#readme
+
+
+
+
+ dummy
+
+
+ Dummy
+
+
+
+
+
+
+
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.json.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.json.bin
index b533e71c3..457968196 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.json.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.json.bin
@@ -2,13 +2,20 @@
"components": [
{
"bom-ref": "dummy",
- "name": "dummy",
- "type": "application",
"evidence": {
- "identity": {
- "field": "group"
- }
- }
+ "copyright": [
+ {
+ "text": "Dummy"
+ }
+ ]
+ },
+ "name": "dummy",
+ "type": "library"
+ }
+ ],
+ "dependencies": [
+ {
+ "ref": "dummy"
}
],
"metadata": {
@@ -70,4 +77,4 @@
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5"
-}
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.xml.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.xml.bin
index 67d8e9bdb..aedaed06f 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.xml.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.5.xml.bin
@@ -37,15 +37,18 @@
-
+
dummy
-
- group
-
+
+ Dummy
+
+
+
+
val1
val2
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.json.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.json.bin
index 62c96b5da..d79dc1d42 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.json.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.json.bin
@@ -2,27 +2,30 @@
"components": [
{
"bom-ref": "dummy",
- "name": "dummy",
- "type": "application",
"evidence": {
+ "copyright": [
+ {
+ "text": "Dummy"
+ }
+ ],
"identity": [
{
- "field": "group"
+ "field": "cpe"
},
{
- "field": "name"
+ "field": "group"
},
{
- "field": "version"
+ "field": "hash"
},
{
- "field": "purl"
+ "field": "name"
},
{
- "field": "cpe"
+ "field": "omniborId"
},
{
- "field": "omniborId"
+ "field": "purl"
},
{
"field": "swhid"
@@ -31,10 +34,17 @@
"field": "swid"
},
{
- "field": "hash"
+ "field": "version"
}
]
- }
+ },
+ "name": "dummy",
+ "type": "library"
+ }
+ ],
+ "dependencies": [
+ {
+ "ref": "dummy"
}
],
"metadata": {
@@ -96,4 +106,4 @@
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6"
-}
+}
\ No newline at end of file
diff --git a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.xml.bin b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.xml.bin
index bc7e7b68d..7bdd5e990 100644
--- a/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.xml.bin
+++ b/tests/_data/snapshots/enum_ComponentIdentityEvidenceField-1.6.xml.bin
@@ -37,15 +37,45 @@
-
+
dummy
+
+ cpe
+
group
+
+ hash
+
+
+ name
+
+
+ omniborId
+
+
+ purl
+
+
+ swhid
+
+
+ swid
+
+
+ version
+
+
+ Dummy
+
+
+
+
val1
val2
diff --git a/tests/test_enums.py b/tests/test_enums.py
index fec8fa9de..ab551c774 100644
--- a/tests/test_enums.py
+++ b/tests/test_enums.py
@@ -26,7 +26,10 @@
from ddt import ddt, idata, named_data
from cyclonedx.exception import MissingOptionalDependencyException
-from cyclonedx.exception.serialization import SerializationOfUnsupportedComponentTypeException
+from cyclonedx.exception.serialization import (
+ SerializationOfUnsupportedComponentIdentityEvidenceFieldException,
+ SerializationOfUnsupportedComponentTypeException,
+)
from cyclonedx.model import AttachedText, ExternalReference, HashType, XsUri
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import (
@@ -34,8 +37,7 @@
ComponentEvidence,
ComponentIdentityEvidence,
ComponentIdentityEvidenceField,
- ComponentIdentityEvidenceMethod,
- ComponentIdentityEvidenceMethodTechnique,
+ Copyright,
Patch,
Pedigree,
)
@@ -364,12 +366,32 @@ def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any,
super()._test_cases_render(bom, of, sv)
+class _DP_ComponentIdentityEvidenceField(): # noqa: N801
+ XML_SCHEMA_XPATH = f"./{SCHEMA_NS}simpleType[@name='identityFieldType']"
+ JSON_SCHEMA_POINTER = ('definitions', 'componentIdentityEvidence', 'properties', 'field')
+
+ @classmethod
+ def unsupported_cases(cls) -> Generator[
+ Tuple[str, OutputFormat, SchemaVersion, ComponentIdentityEvidenceField], None, None
+ ]:
+ for name, of, sv in NAMED_OF_SV:
+ if OutputFormat.XML is of:
+ schema_cases = set(dp_cases_from_xml_schema(SCHEMA_XML[sv], cls.XML_SCHEMA_XPATH))
+ elif OutputFormat.JSON is of:
+ schema_cases = set(dp_cases_from_json_schema(SCHEMA_JSON[sv], cls.JSON_SCHEMA_POINTER))
+ else:
+ raise ValueError(f'unexpected of: {of!r}')
+ for cief in ComponentIdentityEvidenceField:
+ if cief.value not in schema_cases:
+ yield f'{name}-{cief.name}', of, sv, cief
+
+
@ddt
class TestEnumComponentIdentityEvidenceField(_EnumTestCase):
@idata(set(chain(
- # dp_cases_from_xml_schemas(f"./{SCHEMA_NS}simpleType[@name='componentIdentityEvidence']"),
- dp_cases_from_json_schemas('definitions', 'componentIdentityEvidence', 'properties', 'field'),
+ dp_cases_from_xml_schemas(_DP_ComponentIdentityEvidenceField.XML_SCHEMA_XPATH),
+ dp_cases_from_json_schemas(*_DP_ComponentIdentityEvidenceField.JSON_SCHEMA_POINTER),
)))
def test_knows_value(self, value: str) -> None:
super()._test_knows_value(ComponentIdentityEvidenceField, value)
@@ -377,23 +399,53 @@ def test_knows_value(self, value: str) -> None:
@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
+ if OutputFormat.XML is of:
+ schema_cases = set(dp_cases_from_xml_schema(SCHEMA_XML[sv], _DP_ComponentIdentityEvidenceField.XML_SCHEMA_XPATH))
+ elif OutputFormat.JSON is of:
+ schema_cases = set(dp_cases_from_json_schema(SCHEMA_JSON[sv], _DP_ComponentIdentityEvidenceField.JSON_SCHEMA_POINTER))
+ else:
+ raise ValueError(f'unexpected of: {of!r}')
+
bom = _make_bom(components=[
- Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', evidence=ComponentEvidence(
+ Component(bom_ref='dummy', name='dummy', evidence=ComponentEvidence(
+ copyright=[
+ Copyright(text="Dummy")
+ ],
identity=[
ComponentIdentityEvidence(
- field=cief,
- confidence=0.5,
- methods=[
- ComponentIdentityEvidenceMethod(technique=ciemt, confidence=0.5)
- for ciemt in ComponentIdentityEvidenceMethodTechnique
- ]
+ field=ComponentIdentityEvidenceField(cief),
)
+ for cief in schema_cases
+ if cief in schema_cases
]
))
- for cief in ComponentIdentityEvidenceField
])
super()._test_cases_render(bom, of, sv)
+ @named_data(*_DP_ComponentIdentityEvidenceField.unsupported_cases())
+ @patch('cyclonedx.model.ThisTool._version', 'TESTING')
+ def test_cases_render_raises_on_unsupported(self, of: OutputFormat, sv: SchemaVersion,
+ cief: ComponentIdentityEvidenceField,
+ *_: Any, **__: Any) -> None:
+ # componentIdentityEvidence type was not added until 1.5
+ if sv.value < (1, 5):
+ return True
+
+ bom = _make_bom(components=[
+ Component(bom_ref='dummy', name='dummy', evidence=ComponentEvidence(
+ copyright=[
+ Copyright(text="Dummy")
+ ],
+ identity=[
+ ComponentIdentityEvidence(
+ field=cief,
+ )
+ ]
+ ))
+ ])
+ with self.assertRaises(SerializationOfUnsupportedComponentIdentityEvidenceFieldException):
+ super()._test_cases_render(bom, of, sv)
+
@ddt
class TestEnumImpactAnalysisJustification(_EnumTestCase):