scanoss 1.12.2__py3-none-any.whl → 1.43.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. protoc_gen_swagger/__init__.py +13 -13
  2. protoc_gen_swagger/options/__init__.py +13 -13
  3. protoc_gen_swagger/options/annotations_pb2.py +18 -12
  4. protoc_gen_swagger/options/annotations_pb2.pyi +48 -0
  5. protoc_gen_swagger/options/annotations_pb2_grpc.py +20 -0
  6. protoc_gen_swagger/options/openapiv2_pb2.py +110 -99
  7. protoc_gen_swagger/options/openapiv2_pb2.pyi +1317 -0
  8. protoc_gen_swagger/options/openapiv2_pb2_grpc.py +20 -0
  9. scanoss/__init__.py +18 -18
  10. scanoss/api/__init__.py +17 -17
  11. scanoss/api/common/__init__.py +17 -17
  12. scanoss/api/common/v2/__init__.py +17 -17
  13. scanoss/api/common/v2/scanoss_common_pb2.py +49 -20
  14. scanoss/api/common/v2/scanoss_common_pb2_grpc.py +25 -0
  15. scanoss/api/components/__init__.py +17 -17
  16. scanoss/api/components/v2/__init__.py +17 -17
  17. scanoss/api/components/v2/scanoss_components_pb2.py +68 -43
  18. scanoss/api/components/v2/scanoss_components_pb2_grpc.py +83 -22
  19. scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +136 -21
  20. scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +766 -13
  21. scanoss/api/dependencies/__init__.py +17 -17
  22. scanoss/api/dependencies/v2/__init__.py +17 -17
  23. scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +56 -29
  24. scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +94 -8
  25. scanoss/api/geoprovenance/__init__.py +23 -0
  26. scanoss/api/geoprovenance/v2/__init__.py +23 -0
  27. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +92 -0
  28. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +381 -0
  29. scanoss/api/licenses/__init__.py +23 -0
  30. scanoss/api/licenses/v2/__init__.py +23 -0
  31. scanoss/api/licenses/v2/scanoss_licenses_pb2.py +84 -0
  32. scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +302 -0
  33. scanoss/api/scanning/__init__.py +17 -17
  34. scanoss/api/scanning/v2/__init__.py +17 -17
  35. scanoss/api/scanning/v2/scanoss_scanning_pb2.py +42 -13
  36. scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +86 -7
  37. scanoss/api/semgrep/__init__.py +17 -17
  38. scanoss/api/semgrep/v2/__init__.py +17 -17
  39. scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +50 -23
  40. scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +151 -16
  41. scanoss/api/vulnerabilities/__init__.py +17 -17
  42. scanoss/api/vulnerabilities/v2/__init__.py +17 -17
  43. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +78 -31
  44. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +282 -18
  45. scanoss/cli.py +2359 -370
  46. scanoss/components.py +187 -94
  47. scanoss/constants.py +22 -0
  48. scanoss/cryptography.py +308 -0
  49. scanoss/csvoutput.py +91 -58
  50. scanoss/cyclonedx.py +221 -63
  51. scanoss/data/build_date.txt +1 -1
  52. scanoss/data/osadl-copyleft.json +133 -0
  53. scanoss/data/scanoss-settings-schema.json +254 -0
  54. scanoss/delta.py +197 -0
  55. scanoss/export/__init__.py +23 -0
  56. scanoss/export/dependency_track.py +227 -0
  57. scanoss/file_filters.py +582 -0
  58. scanoss/filecount.py +75 -69
  59. scanoss/gitlabqualityreport.py +214 -0
  60. scanoss/header_filter.py +563 -0
  61. scanoss/inspection/__init__.py +23 -0
  62. scanoss/inspection/policy_check/__init__.py +0 -0
  63. scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
  64. scanoss/inspection/policy_check/dependency_track/project_violation.py +479 -0
  65. scanoss/inspection/policy_check/policy_check.py +222 -0
  66. scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
  67. scanoss/inspection/policy_check/scanoss/copyleft.py +243 -0
  68. scanoss/inspection/policy_check/scanoss/undeclared_component.py +309 -0
  69. scanoss/inspection/summary/__init__.py +0 -0
  70. scanoss/inspection/summary/component_summary.py +170 -0
  71. scanoss/inspection/summary/license_summary.py +191 -0
  72. scanoss/inspection/summary/match_summary.py +341 -0
  73. scanoss/inspection/utils/file_utils.py +44 -0
  74. scanoss/inspection/utils/license_utils.py +123 -0
  75. scanoss/inspection/utils/markdown_utils.py +63 -0
  76. scanoss/inspection/utils/scan_result_processor.py +417 -0
  77. scanoss/osadl.py +125 -0
  78. scanoss/results.py +275 -0
  79. scanoss/scancodedeps.py +87 -38
  80. scanoss/scanner.py +431 -539
  81. scanoss/scanners/__init__.py +23 -0
  82. scanoss/scanners/container_scanner.py +476 -0
  83. scanoss/scanners/folder_hasher.py +358 -0
  84. scanoss/scanners/scanner_config.py +73 -0
  85. scanoss/scanners/scanner_hfh.py +252 -0
  86. scanoss/scanoss_settings.py +337 -0
  87. scanoss/scanossapi.py +140 -101
  88. scanoss/scanossbase.py +59 -22
  89. scanoss/scanossgrpc.py +799 -251
  90. scanoss/scanpostprocessor.py +294 -0
  91. scanoss/scantype.py +22 -21
  92. scanoss/services/dependency_track_service.py +132 -0
  93. scanoss/spdxlite.py +532 -174
  94. scanoss/threadeddependencies.py +148 -47
  95. scanoss/threadedscanning.py +53 -37
  96. scanoss/utils/__init__.py +23 -0
  97. scanoss/utils/abstract_presenter.py +103 -0
  98. scanoss/utils/crc64.py +96 -0
  99. scanoss/utils/file.py +84 -0
  100. scanoss/utils/scanoss_scan_results_utils.py +41 -0
  101. scanoss/utils/simhash.py +198 -0
  102. scanoss/winnowing.py +241 -63
  103. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/METADATA +18 -9
  104. scanoss-1.43.1.dist-info/RECORD +110 -0
  105. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/WHEEL +1 -1
  106. scanoss-1.12.2.dist-info/RECORD +0 -58
  107. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/entry_points.txt +0 -0
  108. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info/licenses}/LICENSE +0 -0
  109. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,222 @@
