🚨 Latest Research:Tanstack npm Packages Compromised in Ongoing Mini Shai-Hulud Supply-Chain Attack.Learn More
Socket
Book a DemoSign in
Socket

cyclonedx-python-lib

Package Overview
Dependencies
Maintainers
5
Versions
153
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cyclonedx-python-lib - pypi Package Compare versions

Comparing version
11.6.0
to
11.7.0
+129
cyclonedx/schema/deprecation.py
# 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.
"""
CycloneDX Schema Deprecation Warnings
=====================================
This module provides warning classes for deprecated features in CycloneDX schemas.
Each warning class corresponds to a specific schema version, enabling downstream
code to catch, filter, or otherwise handle schema-specific deprecation warnings.
Intended Usage
--------------
Downstream consumers can manage warnings using Python's ``warnings`` module.
Common scenarios include:
- Filtering by schema version
- Suppressing warnings in tests or batch processing
- Logging or reporting deprecation warnings without raising exceptions
Example
-------
.. code-block:: python
import warnings
from cyclonedx.schema.deprecation import (
BaseSchemaDeprecationWarning,
SchemaDeprecationWarning1Dot7,
)
# Suppress all CycloneDX schema deprecation warnings
warnings.filterwarnings("ignore", category=BaseSchemaDeprecationWarning)
# Suppress only warnings specific to schema version 1.7
warnings.filterwarnings("ignore", category=SchemaDeprecationWarning1Dot7)
Notes
-----
- All deprecation warnings inherit from :class:`BaseSchemaDeprecationWarning`.
- The ``SCHEMA_VERSION`` class variable indicates the CycloneDX schema version
where the feature became deprecated.
- These warning classes are designed for downstream **filtering and logging**,
not for raising exceptions.
"""
from abc import ABC
from typing import ClassVar, Literal, Optional
from warnings import warn
from . import SchemaVersion
__all__ = [
'BaseSchemaDeprecationWarning',
'SchemaDeprecationWarning1Dot1',
'SchemaDeprecationWarning1Dot2',
'SchemaDeprecationWarning1Dot3',
'SchemaDeprecationWarning1Dot4',
'SchemaDeprecationWarning1Dot5',
'SchemaDeprecationWarning1Dot6',
'SchemaDeprecationWarning1Dot7',
]
class BaseSchemaDeprecationWarning(DeprecationWarning, ABC):
"""Base class for warnings about deprecated schema features."""
SCHEMA_VERSION: ClassVar[SchemaVersion]
@classmethod
def _warn(cls, deprecated: str, instead: Optional[str] = None, *, stacklevel: int = 1) -> None:
"""Internal API. Not part of the public interface."""
msg = f'`{deprecated}` is deprecated from CycloneDX v{cls.SCHEMA_VERSION.to_version()} onwards.'
if instead:
msg += f' Please use `{instead}` instead.'
warn(msg, category=cls, stacklevel=stacklevel + 1)
class SchemaDeprecationWarning1Dot7(BaseSchemaDeprecationWarning):
"""Class for warnings about deprecated schema features in CycloneDX 1.7"""
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_7]] = SchemaVersion.V1_7
class SchemaDeprecationWarning1Dot6(BaseSchemaDeprecationWarning):
"""Class for warnings about deprecated schema features in CycloneDX 1.6"""
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_6]] = SchemaVersion.V1_6
class SchemaDeprecationWarning1Dot5(BaseSchemaDeprecationWarning):
"""Class for warnings about deprecated schema features in CycloneDX 1.5"""
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_5]] = SchemaVersion.V1_5
class SchemaDeprecationWarning1Dot4(BaseSchemaDeprecationWarning):
"""Class for warnings about deprecated schema features in CycloneDX 1.4"""
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_4]] = SchemaVersion.V1_4
class SchemaDeprecationWarning1Dot3(BaseSchemaDeprecationWarning):
"""Class for warnings about deprecated schema features in CycloneDX 1.3"""
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_3]] = SchemaVersion.V1_3
class SchemaDeprecationWarning1Dot2(BaseSchemaDeprecationWarning):
"""Class for warnings about deprecated schema features in CycloneDX 1.2"""
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_2]] = SchemaVersion.V1_2
class SchemaDeprecationWarning1Dot1(BaseSchemaDeprecationWarning):
"""Class for warnings about deprecated schema features in CycloneDX 1.1"""
SCHEMA_VERSION: ClassVar[Literal[SchemaVersion.V1_1]] = SchemaVersion.V1_1
# 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 json
import sys
from typing import TYPE_CHECKING, Optional
from cyclonedx.exception import MissingOptionalDependencyException
from cyclonedx.schema import OutputFormat, SchemaVersion
from cyclonedx.validation import make_schemabased_validator
if TYPE_CHECKING:
from cyclonedx.validation.json import JsonValidator
from cyclonedx.validation.xml import XmlValidator
"""
This example demonstrates how to validate CycloneDX documents (both JSON and XML).
Make sure to have the needed dependencies installed - install the library's extra 'validation' for that.
"""
# region Sample SBOMs
JSON_SBOM = """
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"component": {
"type": "application",
"name": "my-app",
"version": "1.0.0"
}
},
"components": []
}
"""
XML_SBOM = """<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.5" version="1">
<metadata>
<component type="application">
<name>my-app</name>
<version>1.0.0</version>
</component>
</metadata>
</bom>
"""
INVALID_JSON_SBOM = """
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"metadata": {
"component": {
"type": "invalid-type",
"name": "my-app"
}
}
}
"""
# endregion Sample SBOMs
# region JSON Validation
print('--- JSON Validation ---')
# Create a JSON validator for a specific schema version
json_validator: 'JsonValidator' = make_schemabased_validator(OutputFormat.JSON, SchemaVersion.V1_5)
# 1. Validate valid SBOM
try:
validation_errors = json_validator.validate_str(JSON_SBOM)
except MissingOptionalDependencyException as error:
print('JSON validation was skipped:', error)
else:
if validation_errors:
print('JSON SBOM is unexpectedly invalid!', file=sys.stderr)
else:
print('JSON SBOM is valid')
# 2. Validate invalid SBOM and inspect details
print('\nChecking invalid JSON SBOM...')
try:
validation_errors = json_validator.validate_str(INVALID_JSON_SBOM)
except MissingOptionalDependencyException as error:
print('JSON validation was skipped:', error)
else:
if validation_errors:
print('Validation failed as expected.')
print(f'Error Message: {validation_errors.data.message}')
print(f'JSON Path: {validation_errors.data.json_path}')
print(f'Invalid Data: {validation_errors.data.instance}')
# endregion JSON Validation
print('\n' + '=' * 30 + '\n')
# region XML Validation
print('--- XML Validation ---')
xml_validator: 'XmlValidator' = make_schemabased_validator(OutputFormat.XML, SchemaVersion.V1_5)
try:
xml_validation_errors = xml_validator.validate_str(XML_SBOM)
if xml_validation_errors:
print('XML SBOM is invalid!', file=sys.stderr)
else:
print('XML SBOM is valid')
except MissingOptionalDependencyException as error:
print('XML validation was skipped:', error)
# endregion XML Validation
print('\n' + '=' * 30 + '\n')
# region Dynamic version detection
print('--- Dynamic Validation ---')
def _detect_json_format(raw_data: str) -> Optional[tuple[OutputFormat, SchemaVersion]]:
"""Detect JSON format and extract schema version."""
try:
data = json.loads(raw_data)
except json.JSONDecodeError:
return None
spec_version_str = data.get('specVersion')
try:
schema_version = SchemaVersion.from_version(spec_version_str)
except Exception:
print('failed to detect schema_version from', repr(spec_version_str), file=sys.stderr)
return None
return (OutputFormat.JSON, schema_version)
def _detect_xml_format(raw_data: str) -> Optional[tuple[OutputFormat, SchemaVersion]]:
try:
from lxml import etree # type: ignore[import-untyped]
except ImportError:
return None
try:
xml_tree = etree.fromstring(raw_data.encode('utf-8'))
except etree.XMLSyntaxError:
return None
for ns in xml_tree.nsmap.values():
if ns and ns.startswith('http://cyclonedx.org/schema/bom/'):
version_str = ns.split('/')[-1]
try:
return (OutputFormat.XML, SchemaVersion.from_version(version_str))
except Exception:
print('failed to detect schema_version from namespace', repr(ns), file=sys.stderr)
return None
print('failed to detect CycloneDX namespace in XML document', file=sys.stderr)
return None
def validate_sbom(raw_data: str) -> bool:
"""Validate an SBOM by detecting its format and version."""
# Detect format and version
format_info = _detect_json_format(raw_data) or _detect_xml_format(raw_data)
if not format_info:
return False
input_format, schema_version = format_info
try:
validator = make_schemabased_validator(input_format, schema_version)
errors = validator.validate_str(raw_data)
if errors:
print(f'Validation failed ({input_format.name} {schema_version.to_version()}): {errors}',
file=sys.stderr)
return False
print(f'Valid {input_format.name} SBOM (schema {schema_version.to_version()})')
return True
except MissingOptionalDependencyException as e:
print(f'Validation skipped (missing dependencies): {e}')
return False
# Execute dynamic validation
validate_sbom(JSON_SBOM)
validate_sbom(XML_SBOM)
# endregion Dynamic version detection
+1
-1

