cyclonedx-python-lib
cyclonedx-python-lib copied to clipboard
AttributeError: There was an AttributeError deserializing JSON to <class 'cyclonedx.model.bom.Bom'> the Property
Cannot parse the cyclonedx 1.5 SBOM:
My code:
import json
from typing import TYPE_CHECKING
from cyclonedx.exception import MissingOptionalDependencyException
from cyclonedx.model.bom import Bom
from cyclonedx.schema import SchemaVersion
from cyclonedx.validation.json import JsonStrictValidator
my_json_validator = JsonStrictValidator(SchemaVersion.V1_6)
try:
file_path = "./test_cylonedx_sbom.json"
with open(file_path) as f:
deserialized_bom = Bom.from_json(data=json.loads(f.read()))
except MissingOptionalDependencyException as error:
print("JSON-validation was skipped due to", error)
Error:
/home/user/fake/path/to/python /home/user/fake/path/to/calculator.py
Traceback (most recent call last):
File "/home/user/fake/path/to/site-packages/serializable/__init__.py", line 324, in from_json
items.append(prop_info.concrete_type.from_json(data=j))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/fake/path/to/site-packages/serializable/__init__.py", line 283, in from_json
for k, v in data.items():
^^^^^^^^^^
AttributeError: 'str' object has no attribute 'items'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/user/fake/path/to/site-packages/serializable/__init__.py", line 333, in from_json
_data[k] = prop_info.concrete_type.from_json(data=v)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/fake/path/to/site-packages/serializable/__init__.py", line 343, in from_json
raise AttributeError(
AttributeError: There was an AttributeError deserializing JSON to <class 'cyclonedx.model.bom.BomMetaData'> the Property <s.oml.SerializableProperty name=tools, custom_names={}, array=True, enum=False, optional=False, c_type=<class 'cyclonedx.model.Tool'>, type=typing.Set[cyclonedx.model.Tool], custom_type=None, xml_attr=False, xml_sequence=3>: 'str' object has no attribute 'items'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/user/fake/path/to/calculator.py", line 49, in <module>
deserialized_bom = Bom.from_json(data=json.loads(f.read()))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/fake/path/to/site-packages/serializable/__init__.py", line 343, in from_json
raise AttributeError(
AttributeError: There was an AttributeError deserializing JSON to <class 'cyclonedx.model.bom.Bom'> the Property <s.oml.SerializableProperty name=metadata, custom_names={}, array=False, enum=False, optional=False, c_type=<class 'cyclonedx.model.bom.BomMetaData'>, type=<class 'cyclonedx.model.bom.BomMetaData'>, custom_type=None, xml_attr=False, xml_sequence=10>: There was an AttributeError deserializing JSON to <class 'cyclonedx.model.bom.BomMetaData'> the Property <s.oml.SerializableProperty name=tools, custom_names={}, array=True, enum=False, optional=False, c_type=<class 'cyclonedx.model.Tool'>, type=typing.Set[cyclonedx.model.Tool], custom_type=None, xml_attr=False, xml_sequence=3>: 'str' object has no attribute 'items'
CycloneDX SBOM:
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:af28ec13-79fa-424d-824e-de926edc5ae8",
"version": 1,
"metadata": {
"timestamp": "2024-04-24T13:07:15+05:45",
"tools": {
"components": [
{
"type": "application",
"author": "anchore",
"name": "syft",
"version": "1.2.0"
}
]
},
"component": {
"bom-ref": "f2447593a456a2a7",
"type": "file",
"name": "test3/"
}
},
"components": [
{
"bom-ref": "pkg:pypi/[email protected]?package-id=d78dba8b757ae22c",
"type": "library",
"name": "black",
"version": "24.2.0",
"cpe": "cpe:2.3:a:python-black:python-black:24.2.0:*:*:*:*:*:*:*",
"purl": "pkg:pypi/[email protected]",
"properties": [
{ "name": "syft:package:foundBy", "value": "python-package-cataloger" },
{ "name": "syft:package:language", "value": "python" },
{ "name": "syft:package:type", "value": "python" },
{
"name": "syft:package:metadataType",
"value": "python-pip-requirements-entry"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python-black:python_black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_black:python-black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_black:python_black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:python-black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:python_black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:black:python-black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:black:python_black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python-black:black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_black:black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:black:24.2.0:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:black:black:24.2.0:*:*:*:*:*:*:*"
},
{ "name": "syft:location:0:path", "value": "/dev/requirements.txt" }
]
},
{
"bom-ref": "pkg:pypi/[email protected]?package-id=7aeb2a6c081f1782",
"type": "library",
"name": "flask",
"version": "2.0.3",
"cpe": "cpe:2.3:a:python-flask:python-flask:2.0.3:*:*:*:*:*:*:*",
"purl": "pkg:pypi/[email protected]",
"properties": [
{ "name": "syft:package:foundBy", "value": "python-package-cataloger" },
{ "name": "syft:package:language", "value": "python" },
{ "name": "syft:package:type", "value": "python" },
{
"name": "syft:package:metadataType",
"value": "python-pip-requirements-entry"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python-flask:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_flask:python-flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_flask:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:python-flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:flask:python-flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:flask:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python-flask:flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_flask:flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:flask:flask:2.0.3:*:*:*:*:*:*:*"
},
{ "name": "syft:location:0:path", "value": "/dev/requirements.txt" }
]
},
{
"bom-ref": "pkg:pypi/[email protected]?package-id=4947d30b71b34501",
"type": "library",
"name": "flask",
"version": "2.0.3",
"cpe": "cpe:2.3:a:python-flask:python-flask:2.0.3:*:*:*:*:*:*:*",
"purl": "pkg:pypi/[email protected]",
"properties": [
{ "name": "syft:package:foundBy", "value": "python-package-cataloger" },
{ "name": "syft:package:language", "value": "python" },
{ "name": "syft:package:type", "value": "python" },
{
"name": "syft:package:metadataType",
"value": "python-pip-requirements-entry"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python-flask:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_flask:python-flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_flask:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:python-flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:flask:python-flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:flask:python_flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python-flask:flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python_flask:flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:python:flask:2.0.3:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:flask:flask:2.0.3:*:*:*:*:*:*:*"
},
{ "name": "syft:location:0:path", "value": "/requirements.txt" }
]
}
]
}
quick tests: given JSON is valid to CDX1.5 and CDX 1.6
background: https://github.com/CycloneDX/cyclonedx-python-lib/issues/561
@rms-sth Thank you for the report.
Be advised, that this library knows how to deserialize documents it created itself. It might not yet capable of deserializing all CycloneDX features.
acknowledged. this is due to the missing data models for that purpose. see https://github.com/CycloneDX/cyclonedx-python-lib/issues/578
CycloneDX python library is maintained by volunteers, and drive by members of the Community. Feel free to contribute the bits and peaces you or your organization needs. If you plan on doing so, please let us know in a respective github issue or in our slack channel (invite)