scanoss 1.40.0__py3-none-any.whl → 1.41.0__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.
- scanoss/__init__.py +1 -1
- scanoss/cli.py +22 -9
- scanoss/constants.py +3 -0
- scanoss/data/build_date.txt +1 -1
- scanoss/data/osadl-copyleft.json +133 -0
- scanoss/filecount.py +37 -38
- scanoss/gitlabqualityreport.py +33 -4
- scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
- scanoss/inspection/{dependency_track → policy_check/dependency_track}/project_violation.py +24 -24
- scanoss/inspection/{policy_check.py → policy_check/policy_check.py} +22 -18
- scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
- scanoss/inspection/{raw → policy_check/scanoss}/copyleft.py +42 -36
- scanoss/inspection/{raw → policy_check/scanoss}/undeclared_component.py +30 -29
- scanoss/inspection/summary/__init__.py +0 -0
- scanoss/inspection/{raw → summary}/component_summary.py +34 -9
- scanoss/inspection/{raw → summary}/license_summary.py +46 -44
- scanoss/inspection/{raw → summary}/match_summary.py +51 -0
- scanoss/inspection/utils/license_utils.py +57 -71
- scanoss/inspection/{raw/raw_base.py → utils/scan_result_processor.py} +47 -59
- scanoss/osadl.py +125 -0
- scanoss/scanner.py +191 -189
- scanoss/scanners/folder_hasher.py +24 -24
- scanoss/scanners/scanner_hfh.py +20 -15
- scanoss/threadedscanning.py +10 -0
- {scanoss-1.40.0.dist-info → scanoss-1.41.0.dist-info}/METADATA +1 -1
- {scanoss-1.40.0.dist-info → scanoss-1.41.0.dist-info}/RECORD +31 -26
- /scanoss/inspection/{raw → policy_check}/__init__.py +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.41.0.dist-info}/WHEEL +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.41.0.dist-info}/entry_points.txt +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.41.0.dist-info}/licenses/LICENSE +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.41.0.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
|
|
27
|
+
from typing import Callable, Dict, Generic, List, NamedTuple, TypeVar
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
29
|
+
from ...scanossbase import ScanossBase
|
|
30
|
+
from ..utils.license_utils import LicenseUtil
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class PolicyStatus(Enum):
|
|
@@ -46,9 +46,13 @@ class PolicyStatus(Enum):
|
|
|
46
46
|
# End of PolicyStatus Class
|
|
47
47
|
#
|
|
48
48
|
|
|
49
|
+
class PolicyOutput(NamedTuple):
|
|
50
|
+
details: str
|
|
51
|
+
summary: str
|
|
52
|
+
|
|
49
53
|
T = TypeVar('T')
|
|
50
54
|
|
|
51
|
-
class PolicyCheck(ScanossBase, Generic[T]):
|
|
55
|
+
class PolicyCheck(ScanossBase, Generic[T], ABC):
|
|
52
56
|
"""
|
|
53
57
|
A base class for implementing various software policy checks.
|
|
54
58
|
|
|
@@ -80,7 +84,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
|
|
|
80
84
|
self.output = output
|
|
81
85
|
|
|
82
86
|
@abstractmethod
|
|
83
|
-
def run(self):
|
|
87
|
+
def run(self)-> tuple[int,PolicyOutput]:
|
|
84
88
|
"""
|
|
85
89
|
Execute the policy check process.
|
|
86
90
|
|
|
@@ -91,14 +95,14 @@ class PolicyCheck(ScanossBase, Generic[T]):
|
|
|
91
95
|
3. Formatting the results
|
|
92
96
|
4. Saving the output to files if required
|
|
93
97
|
|
|
94
|
-
:return: A tuple containing:
|
|
98
|
+
:return: A named tuple containing two elements:
|
|
95
99
|
- First element: PolicyStatus enum value (SUCCESS, FAIL, or ERROR)
|
|
96
|
-
- Second element:
|
|
100
|
+
- Second element: PolicyOutput A tuple containing the policy results.
|
|
97
101
|
"""
|
|
98
102
|
pass
|
|
99
103
|
|
|
100
104
|
@abstractmethod
|
|
101
|
-
def _json(self, data: list[T]) ->
|
|
105
|
+
def _json(self, data: list[T]) -> PolicyOutput:
|
|
102
106
|
"""
|
|
103
107
|
Format the policy checks results as JSON.
|
|
104
108
|
This method should be implemented by subclasses to create a Markdown representation
|
|
@@ -112,7 +116,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
|
|
|
112
116
|
pass
|
|
113
117
|
|
|
114
118
|
@abstractmethod
|
|
115
|
-
def _markdown(self, data: list[T]) ->
|
|
119
|
+
def _markdown(self, data: list[T]) -> PolicyOutput:
|
|
116
120
|
"""
|
|
117
121
|
Generate Markdown output for the policy check results.
|
|
118
122
|
|
|
@@ -125,7 +129,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
|
|
|
125
129
|
pass
|
|
126
130
|
|
|
127
131
|
@abstractmethod
|
|
128
|
-
def _jira_markdown(self, data: list[T]) ->
|
|
132
|
+
def _jira_markdown(self, data: list[T]) -> PolicyOutput:
|
|
129
133
|
"""
|
|
130
134
|
Generate Markdown output for the policy check results.
|
|
131
135
|
|
|
@@ -137,7 +141,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
|
|
|
137
141
|
"""
|
|
138
142
|
pass
|
|
139
143
|
|
|
140
|
-
def _get_formatter(self) -> Callable[[List[dict]],
|
|
144
|
+
def _get_formatter(self) -> Callable[[List[dict]], PolicyOutput]:
|
|
141
145
|
"""
|
|
142
146
|
Get the appropriate formatter function based on the specified format.
|
|
143
147
|
|
|
@@ -145,7 +149,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
|
|
|
145
149
|
"""
|
|
146
150
|
valid_format = self._is_valid_format()
|
|
147
151
|
if not valid_format:
|
|
148
|
-
|
|
152
|
+
raise ValueError('Invalid format specified')
|
|
149
153
|
# a map of which format function to return
|
|
150
154
|
function_map = {
|
|
151
155
|
'json': self._json,
|
|
@@ -205,14 +209,14 @@ class PolicyCheck(ScanossBase, Generic[T]):
|
|
|
205
209
|
if formatter is None:
|
|
206
210
|
return PolicyStatus.ERROR.value, {}
|
|
207
211
|
# Format the results
|
|
208
|
-
|
|
212
|
+
policy_output = formatter(components)
|
|
209
213
|
## Save outputs if required
|
|
210
|
-
self.print_to_file_or_stdout(
|
|
211
|
-
self.print_to_file_or_stderr(
|
|
214
|
+
self.print_to_file_or_stdout(policy_output.details, self.output)
|
|
215
|
+
self.print_to_file_or_stderr(policy_output.summary, self.status)
|
|
212
216
|
# Check to see if we have policy violations
|
|
213
217
|
if len(components) > 0:
|
|
214
|
-
return PolicyStatus.POLICY_FAIL.value,
|
|
215
|
-
return PolicyStatus.POLICY_SUCCESS.value,
|
|
218
|
+
return PolicyStatus.POLICY_FAIL.value, policy_output
|
|
219
|
+
return PolicyStatus.POLICY_SUCCESS.value, policy_output
|
|
216
220
|
#
|
|
217
221
|
# End of PolicyCheck Class
|
|
218
222
|
#
|
|
File without changes
|
|
@@ -24,11 +24,13 @@ SPDX-License-Identifier: MIT
|
|
|
24
24
|
|
|
25
25
|
import json
|
|
26
26
|
from dataclasses import dataclass
|
|
27
|
-
from typing import
|
|
27
|
+
from typing import Dict, List
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
|
|
31
|
-
from .
|
|
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
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
@dataclass
|
|
@@ -45,7 +47,7 @@ class Component:
|
|
|
45
47
|
licenses: List[License]
|
|
46
48
|
status: str
|
|
47
49
|
|
|
48
|
-
class Copyleft(
|
|
50
|
+
class Copyleft(PolicyCheck[Component]):
|
|
49
51
|
"""
|
|
50
52
|
SCANOSS Copyleft class
|
|
51
53
|
Inspects components for copyleft licenses
|
|
@@ -63,6 +65,7 @@ class Copyleft(RawBase[Component]):
|
|
|
63
65
|
include: str = None,
|
|
64
66
|
exclude: str = None,
|
|
65
67
|
explicit: str = None,
|
|
68
|
+
license_sources: list = None,
|
|
66
69
|
):
|
|
67
70
|
"""
|
|
68
71
|
Initialise the Copyleft class.
|
|
@@ -77,18 +80,27 @@ class Copyleft(RawBase[Component]):
|
|
|
77
80
|
:param include: Licenses to include in the analysis
|
|
78
81
|
:param exclude: Licenses to exclude from the analysis
|
|
79
82
|
:param explicit: Explicitly defined licenses
|
|
83
|
+
:param license_sources: List of license sources to check
|
|
80
84
|
"""
|
|
81
|
-
super().__init__(
|
|
85
|
+
super().__init__(
|
|
86
|
+
debug, trace, quiet, format_type, status, name='Copyleft Policy', output=output
|
|
87
|
+
)
|
|
82
88
|
self.license_util.init(include, exclude, explicit)
|
|
83
89
|
self.filepath = filepath
|
|
84
|
-
self.format = format
|
|
85
90
|
self.output = output
|
|
86
91
|
self.status = status
|
|
87
|
-
self.
|
|
88
|
-
self.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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:
|
|
92
104
|
"""
|
|
93
105
|
Format the components with copyleft licenses as JSON.
|
|
94
106
|
|
|
@@ -96,16 +108,16 @@ class Copyleft(RawBase[Component]):
|
|
|
96
108
|
:return: Dictionary with formatted JSON details and summary
|
|
97
109
|
"""
|
|
98
110
|
# A component is considered unique by its combination of PURL (Package URL) and license
|
|
99
|
-
component_licenses = self.
|
|
111
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
100
112
|
details = {}
|
|
101
113
|
if len(components) > 0:
|
|
102
114
|
details = {'components': components}
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
+
)
|
|
107
119
|
|
|
108
|
-
def _markdown(self, components: list[Component]) ->
|
|
120
|
+
def _markdown(self, components: list[Component]) -> PolicyOutput:
|
|
109
121
|
"""
|
|
110
122
|
Format the components with copyleft licenses as Markdown.
|
|
111
123
|
|
|
@@ -114,7 +126,7 @@ class Copyleft(RawBase[Component]):
|
|
|
114
126
|
"""
|
|
115
127
|
return self._md_summary_generator(components, generate_table)
|
|
116
128
|
|
|
117
|
-
def _jira_markdown(self, components: list[Component]) ->
|
|
129
|
+
def _jira_markdown(self, components: list[Component]) -> PolicyOutput:
|
|
118
130
|
"""
|
|
119
131
|
Format the components with copyleft licenses as Markdown.
|
|
120
132
|
|
|
@@ -123,7 +135,7 @@ class Copyleft(RawBase[Component]):
|
|
|
123
135
|
"""
|
|
124
136
|
return self._md_summary_generator(components, generate_jira_table)
|
|
125
137
|
|
|
126
|
-
def _md_summary_generator(self, components: list[Component], table_generator):
|
|
138
|
+
def _md_summary_generator(self, components: list[Component], table_generator) -> PolicyOutput:
|
|
127
139
|
"""
|
|
128
140
|
Generates a Markdown summary for components with a focus on copyleft licenses.
|
|
129
141
|
|
|
@@ -138,15 +150,10 @@ class Copyleft(RawBase[Component]):
|
|
|
138
150
|
A callable function to generate tabular data for components.
|
|
139
151
|
|
|
140
152
|
Returns:
|
|
141
|
-
|
|
142
|
-
A dictionary containing two keys:
|
|
143
|
-
- 'details': A detailed Markdown representation including a table of components
|
|
144
|
-
and associated copyleft license data.
|
|
145
|
-
- 'summary': A textual summary highlighting the total number of components
|
|
146
|
-
with copyleft licenses.
|
|
153
|
+
PolicyOutput
|
|
147
154
|
"""
|
|
148
155
|
# A component is considered unique by its combination of PURL (Package URL) and license
|
|
149
|
-
component_licenses = self.
|
|
156
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
150
157
|
headers = ['Component', 'License', 'URL', 'Copyleft']
|
|
151
158
|
centered_columns = [1, 4]
|
|
152
159
|
rows = []
|
|
@@ -160,10 +167,10 @@ class Copyleft(RawBase[Component]):
|
|
|
160
167
|
rows.append(row)
|
|
161
168
|
# End license loop
|
|
162
169
|
# End component loop
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
+
)
|
|
167
174
|
|
|
168
175
|
def _get_components_with_copyleft_licenses(self, components: list) -> list[Dict]:
|
|
169
176
|
"""
|
|
@@ -202,14 +209,13 @@ class Copyleft(RawBase[Component]):
|
|
|
202
209
|
|
|
203
210
|
:return: A list of processed components with license data, or `None` if `self.results` is not set.
|
|
204
211
|
"""
|
|
205
|
-
if self.
|
|
212
|
+
if self.results_processor.get_results() is None:
|
|
206
213
|
return None
|
|
207
|
-
|
|
208
214
|
components: dict = {}
|
|
209
215
|
# Extract component and license data from file and dependency results. Both helpers mutate `components`
|
|
210
|
-
self.
|
|
211
|
-
self.
|
|
212
|
-
return self.
|
|
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)
|
|
213
219
|
|
|
214
220
|
def run(self):
|
|
215
221
|
"""
|
|
@@ -24,11 +24,11 @@ SPDX-License-Identifier: MIT
|
|
|
24
24
|
|
|
25
25
|
import json
|
|
26
26
|
from dataclasses import dataclass
|
|
27
|
-
from typing import
|
|
27
|
+
from typing import List
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from .
|
|
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
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
@dataclass
|
|
@@ -44,7 +44,7 @@ class Component:
|
|
|
44
44
|
licenses: List[License]
|
|
45
45
|
status: str
|
|
46
46
|
|
|
47
|
-
class UndeclaredComponent(
|
|
47
|
+
class UndeclaredComponent(PolicyCheck[Component]):
|
|
48
48
|
"""
|
|
49
49
|
SCANOSS UndeclaredComponent class
|
|
50
50
|
Inspects for undeclared components
|
|
@@ -59,7 +59,7 @@ class UndeclaredComponent(RawBase[Component]):
|
|
|
59
59
|
format_type: str = 'json',
|
|
60
60
|
status: str = None,
|
|
61
61
|
output: str = None,
|
|
62
|
-
sbom_format: str = 'settings'
|
|
62
|
+
sbom_format: str = 'settings'
|
|
63
63
|
):
|
|
64
64
|
"""
|
|
65
65
|
Initialize the UndeclaredComponent class.
|
|
@@ -74,13 +74,14 @@ class UndeclaredComponent(RawBase[Component]):
|
|
|
74
74
|
:param sbom_format: Sbom format for status output (default 'settings')
|
|
75
75
|
"""
|
|
76
76
|
super().__init__(
|
|
77
|
-
debug, trace, quiet,format_type,
|
|
77
|
+
debug, trace, quiet, format_type, status, name='Undeclared Components Policy', output=output
|
|
78
78
|
)
|
|
79
79
|
self.filepath = filepath
|
|
80
|
-
self.format = format
|
|
81
80
|
self.output = output
|
|
82
81
|
self.status = status
|
|
83
82
|
self.sbom_format = sbom_format
|
|
83
|
+
self.results_processor = ScanResultProcessor(self.debug, self.trace, self.quiet, self.filepath)
|
|
84
|
+
|
|
84
85
|
|
|
85
86
|
def _get_undeclared_components(self, components: list[Component]) -> list or None:
|
|
86
87
|
"""
|
|
@@ -163,7 +164,7 @@ class UndeclaredComponent(RawBase[Component]):
|
|
|
163
164
|
|
|
164
165
|
return summary
|
|
165
166
|
|
|
166
|
-
def _json(self, components: list[Component]) ->
|
|
167
|
+
def _json(self, components: list[Component]) -> PolicyOutput:
|
|
167
168
|
"""
|
|
168
169
|
Format the undeclared components as JSON.
|
|
169
170
|
|
|
@@ -171,16 +172,16 @@ class UndeclaredComponent(RawBase[Component]):
|
|
|
171
172
|
:return: Dictionary with formatted JSON details and summary
|
|
172
173
|
"""
|
|
173
174
|
# Use component grouped by licenses to generate the summary
|
|
174
|
-
component_licenses = self.
|
|
175
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
175
176
|
details = {}
|
|
176
177
|
if len(components) > 0:
|
|
177
178
|
details = {'components': components}
|
|
178
|
-
return
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
return PolicyOutput(
|
|
180
|
+
details=f'{json.dumps(details, indent=2)}\n',
|
|
181
|
+
summary=self._get_summary(component_licenses)
|
|
182
|
+
)
|
|
182
183
|
|
|
183
|
-
def _markdown(self, components: list[Component]) ->
|
|
184
|
+
def _markdown(self, components: list[Component]) -> PolicyOutput:
|
|
184
185
|
"""
|
|
185
186
|
Format the undeclared components as Markdown.
|
|
186
187
|
|
|
@@ -190,15 +191,15 @@ class UndeclaredComponent(RawBase[Component]):
|
|
|
190
191
|
headers = ['Component', 'License']
|
|
191
192
|
rows = []
|
|
192
193
|
# TODO look at using SpdxLite license name lookup method
|
|
193
|
-
component_licenses = self.
|
|
194
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
194
195
|
for component in component_licenses:
|
|
195
196
|
rows.append([component.get('purl'), component.get('spdxid')])
|
|
196
|
-
return
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
return PolicyOutput(
|
|
198
|
+
details= f'### Undeclared components\n{generate_table(headers, rows)}\n',
|
|
199
|
+
summary= self._get_summary(component_licenses),
|
|
200
|
+
)
|
|
200
201
|
|
|
201
|
-
def _jira_markdown(self, components: list) ->
|
|
202
|
+
def _jira_markdown(self, components: list) -> PolicyOutput:
|
|
202
203
|
"""
|
|
203
204
|
Format the undeclared components as Markdown.
|
|
204
205
|
|
|
@@ -208,13 +209,13 @@ class UndeclaredComponent(RawBase[Component]):
|
|
|
208
209
|
headers = ['Component', 'License']
|
|
209
210
|
rows = []
|
|
210
211
|
# TODO look at using SpdxLite license name lookup method
|
|
211
|
-
component_licenses = self.
|
|
212
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
212
213
|
for component in component_licenses:
|
|
213
214
|
rows.append([component.get('purl'), component.get('spdxid')])
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
return PolicyOutput(
|
|
216
|
+
details= f'{generate_jira_table(headers, rows)}',
|
|
217
|
+
summary= self._get_jira_summary(component_licenses),
|
|
218
|
+
)
|
|
218
219
|
|
|
219
220
|
def _get_unique_components(self, components: list) -> list:
|
|
220
221
|
"""
|
|
@@ -272,13 +273,13 @@ class UndeclaredComponent(RawBase[Component]):
|
|
|
272
273
|
|
|
273
274
|
:return: A list of processed components with their licenses, or `None` if `self.results` is not set.
|
|
274
275
|
"""
|
|
275
|
-
if self.
|
|
276
|
+
if self.results_processor.get_results() is None:
|
|
276
277
|
return None
|
|
277
278
|
components: dict = {}
|
|
278
279
|
# Extract file and snippet components
|
|
279
|
-
components = self.
|
|
280
|
+
components = self.results_processor.get_components_data(components)
|
|
280
281
|
# Convert to list and process licenses
|
|
281
|
-
return self.
|
|
282
|
+
return self.results_processor.convert_components_to_list(components)
|
|
282
283
|
|
|
283
284
|
def run(self):
|
|
284
285
|
"""
|
|
File without changes
|
|
@@ -24,11 +24,36 @@ SPDX-License-Identifier: MIT
|
|
|
24
24
|
import json
|
|
25
25
|
from typing import Any
|
|
26
26
|
|
|
27
|
-
from
|
|
28
|
-
from .
|
|
27
|
+
from ...scanossbase import ScanossBase
|
|
28
|
+
from ..policy_check.policy_check import T
|
|
29
|
+
from ..utils.scan_result_processor import ScanResultProcessor
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ComponentSummary(ScanossBase):
|
|
33
|
+
|
|
34
|
+
def __init__( # noqa: PLR0913
|
|
35
|
+
self,
|
|
36
|
+
debug: bool = False,
|
|
37
|
+
trace: bool = False,
|
|
38
|
+
quiet: bool = False,
|
|
39
|
+
filepath: str = None,
|
|
40
|
+
format_type: str = 'json',
|
|
41
|
+
output: str = None,
|
|
42
|
+
):
|
|
43
|
+
"""
|
|
44
|
+
Initialize the ComponentSummary class.
|
|
29
45
|
|
|
46
|
+
:param debug: Enable debug mode
|
|
47
|
+
:param trace: Enable trace mode
|
|
48
|
+
:param quiet: Enable quiet mode
|
|
49
|
+
:param filepath: Path to the file containing component data
|
|
50
|
+
:param format_type: Output format ('json' or 'md')
|
|
51
|
+
"""
|
|
52
|
+
super().__init__(debug, trace, quiet)
|
|
53
|
+
self.filepath = filepath
|
|
54
|
+
self.output = output
|
|
55
|
+
self.results_processor = ScanResultProcessor(debug, trace, quiet, filepath)
|
|
30
56
|
|
|
31
|
-
class ComponentSummary(RawBase):
|
|
32
57
|
|
|
33
58
|
def _json(self, data: dict[str,Any]) -> dict[str,Any]:
|
|
34
59
|
"""
|
|
@@ -77,11 +102,11 @@ class ComponentSummary(RawBase):
|
|
|
77
102
|
"""
|
|
78
103
|
Get a component summary from detected components.
|
|
79
104
|
|
|
80
|
-
:param
|
|
105
|
+
:param scan_components: List of all components
|
|
81
106
|
:return: Dict with license summary information
|
|
82
107
|
"""
|
|
83
108
|
# A component is considered unique by its combination of PURL (Package URL) and license
|
|
84
|
-
component_licenses = self.
|
|
109
|
+
component_licenses = self.results_processor.group_components_by_license(scan_components)
|
|
85
110
|
total_components = len(component_licenses)
|
|
86
111
|
# Get undeclared components
|
|
87
112
|
undeclared_components = len([c for c in component_licenses if c['status'] == 'pending'])
|
|
@@ -121,13 +146,13 @@ class ComponentSummary(RawBase):
|
|
|
121
146
|
|
|
122
147
|
:return: A list of processed components with license data, or `None` if `self.results` is not set.
|
|
123
148
|
"""
|
|
124
|
-
if self.
|
|
125
|
-
raise ValueError(f'Error: No results found in
|
|
149
|
+
if self.results_processor.get_results() is None:
|
|
150
|
+
raise ValueError(f'Error: No results found in {self.filepath}')
|
|
126
151
|
|
|
127
152
|
components: dict = {}
|
|
128
153
|
# Extract component and license data from file and dependency results. Both helpers mutate `components`
|
|
129
|
-
self.
|
|
130
|
-
return self.
|
|
154
|
+
self.results_processor.get_components_data(components)
|
|
155
|
+
return self.results_processor.convert_components_to_list(components)
|
|
131
156
|
|
|
132
157
|
def _format(self, component_summary) -> str:
|
|
133
158
|
# TODO: Implement formatter to support dynamic outputs
|
|
@@ -25,11 +25,12 @@ SPDX-License-Identifier: MIT
|
|
|
25
25
|
import json
|
|
26
26
|
from typing import Any
|
|
27
27
|
|
|
28
|
-
from
|
|
29
|
-
from .
|
|
28
|
+
from ...scanossbase import ScanossBase
|
|
29
|
+
from ..policy_check.policy_check import T
|
|
30
|
+
from ..utils.scan_result_processor import ScanResultProcessor
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
class LicenseSummary(
|
|
33
|
+
class LicenseSummary(ScanossBase):
|
|
33
34
|
"""
|
|
34
35
|
SCANOSS LicenseSummary class
|
|
35
36
|
Inspects results and generates comprehensive license summaries from detected components.
|
|
@@ -38,6 +39,42 @@ class LicenseSummary(RawBase):
|
|
|
38
39
|
information, providing detailed summaries including copyleft analysis and license statistics.
|
|
39
40
|
"""
|
|
40
41
|
|
|
42
|
+
# Define required license fields as class constants
|
|
43
|
+
REQUIRED_LICENSE_FIELDS = ['spdxid', 'url', 'copyleft', 'source']
|
|
44
|
+
|
|
45
|
+
def __init__( # noqa: PLR0913
|
|
46
|
+
self,
|
|
47
|
+
debug: bool = False,
|
|
48
|
+
trace: bool = False,
|
|
49
|
+
quiet: bool = False,
|
|
50
|
+
filepath: str = None,
|
|
51
|
+
status: str = None,
|
|
52
|
+
output: str = None,
|
|
53
|
+
include: str = None,
|
|
54
|
+
exclude: str = None,
|
|
55
|
+
explicit: str = None,
|
|
56
|
+
):
|
|
57
|
+
"""
|
|
58
|
+
Initialize the LicenseSummary class.
|
|
59
|
+
|
|
60
|
+
:param debug: Enable debug mode
|
|
61
|
+
:param trace: Enable trace mode
|
|
62
|
+
:param quiet: Enable quiet mode
|
|
63
|
+
:param filepath: Path to the file containing component data
|
|
64
|
+
:param output: Path to save detailed output
|
|
65
|
+
:param include: Licenses to include in the analysis
|
|
66
|
+
:param exclude: Licenses to exclude from the analysis
|
|
67
|
+
:param explicit: Explicitly defined licenses
|
|
68
|
+
"""
|
|
69
|
+
super().__init__(debug=debug, trace=trace, quiet=quiet)
|
|
70
|
+
self.results_processor = ScanResultProcessor(debug, trace, quiet, filepath, include, exclude, explicit)
|
|
71
|
+
self.filepath = filepath
|
|
72
|
+
self.output = output
|
|
73
|
+
self.status = status
|
|
74
|
+
self.include = include
|
|
75
|
+
self.exclude = exclude
|
|
76
|
+
self.explicit = explicit
|
|
77
|
+
|
|
41
78
|
def _json(self, data: dict[str,Any]) -> dict[str, Any]:
|
|
42
79
|
"""
|
|
43
80
|
Format license summary data as JSON.
|
|
@@ -78,41 +115,6 @@ class LicenseSummary(RawBase):
|
|
|
78
115
|
"""
|
|
79
116
|
pass
|
|
80
117
|
|
|
81
|
-
# Define required license fields as class constants
|
|
82
|
-
REQUIRED_LICENSE_FIELDS = ['spdxid', 'url', 'copyleft', 'source']
|
|
83
|
-
|
|
84
|
-
def __init__( # noqa: PLR0913
|
|
85
|
-
self,
|
|
86
|
-
debug: bool = False,
|
|
87
|
-
trace: bool = False,
|
|
88
|
-
quiet: bool = False,
|
|
89
|
-
filepath: str = None,
|
|
90
|
-
status: str = None,
|
|
91
|
-
output: str = None,
|
|
92
|
-
include: str = None,
|
|
93
|
-
exclude: str = None,
|
|
94
|
-
explicit: str = None,
|
|
95
|
-
):
|
|
96
|
-
"""
|
|
97
|
-
Initialize the LicenseSummary class.
|
|
98
|
-
|
|
99
|
-
:param debug: Enable debug mode
|
|
100
|
-
:param trace: Enable trace mode (default True)
|
|
101
|
-
:param quiet: Enable quiet mode
|
|
102
|
-
:param filepath: Path to the file containing component data
|
|
103
|
-
:param output: Path to save detailed output
|
|
104
|
-
:param include: Licenses to include in the analysis
|
|
105
|
-
:param exclude: Licenses to exclude from the analysis
|
|
106
|
-
:param explicit: Explicitly defined licenses
|
|
107
|
-
"""
|
|
108
|
-
super().__init__(debug, trace, quiet, filepath = filepath, output=output)
|
|
109
|
-
self.license_util.init(include, exclude, explicit)
|
|
110
|
-
self.filepath = filepath
|
|
111
|
-
self.output = output
|
|
112
|
-
self.status = status
|
|
113
|
-
self.include = include
|
|
114
|
-
self.exclude = exclude
|
|
115
|
-
self.explicit = explicit
|
|
116
118
|
|
|
117
119
|
def _get_licenses_summary_from_components(self, components: list)-> dict:
|
|
118
120
|
"""
|
|
@@ -122,7 +124,7 @@ class LicenseSummary(RawBase):
|
|
|
122
124
|
:return: Dict with license summary information
|
|
123
125
|
"""
|
|
124
126
|
# A component is considered unique by its combination of PURL (Package URL) and license
|
|
125
|
-
component_licenses = self.
|
|
127
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
126
128
|
license_component_count = {}
|
|
127
129
|
# Count license per component
|
|
128
130
|
for lic in component_licenses:
|
|
@@ -164,14 +166,14 @@ class LicenseSummary(RawBase):
|
|
|
164
166
|
|
|
165
167
|
:return: A list of processed components with license data, or `None` if `self.results` is not set.
|
|
166
168
|
"""
|
|
167
|
-
if self.
|
|
168
|
-
raise ValueError(f'Error: No results found in
|
|
169
|
+
if self.results_processor.get_results() is None:
|
|
170
|
+
raise ValueError(f'Error: No results found in {self.filepath}')
|
|
169
171
|
|
|
170
172
|
components: dict = {}
|
|
171
173
|
# Extract component and license data from file and dependency results. Both helpers mutate `components`
|
|
172
|
-
self.
|
|
173
|
-
self.
|
|
174
|
-
return self.
|
|
174
|
+
self.results_processor.get_components_data(components)
|
|
175
|
+
self.results_processor.get_dependencies_data(components)
|
|
176
|
+
return self.results_processor.convert_components_to_list(components)
|
|
175
177
|
|
|
176
178
|
def _format(self, license_summary) -> str:
|
|
177
179
|
# TODO: Implement formatter to support dynamic outputs
|