Skip to content

Commit 3814d71

Browse files
authored
Merge branch 'main' into feat/refactor-validation-to-model-validator
2 parents 42240b7 + 752b162 commit 3814d71

32 files changed

Lines changed: 582 additions & 53 deletions

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
<!-- version list -->
44

5+
## v11.7.0 (2026-03-17)
6+
7+
### Documentation
8+
9+
- Add comprehensive SBOM validation guide
10+
([#933](https://github.com/CycloneDX/cyclonedx-python-lib/pull/933),
11+
[`bf596c0`](https://github.com/CycloneDX/cyclonedx-python-lib/commit/bf596c0ed1495bf42add39185f460605b0ecd12a))
12+
13+
- Docstrings for schema version classes
14+
([#946](https://github.com/CycloneDX/cyclonedx-python-lib/pull/946),
15+
[`6460b71`](https://github.com/CycloneDX/cyclonedx-python-lib/commit/6460b71b5189a10819e539f1315a7c05b9b7e40e))
16+
17+
- Modernize RTF setup ([#921](https://github.com/CycloneDX/cyclonedx-python-lib/pull/921),
18+
[`af0059d`](https://github.com/CycloneDX/cyclonedx-python-lib/commit/af0059d8fb6c9f8cc437f6c210487d131a6f658f))
19+
20+
### Features
21+
22+
- Add properties for licenses according to CycloneDX 1.5
23+
([#947](https://github.com/CycloneDX/cyclonedx-python-lib/pull/947),
24+
[`375d209`](https://github.com/CycloneDX/cyclonedx-python-lib/commit/375d209c738473ea2815fc4cb36806563be41e2e))
25+
26+
- Make schema deprecation warnings handle-able
27+
([#945](https://github.com/CycloneDX/cyclonedx-python-lib/pull/945),
28+
[`71edacf`](https://github.com/CycloneDX/cyclonedx-python-lib/commit/71edacfaf5c46088d0ca08196b7c858ff39a23b5))
29+
30+
531
## v11.6.0 (2025-12-02)
632

733
### Documentation

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ poetry install --all-extras
2121

2222
## Code style
2323

24-
THis project loves latest python features.
24+
This project loves latest python features.
2525
This project loves sorted imports.
2626
This project uses [PEP8] Style Guide for Python Code.
2727

@@ -67,7 +67,7 @@ Please sign off your commits, to show that you agree to publish your changes und
6767
, and to indicate agreement with [Developer Certificate of Origin (DCO)](https://developercertificate.org/).
6868

6969
```shell
70-
git commit --signed-off ...
70+
git commit --signoff ...
7171
```
7272

7373
## Pre-commit hooks

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
----
1616

17-
OWASP [CycloneDX][link_website] is a full-stack Bill of Materials (BOM) standard
18-
that provides advanced supply chain capabilities for cyber risk reduction.
17+
OWASP [CycloneDX][link_website] is a fullstack Bill of Materials (BOM) and system‑transparency standard
18+
that provides deep visibility into software, services, hardware, and AI components, enabling advanced supplychain security and cyberrisk reduction.
1919

2020
This Python package provides data models, validators and more,
2121
to help you create/render/read CycloneDX documents.

cyclonedx/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222

2323
# !! version is managed by semantic_release
2424
# do not use typing here, or else `semantic_release` might have issues finding the variable
25-
__version__ = "11.6.0" # noqa:Q000
25+
__version__ = "11.7.0" # noqa:Q000

cyclonedx/model/bom.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from .._internal.compare import ComparableTuple as _ComparableTuple
3131
from .._internal.time import get_now_utc as _get_now_utc
3232
from ..exception.model import LicenseExpressionAlongWithOthersException, UnknownComponentDependencyException
33+
from ..schema.deprecation import SchemaDeprecationWarning1Dot6
3334
from ..schema.schema import (
3435
SchemaVersion1Dot0,
3536
SchemaVersion1Dot1,
@@ -291,10 +292,7 @@ def manufacture(self, manufacture: Optional[OrganizationalEntity]) -> None:
291292
we should set this data on `.component.manufacturer`.
292293
"""
293294
if manufacture is not None:
294-
warn(
295-
'`bom.metadata.manufacture` is deprecated from CycloneDX v1.6 onwards. '
296-
'Please use `bom.metadata.component.manufacturer` instead.',
297-
DeprecationWarning)
295+
SchemaDeprecationWarning1Dot6._warn('bom.metadata.manufacture', 'bom.metadata.component.manufacturer')
298296
self._manufacture = manufacture
299297

300298
@property

cyclonedx/model/component.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
SerializationOfUnexpectedValueException,
4141
SerializationOfUnsupportedComponentTypeException,
4242
)
43+
from ..schema.deprecation import SchemaDeprecationWarning1Dot3, SchemaDeprecationWarning1Dot6
4344
from ..schema.schema import (
4445
SchemaVersion1Dot0,
4546
SchemaVersion1Dot1,
@@ -1185,8 +1186,7 @@ def author(self) -> Optional[str]:
11851186
@author.setter
11861187
def author(self, author: Optional[str]) -> None:
11871188
if author is not None:
1188-
warn('`@.author` is deprecated from CycloneDX v1.6 onwards. '
1189-
'Please use `@.authors` or `@.manufacturer` instead.', DeprecationWarning)
1189+
SchemaDeprecationWarning1Dot6._warn('@.author', '@.authors` or `@.manufacturer')
11901190
self._author = author
11911191

11921192
@property
@@ -1467,8 +1467,7 @@ def modified(self) -> bool:
14671467
@modified.setter
14681468
def modified(self, modified: bool) -> None:
14691469
if modified:
1470-
warn('`@.modified` is deprecated from CycloneDX v1.3 onwards. '
1471-
'Please use `@.pedigree` instead.', DeprecationWarning)
1470+
SchemaDeprecationWarning1Dot3._warn('@.modified', '@.pedigree')
14721471
self._modified = modified
14731472

14741473
@property

cyclonedx/model/license.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
License related things
2121
"""
2222

23+
from collections.abc import Iterable
2324
from enum import Enum
2425
from json import loads as json_loads
2526
from typing import TYPE_CHECKING, Any, Optional, Union
@@ -34,7 +35,7 @@
3435
from ..exception.model import MutuallyExclusivePropertiesException
3536
from ..exception.serialization import CycloneDxDeserializationException
3637
from ..schema.schema import SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7
37-
from . import AttachedText, XsUri
38+
from . import AttachedText, Property, XsUri
3839
from .bom_ref import BomRef
3940

4041

@@ -85,6 +86,7 @@ def __init__(
8586
id: Optional[str] = None, name: Optional[str] = None,
8687
text: Optional[AttachedText] = None, url: Optional[XsUri] = None,
8788
acknowledgement: Optional[LicenseAcknowledgement] = None,
89+
properties: Optional[Iterable[Property]] = None,
8890
) -> None:
8991
if not id and not name:
9092
raise MutuallyExclusivePropertiesException('Either `id` or `name` MUST be supplied')
@@ -99,6 +101,7 @@ def __init__(
99101
self._text = text
100102
self._url = url
101103
self._acknowledgement = acknowledgement
104+
self._properties = SortedSet(properties or [])
102105

103106
@property
104107
@serializable.view(SchemaVersion1Dot5)
@@ -200,17 +203,25 @@ def url(self, url: Optional[XsUri]) -> None:
200203
# def licensing(self, ...) -> None:
201204
# ... # TODO since CDX1.5
202205

203-
# @property
204-
# ...
205-
# @serializable.view(SchemaVersion1Dot5)
206-
# @serializable.view(SchemaVersion1Dot6)
207-
# @serializable.xml_sequence(6)
208-
# def properties(self) -> ...:
209-
# ... # TODO since CDX1.5
210-
#
211-
# @licensing.setter
212-
# def properties(self, ...) -> None:
213-
# ... # TODO since CDX1.5
206+
@property
207+
@serializable.view(SchemaVersion1Dot5)
208+
@serializable.view(SchemaVersion1Dot6)
209+
@serializable.view(SchemaVersion1Dot7)
210+
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
211+
@serializable.xml_sequence(6)
212+
def properties(self) -> 'SortedSet[Property]':
213+
"""
214+
Provides the ability to document properties in a key/value store. This provides flexibility to include data not
215+
officially supported in the standard without having to use additional namespaces or create extensions.
216+
217+
Return:
218+
Set of `Property`
219+
"""
220+
return self._properties
221+
222+
@properties.setter
223+
def properties(self, properties: Iterable[Property]) -> None:
224+
self._properties = SortedSet(properties)
214225

215226
@property
216227
@serializable.view(SchemaVersion1Dot6)
@@ -245,6 +256,7 @@ def __comparable_tuple(self) -> _ComparableTuple:
245256
self._url,
246257
self._text,
247258
self._bom_ref.value,
259+
_ComparableTuple(self._properties),
248260
))
249261

250262
def __eq__(self, other: object) -> bool:

cyclonedx/model/tool.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from collections.abc import Iterable
2020
from itertools import chain
2121
from typing import TYPE_CHECKING, Any, Optional, Union
22-
from warnings import warn
2322
from xml.etree.ElementTree import Element # nosec B405
2423

2524
import py_serializable as serializable
@@ -28,6 +27,7 @@
2827

2928
from .._internal.compare import ComparableTuple as _ComparableTuple
3029
from ..schema import SchemaVersion
30+
from ..schema.deprecation import SchemaDeprecationWarning1Dot5
3131
from ..schema.schema import SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7
3232
from . import ExternalReference, HashType, _HashTypeRepositorySerializationHelper
3333
from .component import Component
@@ -240,9 +240,7 @@ def tools(self) -> 'SortedSet[Tool]':
240240
@tools.setter
241241
def tools(self, tools: Iterable[Tool]) -> None:
242242
if tools:
243-
warn('`@.tools` is deprecated from CycloneDX v1.5 onwards. '
244-
'Please use `@.components` and `@.services` instead.',
245-
DeprecationWarning)
243+
SchemaDeprecationWarning1Dot5._warn('@.tools', '@.components` and `@.services')
246244
self._tools = SortedSet(tools)
247245

248246
def __len__(self) -> int:

cyclonedx/schema/deprecation.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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+
19+
"""
20+
CycloneDX Schema Deprecation Warnings
21+
=====================================
22+
23+
This module provides warning classes for deprecated features in CycloneDX schemas.
24+
Each warning class corresponds to a specific schema version, enabling downstream
25+
code to catch, filter, or otherwise handle schema-specific deprecation warnings.
26+
27+
Intended Usage
28+
--------------
29+
30+
Downstream consumers can manage warnings using Python's ``warnings`` module.
31+
Common scenarios include:
32+
33+
- Filtering by schema version
34+
- Suppressing warnings in tests or batch processing
35+
- Logging or reporting deprecation warnings without raising exceptions
36+
37+
Example
38+
-------
39+
40+
.. code-block:: python
41+
42+
import warnings
43+
from cyclonedx.schema.deprecation import (
44+
BaseSchemaDeprecationWarning,
45+
SchemaDeprecationWarning1Dot7,
46+
)
47+
48+
# Suppress all CycloneDX schema deprecation warnings
49+
warnings.filterwarnings("ignore", category=BaseSchemaDeprecationWarning)
50+
51+
# Suppress only warnings specific to schema version 1.7
52+
warnings.filterwarnings("ignore", category=SchemaDeprecationWarning1Dot7)
53+
54+
Notes
55+
-----
56+
57+
- All deprecation warnings inherit from :class:`BaseSchemaDeprecationWarning`.
58+
- The ``SCHEMA_VERSION`` class variable indicates the CycloneDX schema version
59+
where the feature became deprecated.
60+
- These warning classes are designed for downstream **filtering and logging**,
61+
not for raising exceptions.
62+
"""
63+
64+
65+
from abc import ABC
66+
from typing import ClassVar, Literal, Optional
67+
from warnings import warn
68+
69+
from . import SchemaVersion
70+
71+
__all__ = [
72+
'BaseSchemaDeprecationWarning',
73+
'SchemaDeprecationWarning1Dot1',
74+
'SchemaDeprecationWarning1Dot2',
75+
'SchemaDeprecationWarning1Dot3',
76+
'SchemaDeprecationWarning1Dot4',
77+
'SchemaDeprecationWarning1Dot5',
78+
'SchemaDeprecationWarning1Dot6',
79+
'SchemaDeprecationWarning1Dot7',
80+
]
81+
82+
83+
class BaseSchemaDeprecationWarning(DeprecationWarning, ABC):
84+
"""Base class for warnings about deprecated schema features."""
85+
86+
SCHEMA_VERSION: ClassVar[SchemaVersion]
87+
88+
@classmethod
89+
def _warn(cls, deprecated: str, instead: Optional[str] = None, *, stacklevel: int = 1) -> None:
90+
"""Internal API. Not part of the public interface."""
91+
msg = f'`{deprecated}` is deprecated from CycloneDX v{cls.SCHEMA_VERSION.to_version()} onwards.'
92+
if instead:
93+
msg += f' Please use `{instead}` instead.'
94+
warn(msg, category=cls, stacklevel=stacklevel + 1)
95+
96+
97+
class SchemaDeprecationWarning1Dot7(BaseSchemaDeprecationWarning):
98+
"""Class for warnings about deprecated schema features in CycloneDX 1.7"""
99+
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_7]] = SchemaVersion.V1_7
100+
101+
102+
class SchemaDeprecationWarning1Dot6(BaseSchemaDeprecationWarning):
103+
"""Class for warnings about deprecated schema features in CycloneDX 1.6"""
104+
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_6]] = SchemaVersion.V1_6
105+
106+
107+
class SchemaDeprecationWarning1Dot5(BaseSchemaDeprecationWarning):
108+
"""Class for warnings about deprecated schema features in CycloneDX 1.5"""
109+
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_5]] = SchemaVersion.V1_5
110+
111+
112+
class SchemaDeprecationWarning1Dot4(BaseSchemaDeprecationWarning):
113+
"""Class for warnings about deprecated schema features in CycloneDX 1.4"""
114+
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_4]] = SchemaVersion.V1_4
115+
116+
117+
class SchemaDeprecationWarning1Dot3(BaseSchemaDeprecationWarning):
118+
"""Class for warnings about deprecated schema features in CycloneDX 1.3"""
119+
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_3]] = SchemaVersion.V1_3
120+
121+
122+
class SchemaDeprecationWarning1Dot2(BaseSchemaDeprecationWarning):
123+
"""Class for warnings about deprecated schema features in CycloneDX 1.2"""
124+
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_2]] = SchemaVersion.V1_2
125+
126+
127+
class SchemaDeprecationWarning1Dot1(BaseSchemaDeprecationWarning):
128+
"""Class for warnings about deprecated schema features in CycloneDX 1.1"""
129+
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_1]] = SchemaVersion.V1_1

0 commit comments

Comments
 (0)