Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f6430ec
feat: prepare "contrib" area
jkowalleck Nov 26, 2025
a8a4190
feat: prepare "contrib" area
jkowalleck Nov 27, 2025
0e68f6e
feat: prepare "contrib" area
jkowalleck Nov 27, 2025
3c2ad7d
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
3947eb7
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
885d47e
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
bd37a9e
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
119690d
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
562049c
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
ef83a07
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
7d14191
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
b88d831
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
28b94a7
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
66d199c
feat: prepare "contrib" area
jkowalleck Nov 28, 2025
c1abdc3
feat: prep contrib
jkowalleck Nov 30, 2025
f8c43e6
feat: prepare "contrib" area
jkowalleck Nov 30, 2025
c0ca9bc
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
be8d607
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
840fbd4
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
f61abe9
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
5b98ab4
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
07c2a70
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
0e36aac
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
bccb7e3
feat: prepare "contrib" area
jkowalleck Dec 1, 2025
ff0e11d
typos
jkowalleck Dec 2, 2025
9436747
typos
jkowalleck Dec 2, 2025
0d0443d
typos
jkowalleck Dec 2, 2025
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
2 changes: 2 additions & 0 deletions cyclonedx/builder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@

"""
Builders used in this library.

.. deprecated:: next
"""
100 changes: 41 additions & 59 deletions cyclonedx/builder/this.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,69 +15,51 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.

"""Representation of this very python library."""
"""Representation of this very python library.

__all__ = ['this_component', 'this_tool', ]
.. deprecated:: next
"""

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
__all__ = ['this_component', 'this_tool']

# !!! keep this file in sync with `pyproject.toml`
import sys
from typing import TYPE_CHECKING

if sys.version_info >= (3, 13):
from warnings import deprecated
else:
from typing_extensions import deprecated

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...
)
from ..contrib.this.builders import this_component as _this_component, this_tool as _this_tool

# region deprecated re-export

def this_tool() -> Tool:
"""Representation of this very python library as a :class:`Tool`."""
return Tool.from_component(this_component())
if TYPE_CHECKING:
from ..model.component import Component
from ..model.tool import Tool


@deprecated('Deprecated re-export location - see docstring of "this_component" for details.')
def this_component() -> 'Component':
"""Deprecated — Alias of :func:`cyclonedx.contrib.this.builders.this_component`.

.. deprecated:: next
This re-export location is deprecated.
Use ``from cyclonedx.contrib.this.builders import this_component`` instead.
The exported symbol itself is NOT deprecated — only this import path.
"""
return _this_component()


@deprecated('Deprecated re-export location - see docstring of "this_tool" for details.')
def this_tool() -> 'Tool':
"""Deprecated — Alias of :func:`cyclonedx.contrib.this.builders.this_tool`.

.. deprecated:: next
This re-export location is deprecated.
Use ``from cyclonedx.contrib.this.builders import this_tool`` instead.
The exported symbol itself is NOT deprecated — only this import path.
"""
return _this_tool()

# endregion deprecated re-export
20 changes: 20 additions & 0 deletions cyclonedx/contrib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# CycloneDX Contrib Extensions

This directory contains community-contributed functionality that extends the capabilities of the CycloneDX core library.
Unlike the modules in `../`, these features are not part of the official CycloneDX specification and may vary in stability, scope, or compatibility.

## Contents
- Utilities, helpers, and experimental features developed by the community
- Optional add-ons that may facilitate or enhance use of the CycloneDX core library
- Code that evolves independently of the CycloneDX specification

## Notes
- Contrib modules are optional and not required for strict compliance with the CycloneDX standard.
- They may change more frequently than the core and are not guaranteed to follow the same versioning rules.
- Users should evaluate these modules carefully and consult documentation or source comments for details.

## Contributing
Contributions are welcome. To add an extension:
1. Follow the contribution guidelines in the main repository.
2. Place your code in a clearly named subfolder or file under `contrib/`.
3. Provide documentation and tests to ensure clarity and maintainability.
28 changes: 6 additions & 22 deletions cyclonedx/_internal/hash.py → cyclonedx/contrib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,11 @@


"""
!!! ALL SYMBOLS IN HERE ARE INTERNAL.
Everything might change without any notice.
Some features in this library are marked as contrib.
These are community-provided extensions and are not part of the official standard.
They are optional and may evolve independently from the core.
"""


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()
__all__ = [
# there is no intention to export anything in here.
]
18 changes: 18 additions & 0 deletions cyclonedx/contrib/component/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 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.

"""Component related functionality"""
75 changes: 75 additions & 0 deletions cyclonedx/contrib/component/builders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 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.

"""Component related builders"""

__all__ = ['ComponentBuilder']

from hashlib import sha1
from os.path import exists
from typing import Optional

from ...model import HashAlgorithm, HashType
from ...model.component import Component, ComponentType


class ComponentBuilder:

def make_for_file(self, absolute_file_path: str, *,
name: Optional[str]) -> Component:
"""
Helper method to create a :class:`cyclonedx.model.component.Component`
that represents the provided local file as a Component.

Args:
absolute_file_path:
Absolute path to the file you wish to represent
name:
Optionally, if supplied this is the name that will be used for the component.
Defaults to arg ``absolute_file_path``.

Returns:
`Component` representing the supplied file
"""
if not exists(absolute_file_path):
raise FileExistsError(f'Supplied file path {absolute_file_path!r} does not exist')

return Component(
type=ComponentType.FILE,
name=name or absolute_file_path,
hashes=[
HashType(alg=HashAlgorithm.SHA_1, content=self._file_sha1sum(absolute_file_path))
]
)

@staticmethod
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()
18 changes: 18 additions & 0 deletions cyclonedx/contrib/hash/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 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.

"""Hash related functionality"""
Loading
Loading