updates
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Python library for CycloneDX
|
||||
"""
|
||||
|
||||
# !! version is managed by semantic_release
|
||||
# do not use typing here, or else `semantic_release` might have issues finding the variable
|
||||
__version__ = "9.1.0" # noqa:Q000
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
|
||||
Everything might change without any notice.
|
||||
"""
|
||||
|
||||
# THIS FILE IS INTENDED TO BE EMPTY.
|
||||
# Put symbols in own modules/packages, not in this file!
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,51 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
|
||||
Everything might change without any notice.
|
||||
"""
|
||||
|
||||
from typing import Literal, Optional, Union, overload
|
||||
|
||||
from ..model.bom_ref import BomRef
|
||||
|
||||
|
||||
@overload
|
||||
def bom_ref_from_str(bom_ref: BomRef, optional: bool = ...) -> BomRef:
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
@overload
|
||||
def bom_ref_from_str(bom_ref: Optional[str], optional: Literal[False] = False) -> BomRef:
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
@overload
|
||||
def bom_ref_from_str(bom_ref: Optional[str], optional: Literal[True] = ...) -> Optional[BomRef]:
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
def bom_ref_from_str(bom_ref: Optional[Union[str, BomRef]], optional: bool = False) -> Optional[BomRef]:
|
||||
if isinstance(bom_ref, BomRef):
|
||||
return bom_ref
|
||||
if bom_ref:
|
||||
return BomRef(value=str(bom_ref))
|
||||
return None \
|
||||
if optional \
|
||||
else BomRef()
|
||||
@@ -0,0 +1,82 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
|
||||
Everything might change without any notice.
|
||||
"""
|
||||
|
||||
from itertools import zip_longest
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from packageurl import PackageURL
|
||||
|
||||
|
||||
class ComparableTuple(Tuple[Optional[Any], ...]):
|
||||
"""
|
||||
Allows comparison of tuples, allowing for None values.
|
||||
"""
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
for s, o in zip_longest(self, other):
|
||||
if s == o:
|
||||
continue
|
||||
# the idea is to have any consistent order, not necessarily "natural" order.
|
||||
if s is None:
|
||||
return False
|
||||
if o is None:
|
||||
return True
|
||||
return bool(s < o)
|
||||
return False
|
||||
|
||||
def __gt__(self, other: Any) -> bool:
|
||||
for s, o in zip_longest(self, other):
|
||||
if s == o:
|
||||
continue
|
||||
# the idea is to have any consistent order, not necessarily "natural" order.
|
||||
if s is None:
|
||||
return True
|
||||
if o is None:
|
||||
return False
|
||||
return bool(s > o)
|
||||
return False
|
||||
|
||||
|
||||
class ComparableDict(ComparableTuple):
|
||||
"""
|
||||
Allows comparison of dictionaries, allowing for missing/None values.
|
||||
"""
|
||||
|
||||
def __new__(cls, d: Dict[Any, Any]) -> 'ComparableDict':
|
||||
return super(ComparableDict, cls).__new__(cls, sorted(d.items()))
|
||||
|
||||
|
||||
class ComparablePackageURL(ComparableTuple):
|
||||
"""
|
||||
Allows comparison of PackageURL, allowing for qualifiers.
|
||||
"""
|
||||
|
||||
def __new__(cls, p: 'PackageURL') -> 'ComparablePackageURL':
|
||||
return super(ComparablePackageURL, cls).__new__(cls, (
|
||||
p.type,
|
||||
p.namespace,
|
||||
p.version,
|
||||
ComparableDict(p.qualifiers) if isinstance(p.qualifiers, dict) else p.qualifiers,
|
||||
p.subpath
|
||||
))
|
||||
@@ -0,0 +1,43 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
|
||||
Everything might change without any notice.
|
||||
"""
|
||||
|
||||
|
||||
from hashlib import sha1
|
||||
|
||||
|
||||
def file_sha1sum(filename: str) -> str:
|
||||
"""
|
||||
Generate a SHA1 hash of the provided file.
|
||||
|
||||
Args:
|
||||
filename:
|
||||
Absolute path to file to hash as `str`
|
||||
|
||||
Returns:
|
||||
SHA-1 hash
|
||||
"""
|
||||
h = sha1() # nosec B303, B324
|
||||
with open(filename, 'rb') as f:
|
||||
for byte_block in iter(lambda: f.read(4096), b''):
|
||||
h.update(byte_block)
|
||||
return h.hexdigest()
|
||||
@@ -0,0 +1,29 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
|
||||
Everything might change without any notice.
|
||||
"""
|
||||
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
def get_now_utc() -> datetime:
|
||||
return datetime.now(tz=timezone.utc)
|
||||
@@ -0,0 +1,20 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
"""
|
||||
Builders used in this library.
|
||||
"""
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,83 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
"""Representation of this very python library."""
|
||||
|
||||
__all__ = ['this_component', 'this_tool', ]
|
||||
|
||||
from .. import __version__ as __ThisVersion # noqa: N812
|
||||
from ..model import ExternalReference, ExternalReferenceType, XsUri
|
||||
from ..model.component import Component, ComponentType
|
||||
from ..model.license import DisjunctiveLicense, LicenseAcknowledgement
|
||||
from ..model.tool import Tool
|
||||
|
||||
# !!! keep this file in sync with `pyproject.toml`
|
||||
|
||||
|
||||
def this_component() -> Component:
|
||||
"""Representation of this very python library as a :class:`Component`."""
|
||||
return Component(
|
||||
type=ComponentType.LIBRARY,
|
||||
group='CycloneDX',
|
||||
name='cyclonedx-python-lib',
|
||||
version=__ThisVersion or 'UNKNOWN',
|
||||
description='Python library for CycloneDX',
|
||||
licenses=(DisjunctiveLicense(id='Apache-2.0',
|
||||
acknowledgement=LicenseAcknowledgement.DECLARED),),
|
||||
external_references=(
|
||||
# let's assume this is not a fork
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.WEBSITE,
|
||||
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/#readme')
|
||||
),
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.DOCUMENTATION,
|
||||
url=XsUri('https://cyclonedx-python-library.readthedocs.io/')
|
||||
),
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.VCS,
|
||||
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib')
|
||||
),
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.BUILD_SYSTEM,
|
||||
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/actions')
|
||||
),
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.ISSUE_TRACKER,
|
||||
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/issues')
|
||||
),
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.LICENSE,
|
||||
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE')
|
||||
),
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.RELEASE_NOTES,
|
||||
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md')
|
||||
),
|
||||
# we cannot assert where the lib was fetched from, but we can give a hint
|
||||
ExternalReference(
|
||||
type=ExternalReferenceType.DISTRIBUTION,
|
||||
url=XsUri('https://pypi.org/project/cyclonedx-python-lib/')
|
||||
),
|
||||
),
|
||||
# to be extended...
|
||||
)
|
||||
|
||||
|
||||
def this_tool() -> Tool:
|
||||
"""Representation of this very python library as a :class:`Tool`."""
|
||||
return Tool.from_component(this_component())
|
||||
@@ -0,0 +1,33 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Exceptions that are specific to the CycloneDX library implementation.
|
||||
"""
|
||||
|
||||
|
||||
class CycloneDxException(Exception): # noqa: N818
|
||||
"""
|
||||
Root exception thrown by this library.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class MissingOptionalDependencyException(CycloneDxException): # noqa: N818
|
||||
"""Validation did not happen, due to missing dependencies."""
|
||||
pass
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,58 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Exceptions relating to specific conditions that occur when factoring a model.
|
||||
"""
|
||||
|
||||
from . import CycloneDxException
|
||||
|
||||
|
||||
class CycloneDxFactoryException(CycloneDxException):
|
||||
"""
|
||||
Base exception that covers all exceptions that may be thrown during model factoring..
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class LicenseChoiceFactoryException(CycloneDxFactoryException):
|
||||
"""
|
||||
Base exception that covers all LicenseChoiceFactory exceptions.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidSpdxLicenseException(LicenseChoiceFactoryException):
|
||||
"""
|
||||
Thrown when an invalid SPDX License is provided.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class LicenseFactoryException(CycloneDxFactoryException):
|
||||
"""
|
||||
Base exception that covers all LicenseFactory exceptions.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidLicenseExpressionException(LicenseFactoryException):
|
||||
"""
|
||||
Thrown when an invalid License expressions is provided.
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,133 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Exceptions relating to specific conditions that occur when modelling CycloneDX BOM.
|
||||
"""
|
||||
|
||||
from . import CycloneDxException
|
||||
|
||||
|
||||
class CycloneDxModelException(CycloneDxException):
|
||||
"""
|
||||
Base exception that covers all exceptions that may be thrown during model creation.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidLocaleTypeException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when the supplied locale does not conform to ISO-639 specification.
|
||||
|
||||
Good examples:
|
||||
- en
|
||||
- en-US
|
||||
- en-GB
|
||||
- fr
|
||||
- fr-CA
|
||||
|
||||
The language code MUST be lowercase. If the country code is specified, the country code MUST be upper case.
|
||||
The language code and country code MUST be separated by a minus sign.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidNistQuantumSecurityLevelException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when an invalid value is provided for an NIST Quantum Security Level
|
||||
as defined at https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/
|
||||
evaluation-criteria/security-(evaluation-criteria).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidOmniBorIdException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when a supplied value for an OmniBOR ID does not meet the format requirements
|
||||
as defined at https://www.iana.org/assignments/uri-schemes/prov/gitoid.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidRelatedCryptoMaterialSizeException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when the supplied size of a Related Crypto Material is negative.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidSwhidException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when a supplied value for an Swhid does not meet the format requirements
|
||||
as defined at https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidUriException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when a `str` is provided that needs to be a valid URI, but isn't.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class MutuallyExclusivePropertiesException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when mutually exclusive properties are provided.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class NoPropertiesProvidedException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when attempting to construct a model class and providing NO values (where all properites are defined as
|
||||
Optional, but at least one is required).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class UnknownComponentDependencyException(CycloneDxModelException):
|
||||
"""
|
||||
Exception raised when a dependency has been noted for a Component that is NOT a Component BomRef in this Bom.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class UnknownHashTypeException(CycloneDxModelException):
|
||||
"""
|
||||
Exception raised when we are unable to determine the type of hash from a composite hash string.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class LicenseExpressionAlongWithOthersException(CycloneDxModelException):
|
||||
"""
|
||||
Exception raised when a LicenseExpression was detected along with other licenses.
|
||||
If a LicenseExpression exists, than it must stand alone.
|
||||
|
||||
See https://github.com/CycloneDX/specification/pull/205
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidCreIdException(CycloneDxModelException):
|
||||
"""
|
||||
Raised when a supplied value for an CRE ID does not meet the format requirements
|
||||
as defined at https://opencre.org/
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,39 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Exceptions that are for specific error scenarios during the output of a Model to a SBOM.
|
||||
"""
|
||||
|
||||
from . import CycloneDxException
|
||||
|
||||
|
||||
class BomGenerationErrorException(CycloneDxException):
|
||||
"""
|
||||
Raised if there is an unknown error.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class FormatNotSupportedException(CycloneDxException):
|
||||
"""
|
||||
Exception raised when attempting to output a BOM to a format not supported in the requested version.
|
||||
|
||||
For example, JSON is not supported prior to 1.2.
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,52 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Exceptions relating to specific conditions that occur when (de)serializing/(de)normalizing CycloneDX BOM.
|
||||
"""
|
||||
|
||||
from . import CycloneDxException
|
||||
|
||||
|
||||
class CycloneDxSerializationException(CycloneDxException):
|
||||
"""
|
||||
Base exception that covers all exceptions that may be thrown during model serializing/normalizing.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class CycloneDxDeserializationException(CycloneDxException):
|
||||
"""
|
||||
Base exception that covers all exceptions that may be thrown during model deserializing/denormalizing.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SerializationOfUnsupportedComponentTypeException(CycloneDxSerializationException):
|
||||
"""
|
||||
Raised when attempting serializing/normalizing a :py:class:`cyclonedx.model.component.Component`
|
||||
to a :py:class:`cyclonedx.schema.schema.BaseSchemaVersion`
|
||||
which does not support that :py:class:`cyclonedx.model.component.ComponentType`
|
||||
.
|
||||
"""
|
||||
|
||||
|
||||
class SerializationOfUnexpectedValueException(CycloneDxSerializationException, ValueError):
|
||||
"""
|
||||
Raised when attempting serializing/normalizing a type that is not expected there.
|
||||
"""
|
||||
@@ -0,0 +1,20 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
"""
|
||||
Factories used in this library.
|
||||
"""
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,88 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from ..exception.factory import InvalidLicenseExpressionException, InvalidSpdxLicenseException
|
||||
from ..model.license import DisjunctiveLicense, LicenseExpression
|
||||
from ..spdx import fixup_id as spdx_fixup, is_expression as is_spdx_expression
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..model import AttachedText, XsUri
|
||||
from ..model.license import License, LicenseAcknowledgement
|
||||
|
||||
|
||||
class LicenseFactory:
|
||||
"""Factory for :class:`cyclonedx.model.license.License`."""
|
||||
|
||||
def make_from_string(self, value: str, *,
|
||||
license_text: Optional['AttachedText'] = None,
|
||||
license_url: Optional['XsUri'] = None,
|
||||
license_acknowledgement: Optional['LicenseAcknowledgement'] = None
|
||||
) -> 'License':
|
||||
"""Make a :class:`cyclonedx.model.license.License` from a string."""
|
||||
try:
|
||||
return self.make_with_id(value,
|
||||
text=license_text,
|
||||
url=license_url,
|
||||
acknowledgement=license_acknowledgement)
|
||||
except InvalidSpdxLicenseException:
|
||||
pass
|
||||
try:
|
||||
return self.make_with_expression(value,
|
||||
acknowledgement=license_acknowledgement)
|
||||
except InvalidLicenseExpressionException:
|
||||
pass
|
||||
return self.make_with_name(value,
|
||||
text=license_text,
|
||||
url=license_url,
|
||||
acknowledgement=license_acknowledgement)
|
||||
|
||||
def make_with_expression(self, expression: str, *,
|
||||
acknowledgement: Optional['LicenseAcknowledgement'] = None
|
||||
) -> LicenseExpression:
|
||||
"""Make a :class:`cyclonedx.model.license.LicenseExpression` with a compound expression.
|
||||
|
||||
Utilizes :func:`cyclonedx.spdx.is_expression`.
|
||||
|
||||
:raises InvalidLicenseExpressionException: if param `value` is not known/supported license expression
|
||||
"""
|
||||
if is_spdx_expression(expression):
|
||||
return LicenseExpression(expression, acknowledgement=acknowledgement)
|
||||
raise InvalidLicenseExpressionException(expression)
|
||||
|
||||
def make_with_id(self, spdx_id: str, *,
|
||||
text: Optional['AttachedText'] = None,
|
||||
url: Optional['XsUri'] = None,
|
||||
acknowledgement: Optional['LicenseAcknowledgement'] = None
|
||||
) -> DisjunctiveLicense:
|
||||
"""Make a :class:`cyclonedx.model.license.DisjunctiveLicense` from an SPDX-ID.
|
||||
|
||||
:raises InvalidSpdxLicenseException: if param `spdx_id` was not known/supported SPDX-ID
|
||||
"""
|
||||
spdx_license_id = spdx_fixup(spdx_id)
|
||||
if spdx_license_id is None:
|
||||
raise InvalidSpdxLicenseException(spdx_id)
|
||||
return DisjunctiveLicense(id=spdx_license_id, text=text, url=url, acknowledgement=acknowledgement)
|
||||
|
||||
def make_with_name(self, name: str, *,
|
||||
text: Optional['AttachedText'] = None,
|
||||
url: Optional['XsUri'] = None,
|
||||
acknowledgement: Optional['LicenseAcknowledgement'] = None
|
||||
) -> DisjunctiveLicense:
|
||||
"""Make a :class:`cyclonedx.model.license.DisjunctiveLicense` with a name."""
|
||||
return DisjunctiveLicense(name=name, text=text, url=url, acknowledgement=acknowledgement)
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
748
Backend/venv/lib/python3.12/site-packages/cyclonedx/model/bom.py
Normal file
748
Backend/venv/lib/python3.12/site-packages/cyclonedx/model/bom.py
Normal file
@@ -0,0 +1,748 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
from itertools import chain
|
||||
from typing import TYPE_CHECKING, Generator, Iterable, Optional, Union
|
||||
from uuid import UUID, uuid4
|
||||
from warnings import warn
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from .._internal.time import get_now_utc as _get_now_utc
|
||||
from ..exception.model import LicenseExpressionAlongWithOthersException, UnknownComponentDependencyException
|
||||
from ..schema.schema import (
|
||||
SchemaVersion1Dot0,
|
||||
SchemaVersion1Dot1,
|
||||
SchemaVersion1Dot2,
|
||||
SchemaVersion1Dot3,
|
||||
SchemaVersion1Dot4,
|
||||
SchemaVersion1Dot5,
|
||||
SchemaVersion1Dot6,
|
||||
)
|
||||
from ..serialization import UrnUuidHelper
|
||||
from . import _BOM_LINK_PREFIX, ExternalReference, Property
|
||||
from .bom_ref import BomRef
|
||||
from .component import Component
|
||||
from .contact import OrganizationalContact, OrganizationalEntity
|
||||
from .definition import Definitions
|
||||
from .dependency import Dependable, Dependency
|
||||
from .license import License, LicenseExpression, LicenseRepository, _LicenseRepositorySerializationHelper
|
||||
from .lifecycle import Lifecycle, LifecycleRepository, _LifecycleRepositoryHelper
|
||||
from .service import Service
|
||||
from .tool import Tool, ToolRepository, _ToolRepositoryHelper
|
||||
from .vulnerability import Vulnerability
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from packageurl import PackageURL
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class BomMetaData:
|
||||
"""
|
||||
This is our internal representation of the metadata complex type within the CycloneDX standard.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema for Bom metadata: https://cyclonedx.org/docs/1.6/#type_metadata
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
tools: Optional[Union[Iterable[Tool], ToolRepository]] = None,
|
||||
authors: Optional[Iterable[OrganizationalContact]] = None,
|
||||
component: Optional[Component] = None,
|
||||
supplier: Optional[OrganizationalEntity] = None,
|
||||
licenses: Optional[Iterable[License]] = None,
|
||||
properties: Optional[Iterable[Property]] = None,
|
||||
timestamp: Optional[datetime] = None,
|
||||
manufacturer: Optional[OrganizationalEntity] = None,
|
||||
lifecycles: Optional[Iterable[Lifecycle]] = None,
|
||||
# Deprecated as of v1.6
|
||||
manufacture: Optional[OrganizationalEntity] = None,
|
||||
) -> None:
|
||||
self.timestamp = timestamp or _get_now_utc()
|
||||
self.tools = tools or [] # type:ignore[assignment]
|
||||
self.authors = authors or [] # type:ignore[assignment]
|
||||
self.component = component
|
||||
self.supplier = supplier
|
||||
self.licenses = licenses or [] # type:ignore[assignment]
|
||||
self.properties = properties or [] # type:ignore[assignment]
|
||||
self.manufacturer = manufacturer
|
||||
self.lifecycles = lifecycles or [] # type:ignore[assignment]
|
||||
|
||||
self.manufacture = manufacture
|
||||
if manufacture:
|
||||
warn(
|
||||
'`bom.metadata.manufacture` is deprecated from CycloneDX v1.6 onwards. '
|
||||
'Please use `bom.metadata.component.manufacturer` instead.',
|
||||
DeprecationWarning)
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(serializable.helpers.XsdDateTime)
|
||||
@serializable.xml_sequence(1)
|
||||
def timestamp(self) -> datetime:
|
||||
"""
|
||||
The date and time (in UTC) when this BomMetaData was created.
|
||||
|
||||
Returns:
|
||||
`datetime` instance in UTC timezone
|
||||
"""
|
||||
return self._timestamp
|
||||
|
||||
@timestamp.setter
|
||||
def timestamp(self, timestamp: datetime) -> None:
|
||||
self._timestamp = timestamp
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.type_mapping(_LifecycleRepositoryHelper)
|
||||
@serializable.xml_sequence(2)
|
||||
def lifecycles(self) -> LifecycleRepository:
|
||||
"""
|
||||
An optional list of BOM lifecycle stages.
|
||||
|
||||
Returns:
|
||||
Set of `Lifecycle`
|
||||
"""
|
||||
return self._lifecycles
|
||||
|
||||
@lifecycles.setter
|
||||
def lifecycles(self, lifecycles: Iterable[Lifecycle]) -> None:
|
||||
self._lifecycles = LifecycleRepository(lifecycles)
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(_ToolRepositoryHelper)
|
||||
@serializable.xml_sequence(3)
|
||||
def tools(self) -> ToolRepository:
|
||||
"""
|
||||
Tools used to create this BOM.
|
||||
|
||||
Returns:
|
||||
:class:`ToolRepository` object.
|
||||
"""
|
||||
return self._tools
|
||||
|
||||
@tools.setter
|
||||
def tools(self, tools: Union[Iterable[Tool], ToolRepository]) -> None:
|
||||
self._tools = tools \
|
||||
if isinstance(tools, ToolRepository) \
|
||||
else ToolRepository(tools=tools)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'author')
|
||||
@serializable.xml_sequence(4)
|
||||
def authors(self) -> 'SortedSet[OrganizationalContact]':
|
||||
"""
|
||||
The person(s) who created the BOM.
|
||||
|
||||
Authors are common in BOMs created through manual processes.
|
||||
|
||||
BOMs created through automated means may not have authors.
|
||||
|
||||
Returns:
|
||||
Set of `OrganizationalContact`
|
||||
"""
|
||||
return self._authors
|
||||
|
||||
@authors.setter
|
||||
def authors(self, authors: Iterable[OrganizationalContact]) -> None:
|
||||
self._authors = SortedSet(authors)
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(5)
|
||||
def component(self) -> Optional[Component]:
|
||||
"""
|
||||
The (optional) component that the BOM describes.
|
||||
|
||||
Returns:
|
||||
`cyclonedx.model.component.Component` instance for this Bom Metadata.
|
||||
"""
|
||||
return self._component
|
||||
|
||||
@component.setter
|
||||
def component(self, component: Component) -> None:
|
||||
"""
|
||||
The (optional) component that the BOM describes.
|
||||
|
||||
Args:
|
||||
component
|
||||
`cyclonedx.model.component.Component` instance to add to this Bom Metadata.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
self._component = component
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot2)
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_sequence(6)
|
||||
def manufacture(self) -> Optional[OrganizationalEntity]:
|
||||
"""
|
||||
The organization that manufactured the component that the BOM describes.
|
||||
|
||||
Returns:
|
||||
`OrganizationalEntity` if set else `None`
|
||||
"""
|
||||
return self._manufacture
|
||||
|
||||
@manufacture.setter
|
||||
def manufacture(self, manufacture: Optional[OrganizationalEntity]) -> None:
|
||||
"""
|
||||
@todo Based on https://github.com/CycloneDX/specification/issues/346,
|
||||
we should set this data on `.component.manufacturer`.
|
||||
"""
|
||||
self._manufacture = manufacture
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_sequence(7)
|
||||
def manufacturer(self) -> Optional[OrganizationalEntity]:
|
||||
"""
|
||||
The organization that created the BOM.
|
||||
Manufacturer is common in BOMs created through automated processes. BOMs created through manual means may have
|
||||
`@.authors` instead.
|
||||
|
||||
Returns:
|
||||
`OrganizationalEntity` if set else `None`
|
||||
"""
|
||||
return self._manufacturer
|
||||
|
||||
@manufacturer.setter
|
||||
def manufacturer(self, manufacturer: Optional[OrganizationalEntity]) -> None:
|
||||
self._manufacturer = manufacturer
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(8)
|
||||
def supplier(self) -> Optional[OrganizationalEntity]:
|
||||
"""
|
||||
The organization that supplied the component that the BOM describes.
|
||||
|
||||
The supplier may often be the manufacturer, but may also be a distributor or repackager.
|
||||
|
||||
Returns:
|
||||
`OrganizationalEntity` if set else `None`
|
||||
"""
|
||||
return self._supplier
|
||||
|
||||
@supplier.setter
|
||||
def supplier(self, supplier: Optional[OrganizationalEntity]) -> None:
|
||||
self._supplier = supplier
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.type_mapping(_LicenseRepositorySerializationHelper)
|
||||
@serializable.xml_sequence(9)
|
||||
def licenses(self) -> LicenseRepository:
|
||||
"""
|
||||
A optional list of statements about how this BOM is licensed.
|
||||
|
||||
Returns:
|
||||
Set of `LicenseChoice`
|
||||
"""
|
||||
return self._licenses
|
||||
|
||||
@licenses.setter
|
||||
def licenses(self, licenses: Iterable[License]) -> None:
|
||||
self._licenses = LicenseRepository(licenses)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
|
||||
@serializable.xml_sequence(10)
|
||||
def properties(self) -> 'SortedSet[Property]':
|
||||
"""
|
||||
Provides the ability to document properties in a key/value store. This provides flexibility to include data not
|
||||
officially supported in the standard without having to use additional namespaces or create extensions.
|
||||
|
||||
Property names of interest to the general public are encouraged to be registered in the CycloneDX Property
|
||||
Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. Formal registration is OPTIONAL.
|
||||
|
||||
Return:
|
||||
Set of `Property`
|
||||
"""
|
||||
return self._properties
|
||||
|
||||
@properties.setter
|
||||
def properties(self, properties: Iterable[Property]) -> None:
|
||||
self._properties = SortedSet(properties)
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
_ComparableTuple(self.authors), self.component, _ComparableTuple(self.licenses), self.manufacture,
|
||||
_ComparableTuple(self.properties),
|
||||
_ComparableTuple(self.lifecycles), self.supplier, self.timestamp, self.tools, self.manufacturer
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, BomMetaData):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<BomMetaData timestamp={self.timestamp}, component={self.component}>'
|
||||
|
||||
|
||||
@serializable.serializable_class(ignore_during_deserialization=['$schema', 'bom_format', 'spec_version'])
|
||||
class Bom:
|
||||
"""
|
||||
This is our internal representation of a bill-of-materials (BOM).
|
||||
|
||||
Once you have an instance of `cyclonedx.model.bom.Bom`, you can pass this to an instance of
|
||||
`cyclonedx.output.BaseOutput` to produce a CycloneDX document according to a specific schema version and format.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
components: Optional[Iterable[Component]] = None,
|
||||
services: Optional[Iterable[Service]] = None,
|
||||
external_references: Optional[Iterable[ExternalReference]] = None,
|
||||
serial_number: Optional[UUID] = None,
|
||||
version: int = 1,
|
||||
metadata: Optional[BomMetaData] = None,
|
||||
dependencies: Optional[Iterable[Dependency]] = None,
|
||||
vulnerabilities: Optional[Iterable[Vulnerability]] = None,
|
||||
properties: Optional[Iterable[Property]] = None,
|
||||
definitions: Optional[Definitions] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Create a new Bom that you can manually/programmatically add data to later.
|
||||
|
||||
Returns:
|
||||
New, empty `cyclonedx.model.bom.Bom` instance.
|
||||
"""
|
||||
self.serial_number = serial_number or uuid4()
|
||||
self.version = version
|
||||
self.metadata = metadata or BomMetaData()
|
||||
self.components = components or [] # type:ignore[assignment]
|
||||
self.services = services or [] # type:ignore[assignment]
|
||||
self.external_references = external_references or [] # type:ignore[assignment]
|
||||
self.vulnerabilities = vulnerabilities or [] # type:ignore[assignment]
|
||||
self.dependencies = dependencies or [] # type:ignore[assignment]
|
||||
self.properties = properties or [] # type:ignore[assignment]
|
||||
self.definitions = definitions or Definitions()
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(UrnUuidHelper)
|
||||
@serializable.view(SchemaVersion1Dot1)
|
||||
@serializable.view(SchemaVersion1Dot2)
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_attribute()
|
||||
def serial_number(self) -> UUID:
|
||||
"""
|
||||
Unique UUID for this BOM
|
||||
|
||||
Returns:
|
||||
`UUID` instance
|
||||
`UUID` instance
|
||||
"""
|
||||
return self._serial_number
|
||||
|
||||
@serial_number.setter
|
||||
def serial_number(self, serial_number: UUID) -> None:
|
||||
self._serial_number = serial_number
|
||||
|
||||
@property
|
||||
@serializable.xml_attribute()
|
||||
def version(self) -> int:
|
||||
return self._version
|
||||
|
||||
@version.setter
|
||||
def version(self, version: int) -> None:
|
||||
self._version = version
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot2)
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_sequence(10)
|
||||
def metadata(self) -> BomMetaData:
|
||||
"""
|
||||
Get our internal metadata object for this Bom.
|
||||
|
||||
Returns:
|
||||
Metadata object instance for this Bom.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema for Bom metadata: https://cyclonedx.org/docs/1.6/#type_metadata
|
||||
"""
|
||||
return self._metadata
|
||||
|
||||
@metadata.setter
|
||||
def metadata(self, metadata: BomMetaData) -> None:
|
||||
self._metadata = metadata
|
||||
|
||||
@property
|
||||
@serializable.include_none(SchemaVersion1Dot0)
|
||||
@serializable.include_none(SchemaVersion1Dot1)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'component')
|
||||
@serializable.xml_sequence(20)
|
||||
def components(self) -> 'SortedSet[Component]':
|
||||
"""
|
||||
Get all the Components currently in this Bom.
|
||||
|
||||
Returns:
|
||||
Set of `Component` in this Bom
|
||||
"""
|
||||
return self._components
|
||||
|
||||
@components.setter
|
||||
def components(self, components: Iterable[Component]) -> None:
|
||||
self._components = SortedSet(components)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot2)
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'service')
|
||||
@serializable.xml_sequence(30)
|
||||
def services(self) -> 'SortedSet[Service]':
|
||||
"""
|
||||
Get all the Services currently in this Bom.
|
||||
|
||||
Returns:
|
||||
Set of `Service` in this BOM
|
||||
"""
|
||||
return self._services
|
||||
|
||||
@services.setter
|
||||
def services(self, services: Iterable[Service]) -> None:
|
||||
self._services = SortedSet(services)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot1)
|
||||
@serializable.view(SchemaVersion1Dot2)
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
|
||||
@serializable.xml_sequence(40)
|
||||
def external_references(self) -> 'SortedSet[ExternalReference]':
|
||||
"""
|
||||
Provides the ability to document external references related to the BOM or to the project the BOM describes.
|
||||
|
||||
Returns:
|
||||
Set of `ExternalReference`
|
||||
"""
|
||||
return self._external_references
|
||||
|
||||
@external_references.setter
|
||||
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
|
||||
self._external_references = SortedSet(external_references)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot2)
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'dependency')
|
||||
@serializable.xml_sequence(50)
|
||||
def dependencies(self) -> 'SortedSet[Dependency]':
|
||||
return self._dependencies
|
||||
|
||||
@dependencies.setter
|
||||
def dependencies(self, dependencies: Iterable[Dependency]) -> None:
|
||||
self._dependencies = SortedSet(dependencies)
|
||||
|
||||
# @property
|
||||
# ...
|
||||
# @serializable.view(SchemaVersion1Dot3)
|
||||
# @serializable.view(SchemaVersion1Dot4)
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @serializable.xml_sequence(6)
|
||||
# def compositions(self) -> ...:
|
||||
# ... # TODO Since CDX 1.3
|
||||
#
|
||||
# @compositions.setter
|
||||
# def compositions(self, ...) -> None:
|
||||
# ... # TODO Since CDX 1.3
|
||||
|
||||
@property
|
||||
# @serializable.view(SchemaVersion1Dot3) @todo: Update py-serializable to support view by OutputFormat filtering
|
||||
# @serializable.view(SchemaVersion1Dot4) @todo: Update py-serializable to support view by OutputFormat filtering
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
|
||||
@serializable.xml_sequence(70)
|
||||
def properties(self) -> 'SortedSet[Property]':
|
||||
"""
|
||||
Provides the ability to document properties in a name/value store. This provides flexibility to include data
|
||||
not officially supported in the standard without having to use additional namespaces or create extensions.
|
||||
Property names of interest to the general public are encouraged to be registered in the CycloneDX Property
|
||||
Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. Formal registration is OPTIONAL.
|
||||
|
||||
Return:
|
||||
Set of `Property`
|
||||
"""
|
||||
return self._properties
|
||||
|
||||
@properties.setter
|
||||
def properties(self, properties: Iterable[Property]) -> None:
|
||||
self._properties = SortedSet(properties)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'vulnerability')
|
||||
@serializable.xml_sequence(80)
|
||||
def vulnerabilities(self) -> 'SortedSet[Vulnerability]':
|
||||
"""
|
||||
Get all the Vulnerabilities in this BOM.
|
||||
|
||||
Returns:
|
||||
Set of `Vulnerability`
|
||||
"""
|
||||
return self._vulnerabilities
|
||||
|
||||
@vulnerabilities.setter
|
||||
def vulnerabilities(self, vulnerabilities: Iterable[Vulnerability]) -> None:
|
||||
self._vulnerabilities = SortedSet(vulnerabilities)
|
||||
|
||||
# @property
|
||||
# ...
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @serializable.xml_sequence(9)
|
||||
# def annotations(self) -> ...:
|
||||
# ... # TODO Since CDX 1.5
|
||||
#
|
||||
# @annotations.setter
|
||||
# def annotations(self, ...) -> None:
|
||||
# ... # TODO Since CDX 1.5
|
||||
|
||||
# @property
|
||||
# ...
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @formulation.xml_sequence(10)
|
||||
# def formulation(self) -> ...:
|
||||
# ... # TODO Since CDX 1.5
|
||||
#
|
||||
# @formulation.setter
|
||||
# def formulation(self, ...) -> None:
|
||||
# ... # TODO Since CDX 1.5
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_sequence(110)
|
||||
def definitions(self) -> Optional[Definitions]:
|
||||
"""
|
||||
The repository for definitions
|
||||
|
||||
Returns:
|
||||
`Definitions`
|
||||
"""
|
||||
return self._definitions if len(self._definitions.standards) > 0 else None
|
||||
|
||||
@definitions.setter
|
||||
def definitions(self, definitions: Definitions) -> None:
|
||||
self._definitions = definitions
|
||||
|
||||
def get_component_by_purl(self, purl: Optional['PackageURL']) -> Optional[Component]:
|
||||
"""
|
||||
Get a Component already in the Bom by its PURL
|
||||
|
||||
Args:
|
||||
purl:
|
||||
An instance of `packageurl.PackageURL` to look and find `Component`.
|
||||
|
||||
Returns:
|
||||
`Component` or `None`
|
||||
"""
|
||||
if purl:
|
||||
found = [x for x in self.components if x.purl == purl]
|
||||
if len(found) == 1:
|
||||
return found[0]
|
||||
|
||||
return None
|
||||
|
||||
def get_urn_uuid(self) -> str:
|
||||
"""
|
||||
Get the unique reference for this Bom.
|
||||
|
||||
Returns:
|
||||
URN formatted UUID that uniquely identified this Bom instance.
|
||||
"""
|
||||
return self.serial_number.urn
|
||||
|
||||
def has_component(self, component: Component) -> bool:
|
||||
"""
|
||||
Check whether this Bom contains the provided Component.
|
||||
|
||||
Args:
|
||||
component:
|
||||
The instance of `cyclonedx.model.component.Component` to check if this Bom contains.
|
||||
|
||||
Returns:
|
||||
`bool` - `True` if the supplied Component is part of this Bom, `False` otherwise.
|
||||
"""
|
||||
return component in self.components
|
||||
|
||||
def _get_all_components(self) -> Generator[Component, None, None]:
|
||||
if self.metadata.component:
|
||||
yield from self.metadata.component.get_all_nested_components(include_self=True)
|
||||
for c in self.components:
|
||||
yield from c.get_all_nested_components(include_self=True)
|
||||
|
||||
def get_vulnerabilities_for_bom_ref(self, bom_ref: BomRef) -> 'SortedSet[Vulnerability]':
|
||||
"""
|
||||
Get all known Vulnerabilities that affect the supplied bom_ref.
|
||||
|
||||
Args:
|
||||
bom_ref: `BomRef`
|
||||
|
||||
Returns:
|
||||
`SortedSet` of `Vulnerability`
|
||||
"""
|
||||
|
||||
vulnerabilities: SortedSet[Vulnerability] = SortedSet()
|
||||
for v in self.vulnerabilities:
|
||||
for target in v.affects:
|
||||
if target.ref == bom_ref.value:
|
||||
vulnerabilities.add(v)
|
||||
return vulnerabilities
|
||||
|
||||
def has_vulnerabilities(self) -> bool:
|
||||
"""
|
||||
Check whether this Bom has any declared vulnerabilities.
|
||||
|
||||
Returns:
|
||||
`bool` - `True` if this Bom has at least one Vulnerability, `False` otherwise.
|
||||
"""
|
||||
return bool(self.vulnerabilities)
|
||||
|
||||
def register_dependency(self, target: Dependable, depends_on: Optional[Iterable[Dependable]] = None) -> None:
|
||||
_d = next(filter(lambda _d: _d.ref == target.bom_ref, self.dependencies), None)
|
||||
if _d:
|
||||
# Dependency Target already registered - but it might have new dependencies to add
|
||||
if depends_on:
|
||||
_d.dependencies.update(map(lambda _d: Dependency(ref=_d.bom_ref), depends_on))
|
||||
else:
|
||||
# First time we are seeing this target as a Dependency
|
||||
self._dependencies.add(Dependency(
|
||||
ref=target.bom_ref,
|
||||
dependencies=map(lambda _dep: Dependency(ref=_dep.bom_ref), depends_on) if depends_on else []
|
||||
))
|
||||
|
||||
if depends_on:
|
||||
# Ensure dependents are registered with no further dependents in the DependencyGraph
|
||||
for _d2 in depends_on:
|
||||
self.register_dependency(target=_d2, depends_on=None)
|
||||
|
||||
def urn(self) -> str:
|
||||
return f'{_BOM_LINK_PREFIX}{self.serial_number}/{self.version}'
|
||||
|
||||
def validate(self) -> bool:
|
||||
"""
|
||||
Perform data-model level validations to make sure we have some known data integrity prior to attempting output
|
||||
of this `Bom`
|
||||
|
||||
Returns:
|
||||
`bool`
|
||||
"""
|
||||
# 0. Make sure all Dependable have a Dependency entry
|
||||
if self.metadata.component:
|
||||
self.register_dependency(target=self.metadata.component)
|
||||
for _c in self.components:
|
||||
self.register_dependency(target=_c)
|
||||
for _s in self.services:
|
||||
self.register_dependency(target=_s)
|
||||
|
||||
# 1. Make sure dependencies are all in this Bom.
|
||||
component_bom_refs = set(map(lambda c: c.bom_ref, self._get_all_components())) | set(
|
||||
map(lambda s: s.bom_ref, self.services))
|
||||
dependency_bom_refs = set(chain(
|
||||
(d.ref for d in self.dependencies),
|
||||
chain.from_iterable(d.dependencies_as_bom_refs() for d in self.dependencies)
|
||||
))
|
||||
dependency_diff = dependency_bom_refs - component_bom_refs
|
||||
if len(dependency_diff) > 0:
|
||||
raise UnknownComponentDependencyException(
|
||||
'One or more Components have Dependency references to Components/Services that are not known in this '
|
||||
f'BOM. They are: {dependency_diff}')
|
||||
|
||||
# 2. if root component is set and there are other components: dependencies should exist for the Component
|
||||
# this BOM is describing
|
||||
if self.metadata.component and len(self.components) > 0 and not any(map(
|
||||
lambda d: d.ref == self.metadata.component.bom_ref and len(d.dependencies) > 0, # type: ignore[union-attr]
|
||||
self.dependencies
|
||||
)):
|
||||
warn(
|
||||
f'The Component this BOM is describing {self.metadata.component.purl} has no defined dependencies '
|
||||
'which means the Dependency Graph is incomplete - you should add direct dependencies to this '
|
||||
'"root" Component to complete the Dependency Graph data.',
|
||||
category=UserWarning, stacklevel=1
|
||||
)
|
||||
|
||||
# 3. If a LicenseExpression is set, then there must be no other license.
|
||||
# see https://github.com/CycloneDX/specification/pull/205
|
||||
elem: Union[BomMetaData, Component, Service]
|
||||
for elem in chain( # type: ignore[assignment]
|
||||
[self.metadata],
|
||||
self.metadata.component.get_all_nested_components(include_self=True) if self.metadata.component else [],
|
||||
chain.from_iterable(c.get_all_nested_components(include_self=True) for c in self.components),
|
||||
self.services
|
||||
):
|
||||
if len(elem.licenses) > 1 and any(isinstance(li, LicenseExpression) for li in elem.licenses):
|
||||
raise LicenseExpressionAlongWithOthersException(
|
||||
f'Found LicenseExpression along with others licenses in: {elem!r}')
|
||||
|
||||
return True
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.serial_number, self.version, self.metadata, _ComparableTuple(
|
||||
self.components), _ComparableTuple(self.services),
|
||||
_ComparableTuple(self.external_references), _ComparableTuple(
|
||||
self.dependencies), _ComparableTuple(self.properties),
|
||||
_ComparableTuple(self.vulnerabilities),
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Bom):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Bom uuid={self.serial_number}, hash={hash(self)}>'
|
||||
@@ -0,0 +1,101 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
import py_serializable as serializable
|
||||
|
||||
from ..exception.serialization import CycloneDxDeserializationException, SerializationOfUnexpectedValueException
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import Type, TypeVar
|
||||
|
||||
_T_BR = TypeVar('_T_BR', bound='BomRef')
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class BomRef(serializable.helpers.BaseHelper):
|
||||
"""
|
||||
An identifier that can be used to reference objects elsewhere in the BOM.
|
||||
|
||||
This copies a similar pattern used in the CycloneDX PHP Library.
|
||||
|
||||
.. note::
|
||||
See https://github.com/CycloneDX/cyclonedx-php-library/blob/master/docs/dev/decisions/BomDependencyDataModel.md
|
||||
"""
|
||||
|
||||
def __init__(self, value: Optional[str] = None) -> None:
|
||||
self.value = value
|
||||
|
||||
@property
|
||||
@serializable.json_name('.')
|
||||
@serializable.xml_name('.')
|
||||
def value(self) -> Optional[str]:
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, value: Optional[str]) -> None:
|
||||
# empty strings become `None`
|
||||
self._value = value or None
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
return (self is other) or (
|
||||
isinstance(other, BomRef)
|
||||
# `None` value is not discriminative in this domain
|
||||
# see also: `BomRefDiscriminator`
|
||||
and other._value is not None
|
||||
and self._value is not None
|
||||
and other._value == self._value
|
||||
)
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, BomRef):
|
||||
return str(self) < str(other)
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self._value or f'__id__{id(self)}')
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<BomRef {self._value!r} id={id(self)}>'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self._value or ''
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return self._value is not None
|
||||
|
||||
# region impl BaseHelper
|
||||
|
||||
@classmethod
|
||||
def serialize(cls, o: Any) -> Optional[str]:
|
||||
if isinstance(o, cls):
|
||||
return o.value
|
||||
raise SerializationOfUnexpectedValueException(
|
||||
f'Attempt to serialize a non-BomRef: {o!r}')
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls: 'Type[_T_BR]', o: Any) -> '_T_BR':
|
||||
try:
|
||||
return cls(value=str(o))
|
||||
except ValueError as err:
|
||||
raise CycloneDxDeserializationException(
|
||||
f'BomRef string supplied does not parse: {o!r}'
|
||||
) from err
|
||||
|
||||
# endregion impl BaseHelper
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,386 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from typing import Any, Iterable, Optional, Union
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..schema.schema import SchemaVersion1Dot6
|
||||
from . import XsUri
|
||||
from .bom_ref import BomRef
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class PostalAddress:
|
||||
"""
|
||||
This is our internal representation of the `postalAddressType` complex type that can be used in multiple places
|
||||
within a CycloneDX BOM document.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/xml/#type_postalAddressType
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
bom_ref: Optional[Union[str, BomRef]] = None,
|
||||
country: Optional[str] = None,
|
||||
region: Optional[str] = None,
|
||||
locality: Optional[str] = None,
|
||||
post_office_box_number: Optional[str] = None,
|
||||
postal_code: Optional[str] = None,
|
||||
street_address: Optional[str] = None,
|
||||
) -> None:
|
||||
self._bom_ref = _bom_ref_from_str(bom_ref, optional=True)
|
||||
self.country = country
|
||||
self.region = region
|
||||
self.locality = locality
|
||||
self.post_office_box_number = post_office_box_number
|
||||
self.postal_code = postal_code
|
||||
self.street_address = street_address
|
||||
|
||||
@property
|
||||
@serializable.json_name('bom-ref')
|
||||
@serializable.type_mapping(BomRef)
|
||||
@serializable.xml_attribute()
|
||||
@serializable.xml_name('bom-ref')
|
||||
def bom_ref(self) -> Optional[BomRef]:
|
||||
"""
|
||||
An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be
|
||||
unique within the BOM.
|
||||
|
||||
Returns:
|
||||
`BomRef`
|
||||
"""
|
||||
return self._bom_ref
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(10)
|
||||
def country(self) -> Optional[str]:
|
||||
"""
|
||||
The country name or the two-letter ISO 3166-1 country code.
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._country
|
||||
|
||||
@country.setter
|
||||
def country(self, country: Optional[str]) -> None:
|
||||
self._country = country
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(20)
|
||||
def region(self) -> Optional[str]:
|
||||
"""
|
||||
The region or state in the country. For example, Texas.
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._region
|
||||
|
||||
@region.setter
|
||||
def region(self, region: Optional[str]) -> None:
|
||||
self._region = region
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(30)
|
||||
def locality(self) -> Optional[str]:
|
||||
"""
|
||||
The locality or city within the country. For example, Austin.
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._locality
|
||||
|
||||
@locality.setter
|
||||
def locality(self, locality: Optional[str]) -> None:
|
||||
self._locality = locality
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(40)
|
||||
def post_office_box_number(self) -> Optional[str]:
|
||||
"""
|
||||
The post office box number. For example, 901.
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._post_office_box_number
|
||||
|
||||
@post_office_box_number.setter
|
||||
def post_office_box_number(self, post_office_box_number: Optional[str]) -> None:
|
||||
self._post_office_box_number = post_office_box_number
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(60)
|
||||
def postal_code(self) -> Optional[str]:
|
||||
"""
|
||||
The postal code. For example, 78758.
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._postal_code
|
||||
|
||||
@postal_code.setter
|
||||
def postal_code(self, postal_code: Optional[str]) -> None:
|
||||
self._postal_code = postal_code
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(70)
|
||||
def street_address(self) -> Optional[str]:
|
||||
"""
|
||||
The street address. For example, 100 Main Street.
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._street_address
|
||||
|
||||
@street_address.setter
|
||||
def street_address(self, street_address: Optional[str]) -> None:
|
||||
self._street_address = street_address
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.country, self.region, self.locality, self.postal_code,
|
||||
self.post_office_box_number,
|
||||
self.street_address,
|
||||
None if self.bom_ref is None else self.bom_ref.value,
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, PostalAddress):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, PostalAddress):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<PostalAddress bom-ref={self.bom_ref}, street_address={self.street_address}, country={self.country}>'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class OrganizationalContact:
|
||||
"""
|
||||
This is our internal representation of the `organizationalContact` complex type that can be used in multiple places
|
||||
within a CycloneDX BOM document.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/xml/#type_organizationalContact
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
name: Optional[str] = None,
|
||||
phone: Optional[str] = None,
|
||||
email: Optional[str] = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.email = email
|
||||
self.phone = phone
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
Get the name of the contact.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: Optional[str]) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def email(self) -> Optional[str]:
|
||||
"""
|
||||
Get the email of the contact.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._email
|
||||
|
||||
@email.setter
|
||||
def email(self, email: Optional[str]) -> None:
|
||||
self._email = email
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def phone(self) -> Optional[str]:
|
||||
"""
|
||||
Get the phone of the contact.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._phone
|
||||
|
||||
@phone.setter
|
||||
def phone(self, phone: Optional[str]) -> None:
|
||||
self._phone = phone
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.name, self.email, self.phone
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, OrganizationalContact):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, OrganizationalContact):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<OrganizationalContact name={self.name}, email={self.email}, phone={self.phone}>'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class OrganizationalEntity:
|
||||
"""
|
||||
This is our internal representation of the `organizationalEntity` complex type that can be used in multiple places
|
||||
within a CycloneDX BOM document.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/xml/#type_organizationalEntity
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
name: Optional[str] = None,
|
||||
urls: Optional[Iterable[XsUri]] = None,
|
||||
contacts: Optional[Iterable[OrganizationalContact]] = None,
|
||||
address: Optional[PostalAddress] = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.address = address
|
||||
self.urls = urls or [] # type:ignore[assignment]
|
||||
self.contacts = contacts or [] # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(10)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
Get the name of the organization.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: Optional[str]) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_sequence(20)
|
||||
def address(self) -> Optional[PostalAddress]:
|
||||
"""
|
||||
The physical address (location) of the organization.
|
||||
|
||||
Returns:
|
||||
`PostalAddress` or `None`
|
||||
"""
|
||||
return self._address
|
||||
|
||||
@address.setter
|
||||
def address(self, address: Optional[PostalAddress]) -> None:
|
||||
self._address = address
|
||||
|
||||
@property
|
||||
@serializable.json_name('url')
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'url')
|
||||
@serializable.xml_sequence(30)
|
||||
def urls(self) -> 'SortedSet[XsUri]':
|
||||
"""
|
||||
Get a list of URLs of the organization. Multiple URLs are allowed.
|
||||
|
||||
Returns:
|
||||
Set of `XsUri`
|
||||
"""
|
||||
return self._urls
|
||||
|
||||
@urls.setter
|
||||
def urls(self, urls: Iterable[XsUri]) -> None:
|
||||
self._urls = SortedSet(urls)
|
||||
|
||||
@property
|
||||
@serializable.json_name('contact')
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'contact')
|
||||
@serializable.xml_sequence(40)
|
||||
def contacts(self) -> 'SortedSet[OrganizationalContact]':
|
||||
"""
|
||||
Get a list of contact person at the organization. Multiple contacts are allowed.
|
||||
|
||||
Returns:
|
||||
Set of `OrganizationalContact`
|
||||
"""
|
||||
return self._contacts
|
||||
|
||||
@contacts.setter
|
||||
def contacts(self, contacts: Iterable[OrganizationalContact]) -> None:
|
||||
self._contacts = SortedSet(contacts)
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.name, _ComparableTuple(self.urls), _ComparableTuple(self.contacts)
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, OrganizationalEntity):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, OrganizationalEntity):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<OrganizationalEntity name={self.name}>'
|
||||
1598
Backend/venv/lib/python3.12/site-packages/cyclonedx/model/crypto.py
Normal file
1598
Backend/venv/lib/python3.12/site-packages/cyclonedx/model/crypto.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,623 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any, Iterable, Optional, Union
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..exception.model import InvalidCreIdException
|
||||
from ..exception.serialization import SerializationOfUnexpectedValueException
|
||||
from . import ExternalReference, Property
|
||||
from .bom_ref import BomRef
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import Type, TypeVar
|
||||
|
||||
_T_CreId = TypeVar('_T_CreId', bound='CreId')
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class CreId(serializable.helpers.BaseHelper):
|
||||
"""
|
||||
Helper class that allows us to perform validation on data strings that must conform to
|
||||
Common Requirements Enumeration (CRE) identifier(s).
|
||||
|
||||
"""
|
||||
|
||||
_VALID_CRE_REGEX = re.compile(r'^CRE:[0-9]+-[0-9]+$')
|
||||
|
||||
def __init__(self, id: str) -> None:
|
||||
if CreId._VALID_CRE_REGEX.match(id) is None:
|
||||
raise InvalidCreIdException(
|
||||
f'Supplied value "{id} does not meet format specification.'
|
||||
)
|
||||
self._id = id
|
||||
|
||||
@property
|
||||
@serializable.json_name('.')
|
||||
@serializable.xml_name('.')
|
||||
def id(self) -> str:
|
||||
return self._id
|
||||
|
||||
@classmethod
|
||||
def serialize(cls, o: Any) -> str:
|
||||
if isinstance(o, cls):
|
||||
return str(o)
|
||||
raise SerializationOfUnexpectedValueException(
|
||||
f'Attempt to serialize a non-CreId: {o!r}')
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls: 'Type[_T_CreId]', o: Any) -> '_T_CreId':
|
||||
return cls(id=str(o))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if isinstance(other, CreId):
|
||||
return self._id == other._id
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, CreId):
|
||||
return self._id < other._id
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self._id)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<CreId {self._id}>'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self._id
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class Requirement:
|
||||
"""
|
||||
A requirement comprising a standard.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
bom_ref: Optional[Union[str, BomRef]] = None,
|
||||
identifier: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
text: Optional[str] = None,
|
||||
descriptions: Optional[Iterable[str]] = None,
|
||||
open_cre: Optional[Iterable[CreId]] = None,
|
||||
parent: Optional[Union[str, BomRef]] = None,
|
||||
properties: Optional[Iterable[Property]] = None,
|
||||
external_references: Optional[Iterable[ExternalReference]] = None,
|
||||
) -> None:
|
||||
self._bom_ref = _bom_ref_from_str(bom_ref)
|
||||
self.identifier = identifier
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.descriptions = descriptions or () # type:ignore[assignment]
|
||||
self.open_cre = open_cre or () # type:ignore[assignment]
|
||||
self.parent = parent # type:ignore[assignment]
|
||||
self.properties = properties or () # type:ignore[assignment]
|
||||
self.external_references = external_references or () # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(BomRef)
|
||||
@serializable.json_name('bom-ref')
|
||||
@serializable.xml_name('bom-ref')
|
||||
@serializable.xml_attribute()
|
||||
def bom_ref(self) -> BomRef:
|
||||
"""
|
||||
An optional identifier which can be used to reference the requirement elsewhere in the BOM.
|
||||
Every bom-ref MUST be unique within the BOM.
|
||||
|
||||
Returns:
|
||||
`BomRef`
|
||||
"""
|
||||
return self._bom_ref
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
def identifier(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The identifier of the requirement.
|
||||
"""
|
||||
return self._identifier
|
||||
|
||||
@identifier.setter
|
||||
def identifier(self, identifier: Optional[str]) -> None:
|
||||
self._identifier = identifier
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
def title(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The title of the requirement.
|
||||
"""
|
||||
return self._title
|
||||
|
||||
@title.setter
|
||||
def title(self, title: Optional[str]) -> None:
|
||||
self._title = title
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
def text(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The text of the requirement.
|
||||
"""
|
||||
return self._text
|
||||
|
||||
@text.setter
|
||||
def text(self, text: Optional[str]) -> None:
|
||||
self._text = text
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'description')
|
||||
@serializable.xml_sequence(4)
|
||||
def descriptions(self) -> 'SortedSet[str]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of descriptions of the requirement.
|
||||
"""
|
||||
return self._descriptions
|
||||
|
||||
@descriptions.setter
|
||||
def descriptions(self, descriptions: Iterable[str]) -> None:
|
||||
self._descriptions = SortedSet(descriptions)
|
||||
|
||||
@property
|
||||
@serializable.json_name('openCre')
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'openCre')
|
||||
@serializable.xml_sequence(5)
|
||||
def open_cre(self) -> 'SortedSet[CreId]':
|
||||
"""
|
||||
CRE is a structured and standardized framework for uniting security standards and guidelines. CRE links each
|
||||
section of a resource to a shared topic identifier (a Common Requirement). Through this shared topic link, all
|
||||
resources map to each other. Use of CRE promotes clear and unambiguous communication among stakeholders.
|
||||
|
||||
Returns:
|
||||
The Common Requirements Enumeration (CRE) identifier(s).
|
||||
CREs must match regular expression: ^CRE:[0-9]+-[0-9]+$
|
||||
"""
|
||||
return self._open_cre
|
||||
|
||||
@open_cre.setter
|
||||
def open_cre(self, open_cre: Iterable[CreId]) -> None:
|
||||
self._open_cre = SortedSet(open_cre)
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(BomRef)
|
||||
@serializable.xml_sequence(6)
|
||||
def parent(self) -> Optional[BomRef]:
|
||||
"""
|
||||
Returns:
|
||||
The optional bom-ref to a parent requirement. This establishes a hierarchy of requirements. Top-level
|
||||
requirements must not define a parent. Only child requirements should define parents.
|
||||
"""
|
||||
return self._parent
|
||||
|
||||
@parent.setter
|
||||
def parent(self, parent: Optional[Union[str, BomRef]]) -> None:
|
||||
self._parent = _bom_ref_from_str(parent, optional=True)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
|
||||
@serializable.xml_sequence(7)
|
||||
def properties(self) -> 'SortedSet[Property]':
|
||||
"""
|
||||
Provides the ability to document properties in a key/value store. This provides flexibility to include data not
|
||||
officially supported in the standard without having to use additional namespaces or create extensions.
|
||||
|
||||
Return:
|
||||
Set of `Property`
|
||||
"""
|
||||
return self._properties
|
||||
|
||||
@properties.setter
|
||||
def properties(self, properties: Iterable[Property]) -> None:
|
||||
self._properties = SortedSet(properties)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
|
||||
@serializable.xml_sequence(8)
|
||||
def external_references(self) -> 'SortedSet[ExternalReference]':
|
||||
"""
|
||||
Provides the ability to document external references related to the component or to the project the component
|
||||
describes.
|
||||
|
||||
Returns:
|
||||
Set of `ExternalReference`
|
||||
"""
|
||||
return self._external_references
|
||||
|
||||
@external_references.setter
|
||||
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
|
||||
self._external_references = SortedSet(external_references)
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
# all properties are optional - so need to compare all, in hope that one is unique
|
||||
return _ComparableTuple((
|
||||
self.identifier, self.bom_ref.value,
|
||||
self.title, self.text,
|
||||
_ComparableTuple(self.descriptions),
|
||||
_ComparableTuple(self.open_cre), self.parent, _ComparableTuple(self.properties),
|
||||
_ComparableTuple(self.external_references)
|
||||
))
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, Requirement):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Requirement):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Requirement bom-ref={self._bom_ref}, identifier={self.identifier}, ' \
|
||||
f'title={self.title}, text={self.text}, parent={self.parent}>'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class Level:
|
||||
"""
|
||||
Level of compliance for a standard.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
bom_ref: Optional[Union[str, BomRef]] = None,
|
||||
identifier: Optional[str] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
requirements: Optional[Iterable[Union[str, BomRef]]] = None,
|
||||
) -> None:
|
||||
self._bom_ref = _bom_ref_from_str(bom_ref)
|
||||
self.identifier = identifier
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.requirements = requirements or () # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(BomRef)
|
||||
@serializable.json_name('bom-ref')
|
||||
@serializable.xml_name('bom-ref')
|
||||
@serializable.xml_attribute()
|
||||
def bom_ref(self) -> BomRef:
|
||||
"""
|
||||
An optional identifier which can be used to reference the level elsewhere in the BOM.
|
||||
Every bom-ref MUST be unique within the BOM.
|
||||
|
||||
Returns:
|
||||
`BomRef`
|
||||
"""
|
||||
return self._bom_ref
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
def identifier(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The identifier of the level.
|
||||
"""
|
||||
return self._identifier
|
||||
|
||||
@identifier.setter
|
||||
def identifier(self, identifier: Optional[str]) -> None:
|
||||
self._identifier = identifier
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
def title(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The title of the level.
|
||||
"""
|
||||
return self._title
|
||||
|
||||
@title.setter
|
||||
def title(self, title: Optional[str]) -> None:
|
||||
self._title = title
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
def description(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The description of the level.
|
||||
"""
|
||||
return self._description
|
||||
|
||||
@description.setter
|
||||
def description(self, description: Optional[str]) -> None:
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(4)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'requirement')
|
||||
def requirements(self) -> 'SortedSet[BomRef]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of requirements associated with the level.
|
||||
"""
|
||||
return self._requirements
|
||||
|
||||
@requirements.setter
|
||||
def requirements(self, requirements: Iterable[Union[str, BomRef]]) -> None:
|
||||
self._requirements = SortedSet(map(_bom_ref_from_str, # type: ignore[arg-type]
|
||||
requirements))
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
# all properties are optional - so need to compare all, in hope that one is unique
|
||||
return _ComparableTuple((
|
||||
self.identifier, self.bom_ref.value,
|
||||
self.title, self.description,
|
||||
_ComparableTuple(self.requirements)
|
||||
))
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, Level):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Level):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Level bom-ref={self.bom_ref}, identifier={self.identifier}, ' \
|
||||
f'title={self.title}, description={self.description}>'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class Standard:
|
||||
"""
|
||||
A standard of regulations, industry or organizational-specific standards, maturity models, best practices,
|
||||
or any other requirements.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
bom_ref: Optional[Union[str, BomRef]] = None,
|
||||
name: Optional[str] = None,
|
||||
version: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
owner: Optional[str] = None,
|
||||
requirements: Optional[Iterable[Requirement]] = None,
|
||||
levels: Optional[Iterable[Level]] = None,
|
||||
external_references: Optional[Iterable['ExternalReference']] = None
|
||||
# TODO: signature
|
||||
) -> None:
|
||||
self._bom_ref = _bom_ref_from_str(bom_ref)
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.description = description
|
||||
self.owner = owner
|
||||
self.requirements = requirements or () # type:ignore[assignment]
|
||||
self.levels = levels or () # type:ignore[assignment]
|
||||
self.external_references = external_references or () # type:ignore[assignment]
|
||||
# TODO: signature
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(BomRef)
|
||||
@serializable.json_name('bom-ref')
|
||||
@serializable.xml_name('bom-ref')
|
||||
@serializable.xml_attribute()
|
||||
def bom_ref(self) -> BomRef:
|
||||
"""
|
||||
An optional identifier which can be used to reference the standard elsewhere in the BOM. Every bom-ref MUST be
|
||||
unique within the BOM.
|
||||
|
||||
Returns:
|
||||
`BomRef`
|
||||
"""
|
||||
return self._bom_ref
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The name of the standard
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: Optional[str]) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
def version(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The version of the standard
|
||||
"""
|
||||
return self._version
|
||||
|
||||
@version.setter
|
||||
def version(self, version: Optional[str]) -> None:
|
||||
self._version = version
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
def description(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The description of the standard
|
||||
"""
|
||||
return self._description
|
||||
|
||||
@description.setter
|
||||
def description(self, description: Optional[str]) -> None:
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(4)
|
||||
def owner(self) -> Optional[str]:
|
||||
"""
|
||||
Returns:
|
||||
The owner of the standard, often the entity responsible for its release.
|
||||
"""
|
||||
return self._owner
|
||||
|
||||
@owner.setter
|
||||
def owner(self, owner: Optional[str]) -> None:
|
||||
self._owner = owner
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'requirement')
|
||||
@serializable.xml_sequence(5)
|
||||
def requirements(self) -> 'SortedSet[Requirement]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of requirements comprising the standard.
|
||||
"""
|
||||
return self._requirements
|
||||
|
||||
@requirements.setter
|
||||
def requirements(self, requirements: Iterable[Requirement]) -> None:
|
||||
self._requirements = SortedSet(requirements)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'level')
|
||||
@serializable.xml_sequence(6)
|
||||
def levels(self) -> 'SortedSet[Level]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of levels associated with the standard. Some standards have different levels of compliance.
|
||||
"""
|
||||
return self._levels
|
||||
|
||||
@levels.setter
|
||||
def levels(self, levels: Iterable[Level]) -> None:
|
||||
self._levels = SortedSet(levels)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
|
||||
@serializable.xml_sequence(7)
|
||||
def external_references(self) -> 'SortedSet[ExternalReference]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of external references associated with the standard.
|
||||
"""
|
||||
return self._external_references
|
||||
|
||||
@external_references.setter
|
||||
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
|
||||
self._external_references = SortedSet(external_references)
|
||||
|
||||
# @property
|
||||
# @serializable.xml_sequence(8)
|
||||
# # MUST NOT RENDER FOR XML -- this is JSON only
|
||||
# def signature(self) -> ...:
|
||||
# ...
|
||||
#
|
||||
# @signature.setter
|
||||
# def levels(self, signature: ...) -> None:
|
||||
# ...
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
# all properties are optional - so need to apply all, in hope that one is unique
|
||||
return _ComparableTuple((
|
||||
self.name, self.version,
|
||||
self.bom_ref.value,
|
||||
self.description, self.owner,
|
||||
_ComparableTuple(self.requirements), _ComparableTuple(self.levels),
|
||||
_ComparableTuple(self.external_references)
|
||||
))
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, Standard):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Standard):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Standard bom-ref={self.bom_ref}, ' \
|
||||
f'name={self.name}, version={self.version}, ' \
|
||||
f'description={self.description}, owner={self.owner}>'
|
||||
|
||||
|
||||
@serializable.serializable_class(name='definitions')
|
||||
class Definitions:
|
||||
"""
|
||||
The repository for definitions
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
standards: Optional[Iterable[Standard]] = None
|
||||
) -> None:
|
||||
self.standards = standards or () # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'standard')
|
||||
@serializable.xml_sequence(1)
|
||||
def standards(self) -> 'SortedSet[Standard]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of Standards
|
||||
"""
|
||||
return self._standards
|
||||
|
||||
@standards.setter
|
||||
def standards(self, standards: Iterable[Standard]) -> None:
|
||||
self._standards = SortedSet(standards)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return len(self._standards) > 0
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
# all properties are optional - so need to apply all, in hope that one is unique
|
||||
return _ComparableTuple(self._standards)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Definitions):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, Definitions):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Definitions standards={self.standards!r} >'
|
||||
@@ -0,0 +1,116 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Iterable, List, Optional, Set
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..exception.serialization import SerializationOfUnexpectedValueException
|
||||
from .bom_ref import BomRef
|
||||
|
||||
|
||||
class _DependencyRepositorySerializationHelper(serializable.helpers.BaseHelper):
|
||||
""" THIS CLASS IS NON-PUBLIC API """
|
||||
|
||||
@classmethod
|
||||
def serialize(cls, o: Any) -> List[str]:
|
||||
if isinstance(o, (SortedSet, set)):
|
||||
return [str(i.ref) for i in o]
|
||||
raise SerializationOfUnexpectedValueException(
|
||||
f'Attempt to serialize a non-DependencyRepository: {o!r}')
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, o: Any) -> Set['Dependency']:
|
||||
dependencies = set()
|
||||
if isinstance(o, list):
|
||||
for v in o:
|
||||
dependencies.add(Dependency(ref=BomRef(value=v)))
|
||||
return dependencies
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class Dependency:
|
||||
"""
|
||||
Models a Dependency within a BOM.
|
||||
|
||||
.. note::
|
||||
See https://cyclonedx.org/docs/1.6/xml/#type_dependencyType
|
||||
"""
|
||||
|
||||
def __init__(self, ref: BomRef, dependencies: Optional[Iterable['Dependency']] = None) -> None:
|
||||
self.ref = ref
|
||||
self.dependencies = dependencies or [] # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(BomRef)
|
||||
@serializable.xml_attribute()
|
||||
def ref(self) -> BomRef:
|
||||
return self._ref
|
||||
|
||||
@ref.setter
|
||||
def ref(self, ref: BomRef) -> None:
|
||||
self._ref = ref
|
||||
|
||||
@property
|
||||
@serializable.json_name('dependsOn')
|
||||
@serializable.type_mapping(_DependencyRepositorySerializationHelper)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'dependency')
|
||||
def dependencies(self) -> 'SortedSet[Dependency]':
|
||||
return self._dependencies
|
||||
|
||||
@dependencies.setter
|
||||
def dependencies(self, dependencies: Iterable['Dependency']) -> None:
|
||||
self._dependencies = SortedSet(dependencies)
|
||||
|
||||
def dependencies_as_bom_refs(self) -> Set[BomRef]:
|
||||
return set(map(lambda d: d.ref, self.dependencies))
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.ref, _ComparableTuple(self.dependencies)
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Dependency):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, Dependency):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Dependency ref={self.ref!r}, targets={len(self.dependencies)}>'
|
||||
|
||||
|
||||
class Dependable(ABC):
|
||||
"""
|
||||
Dependable objects can be part of the Dependency Graph
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def bom_ref(self) -> BomRef:
|
||||
... # pragma: no cover
|
||||
@@ -0,0 +1,106 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
This set of classes represents the data about Impact Analysis.
|
||||
|
||||
Impact Analysis is new for CycloneDX schema version 1.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema extension definition https://cyclonedx.org/docs/1.6
|
||||
"""
|
||||
|
||||
|
||||
from enum import Enum
|
||||
|
||||
import py_serializable as serializable
|
||||
|
||||
|
||||
@serializable.serializable_enum
|
||||
class ImpactAnalysisAffectedStatus(str, Enum):
|
||||
"""
|
||||
Enum object that defines the permissible impact analysis affected states.
|
||||
|
||||
The vulnerability status of a given version or range of versions of a product.
|
||||
|
||||
The statuses 'affected' and 'unaffected' indicate that the version is affected or unaffected by the vulnerability.
|
||||
|
||||
The status 'unknown' indicates that it is unknown or unspecified whether the given version is affected. There can
|
||||
be many reasons for an 'unknown' status, including that an investigation has not been undertaken or that a vendor
|
||||
has not disclosed the status.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#type_impactAnalysisAffectedStatusType
|
||||
"""
|
||||
|
||||
AFFECTED = 'affected'
|
||||
UNAFFECTED = 'unaffected'
|
||||
UNKNOWN = 'unknown'
|
||||
|
||||
|
||||
@serializable.serializable_enum
|
||||
class ImpactAnalysisJustification(str, Enum):
|
||||
"""
|
||||
Enum object that defines the rationale of why the impact analysis state was asserted.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#type_impactAnalysisJustificationType
|
||||
"""
|
||||
|
||||
CODE_NOT_PRESENT = 'code_not_present'
|
||||
CODE_NOT_REACHABLE = 'code_not_reachable'
|
||||
PROTECTED_AT_PERIMITER = 'protected_at_perimeter'
|
||||
PROTECTED_AT_RUNTIME = 'protected_at_runtime'
|
||||
PROTECTED_BY_COMPILER = 'protected_by_compiler'
|
||||
PROTECTED_BY_MITIGATING_CONTROL = 'protected_by_mitigating_control'
|
||||
REQUIRES_CONFIGURATION = 'requires_configuration'
|
||||
REQUIRES_DEPENDENCY = 'requires_dependency'
|
||||
REQUIRES_ENVIRONMENT = 'requires_environment'
|
||||
|
||||
|
||||
@serializable.serializable_enum
|
||||
class ImpactAnalysisResponse(str, Enum):
|
||||
"""
|
||||
Enum object that defines the valid rationales as to why the impact analysis state was asserted.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#type_impactAnalysisResponsesType
|
||||
"""
|
||||
|
||||
CAN_NOT_FIX = 'can_not_fix'
|
||||
ROLLBACK = 'rollback'
|
||||
UPDATE = 'update'
|
||||
WILL_NOT_FIX = 'will_not_fix'
|
||||
WORKAROUND_AVAILABLE = 'workaround_available'
|
||||
|
||||
|
||||
@serializable.serializable_enum
|
||||
class ImpactAnalysisState(str, Enum):
|
||||
"""
|
||||
Enum object that defines the permissible impact analysis states.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#type_impactAnalysisStateType
|
||||
"""
|
||||
|
||||
RESOLVED = 'resolved'
|
||||
RESOLVED_WITH_PEDIGREE = 'resolved_with_pedigree'
|
||||
EXPLOITABLE = 'exploitable'
|
||||
IN_TRIAGE = 'in_triage'
|
||||
FALSE_POSITIVE = 'false_positive'
|
||||
NOT_AFFECTED = 'not_affected'
|
||||
@@ -0,0 +1,250 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any, Iterable, Optional
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from . import XsUri
|
||||
|
||||
|
||||
@serializable.serializable_enum
|
||||
class IssueClassification(str, Enum):
|
||||
"""
|
||||
This is our internal representation of the enum `issueClassification`.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/xml/#type_issueClassification
|
||||
"""
|
||||
DEFECT = 'defect'
|
||||
ENHANCEMENT = 'enhancement'
|
||||
SECURITY = 'security'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class IssueTypeSource:
|
||||
"""
|
||||
This is our internal representation ofa source within the IssueType complex type that can be used in multiple
|
||||
places within a CycloneDX BOM document.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/xml/#type_issueType
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
name: Optional[str] = None,
|
||||
url: Optional[XsUri] = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.url = url
|
||||
|
||||
@property
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
The name of the source. For example "National Vulnerability Database", "NVD", and "Apache".
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: Optional[str]) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def url(self) -> Optional[XsUri]:
|
||||
"""
|
||||
Optional url of the issue documentation as provided by the source.
|
||||
|
||||
Returns:
|
||||
`XsUri` if set else `None`
|
||||
"""
|
||||
return self._url
|
||||
|
||||
@url.setter
|
||||
def url(self, url: Optional[XsUri]) -> None:
|
||||
self._url = url
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.name, self.url
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, IssueTypeSource):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, IssueTypeSource):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<IssueTypeSource name={self._name}, url={self.url}>'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class IssueType:
|
||||
"""
|
||||
This is our internal representation of an IssueType complex type that can be used in multiple places within
|
||||
a CycloneDX BOM document.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/xml/#type_issueType
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
type: IssueClassification,
|
||||
id: Optional[str] = None,
|
||||
name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
source: Optional[IssueTypeSource] = None,
|
||||
references: Optional[Iterable[XsUri]] = None,
|
||||
) -> None:
|
||||
self.type = type
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.source = source
|
||||
self.references = references or [] # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.xml_attribute()
|
||||
def type(self) -> IssueClassification:
|
||||
"""
|
||||
Specifies the type of issue.
|
||||
|
||||
Returns:
|
||||
`IssueClassification`
|
||||
"""
|
||||
return self._type
|
||||
|
||||
@type.setter
|
||||
def type(self, type: IssueClassification) -> None:
|
||||
self._type = type
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def id(self) -> Optional[str]:
|
||||
"""
|
||||
The identifier of the issue assigned by the source of the issue.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._id
|
||||
|
||||
@id.setter
|
||||
def id(self, id: Optional[str]) -> None:
|
||||
self._id = id
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
The name of the issue.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: Optional[str]) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def description(self) -> Optional[str]:
|
||||
"""
|
||||
A description of the issue.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._description
|
||||
|
||||
@description.setter
|
||||
def description(self, description: Optional[str]) -> None:
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(4)
|
||||
def source(self) -> Optional[IssueTypeSource]:
|
||||
"""
|
||||
The source of this issue.
|
||||
|
||||
Returns:
|
||||
`IssueTypeSource` if set else `None`
|
||||
"""
|
||||
return self._source
|
||||
|
||||
@source.setter
|
||||
def source(self, source: Optional[IssueTypeSource]) -> None:
|
||||
self._source = source
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'url')
|
||||
@serializable.xml_sequence(5)
|
||||
def references(self) -> 'SortedSet[XsUri]':
|
||||
"""
|
||||
Any reference URLs related to this issue.
|
||||
|
||||
Returns:
|
||||
Set of `XsUri`
|
||||
"""
|
||||
return self._references
|
||||
|
||||
@references.setter
|
||||
def references(self, references: Iterable[XsUri]) -> None:
|
||||
self._references = SortedSet(references)
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.type, self.id, self.name, self.description, self.source,
|
||||
_ComparableTuple(self.references)
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, IssueType):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, IssueType):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<IssueType type={self.type}, id={self.id}, name={self.name}>'
|
||||
@@ -0,0 +1,463 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
License related things
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
from json import loads as json_loads
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
|
||||
from warnings import warn
|
||||
from xml.etree.ElementTree import Element # nosec B405
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..exception.model import MutuallyExclusivePropertiesException
|
||||
from ..exception.serialization import CycloneDxDeserializationException
|
||||
from ..schema.schema import SchemaVersion1Dot6
|
||||
from . import AttachedText, XsUri
|
||||
|
||||
|
||||
@serializable.serializable_enum
|
||||
class LicenseAcknowledgement(str, Enum):
|
||||
"""
|
||||
This is our internal representation of the `type_licenseAcknowledgementEnumerationType` ENUM type
|
||||
within the CycloneDX standard.
|
||||
|
||||
.. note::
|
||||
Introduced in CycloneDX v1.6
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema for hashType:
|
||||
https://cyclonedx.org/docs/1.6/#type_licenseAcknowledgementEnumerationType
|
||||
"""
|
||||
|
||||
CONCLUDED = 'concluded'
|
||||
DECLARED = 'declared'
|
||||
|
||||
|
||||
# In an error, the name of the enum was `LicenseExpressionAcknowledgement`.
|
||||
# Even though this was changed, there might be some downstream usage of this symbol, so we keep it around ...
|
||||
LicenseExpressionAcknowledgement = LicenseAcknowledgement
|
||||
"""Deprecated alias for :class:`LicenseAcknowledgement`"""
|
||||
|
||||
|
||||
@serializable.serializable_class(name='license')
|
||||
class DisjunctiveLicense:
|
||||
"""
|
||||
This is our internal representation of `licenseType` complex type that can be used in multiple places within
|
||||
a CycloneDX BOM document.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/json/#components_items_licenses
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
id: Optional[str] = None, name: Optional[str] = None,
|
||||
text: Optional[AttachedText] = None, url: Optional[XsUri] = None,
|
||||
acknowledgement: Optional[LicenseAcknowledgement] = None,
|
||||
) -> None:
|
||||
if not id and not name:
|
||||
raise MutuallyExclusivePropertiesException('Either `id` or `name` MUST be supplied')
|
||||
if id and name:
|
||||
warn(
|
||||
'Both `id` and `name` have been supplied - `name` will be ignored!',
|
||||
category=RuntimeWarning, stacklevel=1
|
||||
)
|
||||
self._id = id
|
||||
self._name = name if not id else None
|
||||
self._text = text
|
||||
self._url = url
|
||||
self._acknowledgement = acknowledgement
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
def id(self) -> Optional[str]:
|
||||
"""
|
||||
A SPDX license ID.
|
||||
|
||||
.. note::
|
||||
See the list of expected values:
|
||||
https://cyclonedx.org/docs/1.6/json/#components_items_licenses_items_license_id
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._id
|
||||
|
||||
@id.setter
|
||||
def id(self, id: Optional[str]) -> None:
|
||||
self._id = id
|
||||
if id is not None:
|
||||
self._name = None
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
If SPDX does not define the license used, this field may be used to provide the license name.
|
||||
|
||||
Returns:
|
||||
`str` or `None`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: Optional[str]) -> None:
|
||||
self._name = name
|
||||
if name is not None:
|
||||
self._id = None
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
def text(self) -> Optional[AttachedText]:
|
||||
"""
|
||||
Specifies the optional full text of the attachment
|
||||
|
||||
Returns:
|
||||
`AttachedText` else `None`
|
||||
"""
|
||||
return self._text
|
||||
|
||||
@text.setter
|
||||
def text(self, text: Optional[AttachedText]) -> None:
|
||||
self._text = text
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
def url(self) -> Optional[XsUri]:
|
||||
"""
|
||||
The URL to the attachment file. If the attachment is a license or BOM, an externalReference should also be
|
||||
specified for completeness.
|
||||
|
||||
Returns:
|
||||
`XsUri` or `None`
|
||||
"""
|
||||
return self._url
|
||||
|
||||
@url.setter
|
||||
def url(self, url: Optional[XsUri]) -> None:
|
||||
self._url = url
|
||||
|
||||
# @property
|
||||
# ...
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @serializable.view(SchemaVersion1Dot6)
|
||||
# @serializable.xml_sequence(5)
|
||||
# def licensing(self) -> ...:
|
||||
# ... # TODO since CDX1.5
|
||||
#
|
||||
# @licensing.setter
|
||||
# def licensing(self, ...) -> None:
|
||||
# ... # TODO since CDX1.5
|
||||
|
||||
# @property
|
||||
# ...
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @serializable.view(SchemaVersion1Dot6)
|
||||
# @serializable.xml_sequence(6)
|
||||
# def properties(self) -> ...:
|
||||
# ... # TODO since CDX1.5
|
||||
#
|
||||
# @licensing.setter
|
||||
# def properties(self, ...) -> None:
|
||||
# ... # TODO since CDX1.5
|
||||
|
||||
# @property
|
||||
# @serializable.json_name('bom-ref')
|
||||
# @serializable.type_mapping(BomRefHelper)
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @serializable.view(SchemaVersion1Dot6)
|
||||
# @serializable.xml_attribute()
|
||||
# @serializable.xml_name('bom-ref')
|
||||
# def bom_ref(self) -> BomRef:
|
||||
# ... # TODO since CDX1.5
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_attribute()
|
||||
def acknowledgement(self) -> Optional[LicenseAcknowledgement]:
|
||||
"""
|
||||
Declared licenses and concluded licenses represent two different stages in the licensing process within
|
||||
software development.
|
||||
|
||||
Declared licenses refer to the initial intention of the software authors regarding the
|
||||
licensing terms under which their code is released. On the other hand, concluded licenses are the result of a
|
||||
comprehensive analysis of the project's codebase to identify and confirm the actual licenses of the components
|
||||
used, which may differ from the initially declared licenses. While declared licenses provide an upfront
|
||||
indication of the licensing intentions, concluded licenses offer a more thorough understanding of the actual
|
||||
licensing within a project, facilitating proper compliance and risk management. Observed licenses are defined
|
||||
in evidence.licenses. Observed licenses form the evidence necessary to substantiate a concluded license.
|
||||
|
||||
Returns:
|
||||
`LicenseAcknowledgement` or `None`
|
||||
"""
|
||||
return self._acknowledgement
|
||||
|
||||
@acknowledgement.setter
|
||||
def acknowledgement(self, acknowledgement: Optional[LicenseAcknowledgement]) -> None:
|
||||
self._acknowledgement = acknowledgement
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self._acknowledgement,
|
||||
self._id, self._name,
|
||||
self._url,
|
||||
self._text,
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, DisjunctiveLicense):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, DisjunctiveLicense):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
if isinstance(other, LicenseExpression):
|
||||
return False # self after any LicenseExpression
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<License id={self._id!r}, name={self._name!r}>'
|
||||
|
||||
|
||||
@serializable.serializable_class(name='expression')
|
||||
class LicenseExpression:
|
||||
"""
|
||||
This is our internal representation of `licenseType`'s expression type that can be used in multiple places within
|
||||
a CycloneDX BOM document.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition:
|
||||
https://cyclonedx.org/docs/1.6/json/#components_items_licenses_items_expression
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, value: str, *,
|
||||
acknowledgement: Optional[LicenseAcknowledgement] = None,
|
||||
) -> None:
|
||||
self._value = value
|
||||
self._acknowledgement = acknowledgement
|
||||
|
||||
@property
|
||||
@serializable.xml_name('.')
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
@serializable.json_name('expression')
|
||||
def value(self) -> str:
|
||||
"""
|
||||
Value of this LicenseExpression.
|
||||
|
||||
Returns:
|
||||
`str`
|
||||
"""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, value: str) -> None:
|
||||
self._value = value
|
||||
|
||||
# @property
|
||||
# @serializable.json_name('bom-ref')
|
||||
# @serializable.type_mapping(BomRefHelper)
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @serializable.view(SchemaVersion1Dot6)
|
||||
# @serializable.xml_attribute()
|
||||
# @serializable.xml_name('bom-ref')
|
||||
# def bom_ref(self) -> BomRef:
|
||||
# ... # TODO since CDX1.5
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_attribute()
|
||||
def acknowledgement(self) -> Optional[LicenseAcknowledgement]:
|
||||
"""
|
||||
Declared licenses and concluded licenses represent two different stages in the licensing process within
|
||||
software development.
|
||||
|
||||
Declared licenses refer to the initial intention of the software authors regarding the
|
||||
licensing terms under which their code is released. On the other hand, concluded licenses are the result of a
|
||||
comprehensive analysis of the project's codebase to identify and confirm the actual licenses of the components
|
||||
used, which may differ from the initially declared licenses. While declared licenses provide an upfront
|
||||
indication of the licensing intentions, concluded licenses offer a more thorough understanding of the actual
|
||||
licensing within a project, facilitating proper compliance and risk management. Observed licenses are defined
|
||||
in evidence.licenses. Observed licenses form the evidence necessary to substantiate a concluded license.
|
||||
|
||||
Returns:
|
||||
`LicenseAcknowledgement` or `None`
|
||||
"""
|
||||
return self._acknowledgement
|
||||
|
||||
@acknowledgement.setter
|
||||
def acknowledgement(self, acknowledgement: Optional[LicenseAcknowledgement]) -> None:
|
||||
self._acknowledgement = acknowledgement
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self._acknowledgement,
|
||||
self._value,
|
||||
))
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, LicenseExpression):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, LicenseExpression):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
if isinstance(other, DisjunctiveLicense):
|
||||
return True # self before any DisjunctiveLicense
|
||||
return NotImplemented
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<LicenseExpression value={self._value!r}>'
|
||||
|
||||
|
||||
License = Union[LicenseExpression, DisjunctiveLicense]
|
||||
"""TypeAlias for a union of supported license models.
|
||||
|
||||
- :class:`LicenseExpression`
|
||||
- :class:`DisjunctiveLicense`
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
# workaround for https://github.com/python/mypy/issues/5264
|
||||
# this code path is taken when static code analysis or documentation tools runs through.
|
||||
class LicenseRepository(SortedSet[License]):
|
||||
"""Collection of :class:`License`.
|
||||
|
||||
This is a `set`, not a `list`. Order MUST NOT matter here.
|
||||
If you wanted a certain order, then you should also express whether the items are concat by `AND` or `OR`.
|
||||
If you wanted to do so, you should use :class:`LicenseExpression`.
|
||||
|
||||
As a model, this MUST accept multiple :class:`LicenseExpression` along with
|
||||
multiple :class:`DisjunctiveLicense`, as this was an accepted in CycloneDX JSON before v1.5.
|
||||
So for modeling purposes, this is supported.
|
||||
Denormalizers/deserializers will be thankful.
|
||||
The normalization/serialization process SHOULD take care of these facts and do what is needed.
|
||||
"""
|
||||
|
||||
else:
|
||||
class LicenseRepository(SortedSet):
|
||||
"""Collection of :class:`License`.
|
||||
|
||||
This is a `set`, not a `list`. Order MUST NOT matter here.
|
||||
If you wanted a certain order, then you should also express whether the items are concat by `AND` or `OR`.
|
||||
If you wanted to do so, you should use :class:`LicenseExpression`.
|
||||
|
||||
As a model, this MUST accept multiple :class:`LicenseExpression` along with
|
||||
multiple :class:`DisjunctiveLicense`, as this was an accepted in CycloneDX JSON before v1.5.
|
||||
So for modeling purposes, this is supported.
|
||||
Denormalizers/deserializers will be thankful.
|
||||
The normalization/serialization process SHOULD take care of these facts and do what is needed.
|
||||
"""
|
||||
|
||||
|
||||
class _LicenseRepositorySerializationHelper(serializable.helpers.BaseHelper):
|
||||
""" THIS CLASS IS NON-PUBLIC API """
|
||||
|
||||
@classmethod
|
||||
def json_normalize(cls, o: LicenseRepository, *,
|
||||
view: Optional[Type[serializable.ViewType]],
|
||||
**__: Any) -> Any:
|
||||
if len(o) == 0:
|
||||
return None
|
||||
expression = next((li for li in o if isinstance(li, LicenseExpression)), None)
|
||||
if expression:
|
||||
# mixed license expression and license? this is an invalid constellation according to schema!
|
||||
# see https://github.com/CycloneDX/specification/pull/205
|
||||
# but models need to allow it for backwards compatibility with JSON CDX < 1.5
|
||||
return [json_loads(expression.as_json(view_=view))] # type:ignore[attr-defined]
|
||||
return [
|
||||
{'license': json_loads(
|
||||
li.as_json( # type:ignore[attr-defined]
|
||||
view_=view)
|
||||
)}
|
||||
for li in o
|
||||
if isinstance(li, DisjunctiveLicense)
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def json_denormalize(cls, o: List[Dict[str, Any]],
|
||||
**__: Any) -> LicenseRepository:
|
||||
repo = LicenseRepository()
|
||||
for li in o:
|
||||
if 'license' in li:
|
||||
repo.add(DisjunctiveLicense.from_json( # type:ignore[attr-defined]
|
||||
li['license']))
|
||||
elif 'expression' in li:
|
||||
repo.add(LicenseExpression.from_json( # type:ignore[attr-defined]
|
||||
li
|
||||
))
|
||||
else:
|
||||
raise CycloneDxDeserializationException(f'unexpected: {li!r}')
|
||||
return repo
|
||||
|
||||
@classmethod
|
||||
def xml_normalize(cls, o: LicenseRepository, *,
|
||||
element_name: str,
|
||||
view: Optional[Type[serializable.ViewType]],
|
||||
xmlns: Optional[str],
|
||||
**__: Any) -> Optional[Element]:
|
||||
if len(o) == 0:
|
||||
return None
|
||||
elem = Element(element_name)
|
||||
expression = next((li for li in o if isinstance(li, LicenseExpression)), None)
|
||||
if expression:
|
||||
# mixed license expression and license? this is an invalid constellation according to schema!
|
||||
# see https://github.com/CycloneDX/specification/pull/205
|
||||
# but models need to allow it for backwards compatibility with JSON CDX < 1.5
|
||||
elem.append(expression.as_xml( # type:ignore[attr-defined]
|
||||
view_=view, as_string=False, element_name='expression', xmlns=xmlns))
|
||||
else:
|
||||
elem.extend(
|
||||
li.as_xml( # type:ignore[attr-defined]
|
||||
view_=view, as_string=False, element_name='license', xmlns=xmlns)
|
||||
for li in o
|
||||
if isinstance(li, DisjunctiveLicense)
|
||||
)
|
||||
return elem
|
||||
|
||||
@classmethod
|
||||
def xml_denormalize(cls, o: Element,
|
||||
default_ns: Optional[str],
|
||||
**__: Any) -> LicenseRepository:
|
||||
repo = LicenseRepository()
|
||||
for li in o:
|
||||
tag = li.tag if default_ns is None else li.tag.replace(f'{{{default_ns}}}', '')
|
||||
if tag == 'license':
|
||||
repo.add(DisjunctiveLicense.from_xml( # type:ignore[attr-defined]
|
||||
li, default_ns))
|
||||
elif tag == 'expression':
|
||||
repo.add(LicenseExpression.from_xml( # type:ignore[attr-defined]
|
||||
li, default_ns))
|
||||
else:
|
||||
raise CycloneDxDeserializationException(f'unexpected: {li!r}')
|
||||
return repo
|
||||
@@ -0,0 +1,248 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
"""
|
||||
This set of classes represents the lifecycles types in the CycloneDX standard.
|
||||
|
||||
.. note::
|
||||
Introduced in CycloneDX v1.5
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema for lifecycles: https://cyclonedx.org/docs/1.6/#metadata_lifecycles
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
from json import loads as json_loads
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
|
||||
from xml.etree.ElementTree import Element # nosec B405
|
||||
|
||||
import py_serializable as serializable
|
||||
from py_serializable.helpers import BaseHelper
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..exception.serialization import CycloneDxDeserializationException
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from py_serializable import ViewType
|
||||
|
||||
|
||||
@serializable.serializable_enum
|
||||
class LifecyclePhase(str, Enum):
|
||||
"""
|
||||
Enum object that defines the permissible 'phase' for a Lifecycle according to the CycloneDX schema.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#type_classification
|
||||
"""
|
||||
DESIGN = 'design'
|
||||
PRE_BUILD = 'pre-build'
|
||||
BUILD = 'build'
|
||||
POST_BUILD = 'post-build'
|
||||
OPERATIONS = 'operations'
|
||||
DISCOVERY = 'discovery'
|
||||
DECOMMISSION = 'decommission'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class PredefinedLifecycle:
|
||||
"""
|
||||
Object that defines pre-defined phases in the product lifecycle.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#metadata_lifecycles
|
||||
"""
|
||||
|
||||
def __init__(self, phase: LifecyclePhase) -> None:
|
||||
self._phase = phase
|
||||
|
||||
@property
|
||||
def phase(self) -> LifecyclePhase:
|
||||
return self._phase
|
||||
|
||||
@phase.setter
|
||||
def phase(self, phase: LifecyclePhase) -> None:
|
||||
self._phase = phase
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self._phase)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, PredefinedLifecycle):
|
||||
return self._phase == other._phase
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, PredefinedLifecycle):
|
||||
return self._phase < other._phase
|
||||
if isinstance(other, NamedLifecycle):
|
||||
return True # put PredefinedLifecycle before any NamedLifecycle
|
||||
return NotImplemented
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<PredefinedLifecycle phase={self._phase}>'
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class NamedLifecycle:
|
||||
"""
|
||||
Object that defines custom state in the product lifecycle.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#metadata_lifecycles
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, *, description: Optional[str] = None) -> None:
|
||||
self._name = name
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> str:
|
||||
"""
|
||||
Name of the lifecycle phase.
|
||||
|
||||
Returns:
|
||||
`str`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: str) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def description(self) -> Optional[str]:
|
||||
"""
|
||||
Description of the lifecycle phase.
|
||||
|
||||
Returns:
|
||||
`str`
|
||||
"""
|
||||
return self._description
|
||||
|
||||
@description.setter
|
||||
def description(self, description: Optional[str]) -> None:
|
||||
self._description = description
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self._name, self._description
|
||||
))
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, NamedLifecycle):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, NamedLifecycle):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
if isinstance(other, PredefinedLifecycle):
|
||||
return False # put NamedLifecycle after any PredefinedLifecycle
|
||||
return NotImplemented
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<NamedLifecycle name={self._name}>'
|
||||
|
||||
|
||||
Lifecycle = Union[PredefinedLifecycle, NamedLifecycle]
|
||||
"""TypeAlias for a union of supported lifecycle models.
|
||||
|
||||
- :class:`PredefinedLifecycle`
|
||||
- :class:`NamedLifecycle`
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
# workaround for https://github.com/python/mypy/issues/5264
|
||||
# this code path is taken when static code analysis or documentation tools runs through.
|
||||
class LifecycleRepository(SortedSet[Lifecycle]):
|
||||
"""Collection of :class:`Lifecycle`.
|
||||
|
||||
This is a `set`, not a `list`. Order MUST NOT matter here.
|
||||
"""
|
||||
else:
|
||||
class LifecycleRepository(SortedSet):
|
||||
"""Collection of :class:`Lifecycle`.
|
||||
|
||||
This is a `set`, not a `list`. Order MUST NOT matter here.
|
||||
"""
|
||||
|
||||
|
||||
class _LifecycleRepositoryHelper(BaseHelper):
|
||||
@classmethod
|
||||
def json_normalize(cls, o: LifecycleRepository, *,
|
||||
view: Optional[Type['ViewType']],
|
||||
**__: Any) -> Any:
|
||||
if len(o) == 0:
|
||||
return None
|
||||
return [json_loads(li.as_json( # type:ignore[union-attr]
|
||||
view_=view)) for li in o]
|
||||
|
||||
@classmethod
|
||||
def json_denormalize(cls, o: List[Dict[str, Any]],
|
||||
**__: Any) -> LifecycleRepository:
|
||||
repo = LifecycleRepository()
|
||||
for li in o:
|
||||
if 'phase' in li:
|
||||
repo.add(PredefinedLifecycle.from_json( # type:ignore[attr-defined]
|
||||
li))
|
||||
elif 'name' in li:
|
||||
repo.add(NamedLifecycle.from_json( # type:ignore[attr-defined]
|
||||
li))
|
||||
else:
|
||||
raise CycloneDxDeserializationException(f'unexpected: {li!r}')
|
||||
return repo
|
||||
|
||||
@classmethod
|
||||
def xml_normalize(cls, o: LifecycleRepository, *,
|
||||
element_name: str,
|
||||
view: Optional[Type['ViewType']],
|
||||
xmlns: Optional[str],
|
||||
**__: Any) -> Optional[Element]:
|
||||
if len(o) == 0:
|
||||
return None
|
||||
elem = Element(element_name)
|
||||
for li in o:
|
||||
elem.append(li.as_xml( # type:ignore[union-attr]
|
||||
view_=view, as_string=False, element_name='lifecycle', xmlns=xmlns))
|
||||
return elem
|
||||
|
||||
@classmethod
|
||||
def xml_denormalize(cls, o: Element,
|
||||
default_ns: Optional[str],
|
||||
**__: Any) -> LifecycleRepository:
|
||||
repo = LifecycleRepository()
|
||||
ns_map = {'bom': default_ns or ''}
|
||||
# Do not iterate over `o` and do not check for expected `.tag` of items.
|
||||
# This check could have been done by schema validators before even deserializing.
|
||||
for li in o.iterfind('bom:lifecycle', ns_map):
|
||||
if li.find('bom:phase', ns_map) is not None:
|
||||
repo.add(PredefinedLifecycle.from_xml( # type:ignore[attr-defined]
|
||||
li, default_ns))
|
||||
elif li.find('bom:name', ns_map) is not None:
|
||||
repo.add(NamedLifecycle.from_xml( # type:ignore[attr-defined]
|
||||
li, default_ns))
|
||||
else:
|
||||
raise CycloneDxDeserializationException(f'unexpected content: {li!r}')
|
||||
return repo
|
||||
@@ -0,0 +1,256 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Iterable, Optional
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..model import Note, Property, XsUri
|
||||
from ..model.issue import IssueType
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class ReleaseNotes:
|
||||
"""
|
||||
This is our internal representation of a `releaseNotesType` for a Component in a BOM.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.6/#type_releaseNotesType
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
type: str, title: Optional[str] = None,
|
||||
featured_image: Optional[XsUri] = None,
|
||||
social_image: Optional[XsUri] = None,
|
||||
description: Optional[str] = None,
|
||||
timestamp: Optional[datetime] = None,
|
||||
aliases: Optional[Iterable[str]] = None,
|
||||
tags: Optional[Iterable[str]] = None,
|
||||
resolves: Optional[Iterable[IssueType]] = None,
|
||||
notes: Optional[Iterable[Note]] = None,
|
||||
properties: Optional[Iterable[Property]] = None,
|
||||
) -> None:
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.featured_image = featured_image
|
||||
self.social_image = social_image
|
||||
self.description = description
|
||||
self.timestamp = timestamp
|
||||
self.aliases = aliases or [] # type:ignore[assignment]
|
||||
self.tags = tags or [] # type:ignore[assignment]
|
||||
self.resolves = resolves or [] # type:ignore[assignment]
|
||||
self.notes = notes or [] # type:ignore[assignment]
|
||||
self.properties = properties or [] # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def type(self) -> str:
|
||||
"""
|
||||
The software versioning type.
|
||||
|
||||
It is **RECOMMENDED** that the release type use one of 'major', 'minor', 'patch', 'pre-release', or 'internal'.
|
||||
|
||||
Representing all possible software release types is not practical, so standardizing on the recommended values,
|
||||
whenever possible, is strongly encouraged.
|
||||
|
||||
* **major** = A major release may contain significant changes or may introduce breaking changes.
|
||||
* **minor** = A minor release, also known as an update, may contain a smaller number of changes than major
|
||||
releases.
|
||||
* **patch** = Patch releases are typically unplanned and may resolve defects or important security issues.
|
||||
* **pre-release** = A pre-release may include alpha, beta, or release candidates and typically have limited
|
||||
support. They provide the ability to preview a release prior to its general availability.
|
||||
* **internal** = Internal releases are not for public consumption and are intended to be used exclusively by the
|
||||
project or manufacturer that produced it.
|
||||
"""
|
||||
return self._type
|
||||
|
||||
@type.setter
|
||||
def type(self, type: str) -> None:
|
||||
self._type = type
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
def title(self) -> Optional[str]:
|
||||
"""
|
||||
The title of the release.
|
||||
"""
|
||||
return self._title
|
||||
|
||||
@title.setter
|
||||
def title(self, title: Optional[str]) -> None:
|
||||
self._title = title
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
def featured_image(self) -> Optional[XsUri]:
|
||||
"""
|
||||
The URL to an image that may be prominently displayed with the release note.
|
||||
"""
|
||||
return self._featured_image
|
||||
|
||||
@featured_image.setter
|
||||
def featured_image(self, featured_image: Optional[XsUri]) -> None:
|
||||
self._featured_image = featured_image
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(4)
|
||||
def social_image(self) -> Optional[XsUri]:
|
||||
"""
|
||||
The URL to an image that may be used in messaging on social media platforms.
|
||||
"""
|
||||
return self._social_image
|
||||
|
||||
@social_image.setter
|
||||
def social_image(self, social_image: Optional[XsUri]) -> None:
|
||||
self._social_image = social_image
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(5)
|
||||
def description(self) -> Optional[str]:
|
||||
"""
|
||||
A short description of the release.
|
||||
"""
|
||||
return self._description
|
||||
|
||||
@description.setter
|
||||
def description(self, description: Optional[str]) -> None:
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(serializable.helpers.XsdDateTime)
|
||||
@serializable.xml_sequence(6)
|
||||
def timestamp(self) -> Optional[datetime]:
|
||||
"""
|
||||
The date and time (timestamp) when the release note was created.
|
||||
"""
|
||||
return self._timestamp
|
||||
|
||||
@timestamp.setter
|
||||
def timestamp(self, timestamp: Optional[datetime]) -> None:
|
||||
self._timestamp = timestamp
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'alias')
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
@serializable.xml_sequence(7)
|
||||
def aliases(self) -> 'SortedSet[str]':
|
||||
"""
|
||||
One or more alternate names the release may be referred to. This may include unofficial terms used by
|
||||
development and marketing teams (e.g. code names).
|
||||
|
||||
Returns:
|
||||
Set of `str`
|
||||
"""
|
||||
return self._aliases
|
||||
|
||||
@aliases.setter
|
||||
def aliases(self, aliases: Iterable[str]) -> None:
|
||||
self._aliases = SortedSet(aliases)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'tag')
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
@serializable.xml_sequence(8)
|
||||
def tags(self) -> 'SortedSet[str]':
|
||||
"""
|
||||
One or more tags that may aid in search or retrieval of the release note.
|
||||
|
||||
Returns:
|
||||
Set of `str`
|
||||
"""
|
||||
return self._tags
|
||||
|
||||
@tags.setter
|
||||
def tags(self, tags: Iterable[str]) -> None:
|
||||
self._tags = SortedSet(tags)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'issue')
|
||||
@serializable.xml_sequence(9)
|
||||
def resolves(self) -> 'SortedSet[IssueType]':
|
||||
"""
|
||||
A collection of issues that have been resolved.
|
||||
|
||||
Returns:
|
||||
Set of `IssueType`
|
||||
"""
|
||||
return self._resolves
|
||||
|
||||
@resolves.setter
|
||||
def resolves(self, resolves: Iterable[IssueType]) -> None:
|
||||
self._resolves = SortedSet(resolves)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'note')
|
||||
@serializable.xml_sequence(10)
|
||||
def notes(self) -> 'SortedSet[Note]':
|
||||
"""
|
||||
Zero or more release notes containing the locale and content. Multiple note elements may be specified to support
|
||||
release notes in a wide variety of languages.
|
||||
|
||||
Returns:
|
||||
Set of `Note`
|
||||
"""
|
||||
return self._notes
|
||||
|
||||
@notes.setter
|
||||
def notes(self, notes: Iterable[Note]) -> None:
|
||||
self._notes = SortedSet(notes)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
|
||||
@serializable.xml_sequence(11)
|
||||
def properties(self) -> 'SortedSet[Property]':
|
||||
"""
|
||||
Provides the ability to document properties in a name-value store. This provides flexibility to include data not
|
||||
officially supported in the standard without having to use additional namespaces or create extensions. Unlike
|
||||
key-value stores, properties support duplicate names, each potentially having different values.
|
||||
|
||||
Returns:
|
||||
Set of `Property`
|
||||
"""
|
||||
return self._properties
|
||||
|
||||
@properties.setter
|
||||
def properties(self, properties: Iterable[Property]) -> None:
|
||||
self._properties = SortedSet(properties)
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.type, self.title, self.featured_image, self.social_image, self.description, self.timestamp,
|
||||
_ComparableTuple(self.aliases),
|
||||
_ComparableTuple(self.tags),
|
||||
_ComparableTuple(self.resolves),
|
||||
_ComparableTuple(self.notes),
|
||||
_ComparableTuple(self.properties)
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, ReleaseNotes):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<ReleaseNotes type={self.type}, title={self.title}>'
|
||||
@@ -0,0 +1,380 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
This set of classes represents the data that is possible about known Services.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema extension definition https://cyclonedx.org/docs/1.6/xml/#type_servicesType
|
||||
"""
|
||||
|
||||
|
||||
from typing import Any, Iterable, Optional, Union
|
||||
|
||||
import py_serializable as serializable
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..schema.schema import SchemaVersion1Dot3, SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6
|
||||
from . import DataClassification, ExternalReference, Property, XsUri
|
||||
from .bom_ref import BomRef
|
||||
from .contact import OrganizationalEntity
|
||||
from .dependency import Dependable
|
||||
from .license import License, LicenseRepository, _LicenseRepositorySerializationHelper
|
||||
from .release_note import ReleaseNotes
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class Service(Dependable):
|
||||
"""
|
||||
Class that models the `service` complex type in the CycloneDX schema.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX schema: https://cyclonedx.org/docs/1.6/xml/#type_service
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
name: str,
|
||||
bom_ref: Optional[Union[str, BomRef]] = None,
|
||||
provider: Optional[OrganizationalEntity] = None,
|
||||
group: Optional[str] = None,
|
||||
version: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
endpoints: Optional[Iterable[XsUri]] = None,
|
||||
authenticated: Optional[bool] = None,
|
||||
x_trust_boundary: Optional[bool] = None,
|
||||
data: Optional[Iterable[DataClassification]] = None,
|
||||
licenses: Optional[Iterable[License]] = None,
|
||||
external_references: Optional[Iterable[ExternalReference]] = None,
|
||||
properties: Optional[Iterable[Property]] = None,
|
||||
services: Optional[Iterable['Service']] = None,
|
||||
release_notes: Optional[ReleaseNotes] = None,
|
||||
) -> None:
|
||||
self._bom_ref = _bom_ref_from_str(bom_ref)
|
||||
self.provider = provider
|
||||
self.group = group
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.description = description
|
||||
self.endpoints = endpoints or [] # type:ignore[assignment]
|
||||
self.authenticated = authenticated
|
||||
self.x_trust_boundary = x_trust_boundary
|
||||
self.data = data or [] # type:ignore[assignment]
|
||||
self.licenses = licenses or [] # type:ignore[assignment]
|
||||
self.external_references = external_references or [] # type:ignore[assignment]
|
||||
self.services = services or [] # type:ignore[assignment]
|
||||
self.release_notes = release_notes
|
||||
self.properties = properties or [] # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.json_name('bom-ref')
|
||||
@serializable.type_mapping(BomRef)
|
||||
@serializable.xml_attribute()
|
||||
@serializable.xml_name('bom-ref')
|
||||
def bom_ref(self) -> BomRef:
|
||||
"""
|
||||
An optional identifier which can be used to reference the service elsewhere in the BOM. Uniqueness is enforced
|
||||
within all elements and children of the root-level bom element.
|
||||
|
||||
Returns:
|
||||
`BomRef` unique identifier for this Service
|
||||
"""
|
||||
return self._bom_ref
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
def provider(self) -> Optional[OrganizationalEntity]:
|
||||
"""
|
||||
Get the organization that provides the service.
|
||||
|
||||
Returns:
|
||||
`OrganizationalEntity` if set else `None`
|
||||
"""
|
||||
return self._provider
|
||||
|
||||
@provider.setter
|
||||
def provider(self, provider: Optional[OrganizationalEntity]) -> None:
|
||||
self._provider = provider
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def group(self) -> Optional[str]:
|
||||
"""
|
||||
The grouping name, namespace, or identifier. This will often be a shortened, single name of the company or
|
||||
project that produced the service or domain name. Whitespace and special characters should be avoided.
|
||||
|
||||
Returns:
|
||||
`str` if provided else `None`
|
||||
"""
|
||||
return self._group
|
||||
|
||||
@group.setter
|
||||
def group(self, group: Optional[str]) -> None:
|
||||
self._group = group
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> str:
|
||||
"""
|
||||
The name of the service. This will often be a shortened, single name of the service.
|
||||
|
||||
Returns:
|
||||
`str`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: str) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(4)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def version(self) -> Optional[str]:
|
||||
"""
|
||||
The service version.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._version
|
||||
|
||||
@version.setter
|
||||
def version(self, version: Optional[str]) -> None:
|
||||
self._version = version
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(5)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def description(self) -> Optional[str]:
|
||||
"""
|
||||
Specifies a description for the service.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._description
|
||||
|
||||
@description.setter
|
||||
def description(self, description: Optional[str]) -> None:
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'endpoint')
|
||||
@serializable.xml_sequence(6)
|
||||
def endpoints(self) -> 'SortedSet[XsUri]':
|
||||
"""
|
||||
A list of endpoints URI's this service provides.
|
||||
|
||||
Returns:
|
||||
Set of `XsUri`
|
||||
"""
|
||||
return self._endpoints
|
||||
|
||||
@endpoints.setter
|
||||
def endpoints(self, endpoints: Iterable[XsUri]) -> None:
|
||||
self._endpoints = SortedSet(endpoints)
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(7)
|
||||
def authenticated(self) -> Optional[bool]:
|
||||
"""
|
||||
A boolean value indicating if the service requires authentication. A value of true indicates the service
|
||||
requires authentication prior to use.
|
||||
|
||||
A value of false indicates the service does not require authentication.
|
||||
|
||||
Returns:
|
||||
`bool` if set else `None`
|
||||
"""
|
||||
return self._authenticated
|
||||
|
||||
@authenticated.setter
|
||||
def authenticated(self, authenticated: Optional[bool]) -> None:
|
||||
self._authenticated = authenticated
|
||||
|
||||
@property
|
||||
@serializable.json_name('x-trust-boundary')
|
||||
@serializable.xml_name('x-trust-boundary')
|
||||
@serializable.xml_sequence(8)
|
||||
def x_trust_boundary(self) -> Optional[bool]:
|
||||
"""
|
||||
A boolean value indicating if use of the service crosses a trust zone or boundary. A value of true indicates
|
||||
that by using the service, a trust boundary is crossed.
|
||||
|
||||
A value of false indicates that by using the service, a trust boundary is not crossed.
|
||||
|
||||
Returns:
|
||||
`bool` if set else `None`
|
||||
"""
|
||||
return self._x_trust_boundary
|
||||
|
||||
@x_trust_boundary.setter
|
||||
def x_trust_boundary(self, x_trust_boundary: Optional[bool]) -> None:
|
||||
self._x_trust_boundary = x_trust_boundary
|
||||
|
||||
# @property
|
||||
# ...
|
||||
# @serializable.view(SchemaVersion1Dot5)
|
||||
# @serializable.xml_sequence(9)
|
||||
# def trust_zone(self) -> ...:
|
||||
# ... # since CDX1.5
|
||||
#
|
||||
# @trust_zone.setter
|
||||
# def trust_zone(self, ...) -> None:
|
||||
# ... # since CDX1.5
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'classification')
|
||||
@serializable.xml_sequence(10)
|
||||
def data(self) -> 'SortedSet[DataClassification]':
|
||||
"""
|
||||
Specifies the data classification.
|
||||
|
||||
Returns:
|
||||
Set of `DataClassification`
|
||||
"""
|
||||
# TODO since CDX1.5 also supports `dataflow`, not only `DataClassification`
|
||||
return self._data
|
||||
|
||||
@data.setter
|
||||
def data(self, data: Iterable[DataClassification]) -> None:
|
||||
self._data = SortedSet(data)
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(_LicenseRepositorySerializationHelper)
|
||||
@serializable.xml_sequence(11)
|
||||
def licenses(self) -> LicenseRepository:
|
||||
"""
|
||||
A optional list of statements about how this Service is licensed.
|
||||
|
||||
Returns:
|
||||
Set of `LicenseChoice`
|
||||
"""
|
||||
# TODO since CDX1.5 also supports `dataflow`, not only `DataClassification`
|
||||
return self._licenses
|
||||
|
||||
@licenses.setter
|
||||
def licenses(self, licenses: Iterable[License]) -> None:
|
||||
self._licenses = LicenseRepository(licenses)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
|
||||
@serializable.xml_sequence(12)
|
||||
def external_references(self) -> 'SortedSet[ExternalReference]':
|
||||
"""
|
||||
Provides the ability to document external references related to the Service.
|
||||
|
||||
Returns:
|
||||
Set of `ExternalReference`
|
||||
"""
|
||||
return self._external_references
|
||||
|
||||
@external_references.setter
|
||||
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
|
||||
self._external_references = SortedSet(external_references)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot3)
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
|
||||
@serializable.xml_sequence(13)
|
||||
def properties(self) -> 'SortedSet[Property]':
|
||||
"""
|
||||
Provides the ability to document properties in a key/value store. This provides flexibility to include data not
|
||||
officially supported in the standard without having to use additional namespaces or create extensions.
|
||||
|
||||
Return:
|
||||
Set of `Property`
|
||||
"""
|
||||
return self._properties
|
||||
|
||||
@properties.setter
|
||||
def properties(self, properties: Iterable[Property]) -> None:
|
||||
self._properties = SortedSet(properties)
|
||||
|
||||
@property
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'service')
|
||||
@serializable.xml_sequence(14)
|
||||
def services(self) -> "SortedSet['Service']":
|
||||
"""
|
||||
A list of services included or deployed behind the parent service.
|
||||
|
||||
This is not a dependency tree.
|
||||
|
||||
It provides a way to specify a hierarchical representation of service assemblies.
|
||||
|
||||
Returns:
|
||||
Set of `Service`
|
||||
"""
|
||||
return self._services
|
||||
|
||||
@services.setter
|
||||
def services(self, services: Iterable['Service']) -> None:
|
||||
self._services = SortedSet(services)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_sequence(15)
|
||||
def release_notes(self) -> Optional[ReleaseNotes]:
|
||||
"""
|
||||
Specifies optional release notes.
|
||||
|
||||
Returns:
|
||||
`ReleaseNotes` or `None`
|
||||
"""
|
||||
return self._release_notes
|
||||
|
||||
@release_notes.setter
|
||||
def release_notes(self, release_notes: Optional[ReleaseNotes]) -> None:
|
||||
self._release_notes = release_notes
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.group, self.name, self.version,
|
||||
self.bom_ref.value,
|
||||
self.provider, self.description,
|
||||
self.authenticated, _ComparableTuple(self.data), _ComparableTuple(self.endpoints),
|
||||
_ComparableTuple(self.external_references), _ComparableTuple(self.licenses),
|
||||
_ComparableTuple(self.properties), self.release_notes, _ComparableTuple(self.services),
|
||||
self.x_trust_boundary
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Service):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, Service):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Service bom-ref={self.bom_ref}, group={self.group}, name={self.name}, version={self.version}>'
|
||||
@@ -0,0 +1,373 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from itertools import chain
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Type, Union
|
||||
from warnings import warn
|
||||
from xml.etree.ElementTree import Element # nosec B405
|
||||
|
||||
import py_serializable as serializable
|
||||
from py_serializable.helpers import BaseHelper
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
from .._internal.compare import ComparableTuple as _ComparableTuple
|
||||
from ..schema import SchemaVersion
|
||||
from ..schema.schema import SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6
|
||||
from . import ExternalReference, HashType, _HashTypeRepositorySerializationHelper
|
||||
from .component import Component
|
||||
from .service import Service
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from py_serializable import ObjectMetadataLibrary, ViewType
|
||||
|
||||
|
||||
@serializable.serializable_class
|
||||
class Tool:
|
||||
"""
|
||||
This is our internal representation of the `toolType` complex type within the CycloneDX standard.
|
||||
|
||||
Tool(s) are the things used in the creation of the CycloneDX document.
|
||||
|
||||
Tool might be deprecated since CycloneDX 1.5, but it is not deprecated in this library.
|
||||
In fact, this library will try to provide a compatibility layer if needed.
|
||||
|
||||
.. note::
|
||||
See the CycloneDX Schema for toolType: https://cyclonedx.org/docs/1.6/#type_toolType
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
vendor: Optional[str] = None,
|
||||
name: Optional[str] = None,
|
||||
version: Optional[str] = None,
|
||||
hashes: Optional[Iterable[HashType]] = None,
|
||||
external_references: Optional[Iterable[ExternalReference]] = None,
|
||||
) -> None:
|
||||
self.vendor = vendor
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.hashes = hashes or () # type:ignore[assignment]
|
||||
self.external_references = external_references or () # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(1)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def vendor(self) -> Optional[str]:
|
||||
"""
|
||||
The name of the vendor who created the tool.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._vendor
|
||||
|
||||
@vendor.setter
|
||||
def vendor(self, vendor: Optional[str]) -> None:
|
||||
self._vendor = vendor
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(2)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def name(self) -> Optional[str]:
|
||||
"""
|
||||
The name of the tool.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name: Optional[str]) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
@serializable.xml_sequence(3)
|
||||
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
|
||||
def version(self) -> Optional[str]:
|
||||
"""
|
||||
The version of the tool.
|
||||
|
||||
Returns:
|
||||
`str` if set else `None`
|
||||
"""
|
||||
return self._version
|
||||
|
||||
@version.setter
|
||||
def version(self, version: Optional[str]) -> None:
|
||||
self._version = version
|
||||
|
||||
@property
|
||||
@serializable.type_mapping(_HashTypeRepositorySerializationHelper)
|
||||
@serializable.xml_sequence(4)
|
||||
def hashes(self) -> 'SortedSet[HashType]':
|
||||
"""
|
||||
The hashes of the tool (if applicable).
|
||||
|
||||
Returns:
|
||||
Set of `HashType`
|
||||
"""
|
||||
return self._hashes
|
||||
|
||||
@hashes.setter
|
||||
def hashes(self, hashes: Iterable[HashType]) -> None:
|
||||
self._hashes = SortedSet(hashes)
|
||||
|
||||
@property
|
||||
@serializable.view(SchemaVersion1Dot4)
|
||||
@serializable.view(SchemaVersion1Dot5)
|
||||
@serializable.view(SchemaVersion1Dot6)
|
||||
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
|
||||
@serializable.xml_sequence(5)
|
||||
def external_references(self) -> 'SortedSet[ExternalReference]':
|
||||
"""
|
||||
External References provides a way to document systems, sites, and information that may be relevant but which
|
||||
are not included with the BOM.
|
||||
|
||||
Returns:
|
||||
Set of `ExternalReference`
|
||||
"""
|
||||
return self._external_references
|
||||
|
||||
@external_references.setter
|
||||
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
|
||||
self._external_references = SortedSet(external_references)
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
self.vendor, self.name, self.version,
|
||||
_ComparableTuple(self.hashes), _ComparableTuple(self.external_references)
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Tool):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, Tool):
|
||||
return self.__comparable_tuple() < other.__comparable_tuple()
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Tool name={self.name}, version={self.version}, vendor={self.vendor}>'
|
||||
|
||||
@classmethod
|
||||
def from_component(cls: Type['Tool'], component: 'Component') -> 'Tool':
|
||||
return cls(
|
||||
vendor=component.group,
|
||||
name=component.name,
|
||||
version=component.version,
|
||||
hashes=component.hashes,
|
||||
external_references=component.external_references,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_service(cls: Type['Tool'], service: 'Service') -> 'Tool':
|
||||
return cls(
|
||||
vendor=service.group,
|
||||
name=service.name,
|
||||
version=service.version,
|
||||
external_references=service.external_references,
|
||||
)
|
||||
|
||||
|
||||
class ToolRepository:
|
||||
"""
|
||||
The repository of tool formats
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
components: Optional[Iterable[Component]] = None,
|
||||
services: Optional[Iterable[Service]] = None,
|
||||
# Deprecated since v1.5
|
||||
tools: Optional[Iterable[Tool]] = None
|
||||
) -> None:
|
||||
if tools:
|
||||
warn('`@.tools` is deprecated from CycloneDX v1.5 onwards. '
|
||||
'Please use `@.components` and `@.services` instead.',
|
||||
DeprecationWarning)
|
||||
self.components = components or () # type:ignore[assignment]
|
||||
self.services = services or () # type:ignore[assignment]
|
||||
self.tools = tools or () # type:ignore[assignment]
|
||||
|
||||
@property
|
||||
def components(self) -> 'SortedSet[Component]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of Components
|
||||
"""
|
||||
return self._components
|
||||
|
||||
@components.setter
|
||||
def components(self, components: Iterable[Component]) -> None:
|
||||
self._components = SortedSet(components)
|
||||
|
||||
@property
|
||||
def services(self) -> 'SortedSet[Service]':
|
||||
"""
|
||||
Returns:
|
||||
A SortedSet of Services
|
||||
"""
|
||||
return self._services
|
||||
|
||||
@services.setter
|
||||
def services(self, services: Iterable[Service]) -> None:
|
||||
self._services = SortedSet(services)
|
||||
|
||||
@property
|
||||
def tools(self) -> 'SortedSet[Tool]':
|
||||
return self._tools
|
||||
|
||||
@tools.setter
|
||||
def tools(self, tools: Iterable[Tool]) -> None:
|
||||
self._tools = SortedSet(tools)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._tools) \
|
||||
+ len(self._components) \
|
||||
+ len(self._services)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return len(self._tools) > 0 \
|
||||
or len(self._components) > 0 \
|
||||
or len(self._services) > 0
|
||||
|
||||
def __comparable_tuple(self) -> _ComparableTuple:
|
||||
return _ComparableTuple((
|
||||
_ComparableTuple(self._tools),
|
||||
_ComparableTuple(self._components),
|
||||
_ComparableTuple(self._services)
|
||||
))
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, ToolRepository):
|
||||
return self.__comparable_tuple() == other.__comparable_tuple()
|
||||
return False
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__comparable_tuple())
|
||||
|
||||
|
||||
class _ToolRepositoryHelper(BaseHelper):
|
||||
|
||||
@staticmethod
|
||||
def __all_as_tools(o: ToolRepository) -> 'SortedSet[Tool]':
|
||||
# use a set here, so the collection gets deduplicated.
|
||||
# use SortedSet set here, so the order stays reproducible.
|
||||
return SortedSet(chain(
|
||||
o.tools,
|
||||
map(Tool.from_component, o.components),
|
||||
map(Tool.from_service, o.services),
|
||||
))
|
||||
|
||||
@staticmethod
|
||||
def __supports_components_and_services(view: Any) -> bool:
|
||||
try:
|
||||
return view is not None and view().schema_version_enum >= SchemaVersion.V1_5
|
||||
except Exception: # pragma: no cover
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def json_normalize(cls, o: ToolRepository, *,
|
||||
view: Optional[Type['ViewType']],
|
||||
**__: Any) -> Any:
|
||||
if len(o.tools) > 0 or not cls.__supports_components_and_services(view):
|
||||
ts = cls.__all_as_tools(o)
|
||||
return tuple(ts) if ts else None
|
||||
elem: Dict[str, Any] = {}
|
||||
if o.components:
|
||||
elem['components'] = tuple(o.components)
|
||||
if o.services:
|
||||
elem['services'] = tuple(o.services)
|
||||
return elem or None
|
||||
|
||||
@classmethod
|
||||
def json_denormalize(cls, o: Union[List[Dict[str, Any]], Dict[str, Any]],
|
||||
**__: Any) -> ToolRepository:
|
||||
tools = None
|
||||
components = None
|
||||
services = None
|
||||
if isinstance(o, Dict):
|
||||
components = map(lambda c: Component.from_json( # type:ignore[attr-defined]
|
||||
c), o.get('components', ()))
|
||||
services = map(lambda s: Service.from_json( # type:ignore[attr-defined]
|
||||
s), o.get('services', ()))
|
||||
elif isinstance(o, Iterable):
|
||||
tools = map(lambda t: Tool.from_json( # type:ignore[attr-defined]
|
||||
t), o)
|
||||
return ToolRepository(components=components, services=services, tools=tools)
|
||||
|
||||
@classmethod
|
||||
def xml_normalize(cls, o: ToolRepository, *,
|
||||
element_name: str,
|
||||
view: Optional[Type['ViewType']],
|
||||
xmlns: Optional[str],
|
||||
**__: Any) -> Optional[Element]:
|
||||
elem = Element(element_name)
|
||||
if len(o.tools) > 0 or not cls.__supports_components_and_services(view):
|
||||
elem.extend(
|
||||
ti.as_xml( # type:ignore[attr-defined]
|
||||
view_=view, as_string=False, element_name='tool', xmlns=xmlns)
|
||||
for ti in cls.__all_as_tools(o)
|
||||
)
|
||||
else:
|
||||
if o.components:
|
||||
elem_c = Element(f'{{{xmlns}}}components' if xmlns else 'components')
|
||||
elem_c.extend(
|
||||
ci.as_xml( # type:ignore[attr-defined]
|
||||
view_=view, as_string=False, element_name='component', xmlns=xmlns)
|
||||
for ci in o.components)
|
||||
elem.append(elem_c)
|
||||
if o.services:
|
||||
elem_s = Element(f'{{{xmlns}}}services' if xmlns else 'services')
|
||||
elem_s.extend(
|
||||
si.as_xml( # type:ignore[attr-defined]
|
||||
view_=view, as_string=False, element_name='service', xmlns=xmlns)
|
||||
for si in o.services)
|
||||
elem.append(elem_s)
|
||||
return elem \
|
||||
if len(elem) > 0 \
|
||||
else None
|
||||
|
||||
@classmethod
|
||||
def xml_denormalize(cls, o: Element, *,
|
||||
default_ns: Optional[str],
|
||||
prop_info: 'ObjectMetadataLibrary.SerializableProperty',
|
||||
ctx: Type[Any],
|
||||
**kwargs: Any) -> ToolRepository:
|
||||
ns_map = {'bom': default_ns or ''}
|
||||
# Do not iterate over `o` and do not check for expected `.tag` of items.
|
||||
# This check could have been done by schema validators before even deserializing.
|
||||
tools = None
|
||||
components = None
|
||||
services = None
|
||||
ts = o.findall('bom:tool', ns_map)
|
||||
if len(ts) > 0:
|
||||
tools = map(lambda t: Tool.from_xml( # type:ignore[attr-defined]
|
||||
t, default_ns), ts)
|
||||
else:
|
||||
components = map(lambda c: Component.from_xml( # type:ignore[attr-defined]
|
||||
c, default_ns), o.iterfind('./bom:components/bom:component', ns_map))
|
||||
services = map(lambda s: Service.from_xml( # type:ignore[attr-defined]
|
||||
s, default_ns), o.iterfind('./bom:services/bom:service', ns_map))
|
||||
return ToolRepository(components=components, services=services, tools=tools)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,176 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Set of classes and methods for outputting our libraries internal Bom model to CycloneDX documents in varying formats
|
||||
and according to different versions of the CycloneDX schema standard.
|
||||
"""
|
||||
|
||||
import os
|
||||
from abc import ABC, abstractmethod
|
||||
from itertools import chain
|
||||
from random import random
|
||||
from typing import TYPE_CHECKING, Any, Iterable, Literal, Mapping, Optional, Type, Union, overload
|
||||
|
||||
from ..schema import OutputFormat, SchemaVersion
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..model.bom import Bom
|
||||
from ..model.bom_ref import BomRef
|
||||
from .json import Json as JsonOutputter
|
||||
from .xml import Xml as XmlOutputter
|
||||
|
||||
|
||||
class BaseOutput(ABC):
|
||||
|
||||
def __init__(self, bom: 'Bom', **kwargs: int) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self._bom = bom
|
||||
self._generated: bool = False
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def schema_version(self) -> SchemaVersion:
|
||||
... # pragma: no cover
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def output_format(self) -> OutputFormat:
|
||||
... # pragma: no cover
|
||||
|
||||
@property
|
||||
def generated(self) -> bool:
|
||||
return self._generated
|
||||
|
||||
@generated.setter
|
||||
def generated(self, generated: bool) -> None:
|
||||
self._generated = generated
|
||||
|
||||
def get_bom(self) -> 'Bom':
|
||||
return self._bom
|
||||
|
||||
def set_bom(self, bom: 'Bom') -> None:
|
||||
self._bom = bom
|
||||
|
||||
@abstractmethod
|
||||
def generate(self, force_regeneration: bool = False) -> None:
|
||||
... # pragma: no cover
|
||||
|
||||
@abstractmethod
|
||||
def output_as_string(self, *,
|
||||
indent: Optional[Union[int, str]] = None,
|
||||
**kwargs: Any) -> str:
|
||||
... # pragma: no cover
|
||||
|
||||
def output_to_file(self, filename: str, allow_overwrite: bool = False, *,
|
||||
indent: Optional[Union[int, str]] = None,
|
||||
**kwargs: Any) -> None:
|
||||
# Check directory writable
|
||||
output_filename = os.path.realpath(filename)
|
||||
output_directory = os.path.dirname(output_filename)
|
||||
if not os.access(output_directory, os.W_OK):
|
||||
raise PermissionError(output_directory)
|
||||
if os.path.exists(output_filename) and not allow_overwrite:
|
||||
raise FileExistsError(output_filename)
|
||||
with open(output_filename, mode='wb') as f_out:
|
||||
f_out.write(self.output_as_string(indent=indent).encode('utf-8'))
|
||||
|
||||
|
||||
@overload
|
||||
def make_outputter(bom: 'Bom', output_format: Literal[OutputFormat.JSON],
|
||||
schema_version: SchemaVersion) -> 'JsonOutputter':
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
@overload
|
||||
def make_outputter(bom: 'Bom', output_format: Literal[OutputFormat.XML],
|
||||
schema_version: SchemaVersion) -> 'XmlOutputter':
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
@overload
|
||||
def make_outputter(bom: 'Bom', output_format: OutputFormat,
|
||||
schema_version: SchemaVersion) -> Union['XmlOutputter', 'JsonOutputter']:
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
def make_outputter(bom: 'Bom', output_format: OutputFormat, schema_version: SchemaVersion) -> BaseOutput:
|
||||
"""
|
||||
Helper method to quickly get the correct output class/formatter.
|
||||
|
||||
Pass in your BOM and optionally an output format and schema version (defaults to XML and latest schema version).
|
||||
|
||||
|
||||
Raises error when no instance could be made.
|
||||
|
||||
:param bom: Bom
|
||||
:param output_format: OutputFormat
|
||||
:param schema_version: SchemaVersion
|
||||
:return: BaseOutput
|
||||
"""
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
BY_SCHEMA_VERSION: Mapping[SchemaVersion, Type[BaseOutput]] # noqa:N806
|
||||
if OutputFormat.JSON is output_format:
|
||||
from .json import BY_SCHEMA_VERSION
|
||||
elif OutputFormat.XML is output_format:
|
||||
from .xml import BY_SCHEMA_VERSION
|
||||
else:
|
||||
raise ValueError(f'Unexpected output_format: {output_format!r}')
|
||||
|
||||
klass = BY_SCHEMA_VERSION.get(schema_version, None)
|
||||
if klass is None:
|
||||
raise ValueError(f'Unknown {output_format.name}/schema_version: {schema_version!r}')
|
||||
return klass(bom)
|
||||
|
||||
|
||||
class BomRefDiscriminator:
|
||||
|
||||
def __init__(self, bomrefs: Iterable['BomRef'], prefix: str = 'BomRef') -> None:
|
||||
# do not use dict/set here, different BomRefs with same value have same hash and would shadow each other
|
||||
self._bomrefs = tuple((bomref, bomref.value) for bomref in bomrefs)
|
||||
self._prefix = prefix
|
||||
|
||||
def __enter__(self) -> None:
|
||||
self.discriminate()
|
||||
|
||||
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
||||
self.reset()
|
||||
|
||||
def discriminate(self) -> None:
|
||||
known_values = []
|
||||
for bomref, _ in self._bomrefs:
|
||||
value = bomref.value
|
||||
if value is None or value in known_values:
|
||||
value = self._make_unique()
|
||||
bomref.value = value
|
||||
known_values.append(value)
|
||||
|
||||
def reset(self) -> None:
|
||||
for bomref, original_value in self._bomrefs:
|
||||
bomref.value = original_value
|
||||
|
||||
def _make_unique(self) -> str:
|
||||
return f'{self._prefix}{str(random())[1:]}{str(random())[1:]}' # nosec B311
|
||||
|
||||
@classmethod
|
||||
def from_bom(cls, bom: 'Bom', prefix: str = 'BomRef') -> 'BomRefDiscriminator':
|
||||
return cls(chain(
|
||||
map(lambda c: c.bom_ref, bom._get_all_components()),
|
||||
map(lambda s: s.bom_ref, bom.services),
|
||||
map(lambda v: v.bom_ref, bom.vulnerabilities)
|
||||
), prefix)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,142 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
from abc import abstractmethod
|
||||
from json import dumps as json_dumps, loads as json_loads
|
||||
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Type, Union
|
||||
|
||||
from ..exception.output import FormatNotSupportedException
|
||||
from ..schema import OutputFormat, SchemaVersion
|
||||
from ..schema.schema import (
|
||||
SCHEMA_VERSIONS,
|
||||
BaseSchemaVersion,
|
||||
SchemaVersion1Dot0,
|
||||
SchemaVersion1Dot1,
|
||||
SchemaVersion1Dot2,
|
||||
SchemaVersion1Dot3,
|
||||
SchemaVersion1Dot4,
|
||||
SchemaVersion1Dot5,
|
||||
SchemaVersion1Dot6,
|
||||
)
|
||||
from . import BaseOutput, BomRefDiscriminator
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..model.bom import Bom
|
||||
|
||||
|
||||
class Json(BaseOutput, BaseSchemaVersion):
|
||||
|
||||
def __init__(self, bom: 'Bom') -> None:
|
||||
super().__init__(bom=bom)
|
||||
self._bom_json: Dict[str, Any] = dict()
|
||||
|
||||
@property
|
||||
def schema_version(self) -> SchemaVersion:
|
||||
return self.schema_version_enum
|
||||
|
||||
@property
|
||||
def output_format(self) -> Literal[OutputFormat.JSON]:
|
||||
return OutputFormat.JSON
|
||||
|
||||
def generate(self, force_regeneration: bool = False) -> None:
|
||||
if self.generated and not force_regeneration:
|
||||
return
|
||||
|
||||
schema_uri: Optional[str] = self._get_schema_uri()
|
||||
if not schema_uri:
|
||||
raise FormatNotSupportedException(
|
||||
f'JSON is not supported by CycloneDX in schema version {self.schema_version.to_version()}')
|
||||
|
||||
_json_core = {
|
||||
'$schema': schema_uri,
|
||||
'bomFormat': 'CycloneDX',
|
||||
'specVersion': self.schema_version.to_version()
|
||||
}
|
||||
_view = SCHEMA_VERSIONS.get(self.schema_version_enum)
|
||||
bom = self.get_bom()
|
||||
bom.validate()
|
||||
with BomRefDiscriminator.from_bom(bom):
|
||||
bom_json: Dict[str, Any] = json_loads(
|
||||
bom.as_json( # type:ignore[attr-defined]
|
||||
view_=_view))
|
||||
bom_json.update(_json_core)
|
||||
self._bom_json = bom_json
|
||||
self.generated = True
|
||||
|
||||
def output_as_string(self, *,
|
||||
indent: Optional[Union[int, str]] = None,
|
||||
**kwargs: Any) -> str:
|
||||
self.generate()
|
||||
return json_dumps(self._bom_json,
|
||||
indent=indent)
|
||||
|
||||
@abstractmethod
|
||||
def _get_schema_uri(self) -> Optional[str]:
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
class JsonV1Dot0(Json, SchemaVersion1Dot0):
|
||||
|
||||
def _get_schema_uri(self) -> None:
|
||||
return None
|
||||
|
||||
|
||||
class JsonV1Dot1(Json, SchemaVersion1Dot1):
|
||||
|
||||
def _get_schema_uri(self) -> None:
|
||||
return None
|
||||
|
||||
|
||||
class JsonV1Dot2(Json, SchemaVersion1Dot2):
|
||||
|
||||
def _get_schema_uri(self) -> str:
|
||||
return 'http://cyclonedx.org/schema/bom-1.2b.schema.json'
|
||||
|
||||
|
||||
class JsonV1Dot3(Json, SchemaVersion1Dot3):
|
||||
|
||||
def _get_schema_uri(self) -> str:
|
||||
return 'http://cyclonedx.org/schema/bom-1.3a.schema.json'
|
||||
|
||||
|
||||
class JsonV1Dot4(Json, SchemaVersion1Dot4):
|
||||
|
||||
def _get_schema_uri(self) -> str:
|
||||
return 'http://cyclonedx.org/schema/bom-1.4.schema.json'
|
||||
|
||||
|
||||
class JsonV1Dot5(Json, SchemaVersion1Dot5):
|
||||
|
||||
def _get_schema_uri(self) -> str:
|
||||
return 'http://cyclonedx.org/schema/bom-1.5.schema.json'
|
||||
|
||||
|
||||
class JsonV1Dot6(Json, SchemaVersion1Dot6):
|
||||
|
||||
def _get_schema_uri(self) -> str:
|
||||
return 'http://cyclonedx.org/schema/bom-1.6.schema.json'
|
||||
|
||||
|
||||
BY_SCHEMA_VERSION: Dict[SchemaVersion, Type[Json]] = {
|
||||
SchemaVersion.V1_6: JsonV1Dot6,
|
||||
SchemaVersion.V1_5: JsonV1Dot5,
|
||||
SchemaVersion.V1_4: JsonV1Dot4,
|
||||
SchemaVersion.V1_3: JsonV1Dot3,
|
||||
SchemaVersion.V1_2: JsonV1Dot2,
|
||||
SchemaVersion.V1_1: JsonV1Dot1,
|
||||
SchemaVersion.V1_0: JsonV1Dot0,
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Type, Union
|
||||
from xml.dom.minidom import parseString as dom_parseString # nosec B408
|
||||
from xml.etree.ElementTree import Element as XmlElement, tostring as xml_dumps # nosec B405
|
||||
|
||||
from ..schema import OutputFormat, SchemaVersion
|
||||
from ..schema.schema import (
|
||||
SCHEMA_VERSIONS,
|
||||
BaseSchemaVersion,
|
||||
SchemaVersion1Dot0,
|
||||
SchemaVersion1Dot1,
|
||||
SchemaVersion1Dot2,
|
||||
SchemaVersion1Dot3,
|
||||
SchemaVersion1Dot4,
|
||||
SchemaVersion1Dot5,
|
||||
SchemaVersion1Dot6,
|
||||
)
|
||||
from . import BaseOutput, BomRefDiscriminator
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..model.bom import Bom
|
||||
|
||||
|
||||
class Xml(BaseSchemaVersion, BaseOutput):
|
||||
def __init__(self, bom: 'Bom') -> None:
|
||||
super().__init__(bom=bom)
|
||||
self._bom_xml: str = ''
|
||||
|
||||
@property
|
||||
def schema_version(self) -> SchemaVersion:
|
||||
return self.schema_version_enum
|
||||
|
||||
@property
|
||||
def output_format(self) -> Literal[OutputFormat.XML]:
|
||||
return OutputFormat.XML
|
||||
|
||||
def generate(self, force_regeneration: bool = False) -> None:
|
||||
if self.generated and not force_regeneration:
|
||||
return
|
||||
|
||||
_view = SCHEMA_VERSIONS[self.schema_version_enum]
|
||||
bom = self.get_bom()
|
||||
bom.validate()
|
||||
xmlns = self.get_target_namespace()
|
||||
with BomRefDiscriminator.from_bom(bom):
|
||||
self._bom_xml = '<?xml version="1.0" ?>\n' + xml_dumps(
|
||||
bom.as_xml( # type:ignore[attr-defined]
|
||||
_view, as_string=False, xmlns=xmlns),
|
||||
method='xml', default_namespace=xmlns, encoding='unicode',
|
||||
# `xml-declaration` is inconsistent/bugged in py38,
|
||||
# especially on Windows it will print a non-UTF8 codepage.
|
||||
# Furthermore, it might add an encoding of "utf-8" which is redundant default value of XML.
|
||||
# -> so we write the declaration manually, as long as py38 is supported.
|
||||
xml_declaration=False)
|
||||
|
||||
self.generated = True
|
||||
|
||||
@staticmethod
|
||||
def __make_indent(v: Optional[Union[int, str]]) -> str:
|
||||
if isinstance(v, int):
|
||||
return ' ' * v
|
||||
if isinstance(v, str):
|
||||
return v
|
||||
return ''
|
||||
|
||||
def output_as_string(self, *,
|
||||
indent: Optional[Union[int, str]] = None,
|
||||
**kwargs: Any) -> str:
|
||||
self.generate()
|
||||
return self._bom_xml if indent is None else dom_parseString( # nosecc B318
|
||||
self._bom_xml).toprettyxml(
|
||||
indent=self.__make_indent(indent)
|
||||
# do not set `encoding` - this would convert result to binary, not string
|
||||
)
|
||||
|
||||
def get_target_namespace(self) -> str:
|
||||
return f'http://cyclonedx.org/schema/bom/{self.get_schema_version()}'
|
||||
|
||||
|
||||
class XmlV1Dot0(Xml, SchemaVersion1Dot0):
|
||||
|
||||
def _create_bom_element(self) -> XmlElement:
|
||||
return XmlElement('bom', {'xmlns': self.get_target_namespace(), 'version': '1'})
|
||||
|
||||
|
||||
class XmlV1Dot1(Xml, SchemaVersion1Dot1):
|
||||
pass
|
||||
|
||||
|
||||
class XmlV1Dot2(Xml, SchemaVersion1Dot2):
|
||||
pass
|
||||
|
||||
|
||||
class XmlV1Dot3(Xml, SchemaVersion1Dot3):
|
||||
pass
|
||||
|
||||
|
||||
class XmlV1Dot4(Xml, SchemaVersion1Dot4):
|
||||
pass
|
||||
|
||||
|
||||
class XmlV1Dot5(Xml, SchemaVersion1Dot5):
|
||||
pass
|
||||
|
||||
|
||||
class XmlV1Dot6(Xml, SchemaVersion1Dot6):
|
||||
pass
|
||||
|
||||
|
||||
BY_SCHEMA_VERSION: Dict[SchemaVersion, Type[Xml]] = {
|
||||
SchemaVersion.V1_6: XmlV1Dot6,
|
||||
SchemaVersion.V1_5: XmlV1Dot5,
|
||||
SchemaVersion.V1_4: XmlV1Dot4,
|
||||
SchemaVersion.V1_3: XmlV1Dot3,
|
||||
SchemaVersion.V1_2: XmlV1Dot2,
|
||||
SchemaVersion.V1_1: XmlV1Dot1,
|
||||
SchemaVersion.V1_0: XmlV1Dot0,
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# Marker file for PEP 561. This package uses inline types.
|
||||
# This file is needed to allow other packages to type-check their code against this package.
|
||||
@@ -0,0 +1,106 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from enum import Enum, auto, unique
|
||||
from typing import Any, Type, TypeVar
|
||||
|
||||
|
||||
@unique
|
||||
class OutputFormat(Enum):
|
||||
"""Output formats.
|
||||
|
||||
Cases are hashable.
|
||||
|
||||
Do not rely on the actual/literal values, just use enum cases, like so:
|
||||
my_of = OutputFormat.XML
|
||||
"""
|
||||
|
||||
JSON = auto()
|
||||
XML = auto()
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.name)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
return self is other
|
||||
|
||||
|
||||
_SV = TypeVar('_SV', bound='SchemaVersion')
|
||||
|
||||
|
||||
@unique
|
||||
class SchemaVersion(Enum):
|
||||
"""
|
||||
Schema version.
|
||||
|
||||
Cases are hashable.
|
||||
Cases are comparable(!=,>=,>,==,<,<=)
|
||||
|
||||
Do not rely on the actual/literal values, just use enum cases, like so:
|
||||
my_sv = SchemaVersion.V1_3
|
||||
"""
|
||||
|
||||
V1_6 = (1, 6)
|
||||
V1_5 = (1, 5)
|
||||
V1_4 = (1, 4)
|
||||
V1_3 = (1, 3)
|
||||
V1_2 = (1, 2)
|
||||
V1_1 = (1, 1)
|
||||
V1_0 = (1, 0)
|
||||
|
||||
@classmethod
|
||||
def from_version(cls: Type[_SV], version: str) -> _SV:
|
||||
"""Return instance based of a version string - e.g. `1.4`"""
|
||||
return cls(tuple(map(int, version.split('.')))[:2])
|
||||
|
||||
def to_version(self) -> str:
|
||||
"""Return as a version string - e.g. `1.4`"""
|
||||
return '.'.join(map(str, self.value))
|
||||
|
||||
def __ne__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.value != other.value
|
||||
return NotImplemented # pragma: no cover
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.value < other.value
|
||||
return NotImplemented # pragma: no cover
|
||||
|
||||
def __le__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.value <= other.value
|
||||
return NotImplemented # pragma: no cover
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.value == other.value
|
||||
return NotImplemented # pragma: no cover
|
||||
|
||||
def __ge__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.value >= other.value
|
||||
return NotImplemented # pragma: no cover
|
||||
|
||||
def __gt__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.value > other.value
|
||||
return NotImplemented # pragma: no cover
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.name)
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
# Resources: Schema files
|
||||
|
||||
some schema for offline use as download via [script](../../../tools/schema-downloader.py).
|
||||
original sources: <https://github.com/CycloneDX/specification/tree/master/schema>
|
||||
|
||||
Currently using version
|
||||
[8a27bfd1be5be0dcb2c208a34d2f4fa0b6d75bd7](https://github.com/CycloneDX/specification/commit/8a27bfd1be5be0dcb2c208a34d2f4fa0b6d75bd7)
|
||||
|
||||
| file | note |
|
||||
|------|------|
|
||||
| [`bom-1.0.SNAPSHOT.xsd`](bom-1.0.SNAPSHOT.xsd) | applied changes: 1 |
|
||||
| [`bom-1.1.SNAPSHOT.xsd`](bom-1.1.SNAPSHOT.xsd) | applied changes: 1 |
|
||||
| [`bom-1.2.SNAPSHOT.xsd`](bom-1.2.SNAPSHOT.xsd) | applied changes: 1 |
|
||||
| [`bom-1.3.SNAPSHOT.xsd`](bom-1.3.SNAPSHOT.xsd) | applied changes: 1 |
|
||||
| [`bom-1.4.SNAPSHOT.xsd`](bom-1.4.SNAPSHOT.xsd) | applied changes: 1 |
|
||||
| [`bom-1.5.SNAPSHOT.xsd`](bom-1.5.SNAPSHOT.xsd) | applied changes: 1 |
|
||||
| [`bom-1.6.SNAPSHOT.xsd`](bom-1.6.SNAPSHOT.xsd) | applied changes: 1 |
|
||||
| [`bom-1.2.SNAPSHOT.schema.json`](bom-1.2.SNAPSHOT.schema.json) | applied changes: 2,3,4,5 |
|
||||
| [`bom-1.3.SNAPSHOT.schema.json`](bom-1.3.SNAPSHOT.schema.json) | applied changes: 2,3,4,5 |
|
||||
| [`bom-1.4.SNAPSHOT.schema.json`](bom-1.4.SNAPSHOT.schema.json) | applied changes: 2,3,4,5 |
|
||||
| [`bom-1.5.SNAPSHOT.schema.json`](bom-1.5.SNAPSHOT.schema.json) | applied changes: 2,3,4,5 |
|
||||
| [`bom-1.6.SNAPSHOT.schema.json`](bom-1.6.SNAPSHOT.schema.json) | applied changes: 2,3,4,5 |
|
||||
| [`bom-1.2-strict.SNAPSHOT.schema.json`](bom-1.2-strict.SNAPSHOT.schema.json) | applied changes: 2,3,4,5 |
|
||||
| [`bom-1.3-strict.SNAPSHOT.schema.json`](bom-1.3-strict.SNAPSHOT.schema.json) | applied changes: 2,3,4,5 |
|
||||
| [`spdx.SNAPSHOT.xsd`](spdx.SNAPSHOT.xsd) | |
|
||||
| [`spdx.SNAPSHOT.schema.json`](spdx.SNAPSHOT.schema.json) | |
|
||||
| [`jsf-0.82.SNAPSHOT.schema.json`](jsf-0.82.SNAPSHOT.schema.json) | |
|
||||
|
||||
changes:
|
||||
1. `https?://cyclonedx.org/schema/spdx` was replaced with `spdx.SNAPSHOT.xsd`
|
||||
2. `spdx.schema.json` was replaced with `spdx.SNAPSHOT.schema.json`
|
||||
3. `jsf-0.82.schema.json` was replaced with `jsf-0.82.SNAPSHOT.schema.json`
|
||||
4. `properties.$schema.enum` was removed
|
||||
5. `required.version` removed, as it is actually optional with default value
|
||||
@@ -0,0 +1,68 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Content in here is internal, not for public use.
|
||||
Breaking changes without notice may happen.
|
||||
"""
|
||||
|
||||
|
||||
from os.path import dirname, join
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .. import SchemaVersion
|
||||
|
||||
__DIR = dirname(__file__)
|
||||
|
||||
BOM_XML: Dict[SchemaVersion, Optional[str]] = {
|
||||
SchemaVersion.V1_6: join(__DIR, 'bom-1.6.SNAPSHOT.xsd'),
|
||||
SchemaVersion.V1_5: join(__DIR, 'bom-1.5.SNAPSHOT.xsd'),
|
||||
SchemaVersion.V1_4: join(__DIR, 'bom-1.4.SNAPSHOT.xsd'),
|
||||
SchemaVersion.V1_3: join(__DIR, 'bom-1.3.SNAPSHOT.xsd'),
|
||||
SchemaVersion.V1_2: join(__DIR, 'bom-1.2.SNAPSHOT.xsd'),
|
||||
SchemaVersion.V1_1: join(__DIR, 'bom-1.1.SNAPSHOT.xsd'),
|
||||
SchemaVersion.V1_0: join(__DIR, 'bom-1.0.SNAPSHOT.xsd'),
|
||||
}
|
||||
|
||||
BOM_JSON: Dict[SchemaVersion, Optional[str]] = {
|
||||
SchemaVersion.V1_6: join(__DIR, 'bom-1.6.SNAPSHOT.schema.json'),
|
||||
SchemaVersion.V1_5: join(__DIR, 'bom-1.5.SNAPSHOT.schema.json'),
|
||||
SchemaVersion.V1_4: join(__DIR, 'bom-1.4.SNAPSHOT.schema.json'),
|
||||
SchemaVersion.V1_3: join(__DIR, 'bom-1.3.SNAPSHOT.schema.json'),
|
||||
SchemaVersion.V1_2: join(__DIR, 'bom-1.2.SNAPSHOT.schema.json'),
|
||||
# <= v1.1 is not defined in JSON
|
||||
SchemaVersion.V1_1: None,
|
||||
SchemaVersion.V1_0: None,
|
||||
}
|
||||
|
||||
BOM_JSON_STRICT: Dict[SchemaVersion, Optional[str]] = {
|
||||
SchemaVersion.V1_6: BOM_JSON[SchemaVersion.V1_6],
|
||||
SchemaVersion.V1_5: BOM_JSON[SchemaVersion.V1_5],
|
||||
SchemaVersion.V1_4: BOM_JSON[SchemaVersion.V1_4],
|
||||
# <= 1.3 need special files
|
||||
SchemaVersion.V1_3: join(__DIR, 'bom-1.3-strict.SNAPSHOT.schema.json'),
|
||||
SchemaVersion.V1_2: join(__DIR, 'bom-1.2-strict.SNAPSHOT.schema.json'),
|
||||
# <= v1.1 is not defined in JSON
|
||||
SchemaVersion.V1_1: None,
|
||||
SchemaVersion.V1_0: None,
|
||||
}
|
||||
|
||||
SPDX_JSON = join(__DIR, 'spdx.SNAPSHOT.schema.json')
|
||||
SPDX_XML = join(__DIR, 'spdx.SNAPSHOT.xsd')
|
||||
|
||||
JSF = join(__DIR, 'jsf-0.82.SNAPSHOT.schema.json')
|
||||
Binary file not shown.
@@ -0,0 +1,247 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
|
||||
xmlns:bom="http://cyclonedx.org/schema/bom/1.0"
|
||||
xmlns:spdx="http://cyclonedx.org/schema/spdx"
|
||||
elementFormDefault="qualified"
|
||||
targetNamespace="http://cyclonedx.org/schema/bom/1.0"
|
||||
vc:minVersion="1.0"
|
||||
vc:maxVersion="1.1"
|
||||
version="1.0.1">
|
||||
|
||||
<xs:import namespace="http://cyclonedx.org/schema/spdx" schemaLocation="spdx.SNAPSHOT.xsd"/>
|
||||
|
||||
<xs:complexType name="component">
|
||||
<xs:sequence>
|
||||
<xs:element name="publisher" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The person(s) or organization(s) that published the component</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="group" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The grouping name or identifier. This will often be a shortened, single
|
||||
name of the company or project that produced the component, or the source package or
|
||||
domain name. Whitespace and special characters should be avoided. Examples include:
|
||||
apache, org.apache.commons, and apache.org.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="name" type="xs:normalizedString" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the component. This will often be a shortened, single name
|
||||
of the component. Examples: commons-lang3 and jquery</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="version" type="xs:normalizedString" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The component version. The version should ideally comply with semantic versioning
|
||||
but is not enforced.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies a description for the component</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="scope" type="bom:scope" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the scope of the component. If scope is not specified, 'runtime'
|
||||
scope will be assumed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="hashes" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="hash" type="bom:hashType"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="licenses" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="license">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="id" type="spdx:licenseId" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A valid SPDX license ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="name" type="xs:normalizedString" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If SPDX does not define the license used, this field may be used to provide the license name</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="copyright" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An optional copyright notice informing users of the underlying claims to copyright ownership in a published work.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="cpe" type="bom:cpe" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies a well-formed CPE name. See https://nvd.nist.gov/products/cpe</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="purl" type="xs:anyURI" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Specifies the package-url (PURL). The purl, if specified, must be valid and conform
|
||||
to the specification defined at: https://github.com/package-url/purl-spec
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="modified" type="xs:boolean" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
A boolean value indicating is the component has been modified from the original.
|
||||
A value of true indicates the component is a derivative of the original.
|
||||
A value of false indicates the component has not been modified from the original.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="components" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Specifies optional sub-components. This is not a dependency tree. It simply provides
|
||||
an optional way to group large sets of components together.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="component" type="bom:component"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" type="bom:classification" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Specifies the type of component. Software applications, libraries, frameworks, and
|
||||
other dependencies should be classified as 'application'.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax">
|
||||
<xs:annotation>
|
||||
<xs:documentation>User-defined attributes may be used on this element as long as they
|
||||
do not have the same name as an existing attribute used by the schema.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:anyAttribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="hashType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the file hash of the component</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="bom:hashValue">
|
||||
<xs:attribute name="alg" type="bom:hashAlg" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the algorithm used to create hash</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="scope">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The component is required for runtime</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The component is optional at runtime. Optional components are components that
|
||||
are not capable of being called due to them not be installed or otherwise accessible by any means.
|
||||
Components that are installed but due to configuration or other restrictions are prohibited from
|
||||
being called must be scoped as 'required'.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="classification">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="application"/>
|
||||
<xs:enumeration value="framework"/>
|
||||
<xs:enumeration value="library"/>
|
||||
<xs:enumeration value="operating-system"/>
|
||||
<xs:enumeration value="device"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="hashAlg">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="MD5"/>
|
||||
<xs:enumeration value="SHA-1"/>
|
||||
<xs:enumeration value="SHA-256"/>
|
||||
<xs:enumeration value="SHA-384"/>
|
||||
<xs:enumeration value="SHA-512"/>
|
||||
<xs:enumeration value="SHA3-256"/>
|
||||
<xs:enumeration value="SHA3-512"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="hashValue">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="([a-fA-F0-9]{32})|([a-fA-F0-9]{40})|([a-fA-F0-9]{64})|([a-fA-F0-9]{96})|([a-fA-F0-9]{128})"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="cpe">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">
|
||||
Define the format for acceptable CPE URIs. Supports CPE 2.2 and CPE 2.3 formats. Refer to https://nvd.nist.gov/products/cpe for official specification.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})|(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&'\(\)\+,/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&'\(\)\+,/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){4})"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:element name="bom">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="components">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="component" type="bom:component"/>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax">
|
||||
<xs:annotation>
|
||||
<xs:documentation>User-defined attributes may be used on this element as long as they
|
||||
do not have the same name as an existing attribute used by the schema.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:anyAttribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="version" type="xs:integer" default="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The version allows component publishers/authors to make changes to existing
|
||||
BOMs to update various aspects of the document such as description or licenses. When a system
|
||||
is presented with multiiple BOMs for the same component, the system should use the most recent
|
||||
version of the BOM. The default version is '1' and should be incremented for each version of the
|
||||
BOM that is published. Each version of a component should have a unique BOM and if no changes are
|
||||
made to the BOMs, then each BOM will have a version of '1'.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:anyAttribute namespace="##other" processContents="lax">
|
||||
<xs:annotation>
|
||||
<xs:documentation>User-defined attributes may be used on this element as long as they
|
||||
do not have the same name as an existing attribute used by the schema.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:anyAttribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,738 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
CycloneDX Software Bill-of-Material (SBoM) Specification
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
|
||||
xmlns:bom="http://cyclonedx.org/schema/bom/1.1"
|
||||
xmlns:spdx="http://cyclonedx.org/schema/spdx"
|
||||
elementFormDefault="qualified"
|
||||
targetNamespace="http://cyclonedx.org/schema/bom/1.1"
|
||||
vc:minVersion="1.0"
|
||||
vc:maxVersion="1.1"
|
||||
version="1.1">
|
||||
|
||||
<xs:import namespace="http://cyclonedx.org/schema/spdx" schemaLocation="spdx.SNAPSHOT.xsd"/>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<name>CycloneDX Software Bill-of-Material Specification</name>
|
||||
<url>https://cyclonedx.org/</url>
|
||||
<license uri="http://www.apache.org/licenses/LICENSE-2.0"
|
||||
version="2.0">Apache License, Version 2.0</license>
|
||||
<authors>
|
||||
<author>Steve Springett</author>
|
||||
</authors>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:simpleType name="refType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Identifier-DataType for interlinked elements.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string" />
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="componentsType">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="component" type="bom:component"/>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
<xs:anyAttribute namespace="##any" processContents="lax">
|
||||
<xs:annotation>
|
||||
<xs:documentation>User-defined attributes may be used on this element as long as they
|
||||
do not have the same name as an existing attribute used by the schema.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:anyAttribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="component">
|
||||
<xs:sequence>
|
||||
<xs:element name="publisher" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The person(s) or organization(s) that published the component</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="group" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The grouping name or identifier. This will often be a shortened, single
|
||||
name of the company or project that produced the component, or the source package or
|
||||
domain name. Whitespace and special characters should be avoided. Examples include:
|
||||
apache, org.apache.commons, and apache.org.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="name" type="xs:normalizedString" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the component. This will often be a shortened, single name
|
||||
of the component. Examples: commons-lang3 and jquery</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="version" type="xs:normalizedString" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The component version. The version should ideally comply with semantic versioning
|
||||
but is not enforced.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies a description for the component</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="scope" type="bom:scope" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the scope of the component. If scope is not specified, 'runtime'
|
||||
scope should be assumed by the consumer of the BOM</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="hashes" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="hash" type="bom:hashType"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="licenses" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:choice>
|
||||
<xs:element name="license" type="bom:licenseType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="expression" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A valid SPDX license expression.
|
||||
Refer to https://spdx.org/specifications for syntax requirements</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="copyright" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An optional copyright notice informing users of the underlying claims to
|
||||
copyright ownership in a published work.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="cpe" type="bom:cpe" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
DEPRECATED - DO NOT USE. This will be removed in a future version.
|
||||
Specifies a well-formed CPE name. See https://nvd.nist.gov/products/cpe
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="purl" type="xs:anyURI" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Specifies the package-url (PURL). The purl, if specified, must be valid and conform
|
||||
to the specification defined at: https://github.com/package-url/purl-spec
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="modified" type="xs:boolean" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
DEPRECATED - DO NOT USE. This will be removed in a future version. Use the pedigree
|
||||
element instead to supply information on exactly how the component was modified.
|
||||
A boolean value indicating is the component has been modified from the original.
|
||||
A value of true indicates the component is a derivative of the original.
|
||||
A value of false indicates the component has not been modified from the original.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="pedigree" type="bom:pedigreeType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Component pedigree is a way to document complex supply chain scenarios where components are
|
||||
created, distributed, modified, redistributed, combined with other components, etc.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="externalReferences" type="bom:externalReferences" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Provides the ability to document external references related to the
|
||||
component or to the project the component describes.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="components" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Specifies optional sub-components. This is not a dependency tree. It provides a way
|
||||
to specify a hierarchical representation of component assemblies, similar to
|
||||
system -> subsystem -> parts assembly in physical supply chains.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="component" type="bom:component"/>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" type="bom:classification" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Specifies the type of component. For software components, classify as application if no more
|
||||
specific appropriate classification is available or cannot be determined for the component.
|
||||
Valid choices are: application, framework, library, operating-system, device, or file
|
||||
Refer to the bom:classification documentation for information describing each one
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="bom-ref" type="bom:refType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
An optional identifier which can be used to reference the component elsewhere in the BOM.
|
||||
Uniqueness is enforced within all elements and children of the root-level bom element.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:anyAttribute namespace="##any" processContents="lax">
|
||||
<xs:annotation>
|
||||
<xs:documentation>User-defined attributes may be used on this element as long as they
|
||||
do not have the same name as an existing attribute used by the schema.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:anyAttribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="licenseType">
|
||||
<xs:sequence>
|
||||
<xs:choice>
|
||||
<xs:element name="id" type="spdx:licenseId" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A valid SPDX license ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="name" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If SPDX does not define the license used, this field may be used to provide the license name</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
<xs:element name="text" type="bom:licenseTextType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the optional full text of the license</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="url" type="xs:anyURI" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL to the license file. If specified, a 'license'
|
||||
externalReference should also be specified for completeness.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="licenseTextType">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies attributes of the license text</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="content-type" type="xs:normalizedString" default="text/plain">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the content type of the license text. Defaults to text/plain
|
||||
if not specified.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="encoding" type="bom:encoding">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Specifies the optional encoding the license text is represented in
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="hashType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the file hash of the component</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="bom:hashValue">
|
||||
<xs:attribute name="alg" type="bom:hashAlg" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the algorithm used to create the hash</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="scope">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The component is required for runtime</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The component is optional at runtime. Optional components are components that
|
||||
are not capable of being called due to them not be installed or otherwise accessible by any means.
|
||||
Components that are installed but due to configuration or other restrictions are prohibited from
|
||||
being called must be scoped as 'required'.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="excluded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Components that are excluded provide the ability to document component usage
|
||||
for test and other non-runtime purposes. Excluded components are not reachable within a call
|
||||
graph at runtime.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="classification">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="application">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A software application. Refer to https://en.wikipedia.org/wiki/Application_software
|
||||
for information about applications.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="framework">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A software framework. Refer to https://en.wikipedia.org/wiki/Software_framework
|
||||
for information on how frameworks vary slightly from libraries.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="library">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A software library. Refer to https://en.wikipedia.org/wiki/Library_(computing)
|
||||
for information about libraries. All third-party and open source reusable components will likely
|
||||
be a library. If the library also has key features of a framework, then it should be classified
|
||||
as a framework. If not, or is unknown, then specifying library is recommended.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="operating-system">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A software operating system without regard to deployment model
|
||||
(i.e. installed on physical hardware, virtual machine, container image, etc) Refer to
|
||||
https://en.wikipedia.org/wiki/Operating_system</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="device">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A hardware device such as a processor, or chip-set. A hardware device
|
||||
containing firmware should include a component for the physical hardware itself, and another
|
||||
component of type 'application' or 'operating-system' (whichever is relevant), describing
|
||||
information about the firmware.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="file">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A computer file. Refer to https://en.wikipedia.org/wiki/Computer_file
|
||||
for information about files.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="hashAlg">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="MD5"/>
|
||||
<xs:enumeration value="SHA-1"/>
|
||||
<xs:enumeration value="SHA-256"/>
|
||||
<xs:enumeration value="SHA-384"/>
|
||||
<xs:enumeration value="SHA-512"/>
|
||||
<xs:enumeration value="SHA3-256"/>
|
||||
<xs:enumeration value="SHA3-512"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="hashValue">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="([a-fA-F0-9]{32})|([a-fA-F0-9]{40})|([a-fA-F0-9]{64})|([a-fA-F0-9]{96})|([a-fA-F0-9]{128})"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="encoding">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="base64"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="cpe">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">
|
||||
Define the format for acceptable CPE URIs. Supports CPE 2.2 and CPE 2.3 formats.
|
||||
Refer to https://nvd.nist.gov/products/cpe for official specification.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})|(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&'\(\)\+,/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#$$%&'\(\)\+,/:;<=>@\[\]\^`\{\|}~]))+(\?*|\*?))|[\*\-])){4})"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="urnUuid">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">
|
||||
Defines a string representation of a UUID conforming to RFC 4122.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="urn:uuid:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\})"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="externalReferenceType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="vcs">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Version Control System</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="issue-tracker">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Issue or defect tracking system, or an Application Lifecycle Management (ALM) system</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="website">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Website</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="advisories">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Security advisories</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="bom">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Bill-of-material document (CycloneDX, SPDX, SWID, etc)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="mailing-list">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Mailing list or discussion group</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="social">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Social media account</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="chat">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Real-time chat platform</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="documentation">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Documentation, guides, or how-to instructions</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="support">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Community or commercial support</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="distribution">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Direct or repository download location</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="license">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL to the license file. If a license URL has been defined in the license
|
||||
node, it should also be defined as an external reference for completeness</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="build-meta">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Build-system specific meta file (i.e. pom.xml, package.json, .nuspec, etc)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="build-system">
|
||||
<xs:annotation>
|
||||
<xs:documentation>URL to an automated build system</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="other">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use this if no other types accurately describe the purpose of the external reference</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="externalReferences">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">
|
||||
External references provide a way to document systems, sites, and information that may be relevant
|
||||
but which are not included with the BOM.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="reference" type="bom:externalReference">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Zero or more external references can be defined</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="externalReference">
|
||||
<xs:sequence>
|
||||
<xs:element name="url" type="xs:anyURI" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The URL to the external reference</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="comment" type="xs:string" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">An optional comment describing the external reference</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="type" type="bom:externalReferenceType" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the type of external reference. There are built-in types to describe common
|
||||
references. If a type does not exist for the reference being referred to, use the "other" type.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:anyAttribute namespace="##any" processContents="lax">
|
||||
<xs:annotation>
|
||||
<xs:documentation>User-defined attributes may be used on this element as long as they
|
||||
do not have the same name as an existing attribute used by the schema.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:anyAttribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="commitsType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Zero or more commits can be specified.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="commit" type="bom:commitType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Specifies an individual commit.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="commitType">
|
||||
<xs:sequence>
|
||||
<xs:element name="uid" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">A unique identifier of the commit. This may be version control
|
||||
specific. For example, Subversion uses revision numbers whereas git uses commit hashes.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="url" type="xs:anyURI" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The URL to the commit. This URL will typically point to a commit
|
||||
in a version control system.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="author" type="bom:identifiableActionType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The author who created the changes in the commit</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="committer" type="bom:identifiableActionType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The person who committed or pushed the commit</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="message" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The text description of the contents of the commit</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="identifiableActionType">
|
||||
<xs:sequence>
|
||||
<xs:element name="timestamp" type="xs:dateTime" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The timestamp in which the action occurred</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="name" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The name of the individual who performed the action</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="email" type="xs:normalizedString" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The email address of the individual who performed the action</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="pedigreeType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">
|
||||
Component pedigree is a way to document complex supply chain scenarios where components are created,
|
||||
distributed, modified, redistributed, combined with other components, etc. Pedigree supports viewing
|
||||
this complex chain from the beginning, the end, or anywhere in the middle. It also provides a way to
|
||||
document variants where the exact relation may not be known.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:element name="ancestors" type="bom:componentsType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Describes zero or more components in which a component is derived
|
||||
from. This is commonly used to describe forks from existing projects where the forked version
|
||||
contains a ancestor node containing the original component it was forked from. For example,
|
||||
Component A is the original component. Component B is the component being used and documented
|
||||
in the BOM. However, Component B contains a pedigree node with a single ancestor documenting
|
||||
Component A - the original component from which Component B is derived from.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="descendants" type="bom:componentsType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Descendants are the exact opposite of ancestors. This provides a
|
||||
way to document all forks (and their forks) of an original or root component.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="variants" type="bom:componentsType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Variants describe relations where the relationship between the
|
||||
components are not known. For example, if Component A contains nearly identical code to
|
||||
Component B. They are both related, but it is unclear if one is derived from the other,
|
||||
or if they share a common ancestor.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="commits" type="bom:commitsType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">A list of zero or more commits which provide a trail describing
|
||||
how the component deviates from an ancestor, descendant, or variant.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="notes" type="xs:string" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Notes, observations, and other non-structured commentary
|
||||
describing the components pedigree.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:element name="bom">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="components" type="bom:componentsType"/>
|
||||
<xs:element name="externalReferences" type="bom:externalReferences" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Provides the ability to document external references related to the BOM or
|
||||
to the project the BOM describes.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Allows any undeclared elements as long as the elements are placed in a different namespace.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:any>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="version" type="xs:integer" default="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The version allows component publishers/authors to make changes to existing
|
||||
BOMs to update various aspects of the document such as description or licenses. When a system
|
||||
is presented with multiple BOMs for the same component, the system should use the most recent
|
||||
version of the BOM. The default version is '1' and should be incremented for each version of the
|
||||
BOM that is published. Each version of a component should have a unique BOM and if no changes are
|
||||
made to the BOMs, then each BOM will have a version of '1'.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="serialNumber" type="bom:urnUuid">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Every BOM generated should have a unique serial number, even if the contents
|
||||
of the BOM being generated have not changed over time. The process or tool responsible for
|
||||
creating the BOM should create random UUID's for every BOM generated.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:anyAttribute namespace="##any" processContents="lax">
|
||||
<xs:annotation>
|
||||
<xs:documentation>User-defined attributes may be used on this element as long as they
|
||||
do not have the same name as an existing attribute used by the schema.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:anyAttribute>
|
||||
</xs:complexType>
|
||||
<xs:unique name="bom-ref">
|
||||
<xs:selector xpath=".//*"/>
|
||||
<xs:field xpath="@bom-ref"/>
|
||||
</xs:unique>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,240 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://cyclonedx.org/schema/jsf-0.82.schema.json",
|
||||
"type": "object",
|
||||
"title": "JSON Signature Format (JSF) standard",
|
||||
"$comment" : "JSON Signature Format schema is published under the terms of the Apache License 2.0. JSF was developed by Anders Rundgren (anders.rundgren.net@gmail.com) as a part of the OpenKeyStore project. This schema supports the entirely of the JSF standard excluding 'extensions'.",
|
||||
"definitions": {
|
||||
"signature": {
|
||||
"type": "object",
|
||||
"title": "Signature",
|
||||
"oneOf": [
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"signers": {
|
||||
"type": "array",
|
||||
"title": "Signature",
|
||||
"description": "Unique top level property for Multiple Signatures. (multisignature)",
|
||||
"items": {"$ref": "#/definitions/signer"}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"chain": {
|
||||
"type": "array",
|
||||
"title": "Signature",
|
||||
"description": "Unique top level property for Signature Chains. (signaturechain)",
|
||||
"items": {"$ref": "#/definitions/signer"}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Signature",
|
||||
"description": "Unique top level property for simple signatures. (signaturecore)",
|
||||
"$ref": "#/definitions/signer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signer": {
|
||||
"type": "object",
|
||||
"title": "Signature",
|
||||
"required": [
|
||||
"algorithm",
|
||||
"value"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"algorithm": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"title": "Algorithm",
|
||||
"description": "Signature algorithm. The currently recognized JWA [RFC7518] and RFC8037 [RFC8037] asymmetric key algorithms. Note: Unlike RFC8037 [RFC8037] JSF requires explicit Ed* algorithm names instead of \"EdDSA\".",
|
||||
"enum": [
|
||||
"RS256",
|
||||
"RS384",
|
||||
"RS512",
|
||||
"PS256",
|
||||
"PS384",
|
||||
"PS512",
|
||||
"ES256",
|
||||
"ES384",
|
||||
"ES512",
|
||||
"Ed25519",
|
||||
"Ed448",
|
||||
"HS256",
|
||||
"HS384",
|
||||
"HS512"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"title": "Algorithm",
|
||||
"description": "Signature algorithm. Note: If proprietary signature algorithms are added, they must be expressed as URIs.",
|
||||
"format": "uri"
|
||||
}
|
||||
]
|
||||
},
|
||||
"keyId": {
|
||||
"type": "string",
|
||||
"title": "Key ID",
|
||||
"description": "Optional. Application specific string identifying the signature key."
|
||||
},
|
||||
"publicKey": {
|
||||
"title": "Public key",
|
||||
"description": "Optional. Public key object.",
|
||||
"$ref": "#/definitions/publicKey"
|
||||
},
|
||||
"certificatePath": {
|
||||
"type": "array",
|
||||
"title": "Certificate path",
|
||||
"description": "Optional. Sorted array of X.509 [RFC5280] certificates, where the first element must contain the signature certificate. The certificate path must be contiguous but is not required to be complete.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"excludes": {
|
||||
"type": "array",
|
||||
"title": "Excludes",
|
||||
"description": "Optional. Array holding the names of one or more application level properties that must be excluded from the signature process. Note that the \"excludes\" property itself, must also be excluded from the signature process. Since both the \"excludes\" property and the associated data it points to are unsigned, a conforming JSF implementation must provide options for specifying which properties to accept.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"title": "Signature",
|
||||
"description": "The signature data. Note that the binary representation must follow the JWA [RFC7518] specifications."
|
||||
}
|
||||
}
|
||||
},
|
||||
"keyType": {
|
||||
"type": "string",
|
||||
"title": "Key type",
|
||||
"description": "Key type indicator.",
|
||||
"enum": [
|
||||
"EC",
|
||||
"OKP",
|
||||
"RSA"
|
||||
]
|
||||
},
|
||||
"publicKey": {
|
||||
"title": "Public key",
|
||||
"description": "Optional. Public key object.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kty"
|
||||
],
|
||||
"additionalProperties": true,
|
||||
"properties": {
|
||||
"kty": {
|
||||
"$ref": "#/definitions/keyType"
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": { "kty": { "const": "EC" } }
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"kty",
|
||||
"crv",
|
||||
"x",
|
||||
"y"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"kty": {
|
||||
"$ref": "#/definitions/keyType"
|
||||
},
|
||||
"crv": {
|
||||
"type": "string",
|
||||
"title": "Curve name",
|
||||
"description": "EC curve name.",
|
||||
"enum": [
|
||||
"P-256",
|
||||
"P-384",
|
||||
"P-521"
|
||||
]
|
||||
},
|
||||
"x": {
|
||||
"type": "string",
|
||||
"title": "Coordinate",
|
||||
"description": "EC curve point X. The length of this field must be the full size of a coordinate for the curve specified in the \"crv\" parameter. For example, if the value of \"crv\" is \"P-521\", the decoded argument must be 66 bytes."
|
||||
},
|
||||
"y": {
|
||||
"type": "string",
|
||||
"title": "Coordinate",
|
||||
"description": "EC curve point Y. The length of this field must be the full size of a coordinate for the curve specified in the \"crv\" parameter. For example, if the value of \"crv\" is \"P-256\", the decoded argument must be 32 bytes."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": { "kty": { "const": "OKP" } }
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"kty",
|
||||
"crv",
|
||||
"x"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"kty": {
|
||||
"$ref": "#/definitions/keyType"
|
||||
},
|
||||
"crv": {
|
||||
"type": "string",
|
||||
"title": "Curve name",
|
||||
"description": "EdDSA curve name.",
|
||||
"enum": [
|
||||
"Ed25519",
|
||||
"Ed448"
|
||||
]
|
||||
},
|
||||
"x": {
|
||||
"type": "string",
|
||||
"title": "Coordinate",
|
||||
"description": "EdDSA curve point X. The length of this field must be the full size of a coordinate for the curve specified in the \"crv\" parameter. For example, if the value of \"crv\" is \"Ed25519\", the decoded argument must be 32 bytes."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": { "kty": { "const": "RSA" } }
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"kty",
|
||||
"n",
|
||||
"e"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"kty": {
|
||||
"$ref": "#/definitions/keyType"
|
||||
},
|
||||
"n": {
|
||||
"type": "string",
|
||||
"title": "Modulus",
|
||||
"description": "RSA modulus."
|
||||
},
|
||||
"e": {
|
||||
"type": "string",
|
||||
"title": "Exponent",
|
||||
"description": "RSA exponent."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "http://cyclonedx.org/schema/spdx.schema.json",
|
||||
"$comment": "v1.0-3.24.0",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"0BSD",
|
||||
"3D-Slicer-1.0",
|
||||
"AAL",
|
||||
"Abstyles",
|
||||
"AdaCore-doc",
|
||||
"Adobe-2006",
|
||||
"Adobe-Display-PostScript",
|
||||
"Adobe-Glyph",
|
||||
"Adobe-Utopia",
|
||||
"ADSL",
|
||||
"AFL-1.1",
|
||||
"AFL-1.2",
|
||||
"AFL-2.0",
|
||||
"AFL-2.1",
|
||||
"AFL-3.0",
|
||||
"Afmparse",
|
||||
"AGPL-1.0",
|
||||
"AGPL-1.0-only",
|
||||
"AGPL-1.0-or-later",
|
||||
"AGPL-3.0",
|
||||
"AGPL-3.0-only",
|
||||
"AGPL-3.0-or-later",
|
||||
"Aladdin",
|
||||
"AMD-newlib",
|
||||
"AMDPLPA",
|
||||
"AML",
|
||||
"AML-glslang",
|
||||
"AMPAS",
|
||||
"ANTLR-PD",
|
||||
"ANTLR-PD-fallback",
|
||||
"any-OSI",
|
||||
"Apache-1.0",
|
||||
"Apache-1.1",
|
||||
"Apache-2.0",
|
||||
"APAFML",
|
||||
"APL-1.0",
|
||||
"App-s2p",
|
||||
"APSL-1.0",
|
||||
"APSL-1.1",
|
||||
"APSL-1.2",
|
||||
"APSL-2.0",
|
||||
"Arphic-1999",
|
||||
"Artistic-1.0",
|
||||
"Artistic-1.0-cl8",
|
||||
"Artistic-1.0-Perl",
|
||||
"Artistic-2.0",
|
||||
"ASWF-Digital-Assets-1.0",
|
||||
"ASWF-Digital-Assets-1.1",
|
||||
"Baekmuk",
|
||||
"Bahyph",
|
||||
"Barr",
|
||||
"bcrypt-Solar-Designer",
|
||||
"Beerware",
|
||||
"Bitstream-Charter",
|
||||
"Bitstream-Vera",
|
||||
"BitTorrent-1.0",
|
||||
"BitTorrent-1.1",
|
||||
"blessing",
|
||||
"BlueOak-1.0.0",
|
||||
"Boehm-GC",
|
||||
"Borceux",
|
||||
"Brian-Gladman-2-Clause",
|
||||
"Brian-Gladman-3-Clause",
|
||||
"BSD-1-Clause",
|
||||
"BSD-2-Clause",
|
||||
"BSD-2-Clause-Darwin",
|
||||
"BSD-2-Clause-first-lines",
|
||||
"BSD-2-Clause-FreeBSD",
|
||||
"BSD-2-Clause-NetBSD",
|
||||
"BSD-2-Clause-Patent",
|
||||
"BSD-2-Clause-Views",
|
||||
"BSD-3-Clause",
|
||||
"BSD-3-Clause-acpica",
|
||||
"BSD-3-Clause-Attribution",
|
||||
"BSD-3-Clause-Clear",
|
||||
"BSD-3-Clause-flex",
|
||||
"BSD-3-Clause-HP",
|
||||
"BSD-3-Clause-LBNL",
|
||||
"BSD-3-Clause-Modification",
|
||||
"BSD-3-Clause-No-Military-License",
|
||||
"BSD-3-Clause-No-Nuclear-License",
|
||||
"BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"BSD-3-Clause-No-Nuclear-Warranty",
|
||||
"BSD-3-Clause-Open-MPI",
|
||||
"BSD-3-Clause-Sun",
|
||||
"BSD-4-Clause",
|
||||
"BSD-4-Clause-Shortened",
|
||||
"BSD-4-Clause-UC",
|
||||
"BSD-4.3RENO",
|
||||
"BSD-4.3TAHOE",
|
||||
"BSD-Advertising-Acknowledgement",
|
||||
"BSD-Attribution-HPND-disclaimer",
|
||||
"BSD-Inferno-Nettverk",
|
||||
"BSD-Protection",
|
||||
"BSD-Source-beginning-file",
|
||||
"BSD-Source-Code",
|
||||
"BSD-Systemics",
|
||||
"BSD-Systemics-W3Works",
|
||||
"BSL-1.0",
|
||||
"BUSL-1.1",
|
||||
"bzip2-1.0.5",
|
||||
"bzip2-1.0.6",
|
||||
"C-UDA-1.0",
|
||||
"CAL-1.0",
|
||||
"CAL-1.0-Combined-Work-Exception",
|
||||
"Caldera",
|
||||
"Caldera-no-preamble",
|
||||
"Catharon",
|
||||
"CATOSL-1.1",
|
||||
"CC-BY-1.0",
|
||||
"CC-BY-2.0",
|
||||
"CC-BY-2.5",
|
||||
"CC-BY-2.5-AU",
|
||||
"CC-BY-3.0",
|
||||
"CC-BY-3.0-AT",
|
||||
"CC-BY-3.0-AU",
|
||||
"CC-BY-3.0-DE",
|
||||
"CC-BY-3.0-IGO",
|
||||
"CC-BY-3.0-NL",
|
||||
"CC-BY-3.0-US",
|
||||
"CC-BY-4.0",
|
||||
"CC-BY-NC-1.0",
|
||||
"CC-BY-NC-2.0",
|
||||
"CC-BY-NC-2.5",
|
||||
"CC-BY-NC-3.0",
|
||||
"CC-BY-NC-3.0-DE",
|
||||
"CC-BY-NC-4.0",
|
||||
"CC-BY-NC-ND-1.0",
|
||||
"CC-BY-NC-ND-2.0",
|
||||
"CC-BY-NC-ND-2.5",
|
||||
"CC-BY-NC-ND-3.0",
|
||||
"CC-BY-NC-ND-3.0-DE",
|
||||
"CC-BY-NC-ND-3.0-IGO",
|
||||
"CC-BY-NC-ND-4.0",
|
||||
"CC-BY-NC-SA-1.0",
|
||||
"CC-BY-NC-SA-2.0",
|
||||
"CC-BY-NC-SA-2.0-DE",
|
||||
"CC-BY-NC-SA-2.0-FR",
|
||||
"CC-BY-NC-SA-2.0-UK",
|
||||
"CC-BY-NC-SA-2.5",
|
||||
"CC-BY-NC-SA-3.0",
|
||||
"CC-BY-NC-SA-3.0-DE",
|
||||
"CC-BY-NC-SA-3.0-IGO",
|
||||
"CC-BY-NC-SA-4.0",
|
||||
"CC-BY-ND-1.0",
|
||||
"CC-BY-ND-2.0",
|
||||
"CC-BY-ND-2.5",
|
||||
"CC-BY-ND-3.0",
|
||||
"CC-BY-ND-3.0-DE",
|
||||
"CC-BY-ND-4.0",
|
||||
"CC-BY-SA-1.0",
|
||||
"CC-BY-SA-2.0",
|
||||
"CC-BY-SA-2.0-UK",
|
||||
"CC-BY-SA-2.1-JP",
|
||||
"CC-BY-SA-2.5",
|
||||
"CC-BY-SA-3.0",
|
||||
"CC-BY-SA-3.0-AT",
|
||||
"CC-BY-SA-3.0-DE",
|
||||
"CC-BY-SA-3.0-IGO",
|
||||
"CC-BY-SA-4.0",
|
||||
"CC-PDDC",
|
||||
"CC0-1.0",
|
||||
"CDDL-1.0",
|
||||
"CDDL-1.1",
|
||||
"CDL-1.0",
|
||||
"CDLA-Permissive-1.0",
|
||||
"CDLA-Permissive-2.0",
|
||||
"CDLA-Sharing-1.0",
|
||||
"CECILL-1.0",
|
||||
"CECILL-1.1",
|
||||
"CECILL-2.0",
|
||||
"CECILL-2.1",
|
||||
"CECILL-B",
|
||||
"CECILL-C",
|
||||
"CERN-OHL-1.1",
|
||||
"CERN-OHL-1.2",
|
||||
"CERN-OHL-P-2.0",
|
||||
"CERN-OHL-S-2.0",
|
||||
"CERN-OHL-W-2.0",
|
||||
"CFITSIO",
|
||||
"check-cvs",
|
||||
"checkmk",
|
||||
"ClArtistic",
|
||||
"Clips",
|
||||
"CMU-Mach",
|
||||
"CMU-Mach-nodoc",
|
||||
"CNRI-Jython",
|
||||
"CNRI-Python",
|
||||
"CNRI-Python-GPL-Compatible",
|
||||
"COIL-1.0",
|
||||
"Community-Spec-1.0",
|
||||
"Condor-1.1",
|
||||
"copyleft-next-0.3.0",
|
||||
"copyleft-next-0.3.1",
|
||||
"Cornell-Lossless-JPEG",
|
||||
"CPAL-1.0",
|
||||
"CPL-1.0",
|
||||
"CPOL-1.02",
|
||||
"Cronyx",
|
||||
"Crossword",
|
||||
"CrystalStacker",
|
||||
"CUA-OPL-1.0",
|
||||
"Cube",
|
||||
"curl",
|
||||
"cve-tou",
|
||||
"D-FSL-1.0",
|
||||
"DEC-3-Clause",
|
||||
"diffmark",
|
||||
"DL-DE-BY-2.0",
|
||||
"DL-DE-ZERO-2.0",
|
||||
"DOC",
|
||||
"Dotseqn",
|
||||
"DRL-1.0",
|
||||
"DRL-1.1",
|
||||
"DSDP",
|
||||
"dtoa",
|
||||
"dvipdfm",
|
||||
"ECL-1.0",
|
||||
"ECL-2.0",
|
||||
"eCos-2.0",
|
||||
"EFL-1.0",
|
||||
"EFL-2.0",
|
||||
"eGenix",
|
||||
"Elastic-2.0",
|
||||
"Entessa",
|
||||
"EPICS",
|
||||
"EPL-1.0",
|
||||
"EPL-2.0",
|
||||
"ErlPL-1.1",
|
||||
"etalab-2.0",
|
||||
"EUDatagrid",
|
||||
"EUPL-1.0",
|
||||
"EUPL-1.1",
|
||||
"EUPL-1.2",
|
||||
"Eurosym",
|
||||
"Fair",
|
||||
"FBM",
|
||||
"FDK-AAC",
|
||||
"Ferguson-Twofish",
|
||||
"Frameworx-1.0",
|
||||
"FreeBSD-DOC",
|
||||
"FreeImage",
|
||||
"FSFAP",
|
||||
"FSFAP-no-warranty-disclaimer",
|
||||
"FSFUL",
|
||||
"FSFULLR",
|
||||
"FSFULLRWD",
|
||||
"FTL",
|
||||
"Furuseth",
|
||||
"fwlw",
|
||||
"GCR-docs",
|
||||
"GD",
|
||||
"GFDL-1.1",
|
||||
"GFDL-1.1-invariants-only",
|
||||
"GFDL-1.1-invariants-or-later",
|
||||
"GFDL-1.1-no-invariants-only",
|
||||
"GFDL-1.1-no-invariants-or-later",
|
||||
"GFDL-1.1-only",
|
||||
"GFDL-1.1-or-later",
|
||||
"GFDL-1.2",
|
||||
"GFDL-1.2-invariants-only",
|
||||
"GFDL-1.2-invariants-or-later",
|
||||
"GFDL-1.2-no-invariants-only",
|
||||
"GFDL-1.2-no-invariants-or-later",
|
||||
"GFDL-1.2-only",
|
||||
"GFDL-1.2-or-later",
|
||||
"GFDL-1.3",
|
||||
"GFDL-1.3-invariants-only",
|
||||
"GFDL-1.3-invariants-or-later",
|
||||
"GFDL-1.3-no-invariants-only",
|
||||
"GFDL-1.3-no-invariants-or-later",
|
||||
"GFDL-1.3-only",
|
||||
"GFDL-1.3-or-later",
|
||||
"Giftware",
|
||||
"GL2PS",
|
||||
"Glide",
|
||||
"Glulxe",
|
||||
"GLWTPL",
|
||||
"gnuplot",
|
||||
"GPL-1.0",
|
||||
"GPL-1.0+",
|
||||
"GPL-1.0-only",
|
||||
"GPL-1.0-or-later",
|
||||
"GPL-2.0",
|
||||
"GPL-2.0+",
|
||||
"GPL-2.0-only",
|
||||
"GPL-2.0-or-later",
|
||||
"GPL-2.0-with-autoconf-exception",
|
||||
"GPL-2.0-with-bison-exception",
|
||||
"GPL-2.0-with-classpath-exception",
|
||||
"GPL-2.0-with-font-exception",
|
||||
"GPL-2.0-with-GCC-exception",
|
||||
"GPL-3.0",
|
||||
"GPL-3.0+",
|
||||
"GPL-3.0-only",
|
||||
"GPL-3.0-or-later",
|
||||
"GPL-3.0-with-autoconf-exception",
|
||||
"GPL-3.0-with-GCC-exception",
|
||||
"Graphics-Gems",
|
||||
"gSOAP-1.3b",
|
||||
"gtkbook",
|
||||
"Gutmann",
|
||||
"HaskellReport",
|
||||
"hdparm",
|
||||
"Hippocratic-2.1",
|
||||
"HP-1986",
|
||||
"HP-1989",
|
||||
"HPND",
|
||||
"HPND-DEC",
|
||||
"HPND-doc",
|
||||
"HPND-doc-sell",
|
||||
"HPND-export-US",
|
||||
"HPND-export-US-acknowledgement",
|
||||
"HPND-export-US-modify",
|
||||
"HPND-export2-US",
|
||||
"HPND-Fenneberg-Livingston",
|
||||
"HPND-INRIA-IMAG",
|
||||
"HPND-Intel",
|
||||
"HPND-Kevlin-Henney",
|
||||
"HPND-Markus-Kuhn",
|
||||
"HPND-merchantability-variant",
|
||||
"HPND-MIT-disclaimer",
|
||||
"HPND-Pbmplus",
|
||||
"HPND-sell-MIT-disclaimer-xserver",
|
||||
"HPND-sell-regexpr",
|
||||
"HPND-sell-variant",
|
||||
"HPND-sell-variant-MIT-disclaimer",
|
||||
"HPND-sell-variant-MIT-disclaimer-rev",
|
||||
"HPND-UC",
|
||||
"HPND-UC-export-US",
|
||||
"HTMLTIDY",
|
||||
"IBM-pibs",
|
||||
"ICU",
|
||||
"IEC-Code-Components-EULA",
|
||||
"IJG",
|
||||
"IJG-short",
|
||||
"ImageMagick",
|
||||
"iMatix",
|
||||
"Imlib2",
|
||||
"Info-ZIP",
|
||||
"Inner-Net-2.0",
|
||||
"Intel",
|
||||
"Intel-ACPI",
|
||||
"Interbase-1.0",
|
||||
"IPA",
|
||||
"IPL-1.0",
|
||||
"ISC",
|
||||
"ISC-Veillard",
|
||||
"Jam",
|
||||
"JasPer-2.0",
|
||||
"JPL-image",
|
||||
"JPNIC",
|
||||
"JSON",
|
||||
"Kastrup",
|
||||
"Kazlib",
|
||||
"Knuth-CTAN",
|
||||
"LAL-1.2",
|
||||
"LAL-1.3",
|
||||
"Latex2e",
|
||||
"Latex2e-translated-notice",
|
||||
"Leptonica",
|
||||
"LGPL-2.0",
|
||||
"LGPL-2.0+",
|
||||
"LGPL-2.0-only",
|
||||
"LGPL-2.0-or-later",
|
||||
"LGPL-2.1",
|
||||
"LGPL-2.1+",
|
||||
"LGPL-2.1-only",
|
||||
"LGPL-2.1-or-later",
|
||||
"LGPL-3.0",
|
||||
"LGPL-3.0+",
|
||||
"LGPL-3.0-only",
|
||||
"LGPL-3.0-or-later",
|
||||
"LGPLLR",
|
||||
"Libpng",
|
||||
"libpng-2.0",
|
||||
"libselinux-1.0",
|
||||
"libtiff",
|
||||
"libutil-David-Nugent",
|
||||
"LiLiQ-P-1.1",
|
||||
"LiLiQ-R-1.1",
|
||||
"LiLiQ-Rplus-1.1",
|
||||
"Linux-man-pages-1-para",
|
||||
"Linux-man-pages-copyleft",
|
||||
"Linux-man-pages-copyleft-2-para",
|
||||
"Linux-man-pages-copyleft-var",
|
||||
"Linux-OpenIB",
|
||||
"LOOP",
|
||||
"LPD-document",
|
||||
"LPL-1.0",
|
||||
"LPL-1.02",
|
||||
"LPPL-1.0",
|
||||
"LPPL-1.1",
|
||||
"LPPL-1.2",
|
||||
"LPPL-1.3a",
|
||||
"LPPL-1.3c",
|
||||
"lsof",
|
||||
"Lucida-Bitmap-Fonts",
|
||||
"LZMA-SDK-9.11-to-9.20",
|
||||
"LZMA-SDK-9.22",
|
||||
"Mackerras-3-Clause",
|
||||
"Mackerras-3-Clause-acknowledgment",
|
||||
"magaz",
|
||||
"mailprio",
|
||||
"MakeIndex",
|
||||
"Martin-Birgmeier",
|
||||
"McPhee-slideshow",
|
||||
"metamail",
|
||||
"Minpack",
|
||||
"MirOS",
|
||||
"MIT",
|
||||
"MIT-0",
|
||||
"MIT-advertising",
|
||||
"MIT-CMU",
|
||||
"MIT-enna",
|
||||
"MIT-feh",
|
||||
"MIT-Festival",
|
||||
"MIT-Khronos-old",
|
||||
"MIT-Modern-Variant",
|
||||
"MIT-open-group",
|
||||
"MIT-testregex",
|
||||
"MIT-Wu",
|
||||
"MITNFA",
|
||||
"MMIXware",
|
||||
"Motosoto",
|
||||
"MPEG-SSG",
|
||||
"mpi-permissive",
|
||||
"mpich2",
|
||||
"MPL-1.0",
|
||||
"MPL-1.1",
|
||||
"MPL-2.0",
|
||||
"MPL-2.0-no-copyleft-exception",
|
||||
"mplus",
|
||||
"MS-LPL",
|
||||
"MS-PL",
|
||||
"MS-RL",
|
||||
"MTLL",
|
||||
"MulanPSL-1.0",
|
||||
"MulanPSL-2.0",
|
||||
"Multics",
|
||||
"Mup",
|
||||
"NAIST-2003",
|
||||
"NASA-1.3",
|
||||
"Naumen",
|
||||
"NBPL-1.0",
|
||||
"NCBI-PD",
|
||||
"NCGL-UK-2.0",
|
||||
"NCL",
|
||||
"NCSA",
|
||||
"Net-SNMP",
|
||||
"NetCDF",
|
||||
"Newsletr",
|
||||
"NGPL",
|
||||
"NICTA-1.0",
|
||||
"NIST-PD",
|
||||
"NIST-PD-fallback",
|
||||
"NIST-Software",
|
||||
"NLOD-1.0",
|
||||
"NLOD-2.0",
|
||||
"NLPL",
|
||||
"Nokia",
|
||||
"NOSL",
|
||||
"Noweb",
|
||||
"NPL-1.0",
|
||||
"NPL-1.1",
|
||||
"NPOSL-3.0",
|
||||
"NRL",
|
||||
"NTP",
|
||||
"NTP-0",
|
||||
"Nunit",
|
||||
"O-UDA-1.0",
|
||||
"OAR",
|
||||
"OCCT-PL",
|
||||
"OCLC-2.0",
|
||||
"ODbL-1.0",
|
||||
"ODC-By-1.0",
|
||||
"OFFIS",
|
||||
"OFL-1.0",
|
||||
"OFL-1.0-no-RFN",
|
||||
"OFL-1.0-RFN",
|
||||
"OFL-1.1",
|
||||
"OFL-1.1-no-RFN",
|
||||
"OFL-1.1-RFN",
|
||||
"OGC-1.0",
|
||||
"OGDL-Taiwan-1.0",
|
||||
"OGL-Canada-2.0",
|
||||
"OGL-UK-1.0",
|
||||
"OGL-UK-2.0",
|
||||
"OGL-UK-3.0",
|
||||
"OGTSL",
|
||||
"OLDAP-1.1",
|
||||
"OLDAP-1.2",
|
||||
"OLDAP-1.3",
|
||||
"OLDAP-1.4",
|
||||
"OLDAP-2.0",
|
||||
"OLDAP-2.0.1",
|
||||
"OLDAP-2.1",
|
||||
"OLDAP-2.2",
|
||||
"OLDAP-2.2.1",
|
||||
"OLDAP-2.2.2",
|
||||
"OLDAP-2.3",
|
||||
"OLDAP-2.4",
|
||||
"OLDAP-2.5",
|
||||
"OLDAP-2.6",
|
||||
"OLDAP-2.7",
|
||||
"OLDAP-2.8",
|
||||
"OLFL-1.3",
|
||||
"OML",
|
||||
"OpenPBS-2.3",
|
||||
"OpenSSL",
|
||||
"OpenSSL-standalone",
|
||||
"OpenVision",
|
||||
"OPL-1.0",
|
||||
"OPL-UK-3.0",
|
||||
"OPUBL-1.0",
|
||||
"OSET-PL-2.1",
|
||||
"OSL-1.0",
|
||||
"OSL-1.1",
|
||||
"OSL-2.0",
|
||||
"OSL-2.1",
|
||||
"OSL-3.0",
|
||||
"PADL",
|
||||
"Parity-6.0.0",
|
||||
"Parity-7.0.0",
|
||||
"PDDL-1.0",
|
||||
"PHP-3.0",
|
||||
"PHP-3.01",
|
||||
"Pixar",
|
||||
"pkgconf",
|
||||
"Plexus",
|
||||
"pnmstitch",
|
||||
"PolyForm-Noncommercial-1.0.0",
|
||||
"PolyForm-Small-Business-1.0.0",
|
||||
"PostgreSQL",
|
||||
"PPL",
|
||||
"PSF-2.0",
|
||||
"psfrag",
|
||||
"psutils",
|
||||
"Python-2.0",
|
||||
"Python-2.0.1",
|
||||
"python-ldap",
|
||||
"Qhull",
|
||||
"QPL-1.0",
|
||||
"QPL-1.0-INRIA-2004",
|
||||
"radvd",
|
||||
"Rdisc",
|
||||
"RHeCos-1.1",
|
||||
"RPL-1.1",
|
||||
"RPL-1.5",
|
||||
"RPSL-1.0",
|
||||
"RSA-MD",
|
||||
"RSCPL",
|
||||
"Ruby",
|
||||
"SAX-PD",
|
||||
"SAX-PD-2.0",
|
||||
"Saxpath",
|
||||
"SCEA",
|
||||
"SchemeReport",
|
||||
"Sendmail",
|
||||
"Sendmail-8.23",
|
||||
"SGI-B-1.0",
|
||||
"SGI-B-1.1",
|
||||
"SGI-B-2.0",
|
||||
"SGI-OpenGL",
|
||||
"SGP4",
|
||||
"SHL-0.5",
|
||||
"SHL-0.51",
|
||||
"SimPL-2.0",
|
||||
"SISSL",
|
||||
"SISSL-1.2",
|
||||
"SL",
|
||||
"Sleepycat",
|
||||
"SMLNJ",
|
||||
"SMPPL",
|
||||
"SNIA",
|
||||
"snprintf",
|
||||
"softSurfer",
|
||||
"Soundex",
|
||||
"Spencer-86",
|
||||
"Spencer-94",
|
||||
"Spencer-99",
|
||||
"SPL-1.0",
|
||||
"ssh-keyscan",
|
||||
"SSH-OpenSSH",
|
||||
"SSH-short",
|
||||
"SSLeay-standalone",
|
||||
"SSPL-1.0",
|
||||
"StandardML-NJ",
|
||||
"SugarCRM-1.1.3",
|
||||
"Sun-PPP",
|
||||
"Sun-PPP-2000",
|
||||
"SunPro",
|
||||
"SWL",
|
||||
"swrule",
|
||||
"Symlinks",
|
||||
"TAPR-OHL-1.0",
|
||||
"TCL",
|
||||
"TCP-wrappers",
|
||||
"TermReadKey",
|
||||
"TGPPL-1.0",
|
||||
"threeparttable",
|
||||
"TMate",
|
||||
"TORQUE-1.1",
|
||||
"TOSL",
|
||||
"TPDL",
|
||||
"TPL-1.0",
|
||||
"TTWL",
|
||||
"TTYP0",
|
||||
"TU-Berlin-1.0",
|
||||
"TU-Berlin-2.0",
|
||||
"UCAR",
|
||||
"UCL-1.0",
|
||||
"ulem",
|
||||
"UMich-Merit",
|
||||
"Unicode-3.0",
|
||||
"Unicode-DFS-2015",
|
||||
"Unicode-DFS-2016",
|
||||
"Unicode-TOU",
|
||||
"UnixCrypt",
|
||||
"Unlicense",
|
||||
"UPL-1.0",
|
||||
"URT-RLE",
|
||||
"Vim",
|
||||
"VOSTROM",
|
||||
"VSL-1.0",
|
||||
"W3C",
|
||||
"W3C-19980720",
|
||||
"W3C-20150513",
|
||||
"w3m",
|
||||
"Watcom-1.0",
|
||||
"Widget-Workshop",
|
||||
"Wsuipa",
|
||||
"WTFPL",
|
||||
"wxWindows",
|
||||
"X11",
|
||||
"X11-distribute-modifications-variant",
|
||||
"Xdebug-1.03",
|
||||
"Xerox",
|
||||
"Xfig",
|
||||
"XFree86-1.1",
|
||||
"xinetd",
|
||||
"xkeyboard-config-Zinoviev",
|
||||
"xlock",
|
||||
"Xnet",
|
||||
"xpp",
|
||||
"XSkat",
|
||||
"xzoom",
|
||||
"YPL-1.0",
|
||||
"YPL-1.1",
|
||||
"Zed",
|
||||
"Zeeff",
|
||||
"Zend-2.0",
|
||||
"Zimbra-1.3",
|
||||
"Zimbra-1.4",
|
||||
"Zlib",
|
||||
"zlib-acknowledgement",
|
||||
"ZPL-1.1",
|
||||
"ZPL-2.0",
|
||||
"ZPL-2.1",
|
||||
"389-exception",
|
||||
"Asterisk-exception",
|
||||
"Asterisk-linking-protocols-exception",
|
||||
"Autoconf-exception-2.0",
|
||||
"Autoconf-exception-3.0",
|
||||
"Autoconf-exception-generic",
|
||||
"Autoconf-exception-generic-3.0",
|
||||
"Autoconf-exception-macro",
|
||||
"Bison-exception-1.24",
|
||||
"Bison-exception-2.2",
|
||||
"Bootloader-exception",
|
||||
"Classpath-exception-2.0",
|
||||
"CLISP-exception-2.0",
|
||||
"cryptsetup-OpenSSL-exception",
|
||||
"DigiRule-FOSS-exception",
|
||||
"eCos-exception-2.0",
|
||||
"Fawkes-Runtime-exception",
|
||||
"FLTK-exception",
|
||||
"fmt-exception",
|
||||
"Font-exception-2.0",
|
||||
"freertos-exception-2.0",
|
||||
"GCC-exception-2.0",
|
||||
"GCC-exception-2.0-note",
|
||||
"GCC-exception-3.1",
|
||||
"Gmsh-exception",
|
||||
"GNAT-exception",
|
||||
"GNOME-examples-exception",
|
||||
"GNU-compiler-exception",
|
||||
"gnu-javamail-exception",
|
||||
"GPL-3.0-interface-exception",
|
||||
"GPL-3.0-linking-exception",
|
||||
"GPL-3.0-linking-source-exception",
|
||||
"GPL-CC-1.0",
|
||||
"GStreamer-exception-2005",
|
||||
"GStreamer-exception-2008",
|
||||
"i2p-gpl-java-exception",
|
||||
"KiCad-libraries-exception",
|
||||
"LGPL-3.0-linking-exception",
|
||||
"libpri-OpenH323-exception",
|
||||
"Libtool-exception",
|
||||
"Linux-syscall-note",
|
||||
"LLGPL",
|
||||
"LLVM-exception",
|
||||
"LZMA-exception",
|
||||
"mif-exception",
|
||||
"Nokia-Qt-exception-1.1",
|
||||
"OCaml-LGPL-linking-exception",
|
||||
"OCCT-exception-1.0",
|
||||
"OpenJDK-assembly-exception-1.0",
|
||||
"openvpn-openssl-exception",
|
||||
"PCRE2-exception",
|
||||
"PS-or-PDF-font-exception-20170817",
|
||||
"QPL-1.0-INRIA-2004-exception",
|
||||
"Qt-GPL-exception-1.0",
|
||||
"Qt-LGPL-exception-1.1",
|
||||
"Qwt-exception-1.0",
|
||||
"RRDtool-FLOSS-exception-2.0",
|
||||
"SANE-exception",
|
||||
"SHL-2.0",
|
||||
"SHL-2.1",
|
||||
"stunnel-exception",
|
||||
"SWI-exception",
|
||||
"Swift-exception",
|
||||
"Texinfo-exception",
|
||||
"u-boot-exception-2.0",
|
||||
"UBDL-exception",
|
||||
"Universal-FOSS-exception-1.0",
|
||||
"vsftpd-openssl-exception",
|
||||
"WxWindows-exception-3.1",
|
||||
"x11vnc-openssl-exception"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Literal, Type
|
||||
|
||||
from py_serializable import ViewType
|
||||
|
||||
from . import SchemaVersion
|
||||
|
||||
|
||||
class BaseSchemaVersion(ABC, ViewType):
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def schema_version_enum(self) -> SchemaVersion:
|
||||
... # pragma: no cover
|
||||
|
||||
def get_schema_version(self) -> str:
|
||||
return self.schema_version_enum.to_version()
|
||||
|
||||
|
||||
class SchemaVersion1Dot6(BaseSchemaVersion):
|
||||
|
||||
@property
|
||||
def schema_version_enum(self) -> Literal[SchemaVersion.V1_6]:
|
||||
return SchemaVersion.V1_6
|
||||
|
||||
|
||||
class SchemaVersion1Dot5(BaseSchemaVersion):
|
||||
|
||||
@property
|
||||
def schema_version_enum(self) -> Literal[SchemaVersion.V1_5]:
|
||||
return SchemaVersion.V1_5
|
||||
|
||||
|
||||
class SchemaVersion1Dot4(BaseSchemaVersion):
|
||||
|
||||
@property
|
||||
def schema_version_enum(self) -> Literal[SchemaVersion.V1_4]:
|
||||
return SchemaVersion.V1_4
|
||||
|
||||
|
||||
class SchemaVersion1Dot3(BaseSchemaVersion):
|
||||
|
||||
@property
|
||||
def schema_version_enum(self) -> Literal[SchemaVersion.V1_3]:
|
||||
return SchemaVersion.V1_3
|
||||
|
||||
|
||||
class SchemaVersion1Dot2(BaseSchemaVersion):
|
||||
|
||||
@property
|
||||
def schema_version_enum(self) -> Literal[SchemaVersion.V1_2]:
|
||||
return SchemaVersion.V1_2
|
||||
|
||||
|
||||
class SchemaVersion1Dot1(BaseSchemaVersion):
|
||||
|
||||
@property
|
||||
def schema_version_enum(self) -> Literal[SchemaVersion.V1_1]:
|
||||
return SchemaVersion.V1_1
|
||||
|
||||
|
||||
class SchemaVersion1Dot0(BaseSchemaVersion):
|
||||
|
||||
@property
|
||||
def schema_version_enum(self) -> Literal[SchemaVersion.V1_0]:
|
||||
return SchemaVersion.V1_0
|
||||
|
||||
|
||||
SCHEMA_VERSIONS: Dict[SchemaVersion, Type[BaseSchemaVersion]] = {
|
||||
SchemaVersion.V1_6: SchemaVersion1Dot6,
|
||||
SchemaVersion.V1_5: SchemaVersion1Dot5,
|
||||
SchemaVersion.V1_4: SchemaVersion1Dot4,
|
||||
SchemaVersion.V1_3: SchemaVersion1Dot3,
|
||||
SchemaVersion.V1_2: SchemaVersion1Dot2,
|
||||
SchemaVersion.V1_1: SchemaVersion1Dot1,
|
||||
SchemaVersion.V1_0: SchemaVersion1Dot0,
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
"""
|
||||
Set of helper classes for use with ``serializable`` when conducting (de-)serialization.
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
from uuid import UUID
|
||||
|
||||
# See https://github.com/package-url/packageurl-python/issues/65
|
||||
from packageurl import PackageURL
|
||||
from py_serializable.helpers import BaseHelper
|
||||
|
||||
from ..exception.serialization import CycloneDxDeserializationException, SerializationOfUnexpectedValueException
|
||||
from ..model.bom_ref import BomRef
|
||||
from ..model.license import _LicenseRepositorySerializationHelper
|
||||
|
||||
|
||||
class BomRefHelper(BaseHelper):
|
||||
"""**DEPRECATED** in favour of :class:`BomRef`.
|
||||
|
||||
.. deprecated:: 8.6
|
||||
Use :class:`BomRef` instead.
|
||||
"""
|
||||
|
||||
# TODO: remove, no longer needed
|
||||
|
||||
@classmethod
|
||||
def serialize(cls, o: Any) -> Optional[str]:
|
||||
return BomRef.serialize(o)
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, o: Any) -> BomRef:
|
||||
return BomRef.deserialize(o)
|
||||
|
||||
|
||||
class PackageUrl(BaseHelper):
|
||||
|
||||
@classmethod
|
||||
def serialize(cls, o: Any, ) -> str:
|
||||
if isinstance(o, PackageURL):
|
||||
return str(o.to_string())
|
||||
raise SerializationOfUnexpectedValueException(
|
||||
f'Attempt to serialize a non-PackageURL: {o!r}')
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, o: Any) -> PackageURL:
|
||||
try:
|
||||
return PackageURL.from_string(purl=str(o))
|
||||
except ValueError as err:
|
||||
raise CycloneDxDeserializationException(
|
||||
f'PURL string supplied does not parse: {o!r}'
|
||||
) from err
|
||||
|
||||
|
||||
class UrnUuidHelper(BaseHelper):
|
||||
|
||||
@classmethod
|
||||
def serialize(cls, o: Any) -> str:
|
||||
if isinstance(o, UUID):
|
||||
return o.urn
|
||||
raise SerializationOfUnexpectedValueException(
|
||||
f'Attempt to serialize a non-UUID: {o!r}')
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, o: Any) -> UUID:
|
||||
try:
|
||||
return UUID(str(o))
|
||||
except ValueError as err:
|
||||
raise CycloneDxDeserializationException(
|
||||
f'UUID string supplied does not parse: {o!r}'
|
||||
) from err
|
||||
|
||||
|
||||
class LicenseRepositoryHelper(_LicenseRepositorySerializationHelper):
|
||||
"""**DEPRECATED**
|
||||
|
||||
.. deprecated:: 8.6
|
||||
No public API planned for replacing this,
|
||||
"""
|
||||
|
||||
# TODO: remove, no longer needed
|
||||
|
||||
pass
|
||||
Binary file not shown.
77
Backend/venv/lib/python3.12/site-packages/cyclonedx/spdx.py
Normal file
77
Backend/venv/lib/python3.12/site-packages/cyclonedx/spdx.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
__all__ = [
|
||||
'is_supported_id', 'fixup_id',
|
||||
'is_expression'
|
||||
]
|
||||
|
||||
from json import load as json_load
|
||||
from typing import TYPE_CHECKING, Dict, Optional, Set
|
||||
|
||||
from license_expression import get_spdx_licensing # type:ignore[import-untyped]
|
||||
|
||||
from .schema._res import SPDX_JSON as __SPDX_JSON_SCHEMA
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from license_expression import Licensing
|
||||
|
||||
# region init
|
||||
# python's internal module loader will assure that this init-part runs only once.
|
||||
|
||||
# !!! this requires to ship the actual schema data with the package.
|
||||
with open(__SPDX_JSON_SCHEMA) as schema:
|
||||
__IDS: Set[str] = set(json_load(schema).get('enum', []))
|
||||
assert len(__IDS) > 0, 'known SPDX-IDs should be non-empty set'
|
||||
|
||||
__IDS_LOWER_MAP: Dict[str, str] = dict((id_.lower(), id_) for id_ in __IDS)
|
||||
|
||||
__SPDX_EXPRESSION_LICENSING: 'Licensing' = get_spdx_licensing()
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
def is_supported_id(value: str) -> bool:
|
||||
"""Validate SPDX-ID according to current spec."""
|
||||
return value in __IDS
|
||||
|
||||
|
||||
def fixup_id(value: str) -> Optional[str]:
|
||||
"""Fixup SPDX-ID.
|
||||
|
||||
:returns: repaired value string, or `None` if fixup was unable to help.
|
||||
"""
|
||||
return __IDS_LOWER_MAP.get(value.lower())
|
||||
|
||||
|
||||
def is_expression(value: str) -> bool:
|
||||
"""Validate SPDX license expression.
|
||||
|
||||
.. note::
|
||||
Utilizes `license-expression library`_ to
|
||||
validate SPDX compound expression according to `SPDX license expression spec`_.
|
||||
|
||||
.. _SPDX license expression spec: https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/
|
||||
.. _license-expression library: https://github.com/nexB/license-expression
|
||||
"""
|
||||
try:
|
||||
res = __SPDX_EXPRESSION_LICENSING.validate(value)
|
||||
except Exception:
|
||||
# the throw happens when internals crash due to unexpected input characters.
|
||||
return False
|
||||
return 0 == len(res.errors)
|
||||
@@ -0,0 +1,121 @@
|
||||
# This file is part of CycloneDX Python Library
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) OWASP Foundation. All Rights Reserved.
|
||||
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Literal, Optional, Protocol, Union, overload
|
||||
|
||||
from ..schema import OutputFormat
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ..schema import SchemaVersion
|
||||
from .json import JsonValidator
|
||||
from .xml import XmlValidator
|
||||
|
||||
|
||||
class ValidationError:
|
||||
"""Validation failed with this specific error.
|
||||
|
||||
Use :attr:`~data` to access the content.
|
||||
"""
|
||||
|
||||
data: Any
|
||||
|
||||
def __init__(self, data: Any) -> None:
|
||||
self.data = data
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return repr(self.data)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.data)
|
||||
|
||||
|
||||
class SchemabasedValidator(Protocol):
|
||||
"""Schema-based Validator protocol"""
|
||||
|
||||
def validate_str(self, data: str) -> Optional[ValidationError]:
|
||||
"""Validate a string
|
||||
|
||||
:param data: the data string to validate
|
||||
:return: validation error
|
||||
:retval None: if ``data`` is valid
|
||||
:retval ValidationError: if ``data`` is invalid
|
||||
"""
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
class BaseSchemabasedValidator(ABC, SchemabasedValidator):
|
||||
"""Base Schema-based Validator"""
|
||||
|
||||
def __init__(self, schema_version: 'SchemaVersion') -> None:
|
||||
self.__schema_version = schema_version
|
||||
if not self._schema_file:
|
||||
raise ValueError(f'Unsupported schema_version: {schema_version!r}')
|
||||
|
||||
@property
|
||||
def schema_version(self) -> 'SchemaVersion':
|
||||
"""Get the schema version."""
|
||||
return self.__schema_version
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def output_format(self) -> OutputFormat:
|
||||
"""Get the format."""
|
||||
... # pragma: no cover
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def _schema_file(self) -> Optional[str]:
|
||||
"""Get the schema file according to schema version."""
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
@overload
|
||||
def make_schemabased_validator(output_format: Literal[OutputFormat.JSON], schema_version: 'SchemaVersion'
|
||||
) -> 'JsonValidator':
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
@overload
|
||||
def make_schemabased_validator(output_format: Literal[OutputFormat.XML], schema_version: 'SchemaVersion'
|
||||
) -> 'XmlValidator':
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
@overload
|
||||
def make_schemabased_validator(output_format: OutputFormat, schema_version: 'SchemaVersion'
|
||||
) -> Union['JsonValidator', 'XmlValidator']:
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
def make_schemabased_validator(output_format: OutputFormat, schema_version: 'SchemaVersion'
|
||||
) -> 'BaseSchemabasedValidator':
|
||||
"""Get the default Schema-based Validator for a certain :class:`OutputFormat`.
|
||||
|
||||
Raises error when no instance could be made.
|
||||
"""
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from typing import Type
|
||||
Validator: Type[BaseSchemabasedValidator] # noqa:N806
|
||||
if OutputFormat.JSON is output_format:
|
||||
from .json import JsonValidator as Validator
|
||||
elif OutputFormat.XML is output_format:
|
||||
from .xml import XmlValidator as Validator
|
||||
else:
|
||||
raise ValueError(f'Unexpected output_format: {output_format!r}')
|
||||
return Validator(schema_version)
|
||||
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user