Files
Hotel-Booking/Backend/venv/lib/python3.12/site-packages/cyclonedx/model/definition.py
Iliyan Angelov 62c1fe5951 updates
2025-12-01 06:50:10 +02:00

624 lines
20 KiB
Python

# 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} >'