diff --git a/.isort.cfg b/.isort.cfg
index 31d172da..520c9a1b 100644
--- a/.isort.cfg
+++ b/.isort.cfg
@@ -2,7 +2,7 @@
## read the docs: https://pycqa.github.io/isort/docs/configuration/options.html
## keep in sync with flake8 config - in `tox.ini` file
known_first_party = cyclonedx
-skip_gitignore = true
+skip_gitignore = false
skip_glob =
build/*,dist/*,__pycache__,.eggs,*.egg-info*,
*_cache,*.cache,
@@ -15,3 +15,6 @@ ensure_newline_before_comments = true
include_trailing_comma = true
line_length = 120
multi_line_output = 3
+src_paths =
+ cyclonedx
+ tests
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8da43c37..b92ff6d9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,13 +7,13 @@ repos:
entry: poetry run tox -e mypy
pass_filenames: false
language: system
-# - repo: local
-# hooks:
-# - id: system
-# name: isort
-# entry: poetry run isort
-# pass_filenames: false
-# language: system
+ - repo: local
+ hooks:
+ - id: system
+ name: isort
+ entry: poetry run isort -c .
+ pass_filenames: false
+ language: system
- repo: local
hooks:
- id: system
diff --git a/cyclonedx/model/bom.py b/cyclonedx/model/bom.py
index 08636280..9cbf8363 100644
--- a/cyclonedx/model/bom.py
+++ b/cyclonedx/model/bom.py
@@ -18,7 +18,7 @@
# Copyright (c) OWASP Foundation. All Rights Reserved.
import warnings
from datetime import datetime, timezone
-from typing import Iterable, Optional
+from typing import Iterable, Optional, Set
from uuid import UUID, uuid4
from sortedcontainers import SortedSet
@@ -356,6 +356,16 @@ def external_references(self) -> "SortedSet[ExternalReference]":
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
self._external_references = SortedSet(external_references)
+ def _get_all_components(self) -> Set[Component]:
+ components: Set[Component] = set()
+ if self.metadata.component:
+ components.update(self.metadata.component.get_all_nested_components(include_self=True))
+
+ for c in self.components:
+ components.update(c.get_all_nested_components(include_self=True))
+
+ return components
+
def has_vulnerabilities(self) -> bool:
"""
Check whether this Bom has any declared vulnerabilities.
@@ -376,8 +386,8 @@ def validate(self) -> bool:
"""
# 1. Make sure dependencies are all in this Bom.
- all_bom_refs = set([self.metadata.component.bom_ref] if self.metadata.component else []) | set(
- map(lambda c: c.bom_ref, self.components)) | set(map(lambda s: s.bom_ref, self.services))
+ all_bom_refs = set(map(lambda c: c.bom_ref, self._get_all_components())) | set(
+ map(lambda s: s.bom_ref, self.services))
all_dependency_bom_refs = set().union(*(c.dependencies for c in self.components))
dependency_diff = all_dependency_bom_refs - all_bom_refs
@@ -389,9 +399,9 @@ def validate(self) -> bool:
# 2. Dependencies should exist for the Component this BOM is describing, if one is set
if self.metadata.component and not self.metadata.component.dependencies:
warnings.warn(
- f'The Component this BOM is describing {self.metadata.component.purl} has no defined dependencies'
- f'which means the Dependency Graph is incomplete - you should add direct dependencies to this Component'
- f'to complete the Dependency Graph data.',
+ f'The Component this BOM is describing (PURL={self.metadata.component.purl}) has no defined '
+ f'dependencies which means the Dependency Graph is incomplete - you should add direct dependencies to '
+ f'this Component to complete the Dependency Graph data.',
UserWarning
)
diff --git a/cyclonedx/model/component.py b/cyclonedx/model/component.py
index dc1f7dd1..f3074f67 100644
--- a/cyclonedx/model/component.py
+++ b/cyclonedx/model/component.py
@@ -20,7 +20,7 @@
import warnings
from enum import Enum
from os.path import exists
-from typing import Any, Iterable, Optional
+from typing import Any, Iterable, Optional, Set
# See https://github.com/package-url/packageurl-python/issues/65
from packageurl import PackageURL # type: ignore
@@ -1159,6 +1159,16 @@ def has_vulnerabilities(self) -> bool:
"""
return bool(self.get_vulnerabilities())
+ def get_all_nested_components(self, include_self: bool = False) -> Set["Component"]:
+ components = set()
+ if include_self:
+ components.add(self)
+
+ for c in self.components:
+ components.update(c.get_all_nested_components(include_self=True))
+
+ return components
+
def get_pypi_url(self) -> str:
if self.version:
return f'https://pypi.org/project/{self.name}/{self.version}'
diff --git a/cyclonedx/output/json.py b/cyclonedx/output/json.py
index 94630fea..4556f3d2 100644
--- a/cyclonedx/output/json.py
+++ b/cyclonedx/output/json.py
@@ -115,6 +115,9 @@ def _specialise_output_for_schema_version(self, bom_json: Dict[Any, Any]) -> str
del bom_json['metadata']['properties']
# Iterate Components
+ if self.get_bom().metadata.component:
+ bom_json['metadata'] = self._recurse_specialise_component(bom_json=bom_json['metadata'],
+ base_key='component')
bom_json = self._recurse_specialise_component(bom_json=bom_json)
# Iterate Services
@@ -155,60 +158,67 @@ def _get_schema_uri(self) -> Optional[str]:
def _recurse_specialise_component(self, bom_json: Dict[Any, Any], base_key: str = 'components') -> Dict[Any, Any]:
if base_key in bom_json.keys():
- for i in range(len(bom_json[base_key])):
- if not self.component_supports_mime_type_attribute() \
- and 'mime-type' in bom_json[base_key][i].keys():
- del bom_json[base_key][i]['mime-type']
-
- if not self.component_supports_supplier() and 'supplier' in bom_json[base_key][i].keys():
- del bom_json[base_key][i]['supplier']
-
- if not self.component_supports_author() and 'author' in bom_json[base_key][i].keys():
- del bom_json[base_key][i]['author']
-
- if self.component_version_optional() and 'version' in bom_json[base_key][i] \
- and bom_json[base_key][i].get('version', '') == "":
- del bom_json[base_key][i]['version']
-
- if not self.component_supports_pedigree() and 'pedigree' in bom_json[base_key][i].keys():
- del bom_json[base_key][i]['pedigree']
- elif 'pedigree' in bom_json[base_key][i].keys():
- if 'ancestors' in bom_json[base_key][i]['pedigree'].keys():
- # recurse into ancestors
- bom_json[base_key][i]['pedigree'] = self._recurse_specialise_component(
- bom_json=bom_json[base_key][i]['pedigree'], base_key='ancestors'
- )
- if 'descendants' in bom_json[base_key][i]['pedigree'].keys():
- # recurse into descendants
- bom_json[base_key][i]['pedigree'] = self._recurse_specialise_component(
- bom_json=bom_json[base_key][i]['pedigree'], base_key='descendants'
- )
- if 'variants' in bom_json[base_key][i]['pedigree'].keys():
- # recurse into variants
- bom_json[base_key][i]['pedigree'] = self._recurse_specialise_component(
- bom_json=bom_json[base_key][i]['pedigree'], base_key='variants'
- )
-
- if not self.external_references_supports_hashes() and 'externalReferences' \
- in bom_json[base_key][i].keys():
- for j in range(len(bom_json[base_key][i]['externalReferences'])):
- del bom_json[base_key][i]['externalReferences'][j]['hashes']
-
- if not self.component_supports_properties() and 'properties' in bom_json[base_key][i].keys():
- del bom_json[base_key][i]['properties']
-
- # recurse
- if 'components' in bom_json[base_key][i].keys():
- bom_json[base_key][i] = self._recurse_specialise_component(bom_json=bom_json[base_key][i])
-
- if not self.component_supports_evidence() and 'evidence' in bom_json[base_key][i].keys():
- del bom_json[base_key][i]['evidence']
-
- if not self.component_supports_release_notes() and 'releaseNotes' in bom_json[base_key][i].keys():
- del bom_json[base_key][i]['releaseNotes']
+ if isinstance(bom_json[base_key], dict):
+ bom_json[base_key] = self._specialise_component_data(component_json=bom_json[base_key])
+ else:
+ for i in range(len(bom_json[base_key])):
+ bom_json[base_key][i] = self._specialise_component_data(component_json=bom_json[base_key][i])
return bom_json
+ def _specialise_component_data(self, component_json: Dict[Any, Any]) -> Dict[Any, Any]:
+ if not self.component_supports_mime_type_attribute() and 'mime-type' in component_json.keys():
+ del component_json['mime-type']
+
+ if not self.component_supports_supplier() and 'supplier' in component_json.keys():
+ del component_json['supplier']
+
+ if not self.component_supports_author() and 'author' in component_json.keys():
+ del component_json['author']
+
+ if self.component_version_optional() and 'version' in component_json \
+ and component_json.get('version', '') == "":
+ del component_json['version']
+
+ if not self.component_supports_pedigree() and 'pedigree' in component_json.keys():
+ del component_json['pedigree']
+ elif 'pedigree' in component_json.keys():
+ if 'ancestors' in component_json['pedigree'].keys():
+ # recurse into ancestors
+ component_json['pedigree'] = self._recurse_specialise_component(
+ bom_json=component_json['pedigree'], base_key='ancestors'
+ )
+ if 'descendants' in component_json['pedigree'].keys():
+ # recurse into descendants
+ component_json['pedigree'] = self._recurse_specialise_component(
+ bom_json=component_json['pedigree'], base_key='descendants'
+ )
+ if 'variants' in component_json['pedigree'].keys():
+ # recurse into variants
+ component_json['pedigree'] = self._recurse_specialise_component(
+ bom_json=component_json['pedigree'], base_key='variants'
+ )
+
+ if not self.external_references_supports_hashes() and 'externalReferences' \
+ in component_json.keys():
+ for j in range(len(component_json['externalReferences'])):
+ del component_json['externalReferences'][j]['hashes']
+
+ if not self.component_supports_properties() and 'properties' in component_json.keys():
+ del component_json['properties']
+
+ # recurse
+ if 'components' in component_json.keys():
+ component_json = self._recurse_specialise_component(bom_json=component_json)
+
+ if not self.component_supports_evidence() and 'evidence' in component_json.keys():
+ del component_json['evidence']
+
+ if not self.component_supports_release_notes() and 'releaseNotes' in component_json.keys():
+ del component_json['releaseNotes']
+
+ return component_json
+
class JsonV1Dot0(Json, SchemaVersion1Dot0):
diff --git a/poetry.lock b/poetry.lock
index 226d23d1..941c9a9c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -45,7 +45,7 @@ toml = ["tomli"]
[[package]]
name = "distlib"
-version = "0.3.4"
+version = "0.3.5"
description = "Distribution utilities"
category = "dev"
optional = false
@@ -166,7 +166,7 @@ plugins = ["setuptools"]
[[package]]
name = "jsonschema"
-version = "4.6.1"
+version = "4.7.2"
description = "An implementation of JSON Schema validation for Python"
category = "dev"
optional = false
@@ -234,14 +234,15 @@ python-versions = "*"
[[package]]
name = "packageurl-python"
-version = "0.9.9"
+version = "0.10.0"
description = "A purl aka. Package URL parser and builder"
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
-test = ["isort", "pytest"]
+test = ["black", "isort", "pytest"]
+build = ["wheel"]
[[package]]
name = "packaging"
@@ -358,7 +359,7 @@ python-versions = ">=3.6"
[[package]]
name = "tox"
-version = "3.25.0"
+version = "3.25.1"
description = "tox is a generic virtualenv management and test command line tool"
category = "dev"
optional = false
@@ -389,7 +390,7 @@ python-versions = ">=3.6"
[[package]]
name = "types-setuptools"
-version = "57.4.17"
+version = "63.2.2"
description = "Typing stubs for setuptools"
category = "dev"
optional = false
@@ -397,7 +398,7 @@ python-versions = "*"
[[package]]
name = "types-toml"
-version = "0.10.4"
+version = "0.10.8"
description = "Typing stubs for toml"
category = "dev"
optional = false
@@ -413,11 +414,11 @@ python-versions = ">=3.6"
[[package]]
name = "virtualenv"
-version = "20.15.1"
+version = "20.16.2"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+python-versions = ">=3.6"
[package.dependencies]
distlib = ">=0.3.1,<1"
@@ -425,11 +426,10 @@ filelock = ">=3.2,<4"
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""}
platformdirs = ">=2,<3"
-six = ">=1.9.0,<2"
[package.extras]
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"]
-testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
+testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"]
[[package]]
name = "xmldiff"
@@ -458,337 +458,45 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
-content-hash = "2aa5c305ab89542157a2859ca04386e430884cc5e65a5f7d6bc0dad7376abc3a"
+content-hash = "2d54fab7cbd2e7c837e362936f030fe70ce8ed9114529f828a5f865b14f9d1d0"
[metadata.files]
-attrs = [
- {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
- {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
-]
-autopep8 = [
- {file = "autopep8-1.6.0-py2.py3-none-any.whl", hash = "sha256:ed77137193bbac52d029a52c59bec1b0629b5a186c495f1eb21b126ac466083f"},
- {file = "autopep8-1.6.0.tar.gz", hash = "sha256:44f0932855039d2c15c4510d6df665e4730f2b8582704fa48f9c55bd3e17d979"},
-]
-colorama = [
- {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
- {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
-]
-coverage = [
- {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"},
- {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"},
- {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"},
- {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"},
- {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"},
- {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"},
- {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"},
- {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"},
- {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"},
- {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"},
- {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"},
- {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"},
- {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"},
- {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"},
- {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"},
- {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"},
- {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"},
- {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"},
- {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"},
- {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"},
- {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"},
- {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"},
- {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"},
- {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"},
- {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"},
- {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"},
- {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"},
- {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"},
- {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"},
- {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"},
- {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"},
- {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"},
- {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"},
- {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"},
- {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"},
- {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"},
- {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"},
- {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"},
- {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"},
- {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"},
- {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"},
- {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"},
- {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"},
- {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"},
- {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"},
- {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"},
- {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"},
-]
-distlib = [
- {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
- {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
-]
-filelock = [
- {file = "filelock-3.4.1-py3-none-any.whl", hash = "sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"},
- {file = "filelock-3.4.1.tar.gz", hash = "sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06"},
-]
-flake8 = [
- {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
- {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
-]
-flake8-annotations = [
- {file = "flake8-annotations-2.7.0.tar.gz", hash = "sha256:52e53c05b0c06cac1c2dec192ea2c36e85081238add3bd99421d56f574b9479b"},
- {file = "flake8_annotations-2.7.0-py3-none-any.whl", hash = "sha256:3edfbbfb58e404868834fe6ec3eaf49c139f64f0701259f707d043185545151e"},
-]
-flake8-bugbear = [
- {file = "flake8-bugbear-22.7.1.tar.gz", hash = "sha256:e450976a07e4f9d6c043d4f72b17ec1baf717fe37f7997009c8ae58064f88305"},
- {file = "flake8_bugbear-22.7.1-py3-none-any.whl", hash = "sha256:db5d7a831ef4412a224b26c708967ff816818cabae415e76b8c58df156c4b8e5"},
-]
-flake8-isort = [
- {file = "flake8-isort-4.1.2.post0.tar.gz", hash = "sha256:dee69bc3c09f0832df88acf795845db8a6673b79237371a05fa927ce095248e5"},
- {file = "flake8_isort-4.1.2.post0-py3-none-any.whl", hash = "sha256:4f95b40706dbb507cff872b34683283662e945d6028d3c8257e69de5fc6b7446"},
-]
-importlib-metadata = [
- {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"},
- {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"},
-]
-importlib-resources = [
- {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"},
- {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"},
-]
-isort = [
- {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
- {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
-]
-jsonschema = [
- {file = "jsonschema-4.6.1-py3-none-any.whl", hash = "sha256:5eb781753403847fb320f05e9ab2191725b58c5e7f97f1bed63285ca423159bc"},
- {file = "jsonschema-4.6.1.tar.gz", hash = "sha256:ec2802e6a37517f09d47d9ba107947589ae1d25ff557b925d83a321fc2aa5d3b"},
-]
-lxml = [
- {file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"},
- {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"},
- {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc"},
- {file = "lxml-4.9.1-cp27-cp27m-win32.whl", hash = "sha256:86e92728ef3fc842c50a5cb1d5ba2bc66db7da08a7af53fb3da79e202d1b2cd3"},
- {file = "lxml-4.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4cfbe42c686f33944e12f45a27d25a492cc0e43e1dc1da5d6a87cbcaf2e95627"},
- {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dad7b164905d3e534883281c050180afcf1e230c3d4a54e8038aa5cfcf312b84"},
- {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a614e4afed58c14254e67862456d212c4dcceebab2eaa44d627c2ca04bf86837"},
- {file = "lxml-4.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f9ced82717c7ec65a67667bb05865ffe38af0e835cdd78728f1209c8fffe0cad"},
- {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:d9fc0bf3ff86c17348dfc5d322f627d78273eba545db865c3cd14b3f19e57fa5"},
- {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e5f66bdf0976ec667fc4594d2812a00b07ed14d1b44259d19a41ae3fff99f2b8"},
- {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fe17d10b97fdf58155f858606bddb4e037b805a60ae023c009f760d8361a4eb8"},
- {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8caf4d16b31961e964c62194ea3e26a0e9561cdf72eecb1781458b67ec83423d"},
- {file = "lxml-4.9.1-cp310-cp310-win32.whl", hash = "sha256:4780677767dd52b99f0af1f123bc2c22873d30b474aa0e2fc3fe5e02217687c7"},
- {file = "lxml-4.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a188cd292c4d2fcd78d04f863b789ef43aa129b233d7c9004de08693728b"},
- {file = "lxml-4.9.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:be9eb06489bc975c38706902cbc6888f39e946b81383abc2838d186f0e8b6a9d"},
- {file = "lxml-4.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f1be258c4d3dc609e654a1dc59d37b17d7fef05df912c01fc2e15eb43a9735f3"},
- {file = "lxml-4.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:927a9dd016d6033bc12e0bf5dee1dde140235fc8d0d51099353c76081c03dc29"},
- {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9232b09f5efee6a495a99ae6824881940d6447debe272ea400c02e3b68aad85d"},
- {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:04da965dfebb5dac2619cb90fcf93efdb35b3c6994fea58a157a834f2f94b318"},
- {file = "lxml-4.9.1-cp35-cp35m-win32.whl", hash = "sha256:4d5bae0a37af799207140652a700f21a85946f107a199bcb06720b13a4f1f0b7"},
- {file = "lxml-4.9.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4878e667ebabe9b65e785ac8da4d48886fe81193a84bbe49f12acff8f7a383a4"},
- {file = "lxml-4.9.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:1355755b62c28950f9ce123c7a41460ed9743c699905cbe664a5bcc5c9c7c7fb"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:bcaa1c495ce623966d9fc8a187da80082334236a2a1c7e141763ffaf7a405067"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eafc048ea3f1b3c136c71a86db393be36b5b3d9c87b1c25204e7d397cee9536"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:13c90064b224e10c14dcdf8086688d3f0e612db53766e7478d7754703295c7c8"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206a51077773c6c5d2ce1991327cda719063a47adc02bd703c56a662cdb6c58b"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e8f0c9d65da595cfe91713bc1222af9ecabd37971762cb830dea2fc3b3bb2acf"},
- {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0a4d179c9a941eb80c3a63cdb495e539e064f8054230844dcf2fcb812b71d3"},
- {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:830c88747dce8a3e7525defa68afd742b4580df6aa2fdd6f0855481e3994d391"},
- {file = "lxml-4.9.1-cp36-cp36m-win32.whl", hash = "sha256:1e1cf47774373777936c5aabad489fef7b1c087dcd1f426b621fda9dcc12994e"},
- {file = "lxml-4.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:5974895115737a74a00b321e339b9c3f45c20275d226398ae79ac008d908bff7"},
- {file = "lxml-4.9.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1423631e3d51008871299525b541413c9b6c6423593e89f9c4cfbe8460afc0a2"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:2aaf6a0a6465d39b5ca69688fce82d20088c1838534982996ec46633dc7ad6cc"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:9f36de4cd0c262dd9927886cc2305aa3f2210db437aa4fed3fb4940b8bf4592c"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae06c1e4bc60ee076292e582a7512f304abdf6c70db59b56745cca1684f875a4"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:57e4d637258703d14171b54203fd6822fda218c6c2658a7d30816b10995f29f3"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d279033bf614953c3fc4a0aa9ac33a21e8044ca72d4fa8b9273fe75359d5cca"},
- {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a60f90bba4c37962cbf210f0188ecca87daafdf60271f4c6948606e4dabf8785"},
- {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2264f341dd81e41f3fffecec6e446aa2121e0b8d026fb5130e02de1402785"},
- {file = "lxml-4.9.1-cp37-cp37m-win32.whl", hash = "sha256:27e590352c76156f50f538dbcebd1925317a0f70540f7dc8c97d2931c595783a"},
- {file = "lxml-4.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:eea5d6443b093e1545ad0210e6cf27f920482bfcf5c77cdc8596aec73523bb7e"},
- {file = "lxml-4.9.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f05251bbc2145349b8d0b77c0d4e5f3b228418807b1ee27cefb11f69ed3d233b"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:487c8e61d7acc50b8be82bda8c8d21d20e133c3cbf41bd8ad7eb1aaeb3f07c97"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d1a92d8e90b286d491e5626af53afef2ba04da33e82e30744795c71880eaa21"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b570da8cd0012f4af9fa76a5635cd31f707473e65a5a335b186069d5c7121ff2"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ef87fca280fb15342726bd5f980f6faf8b84a5287fcc2d4962ea8af88b35130"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:93e414e3206779ef41e5ff2448067213febf260ba747fc65389a3ddaa3fb8715"},
- {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6653071f4f9bac46fbc30f3c7838b0e9063ee335908c5d61fb7a4a86c8fd2036"},
- {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:32a73c53783becdb7eaf75a2a1525ea8e49379fb7248c3eeefb9412123536387"},
- {file = "lxml-4.9.1-cp38-cp38-win32.whl", hash = "sha256:1a7c59c6ffd6ef5db362b798f350e24ab2cfa5700d53ac6681918f314a4d3b94"},
- {file = "lxml-4.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:1436cf0063bba7888e43f1ba8d58824f085410ea2025befe81150aceb123e345"},
- {file = "lxml-4.9.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:4beea0f31491bc086991b97517b9683e5cfb369205dac0148ef685ac12a20a67"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:41fb58868b816c202e8881fd0f179a4644ce6e7cbbb248ef0283a34b73ec73bb"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bd34f6d1810d9354dc7e35158aa6cc33456be7706df4420819af6ed966e85448"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:edffbe3c510d8f4bf8640e02ca019e48a9b72357318383ca60e3330c23aaffc7"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d949f53ad4fc7cf02c44d6678e7ff05ec5f5552b235b9e136bd52e9bf730b91"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:079b68f197c796e42aa80b1f739f058dcee796dc725cc9a1be0cdb08fc45b000"},
- {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9c3a88d20e4fe4a2a4a84bf439a5ac9c9aba400b85244c63a1ab7088f85d9d25"},
- {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4e285b5f2bf321fc0857b491b5028c5f276ec0c873b985d58d7748ece1d770dd"},
- {file = "lxml-4.9.1-cp39-cp39-win32.whl", hash = "sha256:ef72013e20dd5ba86a8ae1aed7f56f31d3374189aa8b433e7b12ad182c0d2dfb"},
- {file = "lxml-4.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:10d2017f9150248563bb579cd0d07c61c58da85c922b780060dcc9a3aa9f432d"},
- {file = "lxml-4.9.1-pp37-pypy37_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538747a9d7827ce3e16a8fdd201a99e661c7dee3c96c885d8ecba3c35d1032c"},
- {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0645e934e940107e2fdbe7c5b6fb8ec6232444260752598bc4d09511bd056c0b"},
- {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6daa662aba22ef3258934105be2dd9afa5bb45748f4f702a3b39a5bf53a1f4dc"},
- {file = "lxml-4.9.1-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:603a464c2e67d8a546ddaa206d98e3246e5db05594b97db844c2f0a1af37cf5b"},
- {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c4b2e0559b68455c085fb0f6178e9752c4be3bba104d6e881eb5573b399d1eb2"},
- {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0f3f0059891d3254c7b5fb935330d6db38d6519ecd238ca4fce93c234b4a0f73"},
- {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c852b1530083a620cb0de5f3cd6826f19862bafeaf77586f1aef326e49d95f0c"},
- {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"},
- {file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"},
-]
-mccabe = [
- {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
- {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
-]
-mypy = [
- {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"},
- {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"},
- {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"},
- {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"},
- {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"},
- {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"},
- {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"},
- {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"},
- {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"},
- {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"},
- {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"},
- {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"},
- {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"},
- {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"},
- {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"},
- {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"},
- {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"},
- {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"},
- {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"},
- {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"},
- {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"},
- {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"},
- {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"},
-]
-mypy-extensions = [
- {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
- {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
-]
-packageurl-python = [
- {file = "packageurl-python-0.9.9.tar.gz", hash = "sha256:872a0434b9a448b3fa97571711f69dd2a3fb72345ad66c90b17d827afea82f09"},
- {file = "packageurl_python-0.9.9-py3-none-any.whl", hash = "sha256:07aa852d1c48b0e86e625f6a32d83f96427739806b269d0f8142788ee807114b"},
-]
-packaging = [
- {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
- {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
-]
-platformdirs = [
- {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"},
- {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"},
-]
-pluggy = [
- {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
- {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
-]
-py = [
- {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
- {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
-]
-pycodestyle = [
- {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
- {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
-]
-pyflakes = [
- {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
- {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
-]
-pyparsing = [
- {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
- {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
-]
-pyrsistent = [
- {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"},
- {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"},
- {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"},
- {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"},
- {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"},
- {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"},
- {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"},
- {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"},
- {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"},
- {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"},
- {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"},
- {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"},
- {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"},
- {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"},
- {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"},
- {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"},
- {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"},
- {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"},
- {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"},
- {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"},
- {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"},
-]
-six = [
- {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
- {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
-]
-sortedcontainers = [
- {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
- {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
-]
-toml = [
- {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
- {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-tomli = [
- {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"},
- {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"},
-]
-tox = [
- {file = "tox-3.25.0-py2.py3-none-any.whl", hash = "sha256:0805727eb4d6b049de304977dfc9ce315a1938e6619c3ab9f38682bb04662a5a"},
- {file = "tox-3.25.0.tar.gz", hash = "sha256:37888f3092aa4e9f835fc8cc6dadbaaa0782651c41ef359e3a5743fcb0308160"},
-]
-typed-ast = [
- {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"},
- {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"},
- {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"},
- {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"},
- {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"},
- {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"},
- {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"},
- {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"},
- {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"},
- {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"},
- {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"},
- {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"},
- {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"},
- {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"},
- {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"},
- {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"},
- {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"},
- {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"},
- {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"},
- {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"},
- {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"},
- {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"},
- {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
- {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
-]
-types-setuptools = [
- {file = "types-setuptools-57.4.17.tar.gz", hash = "sha256:9d556fcaf6808a1cead4aaa41e5c07a61f0152a875811e1239738eba4e0b7b16"},
- {file = "types_setuptools-57.4.17-py3-none-any.whl", hash = "sha256:9c7cdaf0d55113e24ac17103bde2d434472abf1dbf444238e989fe4e798ffa26"},
-]
-types-toml = [
- {file = "types-toml-0.10.4.tar.gz", hash = "sha256:9340e7c1587715581bb13905b3af30b79fe68afaccfca377665d5e63b694129a"},
- {file = "types_toml-0.10.4-py3-none-any.whl", hash = "sha256:4a9ffd47bbcec49c6fde6351a889b2c1bd3c0ef309fa0eed60dc28e58c8b9ea6"},
-]
-typing-extensions = [
- {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
- {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"},
-]
-virtualenv = [
- {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"},
- {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"},
-]
-xmldiff = [
- {file = "xmldiff-2.4-py2.py3-none-any.whl", hash = "sha256:213c2f4c39ed71811a9ceeec1c8bdf2e673e5527261ea11708b3acfa6c2bdb00"},
- {file = "xmldiff-2.4.tar.gz", hash = "sha256:05bea20ce1f2c9678683bcce0c3ba9981f87d92b709d190e018bcbf047eccf63"},
-]
-zipp = [
- {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
- {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
-]
+attrs = []
+autopep8 = []
+colorama = []
+coverage = []
+distlib = []
+filelock = []
+flake8 = []
+flake8-annotations = []
+flake8-bugbear = []
+flake8-isort = []
+importlib-metadata = []
+importlib-resources = []
+isort = []
+jsonschema = []
+lxml = []
+mccabe = []
+mypy = []
+mypy-extensions = []
+packageurl-python = []
+packaging = []
+platformdirs = []
+pluggy = []
+py = []
+pycodestyle = []
+pyflakes = []
+pyparsing = []
+pyrsistent = []
+six = []
+sortedcontainers = []
+toml = []
+tomli = []
+tox = []
+typed-ast = []
+types-setuptools = []
+types-toml = []
+typing-extensions = []
+virtualenv = []
+xmldiff = []
+zipp = []
diff --git a/pyproject.toml b/pyproject.toml
index 0a993936..1781f647 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,7 +52,7 @@ sortedcontainers = "^2.4.0"
[tool.poetry.dev-dependencies]
tox = "^3.25.0"
coverage = "^6.2"
-mypy = ">= 0.920, < 1.00"
+mypy = ">= 0.920, <= 0.961"
autopep8 = "^1.6.0"
isort = { version = "^5.10.0", python = ">= 3.6.1" }
flake8 = "^4.0.1"
diff --git a/tests/data.py b/tests/data.py
index 585dddf8..4fa73737 100644
--- a/tests/data.py
+++ b/tests/data.py
@@ -138,26 +138,7 @@ def get_bom_with_metadata_component_and_dependencies() -> Bom:
def get_bom_with_component_setuptools_complete() -> Bom:
- component = get_component_setuptools_simple(bom_ref=MOCK_UUID_6)
- component.supplier = get_org_entity_1()
- component.publisher = 'CycloneDX'
- component.description = 'This component is awesome'
- component.scope = ComponentScope.REQUIRED
- component.copyright = 'Apache 2.0 baby!'
- component.cpe = 'cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*'
- component.swid = get_swid_1()
- component.pedigree = get_pedigree_1()
- component.external_references.add(
- get_external_reference_1()
- )
- component.properties = get_properties_1()
- component.components.update([
- get_component_setuptools_simple(),
- get_component_toml_with_hashes_with_references()
- ])
- component.evidence = ComponentEvidence(copyright_=[Copyright(text='Commercial'), Copyright(text='Commercial 2')])
- component.release_notes = get_release_notes()
- return Bom(components=[component])
+ return Bom(components=[get_component_setuptools_complete()])
def get_bom_with_component_setuptools_with_vulnerability() -> Bom:
@@ -229,9 +210,7 @@ def get_bom_with_component_toml_1() -> Bom:
def get_bom_just_complete_metadata() -> Bom:
bom = Bom()
bom.metadata.authors = [get_org_contact_1(), get_org_contact_2()]
- bom.metadata.component = Component(
- name='cyclonedx-python-lib', version='1.0.0', component_type=ComponentType.LIBRARY
- )
+ bom.metadata.component = get_component_setuptools_complete()
bom.metadata.manufacture = get_org_entity_1()
bom.metadata.supplier = get_org_entity_2()
bom.metadata.licenses = [LicenseChoice(license_=License(
@@ -340,10 +319,67 @@ def get_bom_with_nested_services() -> Bom:
return bom
-def get_component_setuptools_simple(bom_ref: Optional[str] = None) -> Component:
+def get_bom_for_issue_275_components() -> Bom:
+ app = Component(bom_ref=MOCK_UUID_1, name="app", version="1.0.0")
+ comp_a = Component(bom_ref=MOCK_UUID_2, name="comp_a", version="1.0.0")
+ comp_b = Component(bom_ref=MOCK_UUID_3, name="comp_b", version="1.0.0")
+ comp_c = Component(bom_ref=MOCK_UUID_4, name="comp_c", version="1.0.0")
+
+ comp_b.components.add(comp_c)
+ comp_b.dependencies.add(comp_c.bom_ref)
+
+ libs = [comp_a, comp_b]
+ app.dependencies.add(comp_a.bom_ref)
+ app.dependencies.add(comp_b.bom_ref)
+
+ bom = Bom(components=libs)
+ bom.metadata.component = app
+ return bom
+
+
+# def get_bom_for_issue_275_services() -> Bom:
+# app = Component(name="app", version="1.0.0")
+# serv_a = Service(name='Service A')
+# serv_b = Service(name='Service B')
+# serv_c = Service(name='Service C')
+#
+# serv_b.services.add(serv_c)
+# serv_b.dependencies.add(serv_c.bom_ref)
+#
+# bom = Bom(services=[serv_a, serv_b])
+# bom.metadata.component = app
+# return bom
+
+
+def get_component_setuptools_complete(include_pedigree: bool = True) -> Component:
+ component = get_component_setuptools_simple(bom_ref=None)
+ component.supplier = get_org_entity_1()
+ component.publisher = 'CycloneDX'
+ component.description = 'This component is awesome'
+ component.scope = ComponentScope.REQUIRED
+ component.copyright = 'Apache 2.0 baby!'
+ component.cpe = 'cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*'
+ component.swid = get_swid_1()
+ if include_pedigree:
+ component.pedigree = get_pedigree_1()
+ component.external_references.add(
+ get_external_reference_1()
+ )
+ component.properties = get_properties_1()
+ component.components.update([
+ get_component_setuptools_simple(),
+ get_component_toml_with_hashes_with_references()
+ ])
+ component.evidence = ComponentEvidence(copyright_=[Copyright(text='Commercial'), Copyright(text='Commercial 2')])
+ component.release_notes = get_release_notes()
+ return component
+
+
+def get_component_setuptools_simple(
+ bom_ref: Optional[str] = 'pkg:pypi/setuptools@50.3.2?extension=tar.gz') -> Component:
return Component(
name='setuptools', version='50.3.2',
- bom_ref=bom_ref or 'pkg:pypi/setuptools@50.3.2?extension=tar.gz',
+ bom_ref=bom_ref,
purl=PackageURL(
type='pypi', name='setuptools', version='50.3.2', qualifiers='extension=tar.gz'
),
diff --git a/tests/fixtures/json/1.2/bom_issue_275_components.json b/tests/fixtures/json/1.2/bom_issue_275_components.json
new file mode 100644
index 00000000..16df9e10
--- /dev/null
+++ b/tests/fixtures/json/1.2/bom_issue_275_components.json
@@ -0,0 +1,64 @@
+{
+ "$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json",
+ "bomFormat": "CycloneDX",
+ "components": [
+ {
+ "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
+ "name": "comp_a",
+ "type": "library",
+ "version": "1.0.0"
+ },
+ {
+ "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857",
+ "components": [
+ {
+ "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789",
+ "name": "comp_c",
+ "type": "library",
+ "version": "1.0.0"
+ }
+ ],
+ "name": "comp_b",
+ "type": "library",
+ "version": "1.0.0"
+ }
+ ],
+ "dependencies": [
+ {
+ "dependsOn": [
+ "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
+ "0b049d09-64c0-4490-a0f5-c84d9aacf857"
+ ],
+ "ref": "be2c6502-7e9a-47db-9a66-e34f729810a3"
+ },
+ {
+ "dependsOn": [],
+ "ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda"
+ },
+ {
+ "dependsOn": [
+ "cd3e9c95-9d41-49e7-9924-8cf0465ae789"
+ ],
+ "ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857"
+ }
+ ],
+ "metadata": {
+ "component": {
+ "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3",
+ "name": "app",
+ "type": "library",
+ "version": "1.0.0"
+ },
+ "timestamp": "2022-07-27T10:06:17.101289+00:00",
+ "tools": [
+ {
+ "name": "cyclonedx-python-lib",
+ "vendor": "CycloneDX",
+ "version": "0.11.0"
+ }
+ ]
+ },
+ "serialNumber": "urn:uuid:b1d647c9-e4c5-4d8f-be8a-1751ac3ad4d5",
+ "specVersion": "1.2",
+ "version": 1
+}
\ No newline at end of file
diff --git a/tests/fixtures/json/1.2/bom_with_full_metadata.json b/tests/fixtures/json/1.2/bom_with_full_metadata.json
index 6b6c7de8..7cfee9db 100644
--- a/tests/fixtures/json/1.2/bom_with_full_metadata.json
+++ b/tests/fixtures/json/1.2/bom_with_full_metadata.json
@@ -25,10 +25,203 @@
}
],
"component": {
- "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857",
"type": "library",
- "name": "cyclonedx-python-lib",
- "version": "1.0.0"
+ "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857",
+ "supplier": {
+ "name": "CycloneDX",
+ "url": [
+ "https://cyclonedx.org"
+ ],
+ "contact": [
+ {
+ "name": "Paul Horton",
+ "email": "paul.horton@owasp.org"
+ },
+ {
+ "name": "A N Other",
+ "email": "someone@somewhere.tld",
+ "phone": "+44 (0)1234 567890"
+ }
+ ]
+ },
+ "author": "Test Author",
+ "publisher": "CycloneDX",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "description": "This component is awesome",
+ "scope": "required",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "copyright": "Apache 2.0 baby!",
+ "cpe": "cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*",
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz",
+ "swid": {
+ "tagId": "swidgen-242eb18a-503e-ca37-393b-cf156ef09691_9.1.1",
+ "name": "Test Application",
+ "version": "3.4.5",
+ "text": {
+ "contentType": "text/xml",
+ "encoding": "base64",
+ "content": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg=="
+ }
+ },
+ "pedigree": {
+ "ancestors": [
+ {
+ "type": "library",
+ "bom-ref": "ccc8d7ee-4b9c-4750-aee0-a72585152291",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "8a3893b3-9923-4adb-a1d3-47456636ba0a",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools?extension=tar.gz"
+ }
+ ],
+ "descendants": [
+ {
+ "type": "library",
+ "bom-ref": "28b2d8ce-def0-446f-a221-58dee0b44acc",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "555ca729-93c6-48f3-956e-bdaa4a2f0bfa",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment"
+ }
+ ]
+ }
+ ],
+ "variants": [
+ {
+ "type": "library",
+ "bom-ref": "e7abdcca-5ba2-4f29-b2cf-b1e1ef788e66",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment"
+ }
+ ]
+ },
+ {
+ "type": "library",
+ "bom-ref": "ded1d73e-1fca-4302-b520-f1bc53979958",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ }
+ ],
+ "commits": [
+ {
+ "uid": "a-random-uid",
+ "message": "A commit message"
+ }
+ ],
+ "patches": [
+ {
+ "type": "backport"
+ }
+ ],
+ "notes": "Some notes here please"
+ },
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment"
+ }
+ ],
+ "components": [
+ {
+ "type": "library",
+ "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment"
+ }
+ ]
+ }
+ ]
},
"manufacture": {
"name": "CycloneDX",
diff --git a/tests/fixtures/json/1.3/bom_issue_275_components.json b/tests/fixtures/json/1.3/bom_issue_275_components.json
new file mode 100644
index 00000000..4a574815
--- /dev/null
+++ b/tests/fixtures/json/1.3/bom_issue_275_components.json
@@ -0,0 +1,64 @@
+{
+ "$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json",
+ "bomFormat": "CycloneDX",
+ "components": [
+ {
+ "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
+ "name": "comp_a",
+ "type": "library",
+ "version": "1.0.0"
+ },
+ {
+ "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857",
+ "components": [
+ {
+ "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789",
+ "name": "comp_c",
+ "type": "library",
+ "version": "1.0.0"
+ }
+ ],
+ "name": "comp_b",
+ "type": "library",
+ "version": "1.0.0"
+ }
+ ],
+ "dependencies": [
+ {
+ "dependsOn": [
+ "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
+ "0b049d09-64c0-4490-a0f5-c84d9aacf857"
+ ],
+ "ref": "be2c6502-7e9a-47db-9a66-e34f729810a3"
+ },
+ {
+ "dependsOn": [],
+ "ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda"
+ },
+ {
+ "dependsOn": [
+ "cd3e9c95-9d41-49e7-9924-8cf0465ae789"
+ ],
+ "ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857"
+ }
+ ],
+ "metadata": {
+ "component": {
+ "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3",
+ "name": "app",
+ "type": "library",
+ "version": "1.0.0"
+ },
+ "timestamp": "2022-07-27T10:06:17.101289+00:00",
+ "tools": [
+ {
+ "name": "cyclonedx-python-lib",
+ "vendor": "CycloneDX",
+ "version": "0.11.0"
+ }
+ ]
+ },
+ "serialNumber": "urn:uuid:b1d647c9-e4c5-4d8f-be8a-1751ac3ad4d5",
+ "specVersion": "1.3",
+ "version": 1
+}
\ No newline at end of file
diff --git a/tests/fixtures/json/1.3/bom_with_full_metadata.json b/tests/fixtures/json/1.3/bom_with_full_metadata.json
index ca5ea2fa..7b217e0d 100644
--- a/tests/fixtures/json/1.3/bom_with_full_metadata.json
+++ b/tests/fixtures/json/1.3/bom_with_full_metadata.json
@@ -25,10 +25,247 @@
}
],
"component": {
- "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
"type": "library",
- "name": "cyclonedx-python-lib",
- "version": "1.0.0"
+ "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
+ "supplier": {
+ "name": "CycloneDX",
+ "url": [
+ "https://cyclonedx.org"
+ ],
+ "contact": [
+ {
+ "name": "Paul Horton",
+ "email": "paul.horton@owasp.org"
+ },
+ {
+ "name": "A N Other",
+ "email": "someone@somewhere.tld",
+ "phone": "+44 (0)1234 567890"
+ }
+ ]
+ },
+ "author": "Test Author",
+ "publisher": "CycloneDX",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "description": "This component is awesome",
+ "scope": "required",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "copyright": "Apache 2.0 baby!",
+ "cpe": "cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*",
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz",
+ "swid": {
+ "tagId": "swidgen-242eb18a-503e-ca37-393b-cf156ef09691_9.1.1",
+ "name": "Test Application",
+ "version": "3.4.5",
+ "text": {
+ "contentType": "text/xml",
+ "encoding": "base64",
+ "content": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg=="
+ }
+ },
+ "pedigree": {
+ "ancestors": [
+ {
+ "type": "library",
+ "bom-ref": "ccc8d7ee-4b9c-4750-aee0-a72585152291",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "8a3893b3-9923-4adb-a1d3-47456636ba0a",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools?extension=tar.gz"
+ }
+ ],
+ "descendants": [
+ {
+ "type": "library",
+ "bom-ref": "28b2d8ce-def0-446f-a221-58dee0b44acc",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "555ca729-93c6-48f3-956e-bdaa4a2f0bfa",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "variants": [
+ {
+ "type": "library",
+ "bom-ref": "e7abdcca-5ba2-4f29-b2cf-b1e1ef788e66",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "library",
+ "bom-ref": "ded1d73e-1fca-4302-b520-f1bc53979958",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ }
+ ],
+ "commits": [
+ {
+ "uid": "a-random-uid",
+ "message": "A commit message"
+ }
+ ],
+ "patches": [
+ {
+ "type": "backport"
+ }
+ ],
+ "notes": "Some notes here please"
+ },
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ],
+ "components": [
+ {
+ "type": "library",
+ "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "evidence": {
+ "copyright": [
+ {
+ "text": "Commercial"
+ },
+ {
+ "text": "Commercial 2"
+ }
+ ]
+ },
+ "properties": [
+ {
+ "name": "key1",
+ "value": "val1"
+ },
+ {
+ "name": "key2",
+ "value": "val2"
+ }
+ ]
},
"manufacture": {
"name": "CycloneDX",
diff --git a/tests/fixtures/json/1.4/bom_issue_275_components.json b/tests/fixtures/json/1.4/bom_issue_275_components.json
new file mode 100644
index 00000000..bd9667dd
--- /dev/null
+++ b/tests/fixtures/json/1.4/bom_issue_275_components.json
@@ -0,0 +1,98 @@
+{
+ "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
+ "bomFormat": "CycloneDX",
+ "components": [
+ {
+ "bom-ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
+ "name": "comp_a",
+ "type": "library",
+ "version": "1.0.0"
+ },
+ {
+ "bom-ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857",
+ "components": [
+ {
+ "bom-ref": "cd3e9c95-9d41-49e7-9924-8cf0465ae789",
+ "name": "comp_c",
+ "type": "library",
+ "version": "1.0.0"
+ }
+ ],
+ "name": "comp_b",
+ "type": "library",
+ "version": "1.0.0"
+ }
+ ],
+ "dependencies": [
+ {
+ "dependsOn": [
+ "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda",
+ "0b049d09-64c0-4490-a0f5-c84d9aacf857"
+ ],
+ "ref": "be2c6502-7e9a-47db-9a66-e34f729810a3"
+ },
+ {
+ "dependsOn": [],
+ "ref": "17e3b199-dc0b-42ef-bfdd-1fa81a1e3eda"
+ },
+ {
+ "dependsOn": [
+ "cd3e9c95-9d41-49e7-9924-8cf0465ae789"
+ ],
+ "ref": "0b049d09-64c0-4490-a0f5-c84d9aacf857"
+ }
+ ],
+ "metadata": {
+ "component": {
+ "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3",
+ "name": "app",
+ "type": "library",
+ "version": "1.0.0"
+ },
+ "timestamp": "2022-07-27T10:06:17.101289+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.github.io/cyclonedx-python-lib/"
+ },
+ {
+ "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://cyclonedx.org"
+ }
+ ],
+ "name": "cyclonedx-python-lib",
+ "vendor": "CycloneDX",
+ "version": "0.11.0"
+ }
+ ]
+ },
+ "serialNumber": "urn:uuid:b1d647c9-e4c5-4d8f-be8a-1751ac3ad4d5",
+ "specVersion": "1.4",
+ "version": 1
+}
\ No newline at end of file
diff --git a/tests/fixtures/json/1.4/bom_with_full_metadata.json b/tests/fixtures/json/1.4/bom_with_full_metadata.json
index 3563916f..5d4aea74 100644
--- a/tests/fixtures/json/1.4/bom_with_full_metadata.json
+++ b/tests/fixtures/json/1.4/bom_with_full_metadata.json
@@ -59,10 +59,304 @@
}
],
"component": {
- "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3",
"type": "library",
- "name": "cyclonedx-python-lib",
- "version": "1.0.0"
+ "bom-ref": "be2c6502-7e9a-47db-9a66-e34f729810a3",
+ "supplier": {
+ "name": "CycloneDX",
+ "url": [
+ "https://cyclonedx.org"
+ ],
+ "contact": [
+ {
+ "name": "Paul Horton",
+ "email": "paul.horton@owasp.org"
+ },
+ {
+ "name": "A N Other",
+ "email": "someone@somewhere.tld",
+ "phone": "+44 (0)1234 567890"
+ }
+ ]
+ },
+ "author": "Test Author",
+ "publisher": "CycloneDX",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "description": "This component is awesome",
+ "scope": "required",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "copyright": "Apache 2.0 baby!",
+ "cpe": "cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*",
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz",
+ "swid": {
+ "tagId": "swidgen-242eb18a-503e-ca37-393b-cf156ef09691_9.1.1",
+ "name": "Test Application",
+ "version": "3.4.5",
+ "text": {
+ "contentType": "text/xml",
+ "encoding": "base64",
+ "content": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg=="
+ }
+ },
+ "pedigree": {
+ "ancestors": [
+ {
+ "type": "library",
+ "bom-ref": "ccc8d7ee-4b9c-4750-aee0-a72585152291",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "8a3893b3-9923-4adb-a1d3-47456636ba0a",
+ "author": "Test Author",
+ "name": "setuptools",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools?extension=tar.gz"
+ }
+ ],
+ "descendants": [
+ {
+ "type": "library",
+ "bom-ref": "28b2d8ce-def0-446f-a221-58dee0b44acc",
+ "author": "Test Author",
+ "name": "setuptools",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "555ca729-93c6-48f3-956e-bdaa4a2f0bfa",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "variants": [
+ {
+ "type": "library",
+ "bom-ref": "e7abdcca-5ba2-4f29-b2cf-b1e1ef788e66",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "library",
+ "bom-ref": "ded1d73e-1fca-4302-b520-f1bc53979958",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ }
+ ],
+ "commits": [
+ {
+ "uid": "a-random-uid",
+ "message": "A commit message"
+ }
+ ],
+ "patches": [
+ {
+ "type": "backport"
+ }
+ ],
+ "notes": "Some notes here please"
+ },
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ],
+ "components": [
+ {
+ "type": "library",
+ "bom-ref": "pkg:pypi/setuptools@50.3.2?extension=tar.gz",
+ "author": "Test Author",
+ "name": "setuptools",
+ "version": "50.3.2",
+ "licenses": [
+ {
+ "expression": "MIT License"
+ }
+ ],
+ "purl": "pkg:pypi/setuptools@50.3.2?extension=tar.gz"
+ },
+ {
+ "type": "library",
+ "bom-ref": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "name": "toml",
+ "version": "0.10.2",
+ "purl": "pkg:pypi/toml@0.10.2?extension=tar.gz",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ],
+ "externalReferences": [
+ {
+ "type": "distribution",
+ "url": "https://cyclonedx.org",
+ "comment": "No comment",
+ "hashes": [
+ {
+ "alg": "SHA-256",
+ "content": "806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "evidence": {
+ "copyright": [
+ {
+ "text": "Commercial"
+ },
+ {
+ "text": "Commercial 2"
+ }
+ ]
+ },
+ "releaseNotes": {
+ "type": "major",
+ "title": "Release Notes Title",
+ "featuredImage": "https://cyclonedx.org/theme/assets/images/CycloneDX-Twitter-Card.png",
+ "socialImage": "https://cyclonedx.org/cyclonedx-icon.png",
+ "description": "This release is a test release",
+ "timestamp": "2021-12-31T10:00:00+00:00",
+ "aliases": [
+ "First Test Release"
+ ],
+ "tags": [
+ "test",
+ "alpha"
+ ],
+ "resolves": [
+ {
+ "type": "security",
+ "id": "CVE-2021-44228",
+ "name": "Apache Log3Shell",
+ "description": "Apache Log4j2 2.0-beta9 through 2.12.1 and 2.13.0 through 2.15.0 JNDI features...",
+ "source": {
+ "name": "NVD",
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
+ },
+ "references": [
+ "https://logging.apache.org/log4j/2.x/security.html",
+ "https://central.sonatype.org/news/20211213_log4shell_help"
+ ]
+ }
+ ],
+ "notes": [
+ {
+ "locale": "en-GB",
+ "text": {
+ "contentType": "text/plain; charset=UTF-8",
+ "encoding": "base64",
+ "content": "U29tZSBzaW1wbGUgcGxhaW4gdGV4dA=="
+ }
+ },
+ {
+ "locale": "en-US",
+ "text": {
+ "contentType": "text/plain; charset=UTF-8",
+ "encoding": "base64",
+ "content": "U29tZSBzaW1wbGUgcGxhaW4gdGV4dA=="
+ }
+ }
+ ],
+ "properties": [
+ {
+ "name": "key1",
+ "value": "val1"
+ },
+ {
+ "name": "key2",
+ "value": "val2"
+ }
+ ]
+ },
+ "properties": [
+ {
+ "name": "key1",
+ "value": "val1"
+ },
+ {
+ "name": "key2",
+ "value": "val2"
+ }
+ ]
},
"manufacture": {
"name": "CycloneDX",
diff --git a/tests/fixtures/xml/1.0/bom_issue_275_components.xml b/tests/fixtures/xml/1.0/bom_issue_275_components.xml
new file mode 100644
index 00000000..c8a909c2
--- /dev/null
+++ b/tests/fixtures/xml/1.0/bom_issue_275_components.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ comp_a
+ 1.0.0
+ false
+
+
+ comp_b
+ 1.0.0
+ false
+
+
+ comp_c
+ 1.0.0
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.1/bom_issue_275_components.xml b/tests/fixtures/xml/1.1/bom_issue_275_components.xml
new file mode 100644
index 00000000..579eca0f
--- /dev/null
+++ b/tests/fixtures/xml/1.1/bom_issue_275_components.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ comp_a
+ 1.0.0
+
+
+ comp_b
+ 1.0.0
+
+
+ comp_c
+ 1.0.0
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.1/bom_setuptools_complete.xml b/tests/fixtures/xml/1.1/bom_setuptools_complete.xml
index 0a116a29..2a72edfb 100644
--- a/tests/fixtures/xml/1.1/bom_setuptools_complete.xml
+++ b/tests/fixtures/xml/1.1/bom_setuptools_complete.xml
@@ -1,7 +1,7 @@
-
+
CycloneDX
setuptools
50.3.2
diff --git a/tests/fixtures/xml/1.2/bom_issue_275_components.xml b/tests/fixtures/xml/1.2/bom_issue_275_components.xml
new file mode 100644
index 00000000..f707c02c
--- /dev/null
+++ b/tests/fixtures/xml/1.2/bom_issue_275_components.xml
@@ -0,0 +1,43 @@
+
+
+
+ 2021-09-01T10:50:42.051979+00:00
+
+
+ CycloneDX
+ cyclonedx-python-lib
+ VERSION
+
+
+
+ app
+ 1.0.0
+
+
+
+
+ comp_a
+ 1.0.0
+
+
+ comp_b
+ 1.0.0
+
+
+ comp_c
+ 1.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.2/bom_setuptools_complete.xml b/tests/fixtures/xml/1.2/bom_setuptools_complete.xml
index 8f86e9ee..7d83187f 100644
--- a/tests/fixtures/xml/1.2/bom_setuptools_complete.xml
+++ b/tests/fixtures/xml/1.2/bom_setuptools_complete.xml
@@ -11,7 +11,7 @@
-
+
CycloneDX
https://cyclonedx.org
@@ -156,6 +156,6 @@
-
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.2/bom_with_full_metadata.xml b/tests/fixtures/xml/1.2/bom_with_full_metadata.xml
index 900b1663..8fe33131 100644
--- a/tests/fixtures/xml/1.2/bom_with_full_metadata.xml
+++ b/tests/fixtures/xml/1.2/bom_with_full_metadata.xml
@@ -21,8 +21,147 @@
- cyclonedx-python-lib
- 1.0.0
+
+ CycloneDX
+ https://cyclonedx.org
+
+ Paul Horton
+ paul.horton@owasp.org
+
+
+ A N Other
+ someone@somewhere.tld
+ +44 (0)1234 567890
+
+
+ Test Author
+ CycloneDX
+ setuptools
+ 50.3.2
+ This component is awesome
+ required
+
+ MIT License
+
+ Apache 2.0 baby!
+ cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg==
+
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+ Test Author
+ setuptools
+
+
+ MIT License
+
+ pkg:pypi/setuptools?extension=tar.gz
+
+
+
+
+ Test Author
+ setuptools
+
+
+ MIT License
+
+ pkg:pypi/setuptools?extension=tar.gz
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+
+
+
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+
+
+ a-random-uid
+ A commit message
+
+
+
+
+
+ Some notes here please
+
+
+
+ https://cyclonedx.org
+ No comment
+
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+
+
+
CycloneDX
diff --git a/tests/fixtures/xml/1.3/bom_issue_275_components.xml b/tests/fixtures/xml/1.3/bom_issue_275_components.xml
new file mode 100644
index 00000000..25d31646
--- /dev/null
+++ b/tests/fixtures/xml/1.3/bom_issue_275_components.xml
@@ -0,0 +1,43 @@
+
+
+
+ 2021-09-01T10:50:42.051979+00:00
+
+
+ CycloneDX
+ cyclonedx-python-lib
+ VERSION
+
+
+
+ app
+ 1.0.0
+
+
+
+
+ comp_a
+ 1.0.0
+
+
+ comp_b
+ 1.0.0
+
+
+ comp_c
+ 1.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.3/bom_setuptools_complete.xml b/tests/fixtures/xml/1.3/bom_setuptools_complete.xml
index 9d318bd1..8a2e1274 100644
--- a/tests/fixtures/xml/1.3/bom_setuptools_complete.xml
+++ b/tests/fixtures/xml/1.3/bom_setuptools_complete.xml
@@ -11,7 +11,7 @@
-
+
CycloneDX
https://cyclonedx.org
@@ -178,6 +178,6 @@
-
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.3/bom_with_full_metadata.xml b/tests/fixtures/xml/1.3/bom_with_full_metadata.xml
index 6c72aea7..e1b0b356 100644
--- a/tests/fixtures/xml/1.3/bom_with_full_metadata.xml
+++ b/tests/fixtures/xml/1.3/bom_with_full_metadata.xml
@@ -21,8 +21,169 @@
- cyclonedx-python-lib
- 1.0.0
+
+ CycloneDX
+ https://cyclonedx.org
+
+ Paul Horton
+ paul.horton@owasp.org
+
+
+ A N Other
+ someone@somewhere.tld
+ +44 (0)1234 567890
+
+
+ Test Author
+ CycloneDX
+ setuptools
+ 50.3.2
+ This component is awesome
+ required
+
+ MIT License
+
+ Apache 2.0 baby!
+ cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg==
+
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+ Test Author
+ setuptools
+
+
+ MIT License
+
+ pkg:pypi/setuptools?extension=tar.gz
+
+
+
+
+ Test Author
+ setuptools
+
+
+ MIT License
+
+ pkg:pypi/setuptools?extension=tar.gz
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+
+
+ a-random-uid
+ A commit message
+
+
+
+
+
+ Some notes here please
+
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+ val1
+ val2
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+
+
+
+ Commercial
+ Commercial 2
+
+
CycloneDX
diff --git a/tests/fixtures/xml/1.4/bom_issue_275_components.xml b/tests/fixtures/xml/1.4/bom_issue_275_components.xml
new file mode 100644
index 00000000..67e58b1e
--- /dev/null
+++ b/tests/fixtures/xml/1.4/bom_issue_275_components.xml
@@ -0,0 +1,69 @@
+
+
+
+ 2021-09-01T10:50:42.051979+00:00
+
+
+ CycloneDX
+ cyclonedx-python-lib
+ VERSION
+
+
+ https://github.com/CycloneDX/cyclonedx-python-lib/actions
+
+
+ https://pypi.org/project/cyclonedx-python-lib/
+
+
+ https://cyclonedx.github.io/cyclonedx-python-lib/
+
+
+ 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://cyclonedx.org
+
+
+
+
+
+ app
+ 1.0.0
+
+
+
+
+ comp_a
+ 1.0.0
+
+
+ comp_b
+ 1.0.0
+
+
+ comp_c
+ 1.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.4/bom_setuptools_complete.xml b/tests/fixtures/xml/1.4/bom_setuptools_complete.xml
index 41bbc6e4..a90e3108 100644
--- a/tests/fixtures/xml/1.4/bom_setuptools_complete.xml
+++ b/tests/fixtures/xml/1.4/bom_setuptools_complete.xml
@@ -37,7 +37,7 @@
-
+
CycloneDX
https://cyclonedx.org
@@ -246,6 +246,6 @@
-
+
\ No newline at end of file
diff --git a/tests/fixtures/xml/1.4/bom_with_full_metadata.xml b/tests/fixtures/xml/1.4/bom_with_full_metadata.xml
index 39cd8b8a..1d8022af 100644
--- a/tests/fixtures/xml/1.4/bom_with_full_metadata.xml
+++ b/tests/fixtures/xml/1.4/bom_with_full_metadata.xml
@@ -47,8 +47,211 @@
- cyclonedx-python-lib
- 1.0.0
+
+ CycloneDX
+ https://cyclonedx.org
+
+ Paul Horton
+ paul.horton@owasp.org
+
+
+ A N Other
+ someone@somewhere.tld
+ +44 (0)1234 567890
+
+
+ Test Author
+ CycloneDX
+ setuptools
+ 50.3.2
+ This component is awesome
+ required
+
+ MIT License
+
+ Apache 2.0 baby!
+ cpe:2.3:a:python:setuptools:50.3.2:*:*:*:*:*:*:*
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg==
+
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+ Test Author
+ setuptools
+
+ MIT License
+
+ pkg:pypi/setuptools?extension=tar.gz
+
+
+
+
+ Test Author
+ setuptools
+
+ MIT License
+
+ pkg:pypi/setuptools?extension=tar.gz
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+
+
+ a-random-uid
+ A commit message
+
+
+
+
+
+ Some notes here please
+
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+ val1
+ val2
+
+
+
+ Test Author
+ setuptools
+ 50.3.2
+
+ MIT License
+
+ pkg:pypi/setuptools@50.3.2?extension=tar.gz
+
+
+ toml
+ 0.10.2
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+ pkg:pypi/toml@0.10.2?extension=tar.gz
+
+
+ https://cyclonedx.org
+ No comment
+
+ 806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b
+
+
+
+
+
+
+
+ Commercial
+ Commercial 2
+
+
+
+ major
+ Release Notes Title
+ https://cyclonedx.org/theme/assets/images/CycloneDX-Twitter-Card.png
+ https://cyclonedx.org/cyclonedx-icon.png
+ This release is a test release
+ 2021-12-31T10:00:00+00:00
+
+ First Test Release
+
+
+ test
+ alpha
+
+
+
+ CVE-2021-44228
+ Apache Log3Shell
+ Apache Log4j2 2.0-beta9 through 2.12.1 and 2.13.0 through 2.15.0 JNDI features...
+
+ NVD
+ https://nvd.nist.gov/vuln/detail/CVE-2021-44228
+
+
+ https://logging.apache.org/log4j/2.x/security.html
+ https://central.sonatype.org/news/20211213_log4shell_help
+
+
+
+
+
+ en-GB
+ U29tZSBzaW1wbGUgcGxhaW4gdGV4dA==
+
+
+ en-US
+ U29tZSBzaW1wbGUgcGxhaW4gdGV4dA==
+
+
+
+ val1
+ val2
+
+
CycloneDX
diff --git a/tests/test_component.py b/tests/test_component.py
index 2a0c676e..f9d1648e 100644
--- a/tests/test_component.py
+++ b/tests/test_component.py
@@ -20,14 +20,13 @@
from os.path import dirname, join
from unittest import TestCase
-from data import get_component_setuptools_simple, get_component_setuptools_simple_no_version
-
# See https://github.com/package-url/packageurl-python/issues/65
from packageurl import PackageURL # type: ignore
from cyclonedx.model import sha1sum
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component
+from data import get_component_setuptools_simple, get_component_setuptools_simple_no_version
FIXTURES_DIRECTORY = 'fixtures/xml/1.4'
diff --git a/tests/test_model_bom.py b/tests/test_model_bom.py
index 868baf48..6636d863 100644
--- a/tests/test_model_bom.py
+++ b/tests/test_model_bom.py
@@ -19,11 +19,10 @@
from unittest import TestCase
-from data import get_bom_with_component_setuptools_with_vulnerability
-
from cyclonedx.model import License, LicenseChoice, OrganizationalContact, OrganizationalEntity, Property
from cyclonedx.model.bom import Bom, BomMetaData, ThisTool, Tool
from cyclonedx.model.component import Component, ComponentType
+from data import get_bom_for_issue_275_components, get_bom_with_component_setuptools_with_vulnerability
class TestBomMetaData(TestCase):
@@ -116,3 +115,15 @@ def test_empty_bom(self) -> None:
def test_bom_with_vulnerabilities(self) -> None:
bom = get_bom_with_component_setuptools_with_vulnerability()
self.assertTrue(bom.has_vulnerabilities())
+
+ def test_bom_nested_components_issue_275(self) -> None:
+ bom = get_bom_for_issue_275_components()
+ self.assertIsInstance(bom.metadata.component, Component)
+ self.assertEqual(2, len(bom.components))
+ bom.validate()
+
+ # def test_bom_nested_services_issue_275(self) -> None:
+ # bom = get_bom_for_issue_275_services()
+ # self.assertIsInstance(bom.metadata.component, Component)
+ # self.assertEqual(2, len(bom.services))
+ # bom.validate()
diff --git a/tests/test_model_component.py b/tests/test_model_component.py
index fb695d53..78e6c4c0 100644
--- a/tests/test_model_component.py
+++ b/tests/test_model_component.py
@@ -22,17 +22,6 @@
from unittest import TestCase
from unittest.mock import Mock, patch
-from data import (
- get_component_setuptools_simple,
- get_component_setuptools_simple_no_version,
- get_component_toml_with_hashes_with_references,
- get_issue_1,
- get_issue_2,
- get_pedigree_1,
- get_swid_1,
- get_swid_2,
-)
-
from cyclonedx.exception.model import NoPropertiesProvidedException
from cyclonedx.model import (
AttachedText,
@@ -55,6 +44,16 @@
Pedigree,
)
from cyclonedx.model.issue import IssueClassification, IssueType
+from data import (
+ get_component_setuptools_simple,
+ get_component_setuptools_simple_no_version,
+ get_component_toml_with_hashes_with_references,
+ get_issue_1,
+ get_issue_2,
+ get_pedigree_1,
+ get_swid_1,
+ get_swid_2,
+)
from tests.data import reorder
@@ -131,7 +130,8 @@ def test_empty_basic_component(self, mock_uuid: Mock) -> None:
self.assertSetEqual(c.external_references, set())
self.assertFalse(c.properties)
self.assertIsNone(c.release_notes)
-
+ self.assertEqual(0, len(c.components))
+ self.assertEqual(0, len(c.get_all_nested_components()))
self.assertEqual(len(c.get_vulnerabilities()), 0)
@patch('cyclonedx.model.bom_ref.uuid4', return_value='6f266d1c-760f-4552-ae3b-41a9b74232fa')
@@ -318,6 +318,28 @@ def test_sort(self) -> None:
expected_components = reorder(components, expected_order)
self.assertListEqual(sorted_components, expected_components)
+ def test_nested_components_1(self) -> None:
+ comp_b = Component(name="comp_b", version="1.0.0")
+ comp_c = Component(name="comp_c", version="1.0.0")
+ comp_b.components.add(comp_c)
+ comp_b.dependencies.add(comp_c.bom_ref)
+
+ self.assertEqual(1, len(comp_b.components))
+ self.assertEqual(2, len(comp_b.get_all_nested_components(include_self=True)))
+ self.assertEqual(1, len(comp_b.get_all_nested_components(include_self=False)))
+
+ def test_nested_components_2(self) -> None:
+ comp_a = Component(name="comp_a", version="1.2.3")
+ comp_b = Component(name="comp_b", version="1.0.0")
+ comp_c = Component(name="comp_c", version="1.0.0")
+ comp_b.components.add(comp_c)
+ comp_b.dependencies.add(comp_c.bom_ref)
+ comp_b.components.add(comp_a)
+
+ self.assertEqual(2, len(comp_b.components))
+ self.assertEqual(3, len(comp_b.get_all_nested_components(include_self=True)))
+ self.assertEqual(2, len(comp_b.get_all_nested_components(include_self=False)))
+
class TestModelComponentEvidence(TestCase):
diff --git a/tests/test_model_issue.py b/tests/test_model_issue.py
index 96bca6bd..c98cbf87 100644
--- a/tests/test_model_issue.py
+++ b/tests/test_model_issue.py
@@ -19,11 +19,10 @@
from unittest import TestCase
-from data import get_issue_1, get_issue_2
-
from cyclonedx.exception.model import NoPropertiesProvidedException
from cyclonedx.model import XsUri
from cyclonedx.model.issue import IssueClassification, IssueType, IssueTypeSource
+from data import get_issue_1, get_issue_2
from tests.data import reorder
diff --git a/tests/test_output_json.py b/tests/test_output_json.py
index 24469cbe..18ffbb89 100644
--- a/tests/test_output_json.py
+++ b/tests/test_output_json.py
@@ -20,10 +20,15 @@
from os.path import dirname, join
from unittest.mock import Mock, patch
+from cyclonedx.exception.model import UnknownComponentDependencyException
+from cyclonedx.exception.output import FormatNotSupportedException
+from cyclonedx.model.bom import Bom
+from cyclonedx.output import OutputFormat, SchemaVersion, get_instance
from data import (
MOCK_UUID_1,
MOCK_UUID_2,
MOCK_UUID_3,
+ MOCK_UUID_6,
TEST_UUIDS,
get_bom_just_complete_metadata,
get_bom_with_component_setuptools_basic,
@@ -41,12 +46,8 @@
get_bom_with_services_complex,
get_bom_with_services_simple,
)
-
-from cyclonedx.exception.model import UnknownComponentDependencyException
-from cyclonedx.exception.output import FormatNotSupportedException
-from cyclonedx.model.bom import Bom
-from cyclonedx.output import OutputFormat, SchemaVersion, get_instance
from tests.base import BaseJsonTestCase
+from tests.data import get_bom_for_issue_275_components
class TestOutputJson(BaseJsonTestCase):
@@ -115,24 +116,30 @@ def test_simple_bom_v1_2_with_cpe(self) -> None:
fixture='bom_setuptools_with_cpe.json'
)
- def test_bom_v1_4_full_component(self) -> None:
+ @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_6)
+ def test_bom_v1_4_full_component(self, mock: Mock) -> None:
self.maxDiff = None
self._validate_json_bom(
bom=get_bom_with_component_setuptools_complete(), schema_version=SchemaVersion.V1_4,
fixture='bom_setuptools_complete.json'
)
+ mock.assert_called()
- def test_bom_v1_3_full_component(self) -> None:
+ @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_6)
+ def test_bom_v1_3_full_component(self, mock: Mock) -> None:
self._validate_json_bom(
bom=get_bom_with_component_setuptools_complete(), schema_version=SchemaVersion.V1_3,
fixture='bom_setuptools_complete.json'
)
+ mock.assert_called()
- def test_bom_v1_2_full_component(self) -> None:
+ @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_6)
+ def test_bom_v1_2_full_component(self, mock: Mock) -> None:
self._validate_json_bom(
bom=get_bom_with_component_setuptools_complete(), schema_version=SchemaVersion.V1_2,
fixture='bom_setuptools_complete.json'
)
+ mock.assert_called()
def test_bom_v1_4_component_hashes_external_references(self) -> None:
self._validate_json_bom(
@@ -197,99 +204,111 @@ def test_bom_v1_3_component_with_vulnerability(self) -> None:
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_1)
def test_bom_v1_4_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_4,
- fixture='bom_with_full_metadata.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_with_full_metadata.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_2)
def test_bom_v1_3_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_3,
- fixture='bom_with_full_metadata.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_with_full_metadata.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_3)
def test_bom_v1_2_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_2,
- fixture='bom_with_full_metadata.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_with_full_metadata.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_4_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_4,
- fixture='bom_services_simple.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_services_simple.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_3_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_3,
- fixture='bom_services_simple.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_services_simple.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_2_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_2,
- fixture='bom_services_simple.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_services_simple.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_4_services_complex(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_4,
- fixture='bom_services_complex.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_services_complex.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_3_services_complex(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_3,
- fixture='bom_services_complex.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_services_complex.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_2_services_complex(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_2,
- fixture='bom_services_complex.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_services_complex.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_4_services_nested(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_4,
- fixture='bom_services_nested.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_services_nested.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_3_services_nested(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_3,
- fixture='bom_services_nested.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_services_nested.json'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_2_services_nested(self, mock_uuid: Mock) -> None:
- self._validate_json_bom(
- bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_2,
- fixture='bom_services_nested.json'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_json_bom(
+ bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_services_nested.json'
+ )
+ mock_uuid.assert_called()
def test_bom_v1_4_dependencies(self) -> None:
self._validate_json_bom(
@@ -334,6 +353,24 @@ def test_bom_v1_4_dependencies_invalid(self) -> None:
fixture='bom_dependencies.json'
)
+ def test_bom_v1_4_issue_275_components(self) -> None:
+ self._validate_json_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_issue_275_components.json'
+ )
+
+ def test_bom_v1_3_issue_275_components(self) -> None:
+ self._validate_json_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_issue_275_components.json'
+ )
+
+ def test_bom_v1_2_issue_275_components(self) -> None:
+ self._validate_json_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_issue_275_components.json'
+ )
+
# Helper methods
def _validate_json_bom(self, bom: Bom, schema_version: SchemaVersion, fixture: str) -> None:
outputter = get_instance(bom=bom, output_format=OutputFormat.JSON, schema_version=schema_version)
diff --git a/tests/test_output_xml.py b/tests/test_output_xml.py
index deffe5e8..487f15e7 100644
--- a/tests/test_output_xml.py
+++ b/tests/test_output_xml.py
@@ -20,8 +20,13 @@
from os.path import dirname, join
from unittest.mock import Mock, patch
+from cyclonedx.exception.model import UnknownComponentDependencyException
+from cyclonedx.model.bom import Bom
+from cyclonedx.output import SchemaVersion, get_instance
from data import (
MOCK_UUID_1,
+ MOCK_UUID_2,
+ MOCK_UUID_3,
MOCK_UUID_4,
MOCK_UUID_5,
MOCK_UUID_6,
@@ -42,11 +47,8 @@
get_bom_with_services_complex,
get_bom_with_services_simple,
)
-
-from cyclonedx.exception.model import UnknownComponentDependencyException
-from cyclonedx.model.bom import Bom
-from cyclonedx.output import SchemaVersion, get_instance
from tests.base import BaseXmlTestCase
+from tests.data import get_bom_for_issue_275_components
class TestOutputXml(BaseXmlTestCase):
@@ -141,29 +143,37 @@ def test_simple_bom_v1_0_with_cpe(self) -> None:
fixture='bom_setuptools_with_cpe.xml'
)
- def test_bom_v1_4_full_component(self) -> None:
+ @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_4)
+ def test_bom_v1_4_full_component(self, mock_uuid: Mock) -> None:
self._validate_xml_bom(
bom=get_bom_with_component_setuptools_complete(), schema_version=SchemaVersion.V1_4,
fixture='bom_setuptools_complete.xml'
)
+ mock_uuid.assert_called()
- def test_bom_v1_3_full_component(self) -> None:
+ @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_3)
+ def test_bom_v1_3_full_component(self, mock_uuid: Mock) -> None:
self._validate_xml_bom(
bom=get_bom_with_component_setuptools_complete(), schema_version=SchemaVersion.V1_3,
fixture='bom_setuptools_complete.xml'
)
+ mock_uuid.assert_called()
- def test_bom_v1_2_full_component(self) -> None:
+ @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_2)
+ def test_bom_v1_2_full_component(self, mock_uuid: Mock) -> None:
self._validate_xml_bom(
bom=get_bom_with_component_setuptools_complete(), schema_version=SchemaVersion.V1_2,
fixture='bom_setuptools_complete.xml'
)
+ mock_uuid.assert_called()
- def test_bom_v1_1_full_component(self) -> None:
+ @patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_1)
+ def test_bom_v1_1_full_component(self, mock_uuid: Mock) -> None:
self._validate_xml_bom(
bom=get_bom_with_component_setuptools_complete(), schema_version=SchemaVersion.V1_1,
fixture='bom_setuptools_complete.xml'
)
+ mock_uuid.assert_called()
def test_bom_v1_0_full_component(self) -> None:
self._validate_xml_bom(
@@ -275,139 +285,156 @@ def test_bom_v1_0_component_with_vulnerability(self) -> None:
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_6)
def test_bom_v1_4_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_4,
- fixture='bom_with_full_metadata.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_with_full_metadata.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_5)
def test_bom_v1_3_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_3,
- fixture='bom_with_full_metadata.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_with_full_metadata.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_4)
def test_bom_v1_2_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_2,
- fixture='bom_with_full_metadata.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_with_full_metadata.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_1)
def test_bom_v1_1_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_1,
- fixture='bom_empty.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_1,
+ fixture='bom_empty.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', return_value=MOCK_UUID_1)
def test_bom_v1_0_with_metadata_component(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_0,
- fixture='bom_empty.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_just_complete_metadata(), schema_version=SchemaVersion.V1_0,
+ fixture='bom_empty.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_4_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_4,
- fixture='bom_services_simple.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_services_simple.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_3_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_3,
- fixture='bom_services_simple.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_services_simple.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_2_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_2,
- fixture='bom_services_simple.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_services_simple.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_1_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_1,
- fixture='bom_empty.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_1,
+ fixture='bom_empty.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_0_services_simple(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_0,
- fixture='bom_empty.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_simple(), schema_version=SchemaVersion.V1_0,
+ fixture='bom_empty.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_4_services_complex(self, mock_uuid4: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_4,
- fixture='bom_services_complex.xml'
- )
- mock_uuid4.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_services_complex.xml'
+ )
+ mock_uuid4.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_3_services_complex(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_3,
- fixture='bom_services_complex.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_services_complex.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_2_services_complex(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_2,
- fixture='bom_services_complex.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_services_complex.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_1_services_complex(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_1,
- fixture='bom_empty.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_services_complex(), schema_version=SchemaVersion.V1_1,
+ fixture='bom_empty.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_4_services_nested(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_4,
- fixture='bom_services_nested.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_services_nested.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_3_services_nested(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_3,
- fixture='bom_services_nested.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_services_nested.xml'
+ )
+ mock_uuid.assert_called()
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=TEST_UUIDS)
def test_bom_v1_2_services_nested(self, mock_uuid: Mock) -> None:
- self._validate_xml_bom(
- bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_2,
- fixture='bom_services_nested.xml'
- )
- mock_uuid.assert_called()
+ with self.assertWarns(UserWarning):
+ self._validate_xml_bom(
+ bom=get_bom_with_nested_services(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_services_nested.xml'
+ )
+ mock_uuid.assert_called()
def test_bom_v1_4_dependencies(self) -> None:
self._validate_xml_bom(
@@ -458,6 +485,36 @@ def test_bom_v1_4_dependencies_invalid(self) -> None:
fixture='bom_dependencies.xml'
)
+ def test_bom_v1_4_issue_275_components(self) -> None:
+ self._validate_xml_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_4,
+ fixture='bom_issue_275_components.xml'
+ )
+
+ def test_bom_v1_3_issue_275_components(self) -> None:
+ self._validate_xml_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_3,
+ fixture='bom_issue_275_components.xml'
+ )
+
+ def test_bom_v1_2_issue_275_components(self) -> None:
+ self._validate_xml_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_2,
+ fixture='bom_issue_275_components.xml'
+ )
+
+ def test_bom_v1_1_issue_275_components(self) -> None:
+ self._validate_xml_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_1,
+ fixture='bom_issue_275_components.xml'
+ )
+
+ def test_bom_v1_0_issue_275_components(self) -> None:
+ self._validate_xml_bom(
+ bom=get_bom_for_issue_275_components(), schema_version=SchemaVersion.V1_0,
+ fixture='bom_issue_275_components.xml'
+ )
+
# Helper methods
def _validate_xml_bom(self, bom: Bom, schema_version: SchemaVersion, fixture: str) -> None:
outputter = get_instance(bom=bom, schema_version=schema_version)