Skip to content

feat!: Add component and services for tools #635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
2bbd659
#561: Add component and services for tools
jkugler Jun 18, 2024
7394c3b
Additioal cleanup and improving semantics
jkugler Jun 18, 2024
d8a1f87
More code cleanup
jkugler Jun 19, 2024
280dc92
Fix json_normalize
jkugler Jun 19, 2024
c90ce1f
Add an explicit __bool__ to ToolRepository
jkugler Jun 19, 2024
e7b4bd9
Removed Tool class from model/__init__.py
jkugler Jun 19, 2024
3fe5ab6
Rename Tool* to Tools*
jkugler Jun 19, 2024
582741e
Fixing a circular import issue
jkugler Jun 19, 2024
7e95ea8
Remove unneeded code
jkugler Jun 19, 2024
ea9ffd4
Adding an XML de-serializer
jkugler Jun 19, 2024
f23ab80
More code clenaup
jkugler Jun 19, 2024
f8efa5a
Final feature addition
jkugler Jun 20, 2024
a40cbe7
Adding tests for new functionality
jkugler Jun 20, 2024
6876042
100% test coverage for tool.py
jkugler Jun 21, 2024
45dd1ce
Put schema version guards around components/services in tools
jkugler Jun 21, 2024
3910030
Merge branch 'main' into 561_add_components_and_services
jkugler Jun 21, 2024
920f0e1
Formatting fix
jkugler Jun 21, 2024
00d287d
Python 3.8 fixes
jkugler Jun 24, 2024
5768a9b
Merge remote-tracking branch 'origin/main' into 561_add_components_an…
jkugler Jun 24, 2024
1a4ccfb
Improve and cleanup tests
jkugler Jun 25, 2024
024a582
Merge remote-tracking branch 'origin/main' into 561_add_components_an…
jkugler Jul 8, 2024
303e29c
Additional test case for ToolsRepository equality
jkugler Jul 8, 2024
1a639a6
First part of changes to use the `.tools` setter/getter
jkugler Jul 9, 2024
ce00e81
Merge remote-tracking branch 'origin/main' into 561_add_components_an…
jkugler Jul 18, 2024
8e32d48
Render components and services to tools when rendering to old schemas
jkugler Jul 19, 2024
b5c7057
Correct an incorrect variable name
jkugler Jul 19, 2024
c730146
Added rendering for XML "down convert."
jkugler Jul 19, 2024
aec8393
Forgot "name" on the service
jkugler Jul 19, 2024
2635278
Additional test coverage for model/tool.py
jkugler Jul 19, 2024
e267c2a
Doc and type updates
jkugler Jul 19, 2024
eeff89b
`bom.tools` revert internal default assignment
jkowalleck Jul 21, 2024
0c70062
chore: add legal header
jkowalleck Jul 21, 2024
3ac3b2b
style: remove redndant braces
jkowalleck Jul 21, 2024
f889823
style: rearance line breaks
jkowalleck Jul 21, 2024
cf0826c
style: simplified init
jkowalleck Jul 21, 2024
3271db7
refactor: rework hashing of `...tools` / `ToolsRepository`
jkowalleck Jul 21, 2024
26f6007
style: simplify init
jkowalleck Jul 21, 2024
61e2d90
fix: tool serialization
jkowalleck Jul 21, 2024
0b6d14d
style: default values
jkowalleck Jul 21, 2024
b90bec5
docs: type hint
jkowalleck Jul 21, 2024
6dc2aab
Merge remote-tracking branch 'origin/main' into 561_add_components_an…
jkugler Aug 1, 2024
76743d9
Addressing comments
jkugler Aug 1, 2024
73007f8
Add logic to down-convert all components/services if there are Tools
jkugler Aug 2, 2024
95fb0a2
tests: remove unnessessary serialization tests
jkowalleck Aug 16, 2024
702893d
tetss: improved deseriualization assert/expect
jkowalleck Aug 16, 2024
2254703
reformat
jkowalleck Aug 16, 2024
e9fe527
tests: improved toolRepo tests
jkowalleck Aug 16, 2024
59b0987
get rid of tool-repo mutial exclusive tool vs component/service
jkowalleck Aug 16, 2024
1a48106
refactor: reimplemented XML/JSON normalization
jkowalleck Aug 16, 2024
96fe96c
style
jkowalleck Aug 16, 2024
7b15d40
refactor: reimplemented XML/JSON denormalization
jkowalleck Aug 16, 2024
427add4
refactor: move ToolsRepositoryHelper to other helpers
jkowalleck Aug 16, 2024
4a2ac65
refactor: move ToolsRepositoryHelper back to tool
jkowalleck Aug 16, 2024
623056e
fix: do not emit empty tools repo
jkowalleck Aug 16, 2024
5a6c3eb
style
jkowalleck Aug 16, 2024
75d20d2
tests
jkowalleck Aug 16, 2024
a3b5eca
tests
jkowalleck Aug 16, 2024
3032416
tests
jkowalleck Aug 19, 2024
5cc06ae
tests
jkowalleck Aug 19, 2024
614968e
tests
jkowalleck Aug 19, 2024
8b9b488
tests
jkowalleck Aug 19, 2024
c937f21
tests
jkowalleck Aug 19, 2024
5329fdf
Merge branch '8.0.0-dev' into 561_add_components_and_services
jkugler Aug 22, 2024
1d3c3fe
refactor: use property setters in `ToolsRepository.__init__()`
jkowalleck Sep 6, 2024
9876139
style: rephrase warning test
jkowalleck Sep 6, 2024
f1fdb53
coverage ignore non-scope
jkowalleck Sep 6, 2024
a678c8c
tests for invalid tool deserialization
jkowalleck Sep 6, 2024
d93fac5
tets: fixed windows compat
jkowalleck Sep 6, 2024
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
139 changes: 5 additions & 134 deletions cyclonedx/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,139 +1127,6 @@ def __repr__(self) -> str:
return f'<Note id={id(self)}, locale={self.locale}>'


