Skip to content

Commit b88d831

Browse files
committed
feat: prepare "contrib" area
Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
1 parent 7d14191 commit b88d831

4 files changed

Lines changed: 91 additions & 47 deletions

File tree

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,4 @@
1515
# SPDX-License-Identifier: Apache-2.0
1616
# Copyright (c) OWASP Foundation. All Rights Reserved.
1717

18-
19-
"""
20-
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
21-
Everything might change without any notice.
22-
"""
23-
24-
25-
from hashlib import sha1
26-
27-
28-
def file_sha1sum(filename: str) -> str:
29-
"""
30-
Generate a SHA1 hash of the provided file.
31-
32-
Args:
33-
filename:
34-
Absolute path to file to hash as `str`
35-
36-
Returns:
37-
SHA-1 hash
38-
"""
39-
h = sha1() # nosec B303, B324
40-
with open(filename, 'rb') as f:
41-
for byte_block in iter(lambda: f.read(4096), b''):
42-
h.update(byte_block)
43-
return h.hexdigest()
18+
"""Component related functionality"""
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# This file is part of CycloneDX Python Library
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
# Copyright (c) OWASP Foundation. All Rights Reserved.
17+
18+
"""Component related builders"""
19+
20+
21+
from hashlib import sha1
22+
from typing import Optional
23+
from os.path import exists
24+
25+
from ...model import HashType, HashAlgorithm
26+
from ...model.component import Component, ComponentType
27+
28+
class ComponentBuilder:
29+
30+
def make_for_file(self, absolute_file_path: str, name: Optional[str]) -> Component:
31+
"""
32+
Helper method to create a :class:`cyclonedx.model.component.Component`
33+
that represents the provided local file as a Component.
34+
35+
Args:
36+
absolute_file_path:
37+
Absolute path to the file you wish to represent
38+
name:
39+
Optionally, if supplied this is the name that will be used for the component.
40+
Defaults to arg ``absolute_file_path``.
41+
42+
Returns:
43+
`Component` representing the supplied file
44+
"""
45+
if not exists(absolute_file_path):
46+
raise FileExistsError(f'Supplied file path {absolute_file_path!r} does not exist')
47+
48+
return Component(
49+
type=ComponentType.FILE,
50+
name=name or absolute_file_path,
51+
hashes=[
52+
HashType(alg=HashAlgorithm.SHA_1, content=self._file_sha1sum(absolute_file_path))
53+
]
54+
)
55+
56+
@staticmethod
57+
def _file_sha1sum(filename: str) -> str:
58+
"""
59+
Generate a SHA1 hash of the provided file.
60+
61+
Args:
62+
filename:
63+
Absolute path to file to hash as `str`
64+
65+
Returns:
66+
SHA-1 hash
67+
"""
68+
h = sha1() # nosec B303, B324
69+
with open(filename, 'rb') as f:
70+
for byte_block in iter(lambda: f.read(4096), b''):
71+
h.update(byte_block)
72+
return h.hexdigest()

cyclonedx/model/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ class HashType:
395395
"""
396396

397397
@staticmethod
398-
def from_hashlib_alg(hashlib_alg: str, content: str) -> 'HashType':
398+
def from_hashlib_alg(hashlib_alg: str, content: str) -> 'HashType': # TODO: move to contrib
399399
"""
400400
Attempts to convert a hashlib-algorithm to our internal model classes.
401401
@@ -419,7 +419,7 @@ def from_hashlib_alg(hashlib_alg: str, content: str) -> 'HashType':
419419
return HashType(alg=alg, content=content)
420420

421421
@staticmethod
422-
def from_composite_str(composite_hash: str) -> 'HashType':
422+
def from_composite_str(composite_hash: str) -> 'HashType': # TODO: move to contrib
423423
"""
424424
Attempts to convert a string which includes both the Hash Algorithm and Hash Value and represent using our
425425
internal model classes.

cyclonedx/model/component.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@
1818
import re
1919
from collections.abc import Iterable
2020
from enum import Enum
21-
from os.path import exists
2221
from typing import Any, Optional, Union
2322
from warnings import warn
2423

2524
# See https://github.com/package-url/packageurl-python/issues/65
2625
import py_serializable as serializable
2726
from packageurl import PackageURL
2827
from sortedcontainers import SortedSet
28+
from typing_extensions import deprecated
2929

30+
from contrib.component.builders import ComponentBuilder
3031
from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
3132
from .._internal.compare import ComparablePackageURL as _ComparablePackageURL, ComparableTuple as _ComparableTuple
32-
from .._internal.hash import file_sha1sum as _file_sha1sum
3333
from ..exception.model import InvalidOmniBorIdException, InvalidSwhidException
3434
from ..exception.serialization import (
3535
CycloneDxDeserializationException,
@@ -50,12 +50,11 @@
5050
from . import (
5151
AttachedText,
5252
ExternalReference,
53-
HashAlgorithm,
5453
HashType,
5554
IdentifiableAction,
5655
Property,
5756
XsUri,
58-
_HashTypeRepositorySerializationHelper,
57+
_HashTypeRepositorySerializationHelper, HashAlgorithm,
5958
)
6059
from .bom_ref import BomRef
6160
from .component_evidence import ComponentEvidence, _ComponentEvidenceSerializationHelper
@@ -955,8 +954,10 @@ class Component(Dependable):
955954
"""
956955

957956
@staticmethod
957+
@deprecated('Deprecated - use cyclonedx.contrib.component.builders.ComponentBuilder.make_for_file instead')
958958
def for_file(absolute_file_path: str, path_for_bom: Optional[str]) -> 'Component':
959-
"""
959+
"""Deprecated — Wrapper of :func:`cyclonedx.contrib.component.builders.ComponentBuilder.make_for_file`.
960+
960961
Helper method to create a Component that represents the provided local file as a Component.
961962
962963
Args:
@@ -967,22 +968,18 @@ def for_file(absolute_file_path: str, path_for_bom: Optional[str]) -> 'Component
967968
968969
Returns:
969970
`Component` representing the supplied file
971+
972+
.. deprecated:: next
973+
Use ``cyclonedx.contrib.component.builders.ComponentBuilder.make_for_file()`` instead.
970974
"""
971-
if not exists(absolute_file_path):
972-
raise FileExistsError(f'Supplied file path {absolute_file_path!r} does not exist')
973-
974-
sha1_hash: str = _file_sha1sum(absolute_file_path)
975-
return Component(
976-
name=path_for_bom if path_for_bom else absolute_file_path,
977-
version=f'0.0.0-{sha1_hash[0:12]}',
978-
hashes=[
979-
HashType(alg=HashAlgorithm.SHA_1, content=sha1_hash)
980-
],
981-
type=ComponentType.FILE, purl=PackageURL(
982-
type='generic', name=path_for_bom if path_for_bom else absolute_file_path,
983-
version=f'0.0.0-{sha1_hash[0:12]}'
984-
)
975+
component = ComponentBuilder().make_for_file(absolute_file_path, path_for_bom)
976+
sha1_hash = next(h.content for h in component.hashes if h.alg is HashAlgorithm.SHA_1)
977+
component.version=f'0.0.0-{sha1_hash[0:12]}'
978+
component.purl=PackageURL( # DEPRECATED: a file has no PURL!
979+
type='generic', name=path_for_bom if path_for_bom else absolute_file_path,
980+
version=f'0.0.0-{sha1_hash[0:12]}'
985981
)
982+
return component
986983

987984
def __init__(
988985
self, *,

0 commit comments

Comments
 (0)