Skip to content
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

Workaround a loading issue with cyclonedx-python-lib #1185 #1186

Merged
merged 2 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ v34.5.0 (unreleased)
symbol, string and comments using Pygments.
https://github.com/nexB/scancode.io/pull/1179

- Workaround an issue with the cyclonedx-python-lib that does not allow to load
SBOMs that contains properties with no values.
https://github.com/nexB/scancode.io/issues/1185

v34.4.0 (2024-04-22)
--------------------

Expand Down
27 changes: 27 additions & 0 deletions scanpipe/pipes/cyclonedx.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def delete_tools(cyclonedx_document_json):

The new structure is not yet supported by the cyclonedx-python-lib, neither for
serialization (output) nor deserialization (input).
https://github.com/CycloneDX/cyclonedx-python-lib/issues/578

The tools are not used anyway in the context of loading the SBOM component data as
packages.
Expand All @@ -199,6 +200,30 @@ def delete_tools(cyclonedx_document_json):
return cyclonedx_document_json


def delete_empty_dict_property(cyclonedx_document_json):
"""
Remove dict entry where keys are defined but no values are set, such as
``{"name": ""}``.

Class like cyclonedx.model.contact.OrganizationalEntity raise a
NoPropertiesProvidedException while it is not enforced in the spec.

See https://github.com/CycloneDX/cyclonedx-python-lib/issues/600
"""
entries_to_delete = []

for component in cyclonedx_document_json["components"]:
for property_name, property_value in component.items():
if isinstance(property_value, dict) and not any(property_value.values()):
entries_to_delete.append((component, property_name))

# Now delete the keys outside the loop
for component, property_name in entries_to_delete:
del component[property_name]

return cyclonedx_document_json


def resolve_cyclonedx_packages(input_location):
"""Resolve the packages from the `input_location` CycloneDX document file."""
input_path = Path(input_location)
Expand All @@ -215,7 +240,9 @@ def resolve_cyclonedx_packages(input_location):
f'CycloneDX document "{input_path.name}" is not valid:\n{errors}'
)
raise ValueError(error_msg)

cyclonedx_document = delete_tools(cyclonedx_document)
cyclonedx_document = delete_empty_dict_property(cyclonedx_document)
cyclonedx_bom = Bom.from_json(data=cyclonedx_document)

else:
Expand Down
18 changes: 13 additions & 5 deletions scanpipe/pipes/resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@
"""


def resolve_manifest_resources(resource, package_registry):
"""Get package data from resource."""
packages = get_packages_from_manifest(resource.location, package_registry) or []

for package_data in packages:
package_data["codebase_resources"] = [resource]

return packages


def get_packages(project, package_registry, manifest_resources, model=None):
"""
Get package data from package manifests/lockfiles/SBOMs or
Expand All @@ -51,15 +61,13 @@ def get_packages(project, package_registry, manifest_resources, model=None):

if not manifest_resources.exists():
project.add_warning(
description="No resources found with package data",
description="No resources containing package data found in codebase.",
model=model,
)
return
return []

for resource in manifest_resources:
if packages := get_packages_from_manifest(resource.location, package_registry):
for package_data in packages:
package_data["codebase_resources"] = [resource]
if packages := resolve_manifest_resources(resource, package_registry):
resolved_packages.extend(packages)
else:
project.add_error(
Expand Down
2 changes: 1 addition & 1 deletion scanpipe/tests/test_pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ def test_scanpipe_resolve_dependencies_pipeline_integration(self):
self.assertEqual(1, project1.projectmessages.count())
message = project1.projectmessages.get()
self.assertEqual("get_packages_from_manifest", message.model)
expected = "No resources found with package data"
expected = "No resources containing package data found in codebase."
self.assertIn(expected, message.description)

def test_scanpipe_resolve_dependencies_pipeline_integration_empty_manifest(self):
Expand Down