Skip to content

Commit fb96cc6

Browse files
committed
Merge resolved
2 parents 58fa002 + df61c28 commit fb96cc6

27 files changed

Lines changed: 528 additions & 81 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/pipelines/deploy_to_develop.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ def steps(cls):
8383
cls.find_grammar_packages,
8484
cls.map_grammar_to_class,
8585
cls.map_jar_to_grammar_source,
86+
cls.find_groovy_packages,
87+
cls.map_groovy_to_class,
88+
cls.map_jar_to_groovy_source,
89+
cls.find_aspectj_packages,
90+
cls.map_aspectj_to_class,
91+
cls.map_jar_to_aspectj_source,
92+
cls.find_clojure_packages,
93+
cls.map_clojure_to_class,
94+
cls.map_jar_to_clojure_source,
8695
cls.find_xtend_packages,
8796
cls.map_xtend_to_class,
8897
cls.map_javascript,
@@ -230,7 +239,7 @@ def find_scala_packages(self):
230239

231240
@optional_step("Scala")
232241
def map_scala_to_class(self):
233-
"""Map a .class compiled file to its .java source."""
242+
"""Map a .class compiled file to its .scala source."""
234243
self.run_d2d_step(d2d.map_jvm_to_class, jvm_lang=jvm.ScalaLanguage)
235244

236245
@optional_step("Scala")
@@ -240,14 +249,14 @@ def map_jar_to_scala_source(self):
240249

241250
@optional_step("Kotlin")
242251
def find_kotlin_packages(self):
243-
"""Find the java package of the .java source files."""
252+
"""Find the java package of the kotlin source files."""
244253
d2d.find_jvm_packages(
245254
project=self.project, jvm_lang=jvm.KotlinLanguage, logger=self.log
246255
)
247256

248257
@optional_step("Kotlin")
249258
def map_kotlin_to_class(self):
250-
"""Map a .class compiled file to its .java source."""
259+
"""Map a .class compiled file to its kotlin source."""
251260
self.run_d2d_step(d2d.map_jvm_to_class, jvm_lang=jvm.KotlinLanguage)
252261

253262
@optional_step("Kotlin")
@@ -272,6 +281,69 @@ def map_jar_to_grammar_source(self):
272281
"""Map .jar files to their related source directory."""
273282
self.run_d2d_step(d2d.map_jar_to_jvm_source, jvm_lang=jvm.GrammarLanguage)
274283

