Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BC: Helpes internal #506

Merged
merged 4 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions cyclonedx/_internal/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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 CLASSES IN HERE ARE INTERNAL.
Everything might change without any notice.
"""
54 changes: 54 additions & 0 deletions cyclonedx/_internal/compare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 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 CLASSES IN HERE ARE INTERNAL.
Everything might change without any notice.
"""


from itertools import zip_longest
from typing import Any, Optional, Tuple


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 True if s < o else False
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 True if s > o else False
return False
41 changes: 41 additions & 0 deletions cyclonedx/_internal/hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 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 CLASSES 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()
27 changes: 27 additions & 0 deletions cyclonedx/_internal/time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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 CLASSES 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)
129 changes: 53 additions & 76 deletions cyclonedx/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,19 @@
"""

import re
from datetime import datetime, timezone
from datetime import datetime
from enum import Enum
from functools import reduce
from hashlib import sha1
from itertools import zip_longest
from json import loads as json_loads
from typing import Any, Dict, FrozenSet, Generator, Iterable, List, Optional, Tuple, Type, TypeVar
from typing import Any, Dict, FrozenSet, Generator, Iterable, List, Optional, Tuple, Type
from warnings import warn
from xml.etree.ElementTree import Element as XmlElement # nosec B405

import serializable
from sortedcontainers import SortedSet

from .. import __version__ as __ThisToolVersion # noqa: N812
from .._internal.compare import ComparableTuple as _ComparableTuple
from ..exception.model import (
InvalidLocaleTypeException,
InvalidUriException,
Expand All @@ -52,61 +51,6 @@
)


def get_now_utc() -> datetime:
return datetime.now(tz=timezone.utc)


def 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()


_T = TypeVar('_T')


class ComparableTuple(Tuple[Optional[_T], ...]):
"""
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 True if s < o else False
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 True if s > o else False
return False


@serializable.serializable_enum
class DataFlow(str, Enum):
"""
Expand Down Expand Up @@ -184,8 +128,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: object) -> bool:
if isinstance(other, DataClassification):
return ComparableTuple((self.flow, self.classification)) < \
ComparableTuple((other.flow, other.classification))
return _ComparableTuple((
self.flow, self.classification
)) < _ComparableTuple((
other.flow, other.classification
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -279,8 +226,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, AttachedText):
return ComparableTuple((self.content_type, self.content, self.encoding)) < \
ComparableTuple((other.content_type, other.content, other.encoding))
return _ComparableTuple((
self.content_type, self.content, self.encoding
)) < _ComparableTuple((
other.content_type, other.content, other.encoding
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -481,7 +431,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, HashType):
return ComparableTuple((self.alg, self.content)) < ComparableTuple((other.alg, other.content))
return _ComparableTuple((
self.alg, self.content
)) < _ComparableTuple((
other.alg, other.content
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -806,8 +760,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, ExternalReference):
return ComparableTuple((self._type, self._url, self._comment)) < \
ComparableTuple((other._type, other._url, other._comment))
return _ComparableTuple((
self._type, self._url, self._comment
)) < _ComparableTuple((
other._type, other._url, other._comment
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -875,7 +832,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, Property):
return ComparableTuple((self.name, self.value)) < ComparableTuple((other.name, other.value))
return _ComparableTuple((
self.name, self.value
)) < _ComparableTuple((
other.name, other.value
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -958,8 +919,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, NoteText):
return ComparableTuple((self.content, self.content_type, self.encoding)) < \
ComparableTuple((other.content, other.content_type, other.encoding))
return _ComparableTuple((
self.content, self.content_type, self.encoding
)) < _ComparableTuple((
other.content, other.content_type, other.encoding
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -1035,7 +999,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, Note):
return ComparableTuple((self.locale, self.text)) < ComparableTuple((other.locale, other.text))
return _ComparableTuple((
self.locale, self.text
)) < _ComparableTuple((
other.locale, other.text
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -1116,8 +1084,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, OrganizationalContact):
return ComparableTuple((self.name, self.email, self.phone)) < \
ComparableTuple((other.name, other.email, other.phone))
return _ComparableTuple((
self.name, self.email, self.phone
)) < _ComparableTuple((
other.name, other.email, other.phone
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -1323,8 +1294,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, Tool):
return ComparableTuple((self.vendor, self.name, self.version)) < \
ComparableTuple((other.vendor, other.name, other.version))
return _ComparableTuple((
self.vendor, self.name, self.version
)) < _ComparableTuple((
other.vendor, other.name, other.version
))
return NotImplemented

def __hash__(self) -> int:
Expand Down Expand Up @@ -1404,8 +1378,11 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, IdentifiableAction):
return ComparableTuple((self.timestamp, self.name, self.email)) < \
ComparableTuple((other.timestamp, other.name, other.email))
return _ComparableTuple((
self.timestamp, self.name, self.email
)) < _ComparableTuple((
other.timestamp, other.name, other.email
))
return NotImplemented

def __hash__(self) -> int:
Expand Down
Loading