scanoss 1.27.1__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 (79) hide show
  1. protoc_gen_swagger/options/annotations_pb2.py +18 -12
  2. protoc_gen_swagger/options/annotations_pb2.pyi +48 -0
  3. protoc_gen_swagger/options/annotations_pb2_grpc.py +20 -0
  4. protoc_gen_swagger/options/openapiv2_pb2.py +110 -99
  5. protoc_gen_swagger/options/openapiv2_pb2.pyi +1317 -0
  6. protoc_gen_swagger/options/openapiv2_pb2_grpc.py +20 -0
  7. scanoss/__init__.py +1 -1
  8. scanoss/api/common/v2/scanoss_common_pb2.py +49 -22
  9. scanoss/api/common/v2/scanoss_common_pb2_grpc.py +25 -0
  10. scanoss/api/components/v2/scanoss_components_pb2.py +68 -43
  11. scanoss/api/components/v2/scanoss_components_pb2_grpc.py +83 -22
  12. scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +136 -47
  13. scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +650 -33
  14. scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +56 -37
  15. scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +64 -12
  16. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +74 -31
  17. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +252 -13
  18. scanoss/api/licenses/__init__.py +23 -0
  19. scanoss/api/licenses/v2/__init__.py +23 -0
  20. scanoss/api/licenses/v2/scanoss_licenses_pb2.py +84 -0
  21. scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +302 -0
  22. scanoss/api/scanning/v2/scanoss_scanning_pb2.py +32 -21
  23. scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +49 -8
  24. scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +50 -23
  25. scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +151 -16
  26. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +78 -31
  27. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +282 -18
  28. scanoss/cli.py +1000 -186
  29. scanoss/components.py +80 -50
  30. scanoss/constants.py +7 -1
  31. scanoss/cryptography.py +89 -55
  32. scanoss/csvoutput.py +13 -7
  33. scanoss/cyclonedx.py +141 -9
  34. scanoss/data/build_date.txt +1 -1
  35. scanoss/data/osadl-copyleft.json +133 -0
  36. scanoss/delta.py +197 -0
  37. scanoss/export/__init__.py +23 -0
  38. scanoss/export/dependency_track.py +227 -0
  39. scanoss/file_filters.py +2 -163
  40. scanoss/filecount.py +37 -38
  41. scanoss/gitlabqualityreport.py +214 -0
  42. scanoss/header_filter.py +563 -0
  43. scanoss/inspection/policy_check/__init__.py +0 -0
  44. scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
  45. scanoss/inspection/policy_check/dependency_track/project_violation.py +479 -0
  46. scanoss/inspection/{policy_check.py → policy_check/policy_check.py} +65 -72
  47. scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
  48. scanoss/inspection/{copyleft.py → policy_check/scanoss/copyleft.py} +89 -73
  49. scanoss/inspection/{undeclared_component.py → policy_check/scanoss/undeclared_component.py} +52 -46
  50. scanoss/inspection/summary/__init__.py +0 -0
  51. scanoss/inspection/summary/component_summary.py +170 -0
  52. scanoss/inspection/{license_summary.py → summary/license_summary.py} +62 -12
  53. scanoss/inspection/summary/match_summary.py +341 -0
  54. scanoss/inspection/utils/file_utils.py +44 -0
  55. scanoss/inspection/utils/license_utils.py +57 -71
  56. scanoss/inspection/utils/markdown_utils.py +63 -0
  57. scanoss/inspection/{inspect_base.py → utils/scan_result_processor.py} +53 -67
  58. scanoss/osadl.py +125 -0
  59. scanoss/scanner.py +135 -253
  60. scanoss/scanners/folder_hasher.py +47 -32
  61. scanoss/scanners/scanner_hfh.py +50 -18
  62. scanoss/scanoss_settings.py +33 -3
  63. scanoss/scanossapi.py +23 -25
  64. scanoss/scanossbase.py +1 -1
  65. scanoss/scanossgrpc.py +543 -289
  66. scanoss/services/dependency_track_service.py +132 -0
  67. scanoss/spdxlite.py +11 -4
  68. scanoss/threadeddependencies.py +19 -18
  69. scanoss/threadedscanning.py +10 -0
  70. scanoss/utils/scanoss_scan_results_utils.py +41 -0
  71. scanoss/winnowing.py +71 -19
  72. {scanoss-1.27.1.dist-info → scanoss-1.43.1.dist-info}/METADATA +8 -5
  73. scanoss-1.43.1.dist-info/RECORD +110 -0
  74. scanoss/inspection/component_summary.py +0 -94
  75. scanoss-1.27.1.dist-info/RECORD +0 -87
  76. {scanoss-1.27.1.dist-info → scanoss-1.43.1.dist-info}/WHEEL +0 -0
  77. {scanoss-1.27.1.dist-info → scanoss-1.43.1.dist-info}/entry_points.txt +0 -0
  78. {scanoss-1.27.1.dist-info → scanoss-1.43.1.dist-info}/licenses/LICENSE +0 -0
  79. {scanoss-1.27.1.dist-info → scanoss-1.43.1.dist-info}/top_level.txt +0 -0
@@ -22,12 +22,12 @@ SPDX-License-Identifier: MIT
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- from abc import abstractmethod
25
+ from abc import ABC, abstractmethod
26
26
  from enum import Enum
27
- from typing import Any, Callable, Dict, List
27
+ from typing import Callable, Dict, Generic, List, NamedTuple, TypeVar
28
28
 
29
- from .inspect_base import InspectBase
30
- from .utils.license_utils import LicenseUtil
29
+ from ...scanossbase import ScanossBase
30
+ from ..utils.license_utils import LicenseUtil
31
31
 
32
32
 
33
33
  class PolicyStatus(Enum):
@@ -35,19 +35,24 @@ class PolicyStatus(Enum):
35
35
  Enumeration representing the status of a policy check.
36
36
 
37
37
  Attributes:
38
- SUCCESS (int): Indicates that the policy check passed successfully (value: 0).
39
- FAIL (int): Indicates that the policy check failed (value: 1).
40
- ERROR (int): Indicates that an error occurred during the policy check (value: 2).
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
41
  """
42
-
43
- SUCCESS = 0
44
- FAIL = 1
45
- ERROR = 2
42
+ POLICY_SUCCESS = 0
43
+ POLICY_FAIL = 2
44
+ ERROR = 1
46
45
  #
47
46
  # End of PolicyStatus Class
48
47
  #
49
48
 
50
- class PolicyCheck(InspectBase):
49
+ class PolicyOutput(NamedTuple):
50
+ details: str
51
+ summary: str
52
+
53
+ T = TypeVar('T')
54
+
55
+ class PolicyCheck(ScanossBase, Generic[T], ABC):
51
56
  """
52
57
  A base class for implementing various software policy checks.
53
58
 
@@ -66,22 +71,20 @@ class PolicyCheck(InspectBase):
66
71
  debug: bool = False,
67
72
  trace: bool = False,
68
73
  quiet: bool = False,
69
- filepath: str = None,
70
74
  format_type: str = None,
71
75
  status: str = None,
72
- output: str = None,
73
76
  name: str = None,
77
+ output: str = None,
74
78
  ):
75
- super().__init__(debug, trace, quiet, filepath, output)
79
+ super().__init__(debug, trace, quiet)
76
80
  self.license_util = LicenseUtil()
77
- self.filepath = filepath
78
81
  self.name = name
79
82
  self.format_type = format_type
80
83
  self.status = status
81
- self.results = self._load_input_file()
84
+ self.output = output
82
85
 
83
86
  @abstractmethod
84
- def run(self):
87
+ def run(self)-> tuple[int,PolicyOutput]:
85
88
  """
86
89
  Execute the policy check process.
87
90
 
@@ -92,95 +95,53 @@ class PolicyCheck(InspectBase):
92
95
  3. Formatting the results
93
96
  4. Saving the output to files if required
94
97
 
95
- :return: A tuple containing:
98
+ :return: A named tuple containing two elements:
96
99
  - First element: PolicyStatus enum value (SUCCESS, FAIL, or ERROR)
97
- - Second element: Dictionary containing the inspection results
100
+ - Second element: PolicyOutput A tuple containing the policy results.
98
101
  """
99
102
  pass
100
103
 
101
104
  @abstractmethod
102
- def _json(self, components: list) -> Dict[str, Any]:
105
+ def _json(self, data: list[T]) -> PolicyOutput:
103
106
  """
104
107
  Format the policy checks results as JSON.
105
108
  This method should be implemented by subclasses to create a Markdown representation
106
109
  of the policy check results.
107
110
 
108
- :param components: List of components to be formatted.
111
+ :param data: List of data to be formatted.
109
112
  :return: A dictionary containing two keys:
110
- - 'details': A JSON-formatted string with the full list of components
113
+ - 'results': A JSON-formatted string with the full list of components
111
114
  - 'summary': A string summarizing the number of components found
112
115
  """
113
116
  pass
114
117
 
115
118
  @abstractmethod
116
- def _markdown(self, components: list) -> Dict[str, Any]:
119
+ def _markdown(self, data: list[T]) -> PolicyOutput:
117
120
  """
118
121
  Generate Markdown output for the policy check results.
119
122
 
120
123
  This method should be implemented by subclasses to create a Markdown representation
121
124
  of the policy check results.
122
125
 
123
- :param components: List of components to be included in the output.
126
+ :param data: List of data to be included in the output.
124
127
  :return: A dictionary representing the Markdown output.
125
128
  """
126
129
  pass
127
130
 
128
131
  @abstractmethod
129
- def _jira_markdown(self, components: list) -> Dict[str, Any]:
132
+ def _jira_markdown(self, data: list[T]) -> PolicyOutput:
130
133
  """
131
134
  Generate Markdown output for the policy check results.
132
135
 
133
136
  This method should be implemented by subclasses to create a Markdown representation
134
137
  of the policy check results.
135
138
 
136
- :param components: List of components to be included in the output.
139
+ :param data: List of data to be included in the output.
137
140
  :return: A dictionary representing the Markdown output.
138
141
  """
139
142
  pass
140
143
 
141
- def generate_table(self, headers, rows, centered_columns=None):
142
- """
143
- Generate a Markdown table.
144
-
145
- :param headers: List of headers for the table.
146
- :param rows: List of rows for the table.
147
- :param centered_columns: List of column indices to be centered.
148
- :return: A string representing the Markdown table.
149
- """
150
- col_sep = ' | '
151
- centered_column_set = set(centered_columns or [])
152
- if headers is None:
153
- self.print_stderr('ERROR: Header are no set')
154
- return None
155
-
156
- # Decide which separator to use
157
- def create_separator(index):
158
- if centered_columns is None:
159
- return '-'
160
- return ':-:' if index in centered_column_set else '-'
161
-
162
- # Build the row separator
163
- row_separator = col_sep + col_sep.join(create_separator(index) for index, _ in enumerate(headers)) + col_sep
164
- # build table rows
165
- table_rows = [col_sep + col_sep.join(headers) + col_sep, row_separator]
166
- table_rows.extend(col_sep + col_sep.join(row) + col_sep for row in rows)
167
- return '\n'.join(table_rows)
168
-
169
- def generate_jira_table(self, headers, rows, centered_columns=None):
170
- col_sep = '*|*'
171
- if headers is None:
172
- self.print_stderr('ERROR: Header are no set')
173
- return None
174
-
175
- table_header = '|*' + col_sep.join(headers) + '*|\n'
176
- table = table_header
177
- for row in rows:
178
- if len(headers) == len(row):
179
- table += '|' + '|'.join(row) + '|\n'
180
-
181
- return table
182
-
183
- def _get_formatter(self) -> Callable[[List[dict]], Dict[str, Any]] or None:
144
+ def _get_formatter(self) -> Callable[[List[dict]], PolicyOutput]:
184
145
  """
185
146
  Get the appropriate formatter function based on the specified format.
186
147
 
@@ -188,7 +149,7 @@ class PolicyCheck(InspectBase):
188
149
  """
189
150
  valid_format = self._is_valid_format()
190
151
  if not valid_format:
191
- return None
152
+ raise ValueError('Invalid format specified')
192
153
  # a map of which format function to return
193
154
  function_map = {
194
155
  'json': self._json,
@@ -208,7 +169,6 @@ class PolicyCheck(InspectBase):
208
169
  self.print_stderr(f'Format: {self.format_type}')
209
170
  self.print_stderr(f'Status: {self.status}')
210
171
  self.print_stderr(f'Output: {self.output}')
211
- self.print_stderr(f'Input: {self.filepath}')
212
172
 
213
173
  def _is_valid_format(self) -> bool:
214
174
  """
@@ -224,6 +184,39 @@ class PolicyCheck(InspectBase):
224
184
  self.print_stderr(f'ERROR: Invalid format "{self.format_type}". Valid formats are: {valid_formats_str}')
225
185
  return False
226
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
227
220
  #
228
221
  # End of PolicyCheck Class
229
222
  #
File without changes
@@ -23,12 +23,31 @@ SPDX-License-Identifier: MIT
23
23
  """
24
24
 
25
25
  import json
26
- from typing import Any, Dict
26
+ from dataclasses import dataclass
27
+ from typing import Dict, List
27
28
 
28
- from .policy_check import PolicyCheck, PolicyStatus
29
+ from scanoss.constants import DEFAULT_COPYLEFT_LICENSE_SOURCES
29
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
30
34
 
31
- class Copyleft(PolicyCheck):
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]):
32
51
  """
33
52
  SCANOSS Copyleft class
34
53
  Inspects components for copyleft licenses
@@ -46,9 +65,10 @@ class Copyleft(PolicyCheck):
46
65
  include: str = None,
47
66
  exclude: str = None,
48
67
  explicit: str = None,
68
+ license_sources: list = None,
49
69
  ):
50
70
  """
51
- Initialize the Copyleft class.
71
+ Initialise the Copyleft class.
52
72
 
53
73
  :param debug: Enable debug mode
54
74
  :param trace: Enable trace mode (default True)
@@ -60,18 +80,27 @@ class Copyleft(PolicyCheck):
60
80
  :param include: Licenses to include in the analysis
61
81
  :param exclude: Licenses to exclude from the analysis
62
82
  :param explicit: Explicitly defined licenses
83
+ :param license_sources: List of license sources to check
63
84
  """
64
- super().__init__(debug, trace, quiet, filepath, format_type, status, output, name='Copyleft Policy')
85
+ super().__init__(
86
+ debug, trace, quiet, format_type, status, name='Copyleft Policy', output=output
87
+ )
65
88
  self.license_util.init(include, exclude, explicit)
66
89
  self.filepath = filepath
67
- self.format = format
68
90
  self.output = output
69
91
  self.status = status
70
- self.include = include
71
- self.exclude = exclude
72
- self.explicit = explicit
73
-
74
- def _json(self, components: list) -> Dict[str, Any]:
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:
75
104
  """
76
105
  Format the components with copyleft licenses as JSON.
77
106
 
@@ -79,70 +108,71 @@ class Copyleft(PolicyCheck):
79
108
  :return: Dictionary with formatted JSON details and summary
80
109
  """
81
110
  # A component is considered unique by its combination of PURL (Package URL) and license
82
- component_licenses = self._group_components_by_license(components)
111
+ component_licenses = self.results_processor.group_components_by_license(components)
83
112
  details = {}
84
113
  if len(components) > 0:
85
114
  details = {'components': components}
86
- return {
87
- 'details': f'{json.dumps(details, indent=2)}\n',
88
- 'summary': f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
89
- }
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
+ )
90
119
 
91
- def _markdown(self, components: list) -> Dict[str, Any]:
120
+ def _markdown(self, components: list[Component]) -> PolicyOutput:
92
121
  """
93
122
  Format the components with copyleft licenses as Markdown.
94
123
 
95
124
  :param components: List of components with copyleft licenses
96
125
  :return: Dictionary with formatted Markdown details and summary
97
126
  """
98
- # A component is considered unique by its combination of PURL (Package URL) and license
99
- component_licenses = self._group_components_by_license(components)
100
- headers = ['Component', 'License', 'URL', 'Copyleft']
101
- centered_columns = [1, 4]
102
- rows: [[]] = []
103
- for comp_lic_item in component_licenses:
104
- row = [
105
- comp_lic_item['purl'],
106
- comp_lic_item['spdxid'],
107
- comp_lic_item['url'],
108
- 'YES' if comp_lic_item['copyleft'] else 'NO',
109
- ]
110
- rows.append(row)
111
- # End license loop
112
- # End component loop
113
- return {
114
- 'details': f'### Copyleft licenses\n{self.generate_table(headers, rows, centered_columns)}\n',
115
- 'summary': f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
116
- }
127
+ return self._md_summary_generator(components, generate_table)
117
128
 
118
- def _jira_markdown(self, components: list) -> Dict[str, Any]:
129
+ def _jira_markdown(self, components: list[Component]) -> PolicyOutput:
119
130
  """
120
131
  Format the components with copyleft licenses as Markdown.
121
132
 
122
133
  :param components: List of components with copyleft licenses
123
134
  :return: Dictionary with formatted Markdown details and summary
124
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
+ """
125
155
  # A component is considered unique by its combination of PURL (Package URL) and license
126
- component_licenses = self._group_components_by_license(components)
156
+ component_licenses = self.results_processor.group_components_by_license(components)
127
157
  headers = ['Component', 'License', 'URL', 'Copyleft']
128
158
  centered_columns = [1, 4]
129
- rows: [[]] = []
159
+ rows = []
130
160
  for comp_lic_item in component_licenses:
131
- row = [
132
- comp_lic_item['purl'],
133
- comp_lic_item['spdxid'],
134
- comp_lic_item['url'],
135
- 'YES' if comp_lic_item['copyleft'] else 'NO',
136
- ]
137
- rows.append(row)
138
- # End license loop
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
139
169
  # End component loop
140
- return {
141
- 'details': f'{self.generate_jira_table(headers, rows, centered_columns)}',
142
- 'summary': f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
143
- }
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
+ )
144
174
 
145
- def _filter_components_with_copyleft_licenses(self, components: list) -> list:
175
+ def _get_components_with_copyleft_licenses(self, components: list) -> list[Dict]:
146
176
  """
147
177
  Filter the components list to include only those with copyleft licenses.
148
178
 
@@ -179,14 +209,13 @@ class Copyleft(PolicyCheck):
179
209
 
180
210
  :return: A list of processed components with license data, or `None` if `self.results` is not set.
181
211
  """
182
- if self.results is None:
212
+ if self.results_processor.get_results() is None:
183
213
  return None
184
-
185
214
  components: dict = {}
186
215
  # Extract component and license data from file and dependency results. Both helpers mutate `components`
187
- self._get_components_data(self.results, components)
188
- self._get_dependencies_data(self.results, components)
189
- return self._convert_components_to_list(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)
190
219
 
191
220
  def run(self):
192
221
  """
@@ -206,22 +235,9 @@ class Copyleft(PolicyCheck):
206
235
  if components is None:
207
236
  return PolicyStatus.ERROR.value, {}
208
237
  # Get a list of copyleft components if they exist
209
- copyleft_components = self._filter_components_with_copyleft_licenses(components)
210
- # Get a formatter for the output results
211
- formatter = self._get_formatter()
212
- if formatter is None:
213
- return PolicyStatus.ERROR.value, {}
214
- # Format the results
215
- results = formatter(copyleft_components)
216
- ## Save outputs if required
217
- self.print_to_file_or_stdout(results['details'], self.output)
218
- self.print_to_file_or_stderr(results['summary'], self.status)
219
- # Check to see if we have policy violations
220
- if len(copyleft_components) <= 0:
221
- return PolicyStatus.FAIL.value, results
222
- return PolicyStatus.SUCCESS.value, results
223
-
224
-
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)
225
241
  #
226
242
  # End of Copyleft Class
227
243
  #
@@ -23,12 +23,28 @@ SPDX-License-Identifier: MIT
23
23
  """
24
24
 
25
25
  import json
26
- from typing import Any, Dict
26
+ from dataclasses import dataclass
27
+ from typing import List
27
28
 
28
- from .policy_check import PolicyCheck, PolicyStatus
29
+ from ...policy_check.policy_check import PolicyCheck, PolicyOutput, PolicyStatus
30
+ from ...utils.markdown_utils import generate_jira_table, generate_table
31
+ from ...utils.scan_result_processor import ScanResultProcessor
29
32
 
30
33
 
31
- class UndeclaredComponent(PolicyCheck):
34
+ @dataclass
35
+ class License:
36
+ spdxid: str
37
+ copyleft: bool
38
+ url: str
39
+
40
+ @dataclass
41
+ class Component:
42
+ purl: str
43
+ version: str
44
+ licenses: List[License]
45
+ status: str
46
+
47
+ class UndeclaredComponent(PolicyCheck[Component]):
32
48
  """
33
49
  SCANOSS UndeclaredComponent class
34
50
  Inspects for undeclared components
@@ -43,7 +59,7 @@ class UndeclaredComponent(PolicyCheck):
43
59
  format_type: str = 'json',
44
60
  status: str = None,
45
61
  output: str = None,
46
- sbom_format: str = 'settings',
62
+ sbom_format: str = 'settings'
47
63
  ):
48
64
  """
49
65
  Initialize the UndeclaredComponent class.
@@ -58,15 +74,16 @@ class UndeclaredComponent(PolicyCheck):
58
74
  :param sbom_format: Sbom format for status output (default 'settings')
59
75
  """
60
76
  super().__init__(
61
- debug, trace, quiet, filepath, format_type, status, output, name='Undeclared Components Policy'
77
+ debug, trace, quiet, format_type, status, name='Undeclared Components Policy', output=output
62
78
  )
63
79
  self.filepath = filepath
64
- self.format = format
65
80
  self.output = output
66
81
  self.status = status
67
82
  self.sbom_format = sbom_format
83
+ self.results_processor = ScanResultProcessor(self.debug, self.trace, self.quiet, self.filepath)
84
+
68
85
 
69
- def _get_undeclared_component(self, components: list) -> list or None:
86
+ def _get_undeclared_components(self, components: list[Component]) -> list or None:
70
87
  """
71
88
  Filter the components list to include only undeclared components.
72
89
 
@@ -90,7 +107,7 @@ class UndeclaredComponent(PolicyCheck):
90
107
  # end component loop
91
108
  return undeclared_components
92
109
 
93
- def _get_jira_summary(self, components: list) -> str:
110
+ def _get_jira_summary(self, components: list[Component]) -> str:
94
111
  """
95
112
  Get a summary of the undeclared components.
96
113
 
@@ -147,7 +164,7 @@ class UndeclaredComponent(PolicyCheck):
147
164
 
148
165
  return summary
149
166
 
150
- def _json(self, components: list) -> Dict[str, Any]:
167
+ def _json(self, components: list[Component]) -> PolicyOutput:
151
168
  """
152
169
  Format the undeclared components as JSON.
153
170
 
@@ -155,16 +172,16 @@ class UndeclaredComponent(PolicyCheck):
155
172
  :return: Dictionary with formatted JSON details and summary
156
173
  """
157
174
  # Use component grouped by licenses to generate the summary
158
- component_licenses = self._group_components_by_license(components)
175
+ component_licenses = self.results_processor.group_components_by_license(components)
159
176
  details = {}
160
177
  if len(components) > 0:
161
178
  details = {'components': components}
162
- return {
163
- 'details': f'{json.dumps(details, indent=2)}\n',
164
- 'summary': self._get_summary(component_licenses),
165
- }
179
+ return PolicyOutput(
180
+ details=f'{json.dumps(details, indent=2)}\n',
181
+ summary=self._get_summary(component_licenses)
182
+ )
166
183
 
167
- def _markdown(self, components: list) -> Dict[str, Any]:
184
+ def _markdown(self, components: list[Component]) -> PolicyOutput:
168
185
  """
169
186
  Format the undeclared components as Markdown.
170
187
 
@@ -172,17 +189,17 @@ class UndeclaredComponent(PolicyCheck):
172
189
  :return: Dictionary with formatted Markdown details and summary
173
190
  """