@@ -25,2 +25,2 @@ # This file is part of CycloneDX Python Library

# do not use typing here, or else `semantic_release` might have issues finding the variable
__version__ = "11.6.0" # noqa:Q000
__version__ = "11.7.0" # noqa:Q000

@@ -33,2 +33,3 @@ # This file is part of CycloneDX Python Library

from ..exception.model import LicenseExpressionAlongWithOthersException, UnknownComponentDependencyException
from ..schema.deprecation import SchemaDeprecationWarning1Dot6
from ..schema.schema import (

@@ -295,6 +296,3 @@ SchemaVersion1Dot0,

if manufacture is not None:
warn(
'`bom.metadata.manufacture` is deprecated from CycloneDX v1.6 onwards. '
'Please use `bom.metadata.component.manufacturer` instead.',
DeprecationWarning)
SchemaDeprecationWarning1Dot6._warn('bom.metadata.manufacture', 'bom.metadata.component.manufacturer')
self._manufacture = manufacture

@@ -301,0 +299,0 @@

@@ -43,2 +43,3 @@ # This file is part of CycloneDX Python Library

)
from ..schema.deprecation import SchemaDeprecationWarning1Dot3, SchemaDeprecationWarning1Dot6
from ..schema.schema import (

@@ -1189,4 +1190,3 @@ SchemaVersion1Dot0,

if author is not None:
warn('`@.author` is deprecated from CycloneDX v1.6 onwards. '
'Please use `@.authors` or `@.manufacturer` instead.', DeprecationWarning)
SchemaDeprecationWarning1Dot6._warn('@.author', '@.authors` or `@.manufacturer')
self._author = author

@@ -1472,4 +1472,3 @@

if modified:
warn('`@.modified` is deprecated from CycloneDX v1.3 onwards. '
'Please use `@.pedigree` instead.', DeprecationWarning)
SchemaDeprecationWarning1Dot3._warn('@.modified', '@.pedigree')
self._modified = modified

@@ -1476,0 +1475,0 @@

@@ -23,2 +23,3 @@ # This file is part of CycloneDX Python Library

from collections.abc import Iterable
from enum import Enum

@@ -38,3 +39,3 @@ from json import loads as json_loads

from ..schema.schema import SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7
from . import AttachedText, XsUri
from . import AttachedText, Property, XsUri
from .bom_ref import BomRef

@@ -90,2 +91,3 @@

acknowledgement: Optional[LicenseAcknowledgement] = None,
properties: Optional[Iterable[Property]] = None,
) -> None:

@@ -105,2 +107,3 @@ if not id and not name:

self._acknowledgement = acknowledgement
self._properties = SortedSet(properties or [])

@@ -207,14 +210,22 @@ @property

# @property
# ...
# @serializable.view(SchemaVersion1Dot5)
# @serializable.view(SchemaVersion1Dot6)
# @serializable.xml_sequence(6)
# def properties(self) -> ...:
# ... # TODO since CDX1.5
#
# @licensing.setter
# def properties(self, ...) -> None:
# ... # TODO since CDX1.5
@property
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
@serializable.view(SchemaVersion1Dot7)
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'property')
@serializable.xml_sequence(6)
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

@@ -253,2 +264,3 @@ @serializable.view(SchemaVersion1Dot6)

self._bom_ref.value,
_ComparableTuple(self._properties),
))

@@ -255,0 +267,0 @@

@@ -22,3 +22,2 @@ # This file is part of CycloneDX Python Library

from typing import TYPE_CHECKING, Any, Optional, Union
from warnings import warn
from xml.etree.ElementTree import Element # nosec B405

@@ -32,2 +31,3 @@

from ..schema import SchemaVersion
from ..schema.deprecation import SchemaDeprecationWarning1Dot5
from ..schema.schema import SchemaVersion1Dot4, SchemaVersion1Dot5, SchemaVersion1Dot6, SchemaVersion1Dot7