@serializable.serializable_class
class Tool:
"""
This is our internal representation of the `toolType` complex type within the CycloneDX standard.

Tool(s) are the things used in the creation of the CycloneDX document.

Tool might be deprecated since CycloneDX 1.5, but it is not deprecated in this library.
In fact, this library will try to provide a compatibility layer if needed.

.. note::
See the CycloneDX Schema for toolType: https://cyclonedx.org/docs/1.3/#type_toolType
"""

def __init__(
self, *,
vendor: Optional[str] = None,
name: Optional[str] = None,
version: Optional[str] = None,
hashes: Optional[Iterable[HashType]] = None,
external_references: Optional[Iterable[ExternalReference]] = None,
) -> None:
self.vendor = vendor
self.name = name
self.version = version
self.hashes = hashes or [] # type:ignore[assignment]
self.external_references = external_references or [] # type:ignore[assignment]

@property
@serializable.xml_sequence(1)
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
def vendor(self) -> Optional[str]:
"""
The name of the vendor who created the tool.

Returns:
`str` if set else `None`
"""
return self._vendor

@vendor.setter
def vendor(self, vendor: Optional[str]) -> None:
self._vendor = vendor

@property
@serializable.xml_sequence(2)
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
def name(self) -> Optional[str]:
"""
The name of the tool.

Returns:
`str` if set else `None`
"""
return self._name

@name.setter
def name(self, name: Optional[str]) -> None:
self._name = name

@property
@serializable.xml_sequence(3)
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
def version(self) -> Optional[str]:
"""
The version of the tool.

Returns:
`str` if set else `None`
"""
return self._version

@version.setter
def version(self, version: Optional[str]) -> None:
self._version = version

@property
@serializable.type_mapping(_HashTypeRepositorySerializationHelper)
@serializable.xml_sequence(4)
def hashes(self) -> 'SortedSet[HashType]':
"""
The hashes of the tool (if applicable).

Returns:
Set of `HashType`
"""
return self._hashes

@hashes.setter
def hashes(self, hashes: Iterable[HashType]) -> None:
self._hashes = SortedSet(hashes)

@property
@serializable.view(SchemaVersion1Dot4)
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'reference')
@serializable.xml_sequence(5)
def external_references(self) -> 'SortedSet[ExternalReference]':
"""
External References provides a way to document systems, sites, and information that may be relevant but which
are not included with the BOM.

Returns:
Set of `ExternalReference`
"""
return self._external_references

@external_references.setter
def external_references(self, external_references: Iterable[ExternalReference]) -> None:
self._external_references = SortedSet(external_references)

