cyclonedx-python-lib icon indicating copy to clipboard operation
cyclonedx-python-lib copied to clipboard

AttributeError: There was an AttributeError deserializing JSON to <class 'cyclonedx.model.bom.Bom'> the Property

Open rms-sth opened this issue 10 months ago • 3 comments

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" }
      ]
    }
  ]
}

rms-sth avatar Apr 24 '24 09:04 rms-sth

quick tests: given JSON is valid to CDX1.5 and CDX 1.6

jkowalleck avatar Apr 24 '24 09:04 jkowalleck

background: https://github.com/CycloneDX/cyclonedx-python-lib/issues/561

jkowalleck avatar Apr 24 '24 09:04 jkowalleck

@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.

jkowalleck avatar Apr 24 '24 09:04 jkowalleck

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)

jkowalleck avatar Jun 14 '24 10:06 jkowalleck