scanoss 1.40.0__py3-none-any.whl → 1.40.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.
- scanoss/__init__.py +1 -1
- scanoss/cli.py +8 -9
- scanoss/data/build_date.txt +1 -1
- 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 +35 -35
- 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/{raw/raw_base.py → utils/scan_result_processor.py} +25 -48
- {scanoss-1.40.0.dist-info → scanoss-1.40.1.dist-info}/METADATA +1 -1
- {scanoss-1.40.0.dist-info → scanoss-1.40.1.dist-info}/RECORD +22 -19
- /scanoss/inspection/{raw → policy_check}/__init__.py +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.40.1.dist-info}/WHEEL +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.40.1.dist-info}/entry_points.txt +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.40.1.dist-info}/licenses/LICENSE +0 -0
- {scanoss-1.40.0.dist-info → scanoss-1.40.1.dist-info}/top_level.txt +0 -0
scanoss/__init__.py
CHANGED
scanoss/cli.py
CHANGED
|
@@ -35,12 +35,6 @@ import pypac
|
|
|
35
35
|
from scanoss.cryptography import Cryptography, create_cryptography_config_from_args
|
|
36
36
|
from scanoss.delta import Delta
|
|
37
37
|
from scanoss.export.dependency_track import DependencyTrackExporter
|
|
38
|
-
from scanoss.inspection.dependency_track.project_violation import (
|
|
39
|
-
DependencyTrackProjectViolationPolicyCheck,
|
|
40
|
-
)
|
|
41
|
-
from scanoss.inspection.raw.component_summary import ComponentSummary
|
|
42
|
-
from scanoss.inspection.raw.license_summary import LicenseSummary
|
|
43
|
-
from scanoss.inspection.raw.match_summary import MatchSummary
|
|
44
38
|
from scanoss.scanners.container_scanner import (
|
|
45
39
|
DEFAULT_SYFT_COMMAND,
|
|
46
40
|
DEFAULT_SYFT_TIMEOUT,
|
|
@@ -75,8 +69,14 @@ from .csvoutput import CsvOutput
|
|
|
75
69
|
from .cyclonedx import CycloneDx
|
|
76
70
|
from .filecount import FileCount
|
|
77
71
|
from .gitlabqualityreport import GitLabQualityReport
|
|
78
|
-
from .inspection.
|
|
79
|
-
|
|
72
|
+
from .inspection.policy_check.dependency_track.project_violation import (
|
|
73
|
+
DependencyTrackProjectViolationPolicyCheck,
|
|
74
|
+
)
|
|
75
|
+
from .inspection.policy_check.scanoss.copyleft import Copyleft
|
|
76
|
+
from .inspection.policy_check.scanoss.undeclared_component import UndeclaredComponent
|
|
77
|
+
from .inspection.summary.component_summary import ComponentSummary
|
|
78
|
+
from .inspection.summary.license_summary import LicenseSummary
|
|
79
|
+
from .inspection.summary.match_summary import MatchSummary
|
|
80
80
|
from .results import Results
|
|
81
81
|
from .scancodedeps import ScancodeDeps
|
|
82
82
|
from .scanner import FAST_WINNOWING, Scanner
|
|
@@ -1753,7 +1753,6 @@ def inspect_copyleft(parser, args):
|
|
|
1753
1753
|
exclude=args.exclude, # Licenses to ignore
|
|
1754
1754
|
explicit=args.explicit, # Explicit license list
|
|
1755
1755
|
)
|
|
1756
|
-
|
|
1757
1756
|
# Execute inspection and exit with appropriate status code
|
|
1758
1757
|
status, _ = i_copyleft.run()
|
|
1759
1758
|
sys.exit(status)
|
scanoss/data/build_date.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
date:
|
|
1
|
+
date: 20251029145743, utime: 1761749863
|
scanoss/gitlabqualityreport.py
CHANGED
|
@@ -74,16 +74,21 @@ class GitLabQualityReport(ScanossBase):
|
|
|
74
74
|
Initialise the GitLabCodeQuality class
|
|
75
75
|
"""
|
|
76
76
|
super().__init__(debug, trace, quiet)
|
|
77
|
+
self.print_trace(f"GitLabQualityReport initialized with debug={debug}, trace={trace}, quiet={quiet}")
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
def _get_code_quality(self, file_name: str, result: dict) -> CodeQuality or None:
|
|
81
|
+
self.print_trace(f"_get_code_quality called for file: {file_name}")
|
|
82
|
+
self.print_trace(f"Processing result: {result}")
|
|
83
|
+
|
|
80
84
|
if not result.get('file_hash'):
|
|
81
85
|
self.print_debug(f"Warning: no hash found for result: {result}")
|
|
82
86
|
return None
|
|
83
87
|
|
|
84
88
|
if result.get('id') == 'file':
|
|
89
|
+
self.print_debug(f"Processing file match for: {file_name}")
|
|
85
90
|
description = f"File match found in: {file_name}"
|
|
86
|
-
|
|
91
|
+
code_quality = CodeQuality(
|
|
87
92
|
description=description,
|
|
88
93
|
check_name=file_name,
|
|
89
94
|
fingerprint=result.get('file_hash'),
|
|
@@ -95,17 +100,21 @@ class GitLabQualityReport(ScanossBase):
|
|
|
95
100
|
)
|
|
96
101
|
)
|
|
97
102
|
)
|
|
103
|
+
self.print_trace(f"Created file CodeQuality object: {code_quality}")
|
|
104
|
+
return code_quality
|
|
98
105
|
|
|
99
106
|
if not result.get('lines'):
|
|
100
107
|
self.print_debug(f"Warning: No lines found for result: {result}")
|
|
101
108
|
return None
|
|
102
109
|
lines = scanoss_scan_results_utils.get_lines(result.get('lines'))
|
|
110
|
+
self.print_trace(f"Extracted lines: {lines}")
|
|
103
111
|
if len(lines) == 0:
|
|
104
112
|
self.print_debug(f"Warning: empty lines for result: {result}")
|
|
105
113
|
return None
|
|
106
114
|
end_line = lines[len(lines) - 1] if len(lines) > 1 else lines[0]
|
|
107
115
|
description = f"Snippet found in: {file_name} - lines {lines[0]}-{end_line}"
|
|
108
|
-
|
|
116
|
+
self.print_debug(f"Processing snippet match for: {file_name}, lines: {lines[0]}-{end_line}")
|
|
117
|
+
code_quality = CodeQuality(
|
|
109
118
|
description=description,
|
|
110
119
|
check_name=file_name,
|
|
111
120
|
fingerprint=result.get('file_hash'),
|
|
@@ -117,35 +126,47 @@ class GitLabQualityReport(ScanossBase):
|
|
|
117
126
|
)
|
|
118
127
|
)
|
|
119
128
|
)
|
|
129
|
+
self.print_trace(f"Created snippet CodeQuality object: {code_quality}")
|
|
130
|
+
return code_quality
|
|
120
131
|
|
|
121
132
|
def _write_output(self, data: list[CodeQuality], output_file: str = None) -> bool:
|
|
122
133
|
"""Write the Gitlab Code Quality Report to output."""
|
|
134
|
+
self.print_trace(f"_write_output called with {len(data)} items, output_file: {output_file}")
|
|
123
135
|
try:
|
|
124
136
|
json_data = [item.to_dict() for item in data]
|
|
137
|
+
self.print_trace(f"JSON data: {json_data}")
|
|
125
138
|
file = open(output_file, 'w') if output_file else sys.stdout
|
|
126
139
|
print(json.dumps(json_data, indent=2), file=file)
|
|
127
140
|
if output_file:
|
|
128
141
|
file.close()
|
|
142
|
+
self.print_debug(f"Wrote output to file: {output_file}")
|
|
143
|
+
else:
|
|
144
|
+
self.print_debug("Wrote output to 'stdout'")
|
|
129
145
|
return True
|
|
130
146
|
except Exception as e:
|
|
131
147
|
self.print_stderr(f'Error writing output: {str(e)}')
|
|
132
148
|
return False
|
|
133
149
|
|
|
134
150
|
def _produce_from_json(self, data: dict, output_file: str = None) -> bool:
|
|
151
|
+
self.print_trace(f"_produce_from_json called with output_file: {output_file}")
|
|
152
|
+
self.print_debug(f"Processing {len(data)} files from JSON data")
|
|
135
153
|
code_quality = []
|
|
136
154
|
for file_name, results in data.items():
|
|
155
|
+
self.print_trace(f"Processing file: {file_name} with {len(results)} results")
|
|
137
156
|
for result in results:
|
|
138
157
|
if not result.get('id'):
|
|
139
158
|
self.print_debug(f"Warning: No ID found for result: {result}")
|
|
140
159
|
continue
|
|
141
160
|
if result.get('id') != 'snippet' and result.get('id') != 'file':
|
|
142
|
-
self.print_debug(f"Skipping non-snippet/file match: {result}")
|
|
161
|
+
self.print_debug(f"Skipping non-snippet/file match: {file_name}, id: '{result['id']}'")
|
|
143
162
|
continue
|
|
144
163
|
code_quality_item = self._get_code_quality(file_name, result)
|
|
145
164
|
if code_quality_item:
|
|
146
165
|
code_quality.append(code_quality_item)
|
|
166
|
+
self.print_trace(f"Added code quality item for {file_name}")
|
|
147
167
|
else:
|
|
148
168
|
self.print_debug(f"Warning: No Code Quality found for result: {result}")
|
|
169
|
+
self.print_debug(f"Generated {len(code_quality)} code quality items")
|
|
149
170
|
self._write_output(data=code_quality,output_file=output_file)
|
|
150
171
|
return True
|
|
151
172
|
|
|
@@ -156,11 +177,15 @@ class GitLabQualityReport(ScanossBase):
|
|
|
156
177
|
:param output_file: Output file (optional)
|
|
157
178
|
:return: True if successful, False otherwise
|
|
158
179
|
"""
|
|
180
|
+
self.print_trace(f"_produce_from_str called with output_file: {output_file}")
|
|
159
181
|
if not json_str:
|
|
160
182
|
self.print_stderr('ERROR: No JSON string provided to parse.')
|
|
161
183
|
return False
|
|
184
|
+
self.print_debug(f"Parsing JSON string of length: {len(json_str)}")
|
|
162
185
|
try:
|
|
163
186
|
data = json.loads(json_str)
|
|
187
|
+
self.print_debug("Successfully parsed JSON data")
|
|
188
|
+
self.print_trace(f"Parsed data structure: {type(data)}")
|
|
164
189
|
except Exception as e:
|
|
165
190
|
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
166
191
|
return False
|
|
@@ -174,12 +199,16 @@ class GitLabQualityReport(ScanossBase):
|
|
|
174
199
|
:param output_file:
|
|
175
200
|
:return: True if successful, False otherwise
|
|
176
201
|
"""
|
|
202
|
+
self.print_trace(f"produce_from_file called with json_file: {json_file}, output_file: {output_file}")
|
|
203
|
+
self.print_debug(f"Input JSON file: {json_file}, output_file: {output_file}")
|
|
177
204
|
if not json_file:
|
|
178
205
|
self.print_stderr('ERROR: No JSON file provided to parse.')
|
|
179
206
|
return False
|
|
180
207
|
if not os.path.isfile(json_file):
|
|
181
208
|
self.print_stderr(f'ERROR: JSON file does not exist or is not a file: {json_file}')
|
|
182
209
|
return False
|
|
210
|
+
self.print_debug(f"Reading JSON file: {json_file}")
|
|
183
211
|
with open(json_file, 'r') as f:
|
|
184
|
-
|
|
212
|
+
json_content = f.read()
|
|
213
|
+
success = self._produce_from_str(json_content, output_file)
|
|
185
214
|
return success
|
|
File without changes
|
|
@@ -26,9 +26,9 @@ import time
|
|
|
26
26
|
from datetime import datetime
|
|
27
27
|
from typing import Any, Dict, List, Optional, TypedDict
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from ..
|
|
29
|
+
from ....services.dependency_track_service import DependencyTrackService
|
|
30
|
+
from ...utils.markdown_utils import generate_jira_table, generate_table
|
|
31
|
+
from ..policy_check import PolicyCheck, PolicyOutput, PolicyStatus
|
|
32
32
|
|
|
33
33
|
# Constants
|
|
34
34
|
PROCESSING_RETRY_DELAY = 5 # seconds
|
|
@@ -171,7 +171,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
171
171
|
self.url = url.strip().rstrip('/') if url else None
|
|
172
172
|
self.dep_track_service = DependencyTrackService(self.api_key, self.url, debug=debug, trace=trace, quiet=quiet)
|
|
173
173
|
|
|
174
|
-
def _json(self, project_violations: list[PolicyViolationDict]) ->
|
|
174
|
+
def _json(self, project_violations: list[PolicyViolationDict]) -> PolicyOutput:
|
|
175
175
|
"""
|
|
176
176
|
Format project violations as JSON.
|
|
177
177
|
|
|
@@ -181,12 +181,12 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
181
181
|
Returns:
|
|
182
182
|
Dictionary containing JSON formatted results and summary
|
|
183
183
|
"""
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
return PolicyOutput(
|
|
185
|
+
details= json.dumps(project_violations, indent=2),
|
|
186
|
+
summary= f'{len(project_violations)} policy violations were found.\n',
|
|
187
|
+
)
|
|
188
188
|
|
|
189
|
-
def _markdown(self, project_violations: list[PolicyViolationDict]) ->
|
|
189
|
+
def _markdown(self, project_violations: list[PolicyViolationDict]) -> PolicyOutput:
|
|
190
190
|
"""
|
|
191
191
|
Format Dependency Track violations to Markdown format.
|
|
192
192
|
|
|
@@ -198,7 +198,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
198
198
|
"""
|
|
199
199
|
return self._md_summary_generator(project_violations, generate_table)
|
|
200
200
|
|
|
201
|
-
def _jira_markdown(self, data: list[PolicyViolationDict]) ->
|
|
201
|
+
def _jira_markdown(self, data: list[PolicyViolationDict]) -> PolicyOutput:
|
|
202
202
|
"""
|
|
203
203
|
Format project violations for Jira Markdown.
|
|
204
204
|
|
|
@@ -357,8 +357,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
357
357
|
self.print_stderr(f'Error: Failed to get project uuid from: {dt_project}')
|
|
358
358
|
raise ValueError(f'Error: Project {self.project_name}@{self.project_version} does not have a valid UUID')
|
|
359
359
|
|
|
360
|
-
|
|
361
|
-
def _sort_project_violations(violations: List[PolicyViolationDict]) -> List[PolicyViolationDict]:
|
|
360
|
+
def _sort_project_violations(self,violations: List[PolicyViolationDict]) -> List[PolicyViolationDict]:
|
|
362
361
|
"""
|
|
363
362
|
Sort project violations by priority.
|
|
364
363
|
|
|
@@ -377,7 +376,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
377
376
|
key=lambda x: -type_priority.get(x.get('type', 'OTHER'), 1)
|
|
378
377
|
)
|
|
379
378
|
|
|
380
|
-
def _md_summary_generator(self, project_violations: list[PolicyViolationDict], table_generator):
|
|
379
|
+
def _md_summary_generator(self, project_violations: list[PolicyViolationDict], table_generator) -> PolicyOutput:
|
|
381
380
|
"""
|
|
382
381
|
Generates a Markdown summary of project policy violations.
|
|
383
382
|
|
|
@@ -396,10 +395,10 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
396
395
|
"""
|
|
397
396
|
if project_violations is None:
|
|
398
397
|
self.print_stderr('Warning: No project violations found. Returning empty results.')
|
|
399
|
-
return
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
398
|
+
return PolicyOutput(
|
|
399
|
+
details= "h3. Dependency Track Project Violations\n\nNo policy violations found.\n",
|
|
400
|
+
summary= "0 policy violations were found.\n",
|
|
401
|
+
)
|
|
403
402
|
headers = ['State', 'Risk Type', 'Policy Name', 'Component', 'Date']
|
|
404
403
|
c_cols = [0, 1]
|
|
405
404
|
rows: List[List[str]] = []
|
|
@@ -424,11 +423,11 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
424
423
|
]
|
|
425
424
|
rows.append(row)
|
|
426
425
|
# End for loop
|
|
427
|
-
return
|
|
428
|
-
|
|
426
|
+
return PolicyOutput(
|
|
427
|
+
details= f'### Dependency Track Project Violations\n{table_generator(headers, rows, c_cols)}\n\n'
|
|
429
428
|
f'View project in Dependency Track [here]({self.url}/projects/{self.project_id}).\n',
|
|
430
|
-
|
|
431
|
-
|
|
429
|
+
summary= f'{len(project_violations)} policy violations were found.\n'
|
|
430
|
+
)
|
|
432
431
|
|
|
433
432
|
def run(self) -> int:
|
|
434
433
|
"""
|
|
@@ -470,10 +469,11 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
470
469
|
self.print_stderr('Error: Invalid format specified.')
|
|
471
470
|
return PolicyStatus.ERROR.value
|
|
472
471
|
# Format and output data - handle empty results gracefully
|
|
473
|
-
|
|
474
|
-
self.print_to_file_or_stdout(
|
|
475
|
-
self.print_to_file_or_stderr(
|
|
472
|
+
policy_output = formatter(self._sort_project_violations(dt_project_violations))
|
|
473
|
+
self.print_to_file_or_stdout(policy_output.details, self.output)
|
|
474
|
+
self.print_to_file_or_stderr(policy_output.summary, self.status)
|
|
476
475
|
# Return appropriate status based on violation count
|
|
477
476
|
if len(dt_project_violations) > 0:
|
|
478
477
|
return PolicyStatus.POLICY_FAIL.value
|
|
479
478
|
return PolicyStatus.POLICY_SUCCESS.value
|
|
479
|
+
|
|
@@ -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,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 Dict, 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
|
|
@@ -45,7 +45,7 @@ class Component:
|
|
|
45
45
|
licenses: List[License]
|
|
46
46
|
status: str
|
|
47
47
|
|
|
48
|
-
class Copyleft(
|
|
48
|
+
class Copyleft(PolicyCheck[Component]):
|
|
49
49
|
"""
|
|
50
50
|
SCANOSS Copyleft class
|
|
51
51
|
Inspects components for copyleft licenses
|
|
@@ -78,17 +78,23 @@ class Copyleft(RawBase[Component]):
|
|
|
78
78
|
:param exclude: Licenses to exclude from the analysis
|
|
79
79
|
:param explicit: Explicitly defined licenses
|
|
80
80
|
"""
|
|
81
|
-
super().__init__(
|
|
81
|
+
super().__init__(
|
|
82
|
+
debug, trace, quiet, format_type, status, name='Copyleft Policy', output=output
|
|
83
|
+
)
|
|
82
84
|
self.license_util.init(include, exclude, explicit)
|
|
83
85
|
self.filepath = filepath
|
|
84
|
-
self.format = format
|
|
85
86
|
self.output = output
|
|
86
87
|
self.status = status
|
|
87
|
-
self.
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
self.results_processor = ScanResultProcessor(
|
|
89
|
+
self.debug,
|
|
90
|
+
self.trace,
|
|
91
|
+
self.quiet,
|
|
92
|
+
self.filepath,
|
|
93
|
+
include,
|
|
94
|
+
exclude,
|
|
95
|
+
explicit)
|
|
90
96
|
|
|
91
|
-
def _json(self, components: list[Component]) ->
|
|
97
|
+
def _json(self, components: list[Component]) -> PolicyOutput:
|
|
92
98
|
"""
|
|
93
99
|
Format the components with copyleft licenses as JSON.
|
|
94
100
|
|
|
@@ -96,16 +102,16 @@ class Copyleft(RawBase[Component]):
|
|
|
96
102
|
:return: Dictionary with formatted JSON details and summary
|
|
97
103
|
"""
|
|
98
104
|
# A component is considered unique by its combination of PURL (Package URL) and license
|
|
99
|
-
component_licenses = self.
|
|
105
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
100
106
|
details = {}
|
|
101
107
|
if len(components) > 0:
|
|
102
108
|
details = {'components': components}
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
return PolicyOutput(
|
|
110
|
+
details= f'{json.dumps(details, indent=2)}\n',
|
|
111
|
+
summary= f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
|
|
112
|
+
)
|
|
107
113
|
|
|
108
|
-
def _markdown(self, components: list[Component]) ->
|
|
114
|
+
def _markdown(self, components: list[Component]) -> PolicyOutput:
|
|
109
115
|
"""
|
|
110
116
|
Format the components with copyleft licenses as Markdown.
|
|
111
117
|
|
|
@@ -114,7 +120,7 @@ class Copyleft(RawBase[Component]):
|
|
|
114
120
|
"""
|
|
115
121
|
return self._md_summary_generator(components, generate_table)
|
|
116
122
|
|
|
117
|
-
def _jira_markdown(self, components: list[Component]) ->
|
|
123
|
+
def _jira_markdown(self, components: list[Component]) -> PolicyOutput:
|
|
118
124
|
"""
|
|
119
125
|
Format the components with copyleft licenses as Markdown.
|
|
120
126
|
|
|
@@ -123,7 +129,7 @@ class Copyleft(RawBase[Component]):
|
|
|
123
129
|
"""
|
|
124
130
|
return self._md_summary_generator(components, generate_jira_table)
|
|
125
131
|
|
|
126
|
-
def _md_summary_generator(self, components: list[Component], table_generator):
|
|
132
|
+
def _md_summary_generator(self, components: list[Component], table_generator) -> PolicyOutput:
|
|
127
133
|
"""
|
|
128
134
|
Generates a Markdown summary for components with a focus on copyleft licenses.
|
|
129
135
|
|
|
@@ -138,15 +144,10 @@ class Copyleft(RawBase[Component]):
|
|
|
138
144
|
A callable function to generate tabular data for components.
|
|
139
145
|
|
|
140
146
|
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.
|
|
147
|
+
PolicyOutput
|
|
147
148
|
"""
|
|
148
149
|
# A component is considered unique by its combination of PURL (Package URL) and license
|
|
149
|
-
component_licenses = self.
|
|
150
|
+
component_licenses = self.results_processor.group_components_by_license(components)
|
|
150
151
|
headers = ['Component', 'License', 'URL', 'Copyleft']
|
|
151
152
|
centered_columns = [1, 4]
|
|
152
153
|
rows = []
|
|
@@ -160,10 +161,10 @@ class Copyleft(RawBase[Component]):
|
|
|
160
161
|
rows.append(row)
|
|
161
162
|
# End license loop
|
|
162
163
|
# End component loop
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
return PolicyOutput(
|
|
165
|
+
details= f'### Copyleft Licenses\n{table_generator(headers, rows, centered_columns)}',
|
|
166
|
+
summary= f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
|
|
167
|
+
)
|
|
167
168
|
|
|
168
169
|
def _get_components_with_copyleft_licenses(self, components: list) -> list[Dict]:
|
|
169
170
|
"""
|
|
@@ -202,14 +203,13 @@ class Copyleft(RawBase[Component]):
|
|
|
202
203
|
|
|
203
204
|
:return: A list of processed components with license data, or `None` if `self.results` is not set.
|
|
204
205
|
"""
|
|
205
|
-
if self.
|
|
206
|
+
if self.results_processor.get_results() is None:
|
|
206
207
|
return None
|
|
207
|
-
|
|
208
208
|
components: dict = {}
|
|
209
209
|
# Extract component and license data from file and dependency results. Both helpers mutate `components`
|
|
210
|
-
self.
|
|
211
|
-
self.
|
|
212
|
-
return self.
|
|
210
|
+
self.results_processor.get_components_data(components)
|
|
211
|
+
self.results_processor.get_dependencies_data(components)
|
|
212
|
+
return self.results_processor.convert_components_to_list(components)
|
|
213
213
|
|
|
214
214
|
def run(self):
|
|
215
215
|
"""
|
|
@@ -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
|
|
@@ -94,6 +94,7 @@ class MatchSummary(ScanossBase):
|
|
|
94
94
|
self.scanoss_results_path = scanoss_results_path
|
|
95
95
|
self.line_range_prefix = line_range_prefix
|
|
96
96
|
self.output = output
|
|
97
|
+
self.print_debug("Initializing MatchSummary class")
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
def _get_match_summary_item(self, file_name: str, result: dict) -> MatchSummaryItem:
|
|
@@ -108,11 +109,16 @@ class MatchSummary(ScanossBase):
|
|
|
108
109
|
:param result: SCANOSS scan result dictionary containing match details
|
|
109
110
|
:return: Populated match summary item with all relevant information
|
|
110
111
|
"""
|
|
112
|
+
self.print_trace(f"Creating match summary item for file: {file_name}, id: {result.get('id')}")
|
|
113
|
+
|
|
111
114
|
if result.get('id') == "snippet":
|
|
112
115
|
# Snippet match: create URL with line range anchor
|
|
113
116
|
lines = scanoss_scan_results_utils.get_lines(result.get('lines'))
|
|
114
117
|
end_line = lines[len(lines) - 1] if len(lines) > 1 else lines[0]
|
|
115
118
|
file_url = f"{self.line_range_prefix}/{file_name}#L{lines[0]}-L{end_line}"
|
|
119
|
+
|
|
120
|
+
self.print_trace(f"Snippet match: lines {lines[0]}-{end_line}, purl: {result.get('purl')[0]}")
|
|
121
|
+
|
|
116
122
|
return MatchSummaryItem(
|
|
117
123
|
file_url=file_url,
|
|
118
124
|
file=file_name,
|
|
@@ -124,6 +130,8 @@ class MatchSummary(ScanossBase):
|
|
|
124
130
|
lines=f"{lines[0]}-{lines[len(lines) - 1] if len(lines) > 1 else lines[0]}"
|
|
125
131
|
)
|
|
126
132
|
# File match: create URL without line range
|
|
133
|
+
self.print_trace(f"File match: {file_name}, purl: {result.get('purl')[0]}, version: {result.get('version')}")
|
|
134
|
+
|
|
127
135
|
return MatchSummaryItem(
|
|
128
136
|
file=file_name,
|
|
129
137
|
file_url=f"{self.line_range_prefix}/{file_name}",
|
|
@@ -176,12 +184,19 @@ class MatchSummary(ScanossBase):
|
|
|
176
184
|
required fields and categorizing matches into file matches and snippet matches.
|
|
177
185
|
Skips invalid or incomplete results with debug messages.
|
|
178
186
|
"""
|
|
187
|
+
self.print_debug(f"Loading scan results from: {self.scanoss_results_path}")
|
|
188
|
+
|
|
179
189
|
# Load scan results from JSON file
|
|
180
190
|
scan_results = load_json_file(self.scanoss_results_path)
|
|
181
191
|
gitlab_matches_summary = ComponentMatchSummary(files=[], snippet=[])
|
|
182
192
|
|
|
193
|
+
self.print_debug(f"Processing {len(scan_results)} files from scan results")
|
|
194
|
+
self.print_trace(f"Line range prefix set to: {self.line_range_prefix}")
|
|
195
|
+
|
|
183
196
|
# Process each file and its results
|
|
184
197
|
for file_name, results in scan_results.items():
|
|
198
|
+
self.print_trace(f"Processing file: {file_name} with {len(results)} results")
|
|
199
|
+
|
|
185
200
|
for result in results:
|
|
186
201
|
# Skip non-matches
|
|
187
202
|
if result.get('id') == "none":
|
|
@@ -196,8 +211,15 @@ class MatchSummary(ScanossBase):
|
|
|
196
211
|
summary_item = self._get_match_summary_item(file_name, result)
|
|
197
212
|
if result.get('id') == "snippet":
|
|
198
213
|
gitlab_matches_summary.snippet.append(summary_item)
|
|
214
|
+
self.print_trace(f"Added snippet match for {file_name}")
|
|
199
215
|
else:
|
|
200
216
|
gitlab_matches_summary.files.append(summary_item)
|
|
217
|
+
self.print_trace(f"Added file match for {file_name}")
|
|
218
|
+
|
|
219
|
+
self.print_debug(
|
|
220
|
+
f"Match summary complete: {len(gitlab_matches_summary.files)} file matches, "
|
|
221
|
+
f"{len(gitlab_matches_summary.snippet)} snippet matches"
|
|
222
|
+
)
|
|
201
223
|
|
|
202
224
|
return gitlab_matches_summary
|
|
203
225
|
|
|
@@ -212,14 +234,23 @@ class MatchSummary(ScanossBase):
|
|
|
212
234
|
:param gitlab_matches_summary: Container with categorized file and snippet matches to format
|
|
213
235
|
:return: Complete Markdown document with formatted match tables
|
|
214
236
|
"""
|
|
237
|
+
self.print_debug("Generating Markdown from match summaries")
|
|
215
238
|
|
|
216
239
|
if len(gitlab_matches_summary.files) == 0 and len(gitlab_matches_summary.snippet) == 0:
|
|
240
|
+
self.print_debug("No matches to format - returning empty string")
|
|
217
241
|
return ""
|
|
218
242
|
|
|
243
|
+
self.print_trace(
|
|
244
|
+
f"Formatting {len(gitlab_matches_summary.files)} file matches and "
|
|
245
|
+
f"{len(gitlab_matches_summary.snippet)} snippet matches"
|
|
246
|
+
)
|
|
247
|
+
|
|
219
248
|
# Define table headers
|
|
220
249
|
file_match_headers = ['File', 'License', 'Similarity', 'PURL', 'Version']
|
|
221
250
|
snippet_match_headers = ['File', 'License', 'Similarity', 'PURL', 'Version', 'Lines']
|
|
251
|
+
|
|
222
252
|
# Build file matches table
|
|
253
|
+
self.print_trace("Building file matches table")
|
|
223
254
|
file_match_rows = []
|
|
224
255
|
for file_match in gitlab_matches_summary.files:
|
|
225
256
|
row = [
|
|
@@ -233,6 +264,7 @@ class MatchSummary(ScanossBase):
|
|
|
233
264
|
file_match_table = generate_table(file_match_headers, file_match_rows)
|
|
234
265
|
|
|
235
266
|
# Build snippet matches table
|
|
267
|
+
self.print_trace("Building snippet matches table")
|
|
236
268
|
snippet_match_rows = []
|
|
237
269
|
for snippet_match in gitlab_matches_summary.snippet:
|
|
238
270
|
row = [
|
|
@@ -262,6 +294,8 @@ class MatchSummary(ScanossBase):
|
|
|
262
294
|
markdown += snippet_match_table
|
|
263
295
|
markdown += "\n</details>\n"
|
|
264
296
|
|
|
297
|
+
self.print_trace(f"Markdown generation complete (length: {len(markdown)} characters)")
|
|
298
|
+
self.print_debug("Match summary Markdown generation complete")
|
|
265
299
|
return markdown
|
|
266
300
|
|
|
267
301
|
def run(self):
|
|
@@ -275,16 +309,33 @@ class MatchSummary(ScanossBase):
|
|
|
275
309
|
3. Generates Markdown report
|
|
276
310
|
4. Outputs to file or stdout
|
|
277
311
|
"""
|
|
312
|
+
self.print_debug("Starting match summary generation process")
|
|
313
|
+
self.print_trace(
|
|
314
|
+
f"Configuration - Results path: {self.scanoss_results_path}, Output: {self.output}, "
|
|
315
|
+
f"Line range prefix: {self.line_range_prefix}"
|
|
316
|
+
)
|
|
317
|
+
|
|
278
318
|
# Load and process scan results into categorized matches
|
|
319
|
+
self.print_trace("Loading and processing scan results")
|
|
279
320
|
matches = self._get_matches_summary()
|
|
280
321
|
|
|
281
322
|
# Format matches as GitLab-compatible Markdown
|
|
323
|
+
self.print_trace("Generating Markdown output")
|
|
282
324
|
matches_md = self._markdown(matches)
|
|
283
325
|
if matches_md == "":
|
|
326
|
+
self.print_debug("No matches found - exiting")
|
|
284
327
|
self.print_stdout("No matches found.")
|
|
285
328
|
return
|
|
329
|
+
|
|
286
330
|
# Output to file or stdout
|
|
331
|
+
self.print_trace("Writing output")
|
|
332
|
+
if self.output:
|
|
333
|
+
self.print_debug(f"Writing match summary to file: {self.output}")
|
|
334
|
+
else:
|
|
335
|
+
self.print_debug("Writing match summary to 'stdout'")
|
|
336
|
+
|
|
287
337
|
self.print_to_file_or_stdout(matches_md, self.output)
|
|
338
|
+
self.print_debug("Match summary generation complete")
|
|
288
339
|
|
|
289
340
|
|
|
290
341
|
|
|
@@ -22,11 +22,10 @@ SPDX-License-Identifier: MIT
|
|
|
22
22
|
THE SOFTWARE.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
|
-
from abc import abstractmethod
|
|
26
25
|
from enum import Enum
|
|
27
26
|
from typing import Any, Dict, TypeVar
|
|
28
27
|
|
|
29
|
-
from
|
|
28
|
+
from ...scanossbase import ScanossBase
|
|
30
29
|
from ..utils.file_utils import load_json_file
|
|
31
30
|
from ..utils.license_utils import LicenseUtil
|
|
32
31
|
|
|
@@ -51,12 +50,13 @@ class ComponentID(Enum):
|
|
|
51
50
|
#
|
|
52
51
|
|
|
53
52
|
T = TypeVar('T')
|
|
54
|
-
class
|
|
53
|
+
class ScanResultProcessor(ScanossBase):
|
|
55
54
|
"""
|
|
56
|
-
A
|
|
55
|
+
A utility class for processing and transforming scan results.
|
|
57
56
|
|
|
58
|
-
This class provides
|
|
59
|
-
|
|
57
|
+
This class provides functionality for processing scan results, including methods for
|
|
58
|
+
loading, parsing, extracting, and aggregating component and license data from scan results.
|
|
59
|
+
It serves as a shared data processing layer used by both policy checks and summary generators.
|
|
60
60
|
|
|
61
61
|
Inherits from:
|
|
62
62
|
ScanossBase: A base class providing common functionality for SCANOSS-related operations.
|
|
@@ -67,40 +67,19 @@ class RawBase(PolicyCheck[T]):
|
|
|
67
67
|
debug: bool = False,
|
|
68
68
|
trace: bool = False,
|
|
69
69
|
quiet: bool = False,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
name: str = None,
|
|
70
|
+
result_file_path: str = None,
|
|
71
|
+
include: str = None,
|
|
72
|
+
exclude: str = None,
|
|
73
|
+
explicit: str = None,
|
|
75
74
|
):
|
|
76
|
-
super().__init__(debug, trace, quiet
|
|
75
|
+
super().__init__(debug, trace, quiet)
|
|
76
|
+
self.result_file_path = result_file_path
|
|
77
77
|
self.license_util = LicenseUtil()
|
|
78
|
-
self.
|
|
79
|
-
self.output = output
|
|
78
|
+
self.license_util.init(include, exclude, explicit)
|
|
80
79
|
self.results = self._load_input_file()
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"""
|
|
85
|
-
Retrieve and process components from the preloaded results.
|
|
86
|
-
|
|
87
|
-
This method performs the following steps:
|
|
88
|
-
1. Checks if the results have been previously loaded (self.results).
|
|
89
|
-
2. Extracts and processes components from the loaded results.
|
|
90
|
-
|
|
91
|
-
:return: A list of processed components, or None if an error occurred during any step.
|
|
92
|
-
|
|
93
|
-
Possible reasons for returning None include:
|
|
94
|
-
- Results not loaded (self.results is None)
|
|
95
|
-
- Failure to extract components from the results
|
|
96
|
-
|
|
97
|
-
Note:
|
|
98
|
-
- This method assumes that the results have been previously loaded and stored in self.results.
|
|
99
|
-
- Implementations must extract components (e.g. via `_get_components_data`,
|
|
100
|
-
`_get_dependencies_data`, or other helpers).
|
|
101
|
-
- If `self.results` is `None`, simply return `None`.
|
|
102
|
-
"""
|
|
103
|
-
pass
|
|
81
|
+
def get_results(self) -> Dict[str, Any]:
|
|
82
|
+
return self.results
|
|
104
83
|
|
|
105
84
|
def _append_component(self, components: Dict[str, Any], new_component: Dict[str, Any]) -> Dict[str, Any]:
|
|
106
85
|
"""
|
|
@@ -213,7 +192,7 @@ class RawBase(PolicyCheck[T]):
|
|
|
213
192
|
else:
|
|
214
193
|
component['undeclared'] += 1
|
|
215
194
|
|
|
216
|
-
def
|
|
195
|
+
def get_components_data(self, components: Dict[str, Any]) -> Dict[str, Any]:
|
|
217
196
|
"""
|
|
218
197
|
Extract and process file and snippet components from results.
|
|
219
198
|
|
|
@@ -230,11 +209,11 @@ class RawBase(PolicyCheck[T]):
|
|
|
230
209
|
which tracks the number of occurrences of each license
|
|
231
210
|
|
|
232
211
|
Args:
|
|
233
|
-
|
|
212
|
+
components: A dictionary containing the raw results of a component scan
|
|
234
213
|
Returns:
|
|
235
214
|
Updated components dictionary with file and snippet data
|
|
236
215
|
"""
|
|
237
|
-
for component in results.values():
|
|
216
|
+
for component in self.results.values():
|
|
238
217
|
for c in component:
|
|
239
218
|
component_id = c.get('id')
|
|
240
219
|
if not component_id:
|
|
@@ -266,15 +245,13 @@ class RawBase(PolicyCheck[T]):
|
|
|
266
245
|
# End components loop
|
|
267
246
|
return components
|
|
268
247
|
|
|
269
|
-
def
|
|
248
|
+
def get_dependencies_data(self,components: Dict[str, Any]) -> Dict[str, Any]:
|
|
270
249
|
"""
|
|
271
250
|
Extract and process dependency components from results.
|
|
272
|
-
|
|
273
|
-
:param results: A dictionary containing the raw results of a component scan
|
|
274
251
|
:param components: Existing components dictionary to update
|
|
275
252
|
:return: Updated components dictionary with dependency data
|
|
276
253
|
"""
|
|
277
|
-
for component in results.values():
|
|
254
|
+
for component in self.results.values():
|
|
278
255
|
for c in component:
|
|
279
256
|
component_id = c.get('id')
|
|
280
257
|
if not component_id:
|
|
@@ -313,12 +290,12 @@ class RawBase(PolicyCheck[T]):
|
|
|
313
290
|
Dict[str, Any]: The parsed JSON data
|
|
314
291
|
"""
|
|
315
292
|
try:
|
|
316
|
-
return load_json_file(self.
|
|
293
|
+
return load_json_file(self.result_file_path)
|
|
317
294
|
except Exception as e:
|
|
318
295
|
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
319
296
|
return None
|
|
320
297
|
|
|
321
|
-
def
|
|
298
|
+
def convert_components_to_list(self, components: dict):
|
|
322
299
|
if components is None:
|
|
323
300
|
self.print_debug(f'WARNING: Components is empty {self.results}')
|
|
324
301
|
return None
|
|
@@ -372,7 +349,7 @@ class RawBase(PolicyCheck[T]):
|
|
|
372
349
|
self.print_debug("No priority sources found, returning all licenses as list")
|
|
373
350
|
return licenses_data
|
|
374
351
|
|
|
375
|
-
def
|
|
352
|
+
def group_components_by_license(self,components):
|
|
376
353
|
"""
|
|
377
354
|
Groups components by their unique component-license pairs.
|
|
378
355
|
|
|
@@ -425,5 +402,5 @@ class RawBase(PolicyCheck[T]):
|
|
|
425
402
|
|
|
426
403
|
|
|
427
404
|
#
|
|
428
|
-
# End of
|
|
429
|
-
#
|
|
405
|
+
# End of ScanResultProcessor Class
|
|
406
|
+
#
|
|
@@ -6,8 +6,8 @@ protoc_gen_swagger/options/annotations_pb2_grpc.py,sha256=KZOW9Ciio-f9iL42FuLFnS
|
|
|
6
6
|
protoc_gen_swagger/options/openapiv2_pb2.py,sha256=w0xDs63uyrWGgzRaQZXfJpfI7Jpyvh-i9ay_uzOR-aM,16475
|
|
7
7
|
protoc_gen_swagger/options/openapiv2_pb2.pyi,sha256=hYOV6uQ2yqhP89042_V3GuAsvoBBiXf5CGuYmnFnfv4,54665
|
|
8
8
|
protoc_gen_swagger/options/openapiv2_pb2_grpc.py,sha256=sje9Nh3yE7CHCUWZwtjTgwsKB4GvyGz5vOrGTnRXJfc,917
|
|
9
|
-
scanoss/__init__.py,sha256=
|
|
10
|
-
scanoss/cli.py,sha256=
|
|
9
|
+
scanoss/__init__.py,sha256=L1J5vnLoMlbihOfQRzq6JZhDU_GCyPSqF1uO7gM1vhI,1146
|
|
10
|
+
scanoss/cli.py,sha256=RbhaH1qkeIAj6ZA7JCLN9NFbfgk51nIaKdP9ppw1ogk,103328
|
|
11
11
|
scanoss/components.py,sha256=NFyt_w3aoMotr_ZaFU-ng00_89sruc0kgY7ERnJXkmM,15891
|
|
12
12
|
scanoss/constants.py,sha256=GHLTaLNVxXdTXRj7ngRK4u4S653pHzM8qFy4JFLa0wQ,450
|
|
13
13
|
scanoss/cryptography.py,sha256=lOoD_dW16ARQxYiYyb5R8S7gx0FqWIsnGkKfsB0nGaU,10627
|
|
@@ -16,7 +16,7 @@ scanoss/cyclonedx.py,sha256=mHeX66yQCk41N3YCIzKy_fI7fLqQnetYPFRIzUKy_M4,18416
|
|
|
16
16
|
scanoss/delta.py,sha256=slmgnD7SsUOmfSE2zb0zdRAGo-JcjPJAtxyzuCSzO3I,9455
|
|
17
17
|
scanoss/file_filters.py,sha256=QcLqunaBKQIafjNZ9_Snh9quBX5_-fsTusVmxwjC1q8,18511
|
|
18
18
|
scanoss/filecount.py,sha256=RZjKQ6M5P_RQg0_PMD2tsRe5Z8f98ke0sxYVjPDN8iQ,6538
|
|
19
|
-
scanoss/gitlabqualityreport.py,sha256=
|
|
19
|
+
scanoss/gitlabqualityreport.py,sha256=_VG0Xoh8wYF3lsXGJvjoj-Ty58OS_-H1Domiq9OpQEo,8830
|
|
20
20
|
scanoss/results.py,sha256=47ZXXuU2sDjYa5vhtbWTmikit9jHhA0rsYKwkvZFI5w,9252
|
|
21
21
|
scanoss/scancodedeps.py,sha256=JbpoGW1POtPMmowzfwa4oh8sSBeeQCqaW9onvc4UFYM,11517
|
|
22
22
|
scanoss/scanner.py,sha256=-RCxLX0EepUebK8jQKvlMxFEQrCc8SwEjxznoWjadkg,45510
|
|
@@ -65,25 +65,28 @@ scanoss/api/vulnerabilities/__init__.py,sha256=IFrDk_DTJgKSZmmU-nuLXuq_s8sQZlrSC
|
|
|
65
65
|
scanoss/api/vulnerabilities/v2/__init__.py,sha256=IFrDk_DTJgKSZmmU-nuLXuq_s8sQZlrSCHhIDMJT4r0,1122
|
|
66
66
|
scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py,sha256=pmm0MSiXkdf8e4rCIIDRcsNRixR2vGvD1Xak4l-wdwI,16550
|
|
67
67
|
scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py,sha256=BNxT5kUKQ-mgtOt5QYBM1Qrg5LNDqSpWKpfEZquIlsM,19127
|
|
68
|
-
scanoss/data/build_date.txt,sha256=
|
|
68
|
+
scanoss/data/build_date.txt,sha256=AZUMMETLX1EWk-JNSGm9CsOJOMQ-GeE5y7XrTKZJC0w,40
|
|
69
69
|
scanoss/data/scanoss-settings-schema.json,sha256=ClkRYAkjAN0Sk704G8BE_Ok006oQ6YnIGmX84CF8h9w,8798
|
|
70
70
|
scanoss/data/spdx-exceptions.json,sha256=s7UTYxC7jqQXr11YBlIWYCNwN6lRDFTR33Y8rpN_dA4,17953
|
|
71
71
|
scanoss/data/spdx-licenses.json,sha256=A6Z0q82gaTLtnopBfzeIVZjJFxkdRW1g2TuumQc-lII,228794
|
|
72
72
|
scanoss/export/__init__.py,sha256=D4C0lWLuNp8k_BjQZEc07WZcUgAvriVwQWOk063b0ZU,1122
|
|
73
73
|
scanoss/export/dependency_track.py,sha256=A_xQH6_r9xL_fth1Wr770GCTRFVyn7XcUPfVUsXp4-w,9271
|
|
74
74
|
scanoss/inspection/__init__.py,sha256=D4C0lWLuNp8k_BjQZEc07WZcUgAvriVwQWOk063b0ZU,1122
|
|
75
|
-
scanoss/inspection/policy_check.py,sha256=
|
|
76
|
-
scanoss/inspection/
|
|
77
|
-
scanoss/inspection/
|
|
78
|
-
scanoss/inspection/
|
|
79
|
-
scanoss/inspection/
|
|
80
|
-
scanoss/inspection/
|
|
81
|
-
scanoss/inspection/
|
|
82
|
-
scanoss/inspection/
|
|
83
|
-
scanoss/inspection/
|
|
75
|
+
scanoss/inspection/policy_check/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
|
+
scanoss/inspection/policy_check/policy_check.py,sha256=nMb5ogRDntYMebm3xrIBde2GqNuzOCAsawyldEtfWkA,8365
|
|
77
|
+
scanoss/inspection/policy_check/dependency_track/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
|
+
scanoss/inspection/policy_check/dependency_track/project_violation.py,sha256=elc35kWffWxOoF8YMi50hK_obtc1Xa8E3dwUnz7WXcA,20713
|
|
79
|
+
scanoss/inspection/policy_check/scanoss/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
|
+
scanoss/inspection/policy_check/scanoss/copyleft.py,sha256=rfw_kEZnVbia72j5T7nX_1hTKHMOJLjegmQv_AGoeks,9375
|
|
81
|
+
scanoss/inspection/policy_check/scanoss/undeclared_component.py,sha256=ZkqPbfFzUHNEde-iVRWUSpkNKMTW_rOZQqG0y94MtUU,11673
|
|
82
|
+
scanoss/inspection/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
|
+
scanoss/inspection/summary/component_summary.py,sha256=ZaQzvZBQDFtow43UC9bij5NCTHjY_QAEqIUyRp3KtWk,7308
|
|
84
|
+
scanoss/inspection/summary/license_summary.py,sha256=BC_REu2e4PkMHp0ttvSdf6yRW3MvFc623fT6_Ze_TQA,7958
|
|
85
|
+
scanoss/inspection/summary/match_summary.py,sha256=GdjIV7ws7fanQTVdBigYGx3683KPL6e6HO4wuXTKq9g,13812
|
|
84
86
|
scanoss/inspection/utils/file_utils.py,sha256=b-xTH6FSyPpl3EPZ9WzK0c4734yE9mAexT1_YLLqymE,1641
|
|
85
87
|
scanoss/inspection/utils/license_utils.py,sha256=Zb6QLmVJb86lKCwZyBsmwakyAtY1SXa54kUyyKmWMqA,5093
|
|
86
88
|
scanoss/inspection/utils/markdown_utils.py,sha256=zkFs48TM-NR6nUHYOyQmHCwV82_fUsks5UB4BmyGifU,2446
|
|
89
|
+
scanoss/inspection/utils/scan_result_processor.py,sha256=z2kST2v5rw9XkRdgYejjMN9UHBmgyN5ZPYAPmJY3nOU,17326
|
|
87
90
|
scanoss/scanners/__init__.py,sha256=D4C0lWLuNp8k_BjQZEc07WZcUgAvriVwQWOk063b0ZU,1122
|
|
88
91
|
scanoss/scanners/container_scanner.py,sha256=fOrb64owrstX7LnTuxiIan059YgLeKXeBS6g2QaCyq0,16346
|
|
89
92
|
scanoss/scanners/folder_hasher.py,sha256=PD1tghOrra3KtfsZJUbqKOmIBF-0Tg14FcBCKkqGUis,12873
|
|
@@ -96,9 +99,9 @@ scanoss/utils/crc64.py,sha256=TMrwQimSdE6imhFOUL7oAG6Kxu-8qMpGWMuMg8QpSVs,3169
|
|
|
96
99
|
scanoss/utils/file.py,sha256=62cA9a17TU9ZvfA3FY5HY4-QOajJeSrc8S6xLA_f-3M,2980
|
|
97
100
|
scanoss/utils/scanoss_scan_results_utils.py,sha256=ho9-DKefHFJlVZkw4gXOmMI-mgPIbV9Y2ftkI83fC1k,1727
|
|
98
101
|
scanoss/utils/simhash.py,sha256=6iu8DOcecPAY36SZjCOzrrLMT9oIE7-gI6QuYwUQ7B0,5793
|
|
99
|
-
scanoss-1.40.
|
|
100
|
-
scanoss-1.40.
|
|
101
|
-
scanoss-1.40.
|
|
102
|
-
scanoss-1.40.
|
|
103
|
-
scanoss-1.40.
|
|
104
|
-
scanoss-1.40.
|
|
102
|
+
scanoss-1.40.1.dist-info/licenses/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
|
|
103
|
+
scanoss-1.40.1.dist-info/METADATA,sha256=_XDz8arP4O48Twcovv2A8x-ODDRIPxlupGrJGyO64O8,6181
|
|
104
|
+
scanoss-1.40.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
105
|
+
scanoss-1.40.1.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
|
|
106
|
+
scanoss-1.40.1.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
|
|
107
|
+
scanoss-1.40.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|