def __eq__(self, other: object) -> bool:
if isinstance(other, Tool):
return hash(other) == hash(self)
return False

def __lt__(self, other: Any) -> bool:
if isinstance(other, Tool):
return _ComparableTuple((
self.vendor, self.name, self.version
)) < _ComparableTuple((
other.vendor, other.name, other.version
))
return NotImplemented

def __hash__(self) -> int:
return hash((self.vendor, self.name, self.version, tuple(self.hashes), tuple(self.external_references)))

def __repr__(self) -> str:
return f'<Tool name={self.name}, version={self.version}, vendor={self.vendor}>'


@serializable.serializable_class
class IdentifiableAction:
"""
Expand Down Expand Up @@ -1397,6 +1264,9 @@ def __repr__(self) -> str:
return f'<Copyright text={self.text}>'


# Importing here to avoid a circular import
from .tool import Tool # pylint: disable=wrong-import-position # noqa: E402

ThisTool = Tool(
vendor='CycloneDX',
name='cyclonedx-python-lib',
Expand Down Expand Up @@ -1434,4 +1304,5 @@ def __repr__(self) -> str:
type=ExternalReferenceType.WEBSITE,
url=XsUri('https://github.com/CycloneDX/cyclonedx-python-lib/#readme')
)
])
]
)
23 changes: 12 additions & 11 deletions cyclonedx/model/bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@
SchemaVersion1Dot6,
)
from ..serialization import LicenseRepositoryHelper, UrnUuidHelper
from . import ExternalReference, Property, ThisTool, Tool
from . import ExternalReference, Property, ThisTool
from .bom_ref import BomRef
from .component import Component
from .contact import OrganizationalContact, OrganizationalEntity
from .dependency import Dependable, Dependency
from .license import License, LicenseExpression, LicenseRepository
from .service import Service
from .tool import Tool, ToolsRepository, _ToolsRepositoryHelper
from .vulnerability import Vulnerability

if TYPE_CHECKING: # pragma: no cover
Expand All @@ -61,7 +62,7 @@ class BomMetaData:

def __init__(
self, *,
tools: Optional[Iterable[Tool]] = None,
tools: Optional[Union[Iterable[Tool], ToolsRepository]] = None,
authors: Optional[Iterable[OrganizationalContact]] = None,
component: Optional[Component] = None,
supplier: Optional[OrganizationalEntity] = None,
Expand Down Expand Up @@ -89,7 +90,7 @@ def __init__(
DeprecationWarning)

if not tools:
self.tools.add(ThisTool)
self.tools.tools.add(ThisTool)

@property
@serializable.type_mapping(serializable.helpers.XsdDateTime)
Expand Down Expand Up @@ -119,22 +120,22 @@ def timestamp(self, timestamp: datetime) -> None:
# ... # TODO since CDX1.5

@property
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'tool')
@serializable.type_mapping(_ToolsRepositoryHelper)
@serializable.xml_sequence(3)
def tools(self) -> 'SortedSet[Tool]':
def tools(self) -> ToolsRepository:
"""
Tools used to create this BOM.

Returns:
`Set` of `Tool` objects.
`ToolsRepository` objects.
"""
# TODO since CDX1.5 also supports `Component` and `Services`, not only `Tool`
return self._tools

@tools.setter
def tools(self, tools: Iterable[Tool]) -> None:
# TODO since CDX1.5 also supports `Component` and `Services`, not only `Tool`
self._tools = SortedSet(tools)
def tools(self, tools: Union[Iterable[Tool], ToolsRepository]) -> None:
self._tools = tools \
if isinstance(tools, ToolsRepository) \
else ToolsRepository(tools=tools)

@property
@serializable.xml_array(serializable.XmlArraySerializationType.NESTED, 'author')
Expand Down Expand Up @@ -292,7 +293,7 @@ def __eq__(self, other: object) -> bool:
def __hash__(self) -> int:
return hash((
tuple(self.authors), self.component, tuple(self.licenses), self.manufacture, tuple(self.properties),
self.supplier, self.timestamp, tuple(self.tools), self.manufacturer,
self.supplier, self.timestamp, self.tools, self.manufacturer,
))

def __repr__(self) -> str:
Expand Down
Loading