Skip to content

Commit 4186863

Browse files
authored
feat: add pipeline specialized in Maven package scan (#1953)
Signed-off-by: Chin Yeung Li <tli@nexb.com>
1 parent a7d3e70 commit 4186863

14 files changed

Lines changed: 3591 additions & 3 deletions

docs/built-in-pipelines.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,14 @@ Scan Single Package
265265
:members:
266266
:member-order: bysource
267267

268+
.. _pipeline_scan_maven_package:
269+
270+
Scan Maven Package
271+
-------------------
272+
.. autoclass:: scanpipe.pipelines.scan_maven_package.ScanMavenPackage()
273+
:members:
274+
:member-order: bysource
275+
268276
Fetch Scores (addon)
269277
--------------------
270278
.. warning::

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ publish_to_federatedcode = "scanpipe.pipelines.publish_to_federatedcode:PublishT
170170
resolve_dependencies = "scanpipe.pipelines.resolve_dependencies:ResolveDependencies"
171171
scan_codebase = "scanpipe.pipelines.scan_codebase:ScanCodebase"
172172
scan_for_virus = "scanpipe.pipelines.scan_for_virus:ScanForVirus"
173+
scan_maven_package = "scanpipe.pipelines.scan_maven_package:ScanMavenPackage"
173174
scan_single_package = "scanpipe.pipelines.scan_single_package:ScanSinglePackage"
174175

175176
[tool.setuptools.packages.find]
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# http://nexb.com and https://github.com/aboutcode-org/scancode.io
4+
# The ScanCode.io software is licensed under the Apache License version 2.0.
5+
# Data generated with ScanCode.io is provided as-is without warranties.
6+
# ScanCode is a trademark of nexB Inc.
7+
#
8+
# You may not use this software except in compliance with the License.
9+
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
10+
# Unless required by applicable law or agreed to in writing, software distributed
11+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
13+
# specific language governing permissions and limitations under the License.
14+
#
15+
# Data Generated with ScanCode.io is provided on an "AS IS" BASIS, WITHOUT WARRANTIES
16+
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
17+
# ScanCode.io should be considered or used as legal advice. Consult an Attorney
18+
# for any legal advice.
19+
#
20+
# ScanCode.io is a free software code scanning tool from nexB Inc. and others.
21+
# Visit https://github.com/aboutcode-org/scancode.io for support and download.
22+
23+
from scanpipe.pipelines.deploy_to_develop import DeployToDevelop
24+
from scanpipe.pipelines.scan_single_package import ScanSinglePackage
25+
from scanpipe.pipes import d2d_config
26+
from scanpipe.pipes.maven import check_input_and_return_purl
27+
from scanpipe.pipes.maven import fetch_and_scan_remote_pom
28+
from scanpipe.pipes.maven import fetch_inputs
29+
from scanpipe.pipes.maven import update_package_license_from_resource_if_missing
30+
31+
32+
class ScanMavenPackage(ScanSinglePackage, DeployToDevelop):
33+
"""
34+
Scan a single Maven package and perform a deployment to development
35+
relation scan.
36+
37+
This pipeline takes a Maven PURL as input, fetches the binary and
38+
source archives (if they exist), and then performs scans for package
39+
metadata, declared dependencies, licenses, license clarity scores, and
40+
copyrights. It also performs a deployment to development relation scan
41+
if both the source and binary archives are available.
42+
43+
The output is a summary of the scan results in JSON format.
44+
"""
45+
46+
download_inputs = False
47+
48+
@classmethod
49+
def steps(cls):
50+
return (
51+
cls.check_input_and_return_purl,
52+
cls.fetch_inputs,
53+
cls.d2d_check,
54+
cls.collect_input_info,
55+
cls.extract_input,
56+
cls.extract_archives,
57+
cls.maven_d2d_steps,
58+
cls.run_scan,
59+
cls.fetch_and_scan_remote_pom,
60+
cls.load_inventory_from_toolkit_scan,
61+
cls.update_package_license_from_resource_if_missing,
62+
cls.make_summary_from_scan_results,
63+
)
64+
65+
def check_input_and_return_purl(self):
66+
"""Validate the input is a PURL string and return the PURL object."""
67+
self.purl = check_input_and_return_purl(self.project)
68+
69+
def fetch_inputs(self):
70+
"""Fetch the binary and source of the given PURL."""
71+
from_file, to_file = fetch_inputs(self.purl)
72+
# The pipeline will call self.extract_inputs_to_codebase_directory()
73+
# from deploy_to_develop.py, which expects both self.from_files and
74+
# self.to_files to be of list type.
75+
self.from_files = [from_file] if from_file else []
76+
self.to_files = [to_file] if to_file else []
77+
78+
def d2d_check(self):
79+
"""
80+
Enable the deployment to development relation scan if both from
81+
and to files are present.
82+
"""
83+
self.d2d_enabled = False
84+
if self.from_files and self.to_files:
85+
self.d2d_enabled = True
86+
87+
def collect_input_info(self):
88+
"""Collect information about the input."""
89+
if not self.d2d_enabled:
90+
if self.from_files:
91+
self.input_path = self.from_files[0]
92+
else:
93+
self.input_path = self.to_files[0]
94+
self.collect_input_information()
95+
96+
def extract_input(self):
97+
"""Extract the input to the codebase directory."""
98+
if not self.d2d_enabled:
99+
self.extract_input_to_codebase_directory()
100+
else:
101+
self.extract_inputs_to_codebase_directory()
102+
103+
def maven_d2d_steps(self):
104+
"""Run deployment to development relation scan steps for Maven projects."""
105+
if self.d2d_enabled:
106+
self.collect_and_create_codebase_resources()
107+
self.fingerprint_codebase_directories()
108+
self.flag_empty_files()
109+
self.flag_whitespace_files()
110+
self.flag_ignored_resources()
111+
self.d2d_ecosystem_config()
112+
self.map_about_files()
113+
self.map_checksum()
114+
self.match_archives_to_purldb()
115+
self.d2d_java()
116+
self.d2d_scala()
117+
self.d2d_kotlin()
118+
self.d2d_javascript()
119+
self.d2d_process()
120+
121+
def d2d_ecosystem_config(self):
122+
options = ["Java", "Kotlin", "Scala", "JavaScript"]
123+
d2d_config.load_ecosystem_config(pipeline=self, options=options)
124+
125+
def d2d_java(self):
126+
self.find_java_packages()
127+
self.map_java_to_class()
128+
self.map_jar_to_java_source()
129+
130+
def d2d_scala(self):
131+
self.find_scala_packages()
132+
self.map_scala_to_class()
133+
self.map_jar_to_scala_source()
134+
135+
def d2d_kotlin(self):
136+
self.find_kotlin_packages()
137+
self.map_kotlin_to_class()
138+
self.map_jar_to_kotlin_source()
139+
140+
def d2d_javascript(self):
141+
self.map_javascript()
142+
self.map_javascript_symbols()
143+
self.map_javascript_strings()
144+
145+
def d2d_process(self):
146+
self.get_symbols_from_binaries()
147+
self.map_elf()
148+
self.match_directories_to_purldb()
149+
self.match_resources_to_purldb()
150+
self.map_javascript_post_purldb_match()
151+
self.map_javascript_path()
152+
self.map_javascript_colocation()
153+
self.map_path()
154+
self.flag_mapped_resources_archives_and_ignored_directories()
155+
self.perform_house_keeping_tasks()
156+
self.match_purldb_resources_post_process()
157+
self.remove_packages_without_resources()
158+
self.scan_ignored_to_files()
159+
self.scan_unmapped_to_files()
160+
self.scan_mapped_from_for_files()
161+
self.collect_and_create_license_detections()
162+
self.flag_deployed_from_resources_with_missing_license()
163+
self.create_local_files_packages()
164+
165+
def fetch_and_scan_remote_pom(self):
166+
"""Fetch and scan remote POM files."""
167+
scanning_errors = fetch_and_scan_remote_pom(
168+
self.purl, self.scan_output_location
169+
)
170+
for scanning_error in scanning_errors:
171+
for resource_reference, errors in scanning_error.items():
172+
self.project.add_error(
173+
description="\n".join(errors),
174+
model=self.pipeline_name,
175+
details={
176+
"resource_path": resource_reference.removeprefix("codebase/")
177+
},
178+
)
179+
180+
def update_package_license_from_resource_if_missing(self):
181+
"""
182+
Fill in missing package licenses using licenses detected in
183+
their resources.
184+
"""
185+
update_package_license_from_resource_if_missing(self.project)

scanpipe/pipes/d2d.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ def map_jar_to_jvm_source(project, jvm_lang: jvm.JvmLanguage, logger=None):
353353
"""Map .jar files to their related source directory."""
354354
project_files = project.codebaseresources.files()
355355
# Include the directories to map on the common source
356-
from_resources = project.codebaseresources.from_codebase()
356+
from_resources = project.codebaseresources.from_codebase().has_no_relation()
357357
to_resources = project_files.to_codebase()
358358
to_jars = to_resources.filter(extension=".jar")
359359

0 commit comments

Comments
 (0)