Skip to content

Commit 86ed153

Browse files
authored
Merge branch 'main' into feat/lifecycles
Signed-off-by: Johannes Feichtner <[email protected]>
2 parents df0fdf0 + 2decdb7 commit 86ed153

File tree

481 files changed

+4765
-10866
lines changed

Some content is hidden

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

481 files changed

+4765
-10866
lines changed

CHANGELOG.md

Lines changed: 52 additions & 714 deletions
Large diffs are not rendered by default.

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ Run all tests in dedicated environments, via:
5252
poetry run tox run
5353
```
5454

55+
See also: [python test snapshots docs](tests/_data/snapshots/README.md)
56+
5557
## Sign off your commits
5658

5759
Please sign off your commits, to show that you agree to publish your changes under the current terms and licenses of the project

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__ = "7.6.2" # noqa:Q000
25+
__version__ = "8.0.0" # noqa:Q000

cyclonedx/builder/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
Builders used in this library.
20+
"""

cyclonedx/builder/this.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
"""Representation of this very python library."""
19+
20+
__all__ = ['this_component', 'this_tool', ]
21+
22+
from .. import __version__ as __ThisVersion # noqa: N812
23+
from ..model import ExternalReference, ExternalReferenceType, XsUri
24+
from ..model.component import Component, ComponentType
25+
from ..model.license import DisjunctiveLicense, LicenseAcknowledgement
26+
from ..model.tool import Tool
27+
28+
# !!! keep this file in sync with `pyproject.toml`
29+
30+
31+
def this_component() -> Component:
32+
"""Representation of this very python library as a :class:`Component`."""
33+
return Component(
34+
type=ComponentType.LIBRARY,
35+
group='CycloneDX',
36+
name='cyclonedx-python-lib',
37+
version=__ThisVersion or 'UNKNOWN',
38+
description='Python library for CycloneDX',
39+
licenses=(DisjunctiveLicense(id='Apache-2.0',
40+
acknowledgement=LicenseAcknowledgement.DECLARED),),
41+
external_references=(
42+
# let's assume this is not a fork
43+
ExternalReference(
44+
type=ExternalReferenceType.WEBSITE,
45+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/#readme')
46+
),
47+
ExternalReference(
48+
type=ExternalReferenceType.DOCUMENTATION,
49+
url=XsUri('https://cyclonedx-python-library.readthedocs.io/')
50+
),
51+
ExternalReference(
52+
type=ExternalReferenceType.VCS,
53+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib')
54+
),
55+
ExternalReference(
56+
type=ExternalReferenceType.BUILD_SYSTEM,
57+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/actions')
58+
),
59+
ExternalReference(
60+
type=ExternalReferenceType.ISSUE_TRACKER,
61+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/issues')
62+
),
63+
ExternalReference(
64+
type=ExternalReferenceType.LICENSE,
65+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE')
66+
),
67+
ExternalReference(
68+
type=ExternalReferenceType.RELEASE_NOTES,
69+
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md')
70+
),
71+
# we cannot assert where the lib was fetched from, but we can give a hint
72+
ExternalReference(
73+
type=ExternalReferenceType.DISTRIBUTION,
74+
url=XsUri('https://pypi.org/project/cyclonedx-python-lib/')
75+
),
76+
),
77+
# to be extended...
78+
)
79+
80+
81+
def this_tool() -> Tool:
82+
"""Representation of this very python library as a :class:`Tool`."""
83+
return Tool.from_component(this_component())

cyclonedx/model/__init__.py

Lines changed: 0 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import serializable
3535
from sortedcontainers import SortedSet
3636

37-
from .. import __version__ as __ThisToolVersion # noqa: N812
3837
from .._internal.compare import ComparableTuple as _ComparableTuple
3938
from ..exception.model import (
4039
InvalidLocaleTypeException,
@@ -1129,139 +1128,6 @@ def __repr__(self) -> str:
11291128
return f'<Note id={id(self)}, locale={self.locale}>'
11301129

11311130

1132-
@serializable.serializable_class
1133-
class Tool:
1134-
"""
1135-
This is our internal representation of the `toolType` complex type within the CycloneDX standard.
1136-
1137-
Tool(s) are the things used in the creation of the CycloneDX document.
1138-
1139-
Tool might be deprecated since CycloneDX 1.5, but it is not deprecated in this library.
1140-
In fact, this library will try to provide a compatibility layer if needed.
1141-
1142-
.. note::
1143-
See the CycloneDX Schema for toolType: https://cyclonedx.org/docs/1.3/#type_toolType
1144-
"""
1145-
1146-
def __init__(
1147-
self, *,
1148-
vendor: Optional[str] = None,
1149-
name: Optional[str] = None,
1150-
version: Optional[str] = None,
1151-
hashes: Optional[Iterable[HashType]] = None,
1152-
external_references: Optional[Iterable[ExternalReference]] = None,
1153-
) -> None:
1154-
self.vendor = vendor
1155-
self.name = name
1156-
self.version = version
1157-
self.hashes = hashes or [] # type:ignore[assignment]
1158-
self.external_references = external_references or [] # type:ignore[assignment]
1159-
1160-
@property
1161-
@serializable.xml_sequence(1)
1162-
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
1163-
def vendor(self) -> Optional[str]:
1164-
"""
1165-
The name of the vendor who created the tool.
1166-
1167-
Returns:
1168-
`str` if set else `None`
1169-
"""
1170-
return self._vendor
1171-
1172-
@vendor.setter
1173-
def vendor(self, vendor: Optional[str]) -> None:
1174-
self._vendor = vendor
1175-
1176-
@property
1177-
@serializable.xml_sequence(2)
1178-
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
1179-
def name(self) -> Optional[str]:
1180-
"""
1181-
The name of the tool.
1182-
1183-
Returns:
1184-
`str` if set else `None`
1185-
"""
1186-
return self._name
1187-
1188-
@name.setter
1189-
def name(self, name: Optional[str]) -> None:
1190-
self._name = name
1191-
1192-
@property
1193-
@serializable.xml_sequence(3)
1194-
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
1195-
def version(self) -> Optional[str]:
1196-
"""
1197-
The version of the tool.
1198-
1199-
Returns:
1200-
`str` if set else `None`
1201-
"""
1202-
return self._version
1203-
1204-
@version.setter
1205-
def version(self, version: Optional[str]) -> None:
1206-
self._version = version
1207-
1208-
@property
1209-
@serializable.type_mapping(_HashTypeRepositorySerializationHelper)
1210-
@serializable.xml_sequence(4)
1211-
def hashes(self) -> 'SortedSet[HashType]':
1212-
"""
1213-
The hashes of the tool (if applicable).
1214-
1215-
Returns:
1216-
Set of `HashType`
1217-
"""
1218-
return self._hashes
1219-
1220-
@hashes.setter
1221-
def hashes(self, hashes: Iterable[HashType]) -> None:
1222-
self._hashes = SortedSet(hashes)
1223-
1224-
@property
1225-
@serializable.view(SchemaVersion1Dot4)
1226-
@serializable.view(SchemaVersion1Dot5)
1227-
@serializable.view(SchemaVersion1Dot6)
1228-
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
1229-
@serializable.xml_sequence(5)
1230-
def external_references(self) -> 'SortedSet[ExternalReference]':
1231-
"""
1232-
External References provides a way to document systems, sites, and information that may be relevant but which
1233-
are not included with the BOM.
1234-
1235-
Returns:
1236-
Set of `ExternalReference`
1237-
"""
1238-
return self._external_references
1239-
1240-
@external_references.setter
1241-
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
1242-
self._external_references = SortedSet(external_references)
1243-
1244-
def __eq__(self, other: object) -> bool:
1245-
if isinstance(other, Tool):
1246-
return hash(other) == hash(self)
1247-
return False
1248-
1249-
def __lt__(self, other: Any) -> bool:
1250-
if isinstance(other, Tool):
1251-
return _ComparableTuple((
1252-
self.vendor, self.name, self.version
1253-
)) < _ComparableTuple((
1254-
other.vendor, other.name, other.version
1255-
))
1256-
return NotImplemented
1257-
1258-
def __hash__(self) -> int:
1259-
return hash((self.vendor, self.name, self.version, tuple(self.hashes), tuple(self.external_references)))
1260-
1261-
def __repr__(self) -> str:
1262-
return f'<Tool name={self.name}, version={self.version}, vendor={self.vendor}>'
1263-
1264-
12651131
@serializable.serializable_class
12661132
class IdentifiableAction:
12671133
"""
@@ -1397,43 +1263,3 @@ def __hash__(self) -> int:
13971263

13981264
def __repr__(self) -> str:
13991265
return f'<Copyright text={self.text}>'
1400-
1401-
1402-
ThisTool = Tool(
1403-
vendor='CycloneDX',
1404-
name='cyclonedx-python-lib',
1405-
version=__ThisToolVersion or 'UNKNOWN',
1406-
external_references=[
1407-
ExternalReference(
1408-
type=ExternalReferenceType.BUILD_SYSTEM,
1409-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/actions')
1410-
),
1411-
ExternalReference(
1412-
type=ExternalReferenceType.DISTRIBUTION,
1413-
url=XsUri('https://pypi.org/project/cyclonedx-python-lib/')
1414-
),
1415-
ExternalReference(
1416-
type=ExternalReferenceType.DOCUMENTATION,
1417-
url=XsUri('https://cyclonedx-python-library.readthedocs.io/')
1418-
),
1419-
ExternalReference(
1420-
type=ExternalReferenceType.ISSUE_TRACKER,
1421-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/issues')
1422-
),
1423-
ExternalReference(
1424-
type=ExternalReferenceType.LICENSE,
1425-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE')
1426-
),
1427-
ExternalReference(
1428-
type=ExternalReferenceType.RELEASE_NOTES,
1429-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md')
1430-
),
1431-
ExternalReference(
1432-
type=ExternalReferenceType.VCS,
1433-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib')
1434-
),
1435-
ExternalReference(
1436-
type=ExternalReferenceType.WEBSITE,
1437-
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/#readme')
1438-
)
1439-
])

cyclonedx/model/bom.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@
3737
SchemaVersion1Dot6,
3838
)
3939
from ..serialization import LicenseRepositoryHelper, UrnUuidHelper
40-
from . import ExternalReference, Property, ThisTool, Tool
40+
from . import ExternalReference, Property
4141
from .bom_ref import BomRef
4242
from .component import Component
4343
from .contact import OrganizationalContact, OrganizationalEntity
4444
from .dependency import Dependable, Dependency
4545
from .license import License, LicenseExpression, LicenseRepository
4646
from .lifecycle import Lifecycle, LifecycleRepository, _LifecycleRepositoryHelper
4747
from .service import Service
48+
from .tool import Tool, ToolRepository, _ToolRepositoryHelper
4849
from .vulnerability import Vulnerability
4950

5051
if TYPE_CHECKING: # pragma: no cover
@@ -62,7 +63,7 @@ class BomMetaData:
6263

6364
def __init__(
6465
self, *,
65-
tools: Optional[Iterable[Tool]] = None,
66+
tools: Optional[Union[Iterable[Tool], ToolRepository]] = None,
6667
authors: Optional[Iterable[OrganizationalContact]] = None,
6768
component: Optional[Component] = None,
6869
supplier: Optional[OrganizationalEntity] = None,
@@ -91,9 +92,6 @@ def __init__(
9192
'Please use `bom.metadata.component.manufacturer` instead.',
9293
DeprecationWarning)
9394

94-
if not tools:
95-
self.tools.add(ThisTool)
96-
9795
@property
9896
@serializable.type_mapping(serializable.helpers.XsdDateTime)
9997
@serializable.xml_sequence(1)
@@ -129,22 +127,22 @@ def lifecycles(self, lifecycles: Iterable[Lifecycle]) -> None:
129127
self._lifecycles = LifecycleRepository(lifecycles)
130128

131129
@property
132-
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'tool')
130+
@serializable.type_mapping(_ToolRepositoryHelper)
133131
@serializable.xml_sequence(3)
134-
def tools(self) -> 'SortedSet[Tool]':
132+
def tools(self) -> ToolRepository:
135133
"""
136134
Tools used to create this BOM.
137135
138136
Returns:
139-
`Set` of `Tool` objects.
137+
:class:`ToolRepository` object.
140138
"""
141-
# TODO since CDX1.5 also supports `Component` and `Services`, not only `Tool`
142139
return self._tools
143140

144141
@tools.setter
145-
def tools(self, tools: Iterable[Tool]) -> None:
146-
# TODO since CDX1.5 also supports `Component` and `Services`, not only `Tool`
147-
self._tools = SortedSet(tools)
142+
def tools(self, tools: Union[Iterable[Tool], ToolRepository]) -> None:
143+
self._tools = tools \
144+
if isinstance(tools, ToolRepository) \
145+
else ToolRepository(tools=tools)
148146

149147
@property
150148
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'author')
@@ -302,7 +300,7 @@ def __eq__(self, other: object) -> bool:
302300
def __hash__(self) -> int:
303301
return hash((
304302
tuple(self.authors), self.component, tuple(self.licenses), self.manufacture, tuple(self.properties),
305-
self.supplier, self.timestamp, tuple(self.tools), tuple(self.lifecycles), self.manufacturer
303+
tuple(self.lifecycles), self.supplier, self.timestamp, tuple(self.tools), self.manufacturer
306304
))
307305

308306
def __repr__(self) -> str:

cyclonedx/model/license.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class LicenseExpression:
250250
"""
251251

252252
def __init__(
253-
self, value: str, # *, # all optional args are intended to be keyword-args
253+
self, value: str, *,
254254
acknowledgement: Optional[LicenseAcknowledgement] = None,
255255
) -> None:
256256
self._value = value

0 commit comments

Comments
 (0)