284+
@optional_step("Groovy")
285+
def find_groovy_packages(self):
286+
"""Find the package of the .groovy source files."""
287+
d2d.find_jvm_packages(
288+
project=self.project, jvm_lang=jvm.GroovyLanguage, logger=self.log
289+
)
290+
291+
@optional_step("Groovy")
292+
def map_groovy_to_class(self):
293+
"""Map a .class compiled file to its .groovy source."""
294+
d2d.map_jvm_to_class(
295+
project=self.project, jvm_lang=jvm.GroovyLanguage, logger=self.log
296+
)
297+
298+
@optional_step("Groovy")
299+
def map_jar_to_groovy_source(self):
300+
"""Map .jar files to their related source directory."""
301+
d2d.map_jar_to_jvm_source(
302+
project=self.project, jvm_lang=jvm.GroovyLanguage, logger=self.log
303+
)
304+
305+
@optional_step("AspectJ")
306+
def find_aspectj_packages(self):
307+
"""Find the package of the .aj source files."""
308+
d2d.find_jvm_packages(
309+
project=self.project, jvm_lang=jvm.AspectJLanguage, logger=self.log
310+
)
311+
312+
@optional_step("AspectJ")
313+
def map_aspectj_to_class(self):
314+
"""Map a .class compiled file to its .aj source."""
315+
d2d.map_jvm_to_class(
316+
project=self.project, jvm_lang=jvm.AspectJLanguage, logger=self.log
317+
)
318+
319+
@optional_step("AspectJ")
320+
def map_jar_to_aspectj_source(self):
321+
"""Map .jar files to their related source directory."""
322+
d2d.map_jar_to_jvm_source(
323+
project=self.project, jvm_lang=jvm.AspectJLanguage, logger=self.log
324+
)
325+
326+
@optional_step("Clojure")
327+
def find_clojure_packages(self):
328+
"""Find the package of the .clj source files."""
329+
d2d.find_jvm_packages(
330+
project=self.project, jvm_lang=jvm.ClojureLanguage, logger=self.log
331+
)
332+
333+
@optional_step("Clojure")
334+
def map_clojure_to_class(self):
335+
"""Map a .class compiled file to its .clj source."""
336+
d2d.map_jvm_to_class(
337+
project=self.project, jvm_lang=jvm.ClojureLanguage, logger=self.log
338+
)
339+
340+
@optional_step("Clojure")
341+
def map_jar_to_clojure_source(self):
342+
"""Map .jar files to their related source directory."""
343+
d2d.map_jar_to_jvm_source(
344+
project=self.project, jvm_lang=jvm.ClojureLanguage, logger=self.log
345+
)
346+
275347
@optional_step("Xtend")
276348
def find_xtend_packages(self):
277349
"""Find the java package of the xtend source files."""

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: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,21 @@ def _map_jvm_to_class_resource(
174174
from/ fully qualified binary files.
175175
"""
176176
for extension in jvm_lang.source_extensions:
177-
normalized_path = jvm_lang.get_normalized_path(
177+
# Perform basic conversion from .class to source file path
178+
source_path = jvm_lang.get_source_path(
178179
path=to_resource.path, extension=extension
179180
)
180-
match = pathmap.find_paths(path=normalized_path, index=from_classes_index)
181+
# Perform basic mapping without normalization for scenarios listed in
182+
# https://github.com/aboutcode-org/scancode.io/issues/1873
183+
match = pathmap.find_paths(path=source_path, index=from_classes_index)
184+
181185
if not match:
182-
return
186+
normalized_path = jvm_lang.get_normalized_path(
187+
path=to_resource.path, extension=extension
188+
)
189+
match = pathmap.find_paths(path=normalized_path, index=from_classes_index)
190+
if not match:
191+
return
183192

184193
for resource_id in match.resource_ids:
185194
from_resource = from_resources.get(id=resource_id)
@@ -240,8 +249,8 @@ def map_jvm_to_class(
240249

241250
if logger:
242251
logger(
243-
f"Mapping {to_resource_count:,d} .class resources to "
244-
f"{from_resource_count:,d} {jvm_lang.source_extensions}"
252+
f"Mapping {to_resource_count:,d} .class (or other deployed file) "
253+
f"resources to {from_resource_count:,d} {jvm_lang.source_extensions}"
245254
)
246255

247256
# build an index using from-side fully qualified class file names

scanpipe/pipes/d2d_config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ class EcosystemConfig:
112112
"*kotlin-project-structure-metadata.json",
113113
],
114114
),
115+
"Groovy": EcosystemConfig(
116+
ecosystem_option="Groovy",
117+
matchable_package_extensions=[".jar", ".war"],
118+
matchable_resource_extensions=[".class"],
119+
deployed_resource_path_exclusions=[
120+
"*META-INF/*",
121+
],
122+
),
115123
"JavaScript": EcosystemConfig(
116124
ecosystem_option="JavaScript",
117125
matchable_resource_extensions=[

scanpipe/pipes/jvm.py

Lines changed: 44 additions & 2 deletions
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
"""
@@ -177,12 +192,39 @@ class JavaLanguage(JvmLanguage):
177192
class ScalaLanguage(JvmLanguage):
178193
name = "scala"
179194
source_extensions = (".scala",)
180-
binary_extensions = (".class",)
195+
binary_extensions = (".class", ".tasty")
181196
source_package_attribute_name = "scala_package"
182197
package_regex = re.compile(r"^\s*package\s+([\w\.]+)\s*;?")
183198
binary_map_type = "scala_to_class"
184199

185200

201+
class GroovyLanguage(JvmLanguage):
202+
name = "groovy"
203+
source_extensions = (".groovy",)
204+
binary_extensions = (".class",)
205+
source_package_attribute_name = "groovy_package"
206+
package_regex = re.compile(r"^\s*package\s+([\w\.]+)\s*;?")
207+
binary_map_type = "groovy_to_class"
208+
209+
210+
class AspectJLanguage(JvmLanguage):
211+
name = "aspectj"
212+
source_extensions = (".aj",)
213+
binary_extensions = (".class",)
214+
source_package_attribute_name = "aspectj_package"
215+
package_regex = re.compile(r"^\s*package\s+([\w\.]+)\s*;?")
216+
binary_map_type = "aspectj_to_class"
217+
218+
219+
class ClojureLanguage(JvmLanguage):
220+
name = "clojure"
221+
source_extensions = (".clj",)
222+
binary_extensions = (".class",)
223+
source_package_attribute_name = "clojure_package"
224+
package_regex = re.compile(r"^\s*package\s+([\w\.]+)\s*;?")
225+
binary_map_type = "clojure_to_class"
226+
227+
186228
class KotlinLanguage(JvmLanguage):
187229
name = "kotlin"
188230
source_extensions = (".kt", ".kts")

0 commit comments

Comments
 (0)