Skip to content

Commit 34cf6e3

Browse files
authored
feat!: v5.0.0 (#797)
### BREAKING Changes * Emitted metadata tool name is `cyclonedx-py`, was `cyclonedx-bom`. * Emitted metadata tools are up to non-deprecated CycloneDX specification. * No longer emit deprecated or undocumented properties in namespace [`cdx:poetry`](https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx/poetry.md) (see previous release 4.6.0 for official replacements). - `cdx:poetry:source:package:reference` - `cdx:poetry:package:source:resolved_reference` - `cdx:poetry:package:source:vcs:requested_revision` - `cdx:poetry:package:source:vcs:commit_id` The mentioned changes are considered "breaking" for processes that relied on the respective data structures. Migration paths are self-explanatory. ### Dependencies * Requires `cyclonedx-python-lib>=8.0.0,<9 ` now, was `>=7.3.0,<8.0.0,!=7.3.1`. --------- Signed-off-by: Jan Kowalleck <[email protected]>
1 parent 6707959 commit 34cf6e3

File tree

919 files changed

+23999
-9019
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

919 files changed

+23999
-9019
lines changed

cyclonedx_py/_internal/__init__.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,6 @@ class PropertyName(Enum):
7171
# region poetry
7272
# see https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx/poetry.md
7373
PoetryGroup = 'cdx:poetry:group'
74-
# region poetry-deprecated
75-
# the following property names are deprecated
76-
PoetryPackageSourceReference_misspelled = 'cdx:poetry:source:package:reference'
77-
PoetryPackageSourceResolvedReference = 'cdx:poetry:package:source:resolved_reference'
78-
PoetryPackageSourceVcsRequestedRevision = 'cdx:poetry:package:source:vcs:requested_revision'
79-
PoetryPackageSourceVcsCommitId = 'cdx:poetry:package:source:vcs:commit_id'
80-
# endregion poetry-deprecated
8174
# endregion poetry
8275

8376
# region pipenv

cyclonedx_py/_internal/environment.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,16 +245,10 @@ def __component_add_extref_and_purl(self, component: 'Component',
245245
component.properties.add(Property(
246246
name=PropertyName.PythonPackageSourceVcsCommitId.value,
247247
value=packagesource.commit_id))
248-
component.properties.add(Property(
249-
name=PropertyName.PoetryPackageSourceVcsCommitId.value, # deprecated
250-
value=packagesource.commit_id))
251248
if packagesource.requested_revision:
252249
component.properties.add(Property(
253250
name=PropertyName.PythonPackageSourceVcsRequestedRevision.value,
254251
value=packagesource.requested_revision))
255-
component.properties.add(Property(
256-
name=PropertyName.PoetryPackageSourceVcsRequestedRevision.value, # deprecated
257-
value=packagesource.requested_revision))
258252
elif isinstance(packagesource, PackageSourceArchive):
259253
if '://files.pythonhosted.org/' not in packagesource.url:
260254
# skip PURL bloat, do not add implicit information

cyclonedx_py/_internal/poetry.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -416,16 +416,6 @@ def __make_component4lock(self, package: 'T_NameDict') -> 'Component':
416416
name=PropertyName.PoetryGroup.value,
417417
value=package['category']
418418
) if 'category' in package else None,
419-
# region deprecated
420-
Property(
421-
name=PropertyName.PoetryPackageSourceReference_misspelled.value, # deprecated
422-
value=source['reference']
423-
) if is_vcs and 'reference' in source else None,
424-
Property(
425-
name=PropertyName.PoetryPackageSourceResolvedReference.value, # deprecated
426-
value=source['resolved_reference']
427-
) if is_vcs and 'resolved_reference' in source else None,
428-
# endregion deprecated
429419
)),
430420
purl=PackageURL(
431421
type=PurlTypePypi,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# This file is part of CycloneDX Python
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.

cyclonedx_py/_internal/utils/cdx.py

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -23,54 +23,67 @@
2323
from re import compile as re_compile
2424
from typing import Any, Dict, Iterable
2525

26-
from cyclonedx.model import ExternalReference, ExternalReferenceType, Tool, XsUri
26+
from cyclonedx.builder.this import this_component as lib_component
27+
from cyclonedx.model import ExternalReference, ExternalReferenceType, XsUri
2728
from cyclonedx.model.bom import Bom
28-
from cyclonedx.model.license import License, LicenseExpression
29+
from cyclonedx.model.component import Component, ComponentType
30+
from cyclonedx.model.license import DisjunctiveLicense, License, LicenseAcknowledgement, LicenseExpression
2931

30-
from cyclonedx_py import __version__
32+
from ... import __version__ as __THIS_VERSION # noqa:N812
3133

3234

3335
def make_bom(**kwargs: Any) -> Bom:
3436
bom = Bom(**kwargs)
35-
bom.metadata.tools.add(Tool(
36-
# keep in sync with `../../../pyproject.toml`
37-
vendor='CycloneDX',
38-
name='cyclonedx-bom',
39-
version=__version__,
40-
external_references=[
41-
ExternalReference(
42-
type=ExternalReferenceType.BUILD_SYSTEM,
43-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/actions')
37+
bom.metadata.tools.components.update((
38+
lib_component(),
39+
Component(
40+
type=ComponentType.APPLICATION,
41+
group='CycloneDX',
42+
# package is called 'cyclonedx-bom', but the tool is called 'cyclonedx-py'
43+
name='cyclonedx-py',
44+
version=__THIS_VERSION,
45+
description='CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments',
46+
licenses=(DisjunctiveLicense(id='Apache-2.0',
47+
acknowledgement=LicenseAcknowledgement.DECLARED),),
48+
external_references=(
49+
# let's assume this is not a fork
50+
ExternalReference(
51+
type=ExternalReferenceType.WEBSITE,
52+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/#readme')
53+
),
54+
ExternalReference(
55+
type=ExternalReferenceType.DOCUMENTATION,
56+
url=XsUri('https://cyclonedx-bom-tool.readthedocs.io/')
57+
),
58+
ExternalReference(
59+
type=ExternalReferenceType.VCS,
60+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/')
61+
),
62+
ExternalReference(
63+
type=ExternalReferenceType.BUILD_SYSTEM,
64+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/actions')
65+
),
66+
ExternalReference(
67+
type=ExternalReferenceType.ISSUE_TRACKER,
68+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/issues')
69+
),
70+
ExternalReference(
71+
type=ExternalReferenceType.LICENSE,
72+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE')
73+
),
74+
ExternalReference(
75+
type=ExternalReferenceType.RELEASE_NOTES,
76+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md')
77+
),
78+
# we cannot assert where the lib was fetched from, but we can give a hint
79+
ExternalReference(
80+
type=ExternalReferenceType.DISTRIBUTION,
81+
url=XsUri('https://pypi.org/project/cyclonedx-bom/')
82+
),
4483
),
45-
ExternalReference(
46-
type=ExternalReferenceType.DISTRIBUTION,
47-
url=XsUri('https://pypi.org/project/cyclonedx-bom/')
48-
),
49-
ExternalReference(
50-
type=ExternalReferenceType.DOCUMENTATION,
51-
url=XsUri('https://cyclonedx-bom-tool.readthedocs.io/')
52-
),
53-
ExternalReference(
54-
type=ExternalReferenceType.ISSUE_TRACKER,
55-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/issues')
56-
),
57-
ExternalReference(
58-
type=ExternalReferenceType.LICENSE,
59-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/LICENSE')
60-
),
61-
ExternalReference(
62-
type=ExternalReferenceType.RELEASE_NOTES,
63-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/blob/main/CHANGELOG.md')
64-
),
65-
ExternalReference(
66-
type=ExternalReferenceType.VCS,
67-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/')
68-
),
69-
ExternalReference(
70-
type=ExternalReferenceType.WEBSITE,
71-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python/#readme')
72-
)
73-
]))
84+
# to be extended...
85+
),
86+
))
7487
return bom
7588

7689

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ build-backend = "poetry.core.masonry.api"
55

66

77
[tool.poetry]
8-
# keep in sync with `cyclonedx_py/_internal/utils/cdx.py`
98
name = "cyclonedx-bom"
109
version = "4.6.1"
1110
description = "CycloneDX Software Bill of Materials (SBOM) generator for Python projects and environments"
@@ -69,7 +68,7 @@ cyclonedx-py = "cyclonedx_py._internal.cli:run"
6968

7069
[tool.poetry.dependencies]
7170
python = "^3.8"
72-
cyclonedx-python-lib = { version = "^7.3.0, !=7.3.1", extras = ["validation"] }
71+
cyclonedx-python-lib = { version = "^8.0", extras = ["validation"] }
7372
packageurl-python = ">=0.11, <2" # keep in sync with same dep in `cyclonedx-python-lib`
7473
pip-requirements-parser = "^32.0"
7574
packaging = "^22 || ^23 || ^24"
@@ -92,6 +91,7 @@ isort = "5.13.2"
9291
autopep8 = "2.3.1"
9392
mypy = "1.11.2"
9493
bandit = "1.7.10"
94+
tomli = { version = "^2.0.1", python = "<3.11" }
9595
tox = "4.21.2"
9696
# min version required to be able to install some dependencies
9797
# see https://github.com/MichaelKim0407/flake8-use-fstring/issues/33

tests/__init__.py

Lines changed: 93 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@
1515
# SPDX-License-Identifier: Apache-2.0
1616
# Copyright (c) OWASP Foundation. All Rights Reserved.
1717

18-
18+
import sys
1919
from json import dumps as json_dumps
20-
from os import getenv
21-
from os.path import dirname, join
20+
from os import getenv, path
2221
from pathlib import Path
2322
from re import sub as re_sub
24-
from sys import stderr
25-
from typing import Union
23+
from typing import Any, Dict, Union
2624
from unittest import TestCase
2725
from xml.sax.saxutils import escape as xml_escape, quoteattr as xml_quoteattr # nosec:B406
2826

@@ -32,16 +30,16 @@
3230

3331
RECREATE_SNAPSHOTS = '1' == getenv('CDX_TEST_RECREATE_SNAPSHOTS')
3432
if RECREATE_SNAPSHOTS:
35-
print('!!! WILL RECREATE ALL SNAPSHOTS !!!', file=stderr)
33+
print('!!! WILL RECREATE ALL SNAPSHOTS !!!', file=sys.stderr)
3634

3735
INIT_TESTBEDS = '1' != getenv('CDX_TEST_SKIP_INIT_TESTBEDS')
3836
if INIT_TESTBEDS:
39-
print('!!! WILL INIT TESTBEDS !!!', file=stderr)
37+
print('!!! WILL INIT TESTBEDS !!!', file=sys.stderr)
4038

41-
_TESTDATA_DIRECTORY = join(dirname(__file__), '_data')
39+
_TESTDATA_DIRECTORY = path.join(path.dirname(__file__), '_data')
4240

43-
INFILES_DIRECTORY = join(_TESTDATA_DIRECTORY, 'infiles')
44-
SNAPSHOTS_DIRECTORY = join(_TESTDATA_DIRECTORY, 'snapshots')
41+
INFILES_DIRECTORY = path.join(_TESTDATA_DIRECTORY, 'infiles')
42+
SNAPSHOTS_DIRECTORY = path.join(_TESTDATA_DIRECTORY, 'snapshots')
4543

4644
UNSUPPORTED_OF_SV = (
4745
(OutputFormat.JSON, SchemaVersion.V1_1),
@@ -60,7 +58,7 @@ class SnapshotMixin:
6058

6159
@staticmethod
6260
def getSnapshotFile(snapshot_name: str) -> str: # noqa: N802
63-
return join(SNAPSHOTS_DIRECTORY, f'{snapshot_name}.bin')
61+
return path.join(SNAPSHOTS_DIRECTORY, f'{snapshot_name}.bin')
6462

6563
@classmethod
6664
def writeSnapshot(cls, snapshot_name: str, data: str) -> None: # noqa: N802
@@ -92,62 +90,121 @@ def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], # noqa: N802
9290
_root_file_uri_xml_attr = xml_quoteattr(_root_file_uri)[1:-1]
9391
_root_file_uri_json = json_dumps(_root_file_uri)[1:-1]
9492

93+
# package is called 'cyclonedx-bom', but the tool is called 'cyclonedx-py'
94+
EXPECTED_TOOL_NAME = 'cyclonedx-py'
95+
9596

9697
def make_xml_comparable(bom: str) -> str:
9798
bom = bom.replace(_root_file_uri_xml, 'file://.../')
9899
bom = bom.replace(_root_file_uri_xml_attr, 'file://.../')
99-
bom = bom.replace( # replace metadata.tools.version
100+
bom = bom.replace( # replace this version in metadata.tools.components
101+
' <group>CycloneDX</group>\n'
102+
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
103+
f' <version>{__this_version}</version>',
104+
' <group>CycloneDX</group>\n'
105+
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
106+
' <version>thisVersion-testing</version>')
107+
bom = bom.replace( # replace this version in metadata.tools
100108
' <vendor>CycloneDX</vendor>\n'
101-
' <name>cyclonedx-bom</name>\n'
109+
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
102110
f' <version>{__this_version}</version>',
103111
' <vendor>CycloneDX</vendor>\n'
104-
' <name>cyclonedx-bom</name>\n'
112+
f' <name>{EXPECTED_TOOL_NAME}</name>\n'
105113
' <version>thisVersion-testing</version>')
106-
bom = re_sub( # replace metadata.tools.version
114+
bom = re_sub( # replace lib-dynamics in metadata.tools.components
115+
' <group>CycloneDX</group>\n'
116+
' <name>cyclonedx-python-lib</name>\n'
117+
' <version>.*?</version>\n'
118+
' <description>.*?</description>\n'
119+
' <licenses>\n'
120+
'(?: .*?\n)*'
121+
' </licenses>\n'
122+
' <externalReferences>\n'
123+
'(?: .*?\n)*'
124+
' </externalReferences>',
125+
' <group>CycloneDX</group>\n'
126+
' <name>cyclonedx-python-lib</name>\n'
127+
' <version>libVersion-testing</version>\n'
128+
' <description><!-- stripped --></description>\n'
129+
' <licenses><!-- stripped --></licenses>\n'
130+
' <externalReferences><!-- stripped --></externalReferences>',
131+
bom)
132+
bom = re_sub( # replace lib-dynamics version in metadata.tools[]
107133
' <vendor>CycloneDX</vendor>\n'
108134
' <name>cyclonedx-python-lib</name>\n'
109135
' <version>.*?</version>',
110136
' <vendor>CycloneDX</vendor>\n'
111137
' <name>cyclonedx-python-lib</name>\n'
112138
' <version>libVersion-testing</version>',
113139
bom)
114-
bom = re_sub( # replace metadata.tools.externalReferences
140+
bom = re_sub( # replace lib-dynamics externalReferences in metadata.tools[]
115141
' <vendor>CycloneDX</vendor>\n'
116142
' <name>cyclonedx-python-lib</name>\n'
117-
r' <version>(.*?)</version>\n'
118-
r' <externalReferences>[\s\S]*?</externalReferences>',
143+
' <version>(.*?)</version>\n'
144+
' <externalReferences>\n'
145+
'(?: .*?\n)*'
146+
' </externalReferences>',
119147
' <vendor>CycloneDX</vendor>\n'
120148
' <name>cyclonedx-python-lib</name>\n'
121-
r' <version>\1</version>''\n'
149+
' <version>\\1</version>\n'
122150
' <externalReferences><!-- stripped --></externalReferences>',
123151
bom)
124152
return bom
125153

126154

127155
def make_json_comparable(bom: str) -> str:
128156
bom = bom.replace(_root_file_uri_json, 'file://.../')
129-
bom = bom.replace( # replace metadata.tools.version
130-
' "name": "cyclonedx-bom",\n'
157+
bom = bom.replace( # replace this version in metadata.tools.components[]
158+
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
159+
' "type": "application",\n'
160+
f' "version": {json_dumps(__this_version)}',
161+
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
162+
' "type": "application",\n'
163+
' "version": "thisVersion-testing"')
164+
bom = bom.replace( # replace this version in metadata.tools[]
165+
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
131166
' "vendor": "CycloneDX",\n'
132167
f' "version": {json_dumps(__this_version)}',
133-
' "name": "cyclonedx-bom",\n'
168+
f' "name": {json_dumps(EXPECTED_TOOL_NAME)},\n'
134169
' "vendor": "CycloneDX",\n'
135170
' "version": "thisVersion-testing"')
136-
bom = re_sub( # replace metadata.tools.version
171+
bom = re_sub( # replace lib-dynamics in metadata.tools.components[]
172+
' "description": ".*?",\n'
173+
' "externalReferences": \\[\n'
174+
'(?: .*?\n)*'
175+
' \\],\n'
176+
' "group": "CycloneDX",\n'
177+
' "licenses": \\[\n'
178+
'(?: .*?\n)*'
179+
' \\],\n'
180+
' "name": "cyclonedx-python-lib",\n'
181+
' "type": "library",\n'
182+
' "version": ".*?"',
183+
' "description": "stripped",\n'
184+
' "externalReferences": [ ],\n'
185+
' "group": "CycloneDX",\n'
186+
' "licenses": [ ],\n'
187+
' "name": "cyclonedx-python-lib",\n'
188+
' "type": "library",\n'
189+
' "version": "libVersion-testing"',
190+
bom)
191+
bom = re_sub( # replace lib-dynamics version in metadata.tools[]
137192
' "name": "cyclonedx-python-lib",\n'
138193
' "vendor": "CycloneDX",\n'
139194
' "version": ".*?"',
140195
' "name": "cyclonedx-python-lib",\n'
141196
' "vendor": "CycloneDX",\n'
142197
' "version": "libVersion-testing"',
143198
bom)
144-
bom = re_sub( # replace metadata.tools.externalReferences
145-
r' "externalReferences": \[[\s\S]*?\],\n'
199+
bom = re_sub( # replace lib-dynamics externalReferences in metadata.tools[]
200+
' "externalReferences": \\[\n'
201+
'(?: .*?\n)*'
202+
' \\],\n'
146203
' "name": "cyclonedx-python-lib",\n'
147-
' "vendor": "CycloneDX"',
204+
' "vendor": "CycloneDX",\n',
148205
' "externalReferences": [ ],\n'
149206
' "name": "cyclonedx-python-lib",\n'
150-
' "vendor": "CycloneDX"',
207+
' "vendor": "CycloneDX",\n',
151208
bom)
152209
return bom
153210

@@ -160,3 +217,12 @@ def make_comparable(bom: str, of: OutputFormat) -> str:
160217
raise NotImplementedError(f'unknown OutputFormat: {of!r}')
161218

162219
# endregion reproducible test results
220+
221+
222+
def load_pyproject() -> Dict[str, Any]:
223+
if sys.version_info >= (3, 11):
224+
from tomllib import load as toml_load
225+
else:
226+
from tomli import load as toml_load
227+
with open(path.join(path.dirname(__file__), '..', 'pyproject.toml'), 'rb') as f:
228+
return toml_load(f)

0 commit comments

Comments
 (0)