@@ -245,5 +245,3 @@ from . import ExternalReference, HashType, _HashTypeRepositorySerializationHelper

if tools:
warn('`@.tools` is deprecated from CycloneDX v1.5 onwards. '
'Please use `@.components` and `@.services` instead.',
DeprecationWarning)
SchemaDeprecationWarning1Dot5._warn('@.tools', '@.components` and `@.services')
self._tools = SortedSet(tools)

@@ -250,0 +248,0 @@

@@ -26,4 +26,4 @@ # This file is part of CycloneDX Python Library

class BaseSchemaVersion(ABC, ViewType):
class BaseSchemaVersion(ViewType, ABC):
"""Base class for schema version views."""
@property

@@ -39,3 +39,3 @@ @abstractmethod

class SchemaVersion1Dot7(BaseSchemaVersion):
"""Schema version views 1.7"""
@property

@@ -47,3 +47,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_7]:

class SchemaVersion1Dot6(BaseSchemaVersion):
"""Schema version views 1.6"""
@property

@@ -55,3 +55,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_6]:

class SchemaVersion1Dot5(BaseSchemaVersion):
"""Schema version views 1.5"""
@property

@@ -63,3 +63,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_5]:

class SchemaVersion1Dot4(BaseSchemaVersion):
"""Schema version views 1.4"""
@property

@@ -71,3 +71,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_4]:

class SchemaVersion1Dot3(BaseSchemaVersion):
"""Schema version views 1.3"""
@property

@@ -79,3 +79,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_3]:

class SchemaVersion1Dot2(BaseSchemaVersion):
"""Schema version views 1.2"""
@property

@@ -87,3 +87,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_2]:

class SchemaVersion1Dot1(BaseSchemaVersion):
"""Schema version views 1.1"""
@property

@@ -95,3 +95,3 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_1]:

class SchemaVersion1Dot0(BaseSchemaVersion):
"""Schema version views 1.0"""
@property

@@ -98,0 +98,0 @@ def schema_version_enum(self) -> Literal[SchemaVersion.V1_0]:

@@ -26,3 +26,3 @@ # Licensed under the Apache License, Version 2.0 (the "License");

# !! version is managed by semantic_release
release = '11.6.0'
release = '11.7.0'

@@ -29,0 +29,0 @@ # -- General configuration ---------------------------------------------------

@@ -32,1 +32,9 @@ .. # Licensed under the Apache License, Version 2.0 (the "License");

:linenos:
Complex Validation
------------------
.. literalinclude:: ../examples/complex_validation.py
:language: python
:linenos:
m2r2>=0.3.2
sphinx>=7.2.6,<9
sphinx-autoapi>=3.0.0,<4
sphinx-rtd-theme>=2.0.0,<3
sphinx>=8,<9
sphinx-autoapi>=3,<4
sphinx-rtd-theme>=3,<4

@@ -19,2 +19,3 @@ # This file is part of CycloneDX Python Library