174
191
  headers = ['Component', 'License']
175
- rows: [[]] = []
192
+ rows = []
176
193
  # TODO look at using SpdxLite license name lookup method
177
- component_licenses = self._group_components_by_license(components)
194
+ component_licenses = self.results_processor.group_components_by_license(components)
178
195
  for component in component_licenses:
179
196
  rows.append([component.get('purl'), component.get('spdxid')])
180
- return {
181
- 'details': f'### Undeclared components\n{self.generate_table(headers, rows)}\n',
182
- 'summary': self._get_summary(component_licenses),
183
- }
197
+ return PolicyOutput(
198
+ details= f'### Undeclared components\n{generate_table(headers, rows)}\n',
199
+ summary= self._get_summary(component_licenses),
200
+ )
184
201
 
185
- def _jira_markdown(self, components: list) -> Dict[str, Any]:
202
+ def _jira_markdown(self, components: list) -> PolicyOutput:
186
203
  """
187
204
  Format the undeclared components as Markdown.
188
205
 
@@ -190,15 +207,15 @@ class UndeclaredComponent(PolicyCheck):
190
207
  :return: Dictionary with formatted Markdown details and summary
191
208
  """
192
209
  headers = ['Component', 'License']
193
- rows: [[]] = []
210
+ rows = []
194
211
  # TODO look at using SpdxLite license name lookup method
195
- component_licenses = self._group_components_by_license(components)
212
+ component_licenses = self.results_processor.group_components_by_license(components)
196
213
  for component in component_licenses:
197
214
  rows.append([component.get('purl'), component.get('spdxid')])
198
- return {
199
- 'details': f'{self.generate_jira_table(headers, rows)}',
200
- 'summary': self._get_jira_summary(component_licenses),
201
- }
215
+ return PolicyOutput(
216
+ details= f'{generate_jira_table(headers, rows)}',
217
+ summary= self._get_jira_summary(component_licenses),
218
+ )
202
219
 
203
220
  def _get_unique_components(self, components: list) -> list:
204
221
  """
@@ -256,13 +273,13 @@ class UndeclaredComponent(PolicyCheck):
256
273
 
257
274
  :return: A list of processed components with their licenses, or `None` if `self.results` is not set.
258
275
  """
259
- if self.results is None:
276
+ if self.results_processor.get_results() is None:
260
277
  return None
261
278
  components: dict = {}
262
279
  # Extract file and snippet components
263
- components = self._get_components_data(self.results, components)
280
+ components = self.results_processor.get_components_data(components)
264
281
  # Convert to list and process licenses
265
- return self._convert_components_to_list(components)
282
+ return self.results_processor.convert_components_to_list(components)
266
283
 
267
284
  def run(self):
268
285
  """
@@ -280,24 +297,13 @@ class UndeclaredComponent(PolicyCheck):
280
297
  components = self._get_components()
281
298
  if components is None:
282
299
  return PolicyStatus.ERROR.value, {}
283
- # Get undeclared component summary (if any)
284
- undeclared_components = self._get_undeclared_component(components)
300
+ # Get an undeclared component summary (if any)
301
+ undeclared_components = self._get_undeclared_components(components)
285
302
  if undeclared_components is None:
286
303
  return PolicyStatus.ERROR.value, {}
287
304
  self.print_debug(f'Undeclared components: {undeclared_components}')
288
- formatter = self._get_formatter()
289
- if formatter is None:
290
- return PolicyStatus.ERROR.value, {}
291
- results = formatter(undeclared_components)
292
- # Output the results
293
- self.print_to_file_or_stdout(results['details'], self.output)
294
- self.print_to_file_or_stderr(results['summary'], self.status)
295
- # Determine if the filter found results or not
296
- if len(undeclared_components) <= 0:
297
- return PolicyStatus.FAIL.value, results
298
- return PolicyStatus.SUCCESS.value, results
299
-
300
-
305
+ # Format the results and save to files if required
306
+ return self._generate_formatter_report(undeclared_components)
301
307
  #
302
308
  # End of UndeclaredComponent Class
303
309
  #
File without changes