1
+ """
2
+ SPDX-License-Identifier: MIT
3
+
4
+ Copyright (c) 2025, SCANOSS
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ """
24
+
25
+ from abc import ABC, abstractmethod
26
+ from enum import Enum
27
+ from typing import Callable, Dict, Generic, List, NamedTuple, TypeVar
28
+
29
+ from ...scanossbase import ScanossBase
30
+ from ..utils.license_utils import LicenseUtil
31
+
32
+
33
+ class PolicyStatus(Enum):
34
+ """
35
+ Enumeration representing the status of a policy check.
36
+
37
+ Attributes:
38
+ POLICY_SUCCESS (int): Indicates that the policy check passed successfully (value: 0).
39
+ POLICY_FAIL (int): Indicates that the policy check failed (value: 2).
40
+ ERROR (int): Indicates that an error occurred during the policy check (value: 1).
41
+ """
42
+ POLICY_SUCCESS = 0
43
+ POLICY_FAIL = 2
44
+ ERROR = 1
45
+ #
46
+ # End of PolicyStatus Class
47
+ #
48
+
49
+ class PolicyOutput(NamedTuple):
50
+ details: str
51
+ summary: str
52
+
53
+ T = TypeVar('T')
54
+
55
+ class PolicyCheck(ScanossBase, Generic[T], ABC):
56
+ """
57
+ A base class for implementing various software policy checks.
58
+
59
+ This class provides a framework for policy checking, including methods for
60
+ processing components, generating output in different formats.
61
+
62
+ Attributes:
63
+ VALID_FORMATS (set): A set of valid output formats ('md', 'json').
64
+
65
+ Inherits from:
66
+ InspectBase: A base class providing common functionality for SCANOSS-related operations.
67
+ """
68
+ VALID_FORMATS = {'md', 'json', 'jira_md'}
69
+ def __init__( # noqa: PLR0913
70
+ self,
71
+ debug: bool = False,
72
+ trace: bool = False,
73
+ quiet: bool = False,
74
+ format_type: str = None,
75
+ status: str = None,
76
+ name: str = None,
77
+ output: str = None,
78
+ ):
79
+ super().__init__(debug, trace, quiet)
80
+ self.license_util = LicenseUtil()
81
+ self.name = name
82
+ self.format_type = format_type
83
+ self.status = status
84
+ self.output = output
85
+
86
+ @abstractmethod
87
+ def run(self)-> tuple[int,PolicyOutput]:
88
+ """
89
+ Execute the policy check process.
90
+
91
+ This abstract method should be implemented by subclasses to perform specific
92
+ policy checks. The general structure of this method typically includes:
93
+ 1. Retrieving components
94
+ 2. Filtering components based on specific criteria
95
+ 3. Formatting the results
96
+ 4. Saving the output to files if required
97
+
98
+ :return: A named tuple containing two elements:
99
+ - First element: PolicyStatus enum value (SUCCESS, FAIL, or ERROR)
100
+ - Second element: PolicyOutput A tuple containing the policy results.
101
+ """
102
+ pass
103
+
104
+ @abstractmethod
105
+ def _json(self, data: list[T]) -> PolicyOutput:
106
+ """
107
+ Format the policy checks results as JSON.
108
+ This method should be implemented by subclasses to create a Markdown representation
109
+ of the policy check results.
110
+
111
+ :param data: List of data to be formatted.
112
+ :return: A dictionary containing two keys:
113
+ - 'results': A JSON-formatted string with the full list of components
114
+ - 'summary': A string summarizing the number of components found
115
+ """
116
+ pass
117
+
118
+ @abstractmethod
119
+ def _markdown(self, data: list[T]) -> PolicyOutput:
120
+ """
121
+ Generate Markdown output for the policy check results.
122
+
123
+ This method should be implemented by subclasses to create a Markdown representation
124
+ of the policy check results.
125
+
126
+ :param data: List of data to be included in the output.
127
+ :return: A dictionary representing the Markdown output.
128
+ """
129
+ pass
130
+
131
+ @abstractmethod
132
+ def _jira_markdown(self, data: list[T]) -> PolicyOutput:
133
+ """
134
+ Generate Markdown output for the policy check results.
135
+
136
+ This method should be implemented by subclasses to create a Markdown representation
137
+ of the policy check results.
138
+
139
+ :param data: List of data to be included in the output.
140
+ :return: A dictionary representing the Markdown output.
141
+ """
142
+ pass
143
+
144
+ def _get_formatter(self) -> Callable[[List[dict]], PolicyOutput]:
145
+ """
146
+ Get the appropriate formatter function based on the specified format.
147
+
148
+ :return: Formatter function (either _json or _markdown)
149
+ """
150
+ valid_format = self._is_valid_format()
151
+ if not valid_format:
152
+ raise ValueError('Invalid format specified')
153
+ # a map of which format function to return
154
+ function_map = {
155
+ 'json': self._json,
156
+ 'md': self._markdown,
157
+ 'jira_md': self._jira_markdown,
158
+ }
159
+ return function_map[self.format_type]
160
+
161
+ def _debug(self):
162
+ """
163
+ Print debug information about the policy check.
164
+
165
+ This method prints various attributes of the PolicyCheck instance for debugging purposes.
166
+ """
167
+ if self.debug:
168
+ self.print_stderr(f'Policy: {self.name}')
169
+ self.print_stderr(f'Format: {self.format_type}')
170
+ self.print_stderr(f'Status: {self.status}')
171
+ self.print_stderr(f'Output: {self.output}')
172
+
173
+ def _is_valid_format(self) -> bool:
174
+ """
175
+ Validate if the format specified is supported.
176
+
177
+ This method checks if the format stored in format is one of the
178
+ valid formats defined in self.VALID_FORMATS.
179
+
180
+ :return: bool: True if the format is valid, False otherwise.
181
+ """
182
+ if self.format_type not in self.VALID_FORMATS:
183
+ valid_formats_str = ', '.join(self.VALID_FORMATS)
184
+ self.print_stderr(f'ERROR: Invalid format "{self.format_type}". Valid formats are: {valid_formats_str}')
185
+ return False
186
+ return True
187
+
188
+ def _generate_formatter_report(self, components: list[Dict]):
189
+ """
190
+ Generates a formatted report for a given component based on the defined formatter.
191
+
192
+ Parameters:
193
+ components (List[dict]): A list of dictionaries representing the components to be
194
+ processed and formatted. Each dictionary contains detailed information that adheres
195
+ to the format requirements for the specified formatter.
196
+
197
+ Returns:
198
+ Tuple[int, dict]: A tuple where the first element represents the policy status code
199
+ and the second element is a dictionary containing formatted results information,
200
+ typically with keys 'details' and 'summary'.
201
+
202
+ Raises:
203
+ KeyError: When a required key is missing from the provided component, causing the
204
+ formatter to fail.
205
+ ValueError: If an invalid component is passed and renders unable to process.
206
+ """
207
+ # Get a formatter for the output results
208
+ formatter = self._get_formatter()
209
+ if formatter is None:
210
+ return PolicyStatus.ERROR.value, {}
211
+ # Format the results
212
+ policy_output = formatter(components)
213
+ ## Save outputs if required
214
+ self.print_to_file_or_stdout(policy_output.details, self.output)
215
+ self.print_to_file_or_stderr(policy_output.summary, self.status)
216
+ # Check to see if we have policy violations
217
+ if len(components) > 0:
218
+ return PolicyStatus.POLICY_FAIL.value, policy_output
219
+ return PolicyStatus.POLICY_SUCCESS.value, policy_output
220
+ #
221
+ # End of PolicyCheck Class
222
+ #
File without changes
@@ -0,0 +1,243 @@
1
+ """
2
+ SPDX-License-Identifier: MIT
3
+
4
+ Copyright (c) 2025, SCANOSS
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ """
24
+
25
+ import json
26
+ from dataclasses import dataclass
27
+ from typing import Dict, List
28
+
29
+ from scanoss.constants import DEFAULT_COPYLEFT_LICENSE_SOURCES
30
+
31
+ from ...policy_check.policy_check import PolicyCheck, PolicyOutput, PolicyStatus
32
+ from ...utils.markdown_utils import generate_jira_table, generate_table
33
+ from ...utils.scan_result_processor import ScanResultProcessor
34
+
35
+
36
+ @dataclass
37
+ class License:
38
+ spdxid: str
39
+ copyleft: bool
40
+ url: str
41
+ source: str
42
+
43
+ @dataclass
44
+ class Component:
45
+ purl: str
46
+ version: str
47
+ licenses: List[License]
48
+ status: str
49
+
50
+ class Copyleft(PolicyCheck[Component]):
51
+ """
52
+ SCANOSS Copyleft class
53
+ Inspects components for copyleft licenses
54
+ """
55
+
56
+ def __init__( # noqa: PLR0913
57
+ self,
58
+ debug: bool = False,
59
+ trace: bool = False,
60
+ quiet: bool = False,
61
+ filepath: str = None,
62
+ format_type: str = 'json',
63
+ status: str = None,
64
+ output: str = None,
65
+ include: str = None,
66
+ exclude: str = None,
67
+ explicit: str = None,
68
+ license_sources: list = None,
69
+ ):
70
+ """
71
+ Initialise the Copyleft class.
72
+
73
+ :param debug: Enable debug mode
74
+ :param trace: Enable trace mode (default True)
75
+ :param quiet: Enable quiet mode
76
+ :param filepath: Path to the file containing component data
77
+ :param format_type: Output format ('json' or 'md')
78
+ :param status: Path to save the status output
79
+ :param output: Path to save detailed output
80
+ :param include: Licenses to include in the analysis
81
+ :param exclude: Licenses to exclude from the analysis
82
+ :param explicit: Explicitly defined licenses
83
+ :param license_sources: List of license sources to check
84
+ """
85
+ super().__init__(
86
+ debug, trace, quiet, format_type, status, name='Copyleft Policy', output=output
87
+ )
88
+ self.license_util.init(include, exclude, explicit)
89
+ self.filepath = filepath
90
+ self.output = output
91
+ self.status = status
92
+ self.license_sources = license_sources or DEFAULT_COPYLEFT_LICENSE_SOURCES
93
+ self.results_processor = ScanResultProcessor(
94
+ self.debug,
95
+ self.trace,
96
+ self.quiet,
97
+ self.filepath,
98
+ include,
99
+ exclude,
100
+ explicit,
101
+ self.license_sources)
102
+
103
+ def _json(self, components: list[Component]) -> PolicyOutput:
104
+ """
105
+ Format the components with copyleft licenses as JSON.
106
+
107
+ :param components: List of components with copyleft licenses
108
+ :return: Dictionary with formatted JSON details and summary
109
+ """
110
+ # A component is considered unique by its combination of PURL (Package URL) and license
111
+ component_licenses = self.results_processor.group_components_by_license(components)
112
+ details = {}
113
+ if len(components) > 0:
114
+ details = {'components': components}
115
+ return PolicyOutput(
116
+ details= f'{json.dumps(details, indent=2)}\n',
117
+ summary= f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
118
+ )
119
+
120
+ def _markdown(self, components: list[Component]) -> PolicyOutput:
121
+ """
122
+ Format the components with copyleft licenses as Markdown.
123
+
124
+ :param components: List of components with copyleft licenses
125
+ :return: Dictionary with formatted Markdown details and summary
126
+ """
127
+ return self._md_summary_generator(components, generate_table)
128
+
129
+ def _jira_markdown(self, components: list[Component]) -> PolicyOutput:
130
+ """
131
+ Format the components with copyleft licenses as Markdown.
132
+
133
+ :param components: List of components with copyleft licenses
134
+ :return: Dictionary with formatted Markdown details and summary
135
+ """
136
+ return self._md_summary_generator(components, generate_jira_table)
137
+
138
+ def _md_summary_generator(self, components: list[Component], table_generator) -> PolicyOutput:
139
+ """
140
+ Generates a Markdown summary for components with a focus on copyleft licenses.
141
+
142
+ This function processes a list of components and groups them by their licenses.
143
+ For each group, the components are mapped with their license data and a tabular representation is created.
144
+ The generated Markdown summary includes a detailed table and a summary overview.
145
+
146
+ Parameters:
147
+ components: list[Component]
148
+ A list of Component objects to process for generating the summary.
149
+ table_generator
150
+ A callable function to generate tabular data for components.
151
+
152
+ Returns:
153
+ PolicyOutput
154
+ """
155
+ # A component is considered unique by its combination of PURL (Package URL) and license
156
+ component_licenses = self.results_processor.group_components_by_license(components)
157
+ headers = ['Component', 'License', 'URL', 'Copyleft']
158
+ centered_columns = [1, 4]
159
+ rows = []
160
+ for comp_lic_item in component_licenses:
161
+ row = [
162
+ comp_lic_item['purl'],
163
+ comp_lic_item['spdxid'],
164
+ comp_lic_item['url'],
165
+ 'YES' if comp_lic_item['copyleft'] else 'NO',
166
+ ]
167
+ rows.append(row)
168
+ # End license loop
169
+ # End component loop
170
+ return PolicyOutput(
171
+ details= f'### Copyleft Licenses\n{table_generator(headers, rows, centered_columns)}',
172
+ summary= f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
173
+ )
174
+
175
+ def _get_components_with_copyleft_licenses(self, components: list) -> list[Dict]:
176
+ """
177
+ Filter the components list to include only those with copyleft licenses.
178
+
179
+ :param components: List of all components
180
+ :return: List of components with copyleft licenses
181
+ """
182
+ filtered_components = []
183
+ for component in components:
184
+ copyleft_licenses = [lic for lic in component['licenses'] if lic['copyleft']]
185
+ if copyleft_licenses:
186
+ # Remove unused keys
187
+ del component['count']
188
+ del component['declared']
189
+ del component['undeclared']
190
+ filtered_component = component
191
+ # Remove 'count' from each license using pop
192
+ for lic in copyleft_licenses:
193
+ lic.pop('count', None) # None is default value if key doesn't exist
194
+
195
+ filtered_component['licenses'] = copyleft_licenses
196
+ filtered_components.append(filtered_component)
197
+ # End component loop
198
+ self.print_debug(f'Copyleft components: {filtered_components}')
199
+ return filtered_components
200
+
201
+ def _get_components(self):
202
+ """
203
+ Extract and process components from results and their dependencies.
204
+
205
+ This method performs the following steps:
206
+ 1. Validates that `self.results` is loaded. Returns `None` if not.
207
+ 2. Extracts file, snippet, and dependency components into a dictionary.
208
+ 3. Converts components to a list and processes their licenses.
209
+
210
+ :return: A list of processed components with license data, or `None` if `self.results` is not set.
211
+ """
212
+ if self.results_processor.get_results() is None:
213
+ return None
214
+ components: dict = {}
215
+ # Extract component and license data from file and dependency results. Both helpers mutate `components`
216
+ self.results_processor.get_components_data(components)
217
+ self.results_processor.get_dependencies_data(components)
218
+ return self.results_processor.convert_components_to_list(components)
219
+
220
+ def run(self):
221
+ """
222
+ Run the copyleft license inspection process.
223
+
224
+ This method performs the following steps:
225
+ 1. Get all components
226
+ 2. Filter components with copyleft licenses
227
+ 3. Format the results
228
+ 4. Save the output to files if required
229
+
230
+ :return: Dictionary containing the inspection results
231
+ """
232
+ self._debug()
233
+ # Get the components from the results
234
+ components = self._get_components()
235
+ if components is None:
236
+ return PolicyStatus.ERROR.value, {}
237
+ # Get a list of copyleft components if they exist
238
+ copyleft_components = self._get_components_with_copyleft_licenses(components)
239
+ # Format the results and save to files if required
240
+ return self._generate_formatter_report(copyleft_components)
241
+ #
242
+ # End of Copyleft Class
243
+ #