|
| 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) |
0 commit comments