import sys
import warnings
from json import loads as json_loads

@@ -28,2 +29,3 @@ from typing import TYPE_CHECKING

from cyclonedx.schema import OutputFormat, SchemaVersion
from cyclonedx.schema.deprecation import BaseSchemaDeprecationWarning
from cyclonedx.validation import make_schemabased_validator

@@ -159,4 +161,6 @@ from cyclonedx.validation.json import JsonStrictValidator

print('JSON-validation was skipped due to', error)
bom_from_json = Bom.from_json( # type: ignore[attr-defined]
json_loads(json_data))
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=BaseSchemaDeprecationWarning)
bom_from_json = Bom.from_json( # type: ignore[attr-defined]
json_loads(json_data))
print('bom_from_json', repr(bom_from_json))

@@ -261,4 +265,6 @@

print('XML-validation was skipped due to', error)
bom_from_xml = Bom.from_xml( # type: ignore[attr-defined]
SafeElementTree.fromstring(xml_data))
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=BaseSchemaDeprecationWarning)
bom_from_xml = Bom.from_xml( # type: ignore[attr-defined]
SafeElementTree.fromstring(xml_data))
print('bom_from_xml', repr(bom_from_xml))

@@ -265,0 +271,0 @@

Metadata-Version: 2.4
Name: cyclonedx-python-lib
Version: 11.6.0
Version: 11.7.0
Summary: Python library for CycloneDX

@@ -5,0 +5,0 @@ License: Apache-2.0

@@ -8,3 +8,3 @@ [build-system]

# !! version is managed by semantic_release
version = "11.6.0"
version = "11.7.0"
description = "Python library for CycloneDX"

@@ -101,3 +101,3 @@ authors = [

autopep8 = "2.3.2"
mypy = "1.19.0"
mypy = "1.19.1"
tomli = { version = "2.3.0", python = "<3.11" }

@@ -107,3 +107,6 @@ tox = "4.30.3"

bandit = "1.8.6"
pyupgrade = "3.21.0"
pyupgrade = [
{ version = "3.21.0", python = "<3.10" },
{ version = "3.21.2", python = ">=3.10" },
]
deptry = "0.23.1"

@@ -110,0 +113,0 @@ # for tests, use the GPL-version of jsonschema format validators - they are faster

@@ -1085,2 +1085,10 @@ # This file is part of CycloneDX Python Library

]),
Component(name='c-with-license-properties', type=ComponentType.LIBRARY, bom_ref='C4',
licenses=[
DisjunctiveLicense(id='Apache-2.0',
properties=[Property(name='key1', value='val1'),
Property(name='key2', value='val2')]),
DisjunctiveLicense(name='some other license',
properties=[Property(name='myname', value='proprietary')]),
]),
],

@@ -1087,0 +1095,0 @@ services=[

@@ -24,3 +24,3 @@ # This file is part of CycloneDX Python Library

from cyclonedx.exception.model import MutuallyExclusivePropertiesException
from cyclonedx.model import AttachedText, XsUri
from cyclonedx.model import AttachedText, Property, XsUri
from cyclonedx.model.license import DisjunctiveLicense, LicenseExpression

@@ -85,3 +85,21 @@ from tests import reorder

def test_create_with_properties(self) -> None:
properties = [Property(name='key1', value='value1')]
license = DisjunctiveLicense(id='MIT', properties=properties)
self.assertEqual(1, len(license.properties))
def test_set_properties(self) -> None:
license = DisjunctiveLicense(id='MIT')
self.assertEqual(0, len(license.properties))
license.properties = [Property(name='key1', value='value1')]
self.assertEqual(1, len(license.properties))
def test_equal_with_properties(self) -> None:
a = DisjunctiveLicense(id='MIT', properties=[Property(name='key1', value='value1')])
b = DisjunctiveLicense(id='MIT', properties=[Property(name='key1', value='value1')])
c = DisjunctiveLicense(id='MIT')
self.assertEqual(a, b)
self.assertNotEqual(a, c)
class TestModelLicenseExpression(TestCase):

@@ -88,0 +106,0 @@ def test_create(self) -> None:

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet