Skip to content

Commit 567ebe9

Browse files
committed
Resolve merge conflict with upstream/main
2 parents 4b31880 + 25cad21 commit 567ebe9

24 files changed

Lines changed: 306 additions & 87 deletions

README.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,8 @@ for any legal advice.
5757

5858

5959

60-
61-
.. |ci-tests| image:: https://github.com/aboutcode-org/scancode.io/actions/workflows/ci.yml/badge.svg?branch=main
62-
:target: https://github.com/aboutcode-org/scancode.io/actions/workflows/ci.yml
60+
.. |ci-tests| image:: https://github.com/aboutcode-org/scancode.io/actions/workflows/run-unit-tests.yml/badge.svg?branch=main
61+
:target: https://github.com/aboutcode-org/scancode.io/actions/workflows/run-unit-tests.yml
6362
:alt: CI Tests Status
6463

6564
.. |docs-rtd| image:: https://readthedocs.org/projects/scancodeio/badge/?version=latest

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ android_analysis = [
123123
"android_inspector==0.0.1"
124124
]
125125
mining = [
126-
"minecode_pipelines==0.0.1b8"
126+
"minecode_pipelines==0.1.1"
127127
]
128128

129129
[project.urls]

scancodeio/static/main.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,12 @@ progress.file-upload::before {
391391
#message-list th#column-severity {
392392
min-width: 110px;
393393
}
394+
th#column-vulnerability_id {
395+
min-width: 220px;
396+
}
397+
th#column-summary {
398+
width: 40%;
399+
}
394400
.menu.is-info .is-active {
395401
background-color: #3e8ed0;
396402
}

scanpipe/management/commands/check-compliance.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,17 @@ def check_compliance(self, fail_level):
104104
return total_issues > 0
105105

106106
def check_vulnerabilities(self):
107-
packages = self.project.discoveredpackages.vulnerable_ordered()
108-
dependencies = self.project.discovereddependencies.vulnerable_ordered()
109-
110-
vulnerable_records = list(packages) + list(dependencies)
111-
count = len(vulnerable_records)
107+
all_vulnerabilities = self.project.vulnerabilities
108+
vulnerabilities_count = len(all_vulnerabilities)
112109

113110
if self.verbosity > 0:
114-
if count:
115-
self.stderr.write(f"{count} vulnerable records found:")
116-
for entry in vulnerable_records:
117-
self.stderr.write(str(entry))
118-
vulnerability_ids = [
119-
vulnerability.get("vulnerability_id")
120-
for vulnerability in entry.affected_by_vulnerabilities
121-
]
122-
self.stderr.write(" > " + ", ".join(vulnerability_ids))
111+
if vulnerabilities_count:
112+
self.stderr.write(f"{vulnerabilities_count} vulnerabilities found:")
113+
for vulnerability_id, vulnerability_data in all_vulnerabilities.items():
114+
self.stderr.write(str(vulnerability_id))
115+
for affected_obj in vulnerability_data.get("affects", []):
116+
self.stderr.write(f" > {affected_obj}")
123117
else:
124118
self.stdout.write("No vulnerabilities found")
125119

126-
return count > 0
120+
return vulnerabilities_count > 0

scanpipe/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,11 @@ def vulnerable_dependency_count(self):
14951495
"""Return the number of vulnerable dependencies related to this project."""
14961496
return self.vulnerable_dependencies.count()
14971497

1498+
@cached_property
1499+
def vulnerability_count(self):
1500+
"""Return the number of vulnerabilities related to this project."""
1501+
return self.vulnerable_package_count + self.vulnerable_dependency_count
1502+
14981503
@cached_property
14991504
def dependency_count(self):
15001505
"""Return the number of dependencies related to this project."""

scanpipe/pipes/cyclonedx.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from cyclonedx.model import license as cdx_license_model
3131
from cyclonedx.model.bom import Bom
3232
from cyclonedx.schema import SchemaVersion
33+
from cyclonedx.schema.schema import BaseSchemaVersion
3334
from cyclonedx.validation import ValidationError
3435
from cyclonedx.validation.json import JsonStrictValidator
3536
from defusedxml import ElementTree as SafeElementTree
@@ -184,10 +185,12 @@ def cyclonedx_component_to_package_data(
184185
affected_by_vulnerabilities = []
185186
if affected_by := vulnerabilities.get(bom_ref):
186187
for cdx_vulnerability in affected_by:
188+
cdx_vulnerability_json = cdx_vulnerability.as_json(view_=BaseSchemaVersion)
187189
affected_by_vulnerabilities.append(
188190
{
189191
"vulnerability_id": str(cdx_vulnerability.id),
190192
"summary": cdx_vulnerability.description,
193+
"cdx_vulnerability_data": json.loads(cdx_vulnerability_json),
191194
}
192195
)
193196

scanpipe/pipes/d2d.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -165,31 +165,41 @@ def _map_jvm_to_class_resource(
165165
to_resource, from_resources, from_classes_index, jvm_lang: jvm.JvmLanguage
166166
):
167167
for extension in jvm_lang.source_extensions:
168-
normalized_path = jvm_lang.get_normalized_path(
168+
# Perform basic conversion from .class to source file path
169+
source_path = jvm_lang.get_source_path(
169170
path=to_resource.path, extension=extension
170171
)
172+
# Perform basic mapping without normalization for scenarios listed in
173+
# https://github.com/aboutcode-org/scancode.io/issues/1873
174+
match = pathmap.find_paths(path=source_path, index=from_classes_index)
171175

172-
match = pathmap.find_paths(path=normalized_path, index=from_classes_index)
173-
174-
if not match and jvm_lang.name == "scala":
175-
package_path = str(Path(to_resource.path).parent)
176-
potential_sources = from_resources.filter(
177-
path__startswith=package_path.replace("to/", "from/"),
178-
extension__in=jvm_lang.source_extensions,
176+
if not match:
177+
normalized_path = jvm_lang.get_normalized_path(
178+
path=to_resource.path, extension=extension
179179
)
180-
for from_resource in potential_sources:
181-
from_source_root_parts = from_resource.path.strip("/").split("/")
182-
from_source_root = "/".join(from_source_root_parts[:-1])
183-
pipes.make_relation(
184-
from_resource=from_resource,
185-
to_resource=to_resource,
186-
map_type=jvm_lang.binary_map_type,
187-
extra_data={"from_source_root": f"{from_source_root}/"},
180+
match = pathmap.find_paths(path=normalized_path, index=from_classes_index)
181+
182+
# Scala fallback for case classes and inner classes
183+
# https://github.com/aboutcode-org/scancode.io/issues/1875
184+
if not match and jvm_lang.name == "scala":
185+
package_path = str(Path(to_resource.path).parent)
186+
potential_sources = from_resources.filter(
187+
path__startswith=package_path.replace("to/", "from/"),
188+
extension__in=jvm_lang.source_extensions,
188189
)
189-
continue
190+
for from_resource in potential_sources:
191+
from_source_root_parts = from_resource.path.strip("/").split("/")
192+
from_source_root = "/".join(from_source_root_parts[:-1])
193+
pipes.make_relation(
194+
from_resource=from_resource,
195+
to_resource=to_resource,
196+
map_type=jvm_lang.binary_map_type,
197+
extra_data={"from_source_root": f"{from_source_root}/"},
198+
)
199+
continue
190200

191-
if not match:
192-
continue
201+
if not match:
202+
continue
193203

194204
for resource_id in match.resource_ids:
195205
from_resource = from_resources.get(id=resource_id)

scanpipe/pipes/jvm.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,27 @@ def get_normalized_path(cls, path, extension):
140140
# https://github.com/aboutcode-org/scancode.io/issues/1994
141141
if class_name.endswith("_$logger.class"):
142142
class_name, _, _ = class_name.partition("_$logger.class")
143-
elif "$" in class_name: # inner class
143+
elif "$" in class_name and not class_name.startswith("$"): # inner class
144144
class_name, _, _ = class_name.partition("$")
145145
else:
146146
class_name, _, _ = class_name.partition(".") # plain .class
147147
return str(path.parent / f"{class_name}{extension}")
148148

149+
@classmethod
150+
def get_source_path(cls, path, extension):
151+
"""
152+
Return a JVM file path for ``path`` .class file path string.
153+
No normalization is performed.
154+
"""
155+
if not path.endswith(cls.binary_extensions):
156+
raise ValueError(
157+
f"Only path ending with {cls.binary_extensions} are supported."
158+
)
159+
path = Path(path.strip("/"))
160+
class_name = path.name
161+
class_name, _, _ = class_name.partition(".") # plain .class
162+
return str(path.parent / f"{class_name}{extension}")
163+
149164

150165
def find_expression(lines, regex):
151166
"""

scanpipe/pipes/ort.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,28 @@ def get_ort_project_type(project):
130130
return "docker"
131131

132132

133+
def sanitize_id_part(value):
134+
"""
135+
Sanitize an identifier part by replacing colons with underscores.
136+
ORT uses colons as separators in the identifier string representation.
137+
"""
138+
if value:
139+
return value.replace(":", "_")
140+
return value
141+
142+
133143
def to_ort_package_list_yml(project):
134144
"""Convert a project object into a YAML string in the ORT package list format."""
135145
project_type = get_ort_project_type(project)
136146

137147
dependencies = []
138148
for package in project.discoveredpackages.all():
149+
type_ = sanitize_id_part(project_type or package.type)
150+
name = sanitize_id_part(package.name)
151+
version = sanitize_id_part(package.version)
152+
139153
dependency = Dependency(
140-
id=f"{project_type or package.type}::{package.name}:{package.version}",
154+
id=f"{type_}::{name}:{version}",
141155
purl=package.purl,
142156
sourceArtifact=SourceArtifact(url=package.download_url),
143157
declaredLicenses=[package.get_declared_license_expression_spdx()],

scanpipe/pipes/pathmap.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,12 @@ def get_reversed_path_segments(path):
164164
Return reversed segments list given a POSIX ``path`` string. We reverse
165165
based on path segments separated by a "/".
166166
167-
Note that the inputh ``path`` is assumed to be normalized, not relative and
167+
Note that the input ``path`` is assumed to be normalized, not relative and
168168
not containing double slash.
169169
170-
For example::
171-
>>> assert get_reversed_path_segments("a/b/c.js") == ["c.js", "b", "a"]
170+
For example:
171+
>>> get_reversed_path_segments("a/b/c.js")
172+
['c.js', 'b', 'a']
172173
"""
173174
# [::-1] does the list reversing
174175
reversed_segments = path.strip("/").split("/")[::-1]
@@ -177,13 +178,14 @@ def get_reversed_path_segments(path):
177178

178179
def convert_segments_to_path(segments):
179180
"""
180-
Return a path string is suitable for indexing or matching given a
181+
Return a path string suitable for indexing or matching given a
181182
``segments`` sequence of path segment strings.
182183
The resulting reversed path is prefixed and suffixed by a "/" irrespective
183184
of whether the original path is a file or directory and had such prefix or
184185
suffix.
185186
186-
For example::
187-
>>> assert convert_segments_to_path(["c.js", "b", "a"]) == "/c.js/b/a/"
187+
For example:
188+
>>> convert_segments_to_path(["c.js", "b", "a"])
189+
'/c.js/b/a/'
188190
"""
189191
return "/" + "/".join(segments) + "/"

0 commit comments

Comments
 (0)