fosslight-source 2.3.4__tar.gz → 2.3.6__tar.gz
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.
- {fosslight_source-2.3.4/src/fosslight_source.egg-info → fosslight_source-2.3.6}/PKG-INFO +1 -2
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/pyproject.toml +4 -2
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_help.py +1 -1
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_parsing_scancode_file_item.py +22 -8
- fosslight_source-2.3.6/src/fosslight_source/_scancode_ignore_binaries.py +69 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/cli.py +85 -28
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/run_scancode.py +14 -128
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/run_scanoss.py +76 -11
- {fosslight_source-2.3.4 → fosslight_source-2.3.6/src/fosslight_source.egg-info}/PKG-INFO +1 -2
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source.egg-info/SOURCES.txt +1 -0
- fosslight_source-2.3.6/src/fosslight_source.egg-info/entry_points.txt +5 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source.egg-info/requires.txt +0 -1
- fosslight_source-2.3.4/src/fosslight_source.egg-info/entry_points.txt +0 -2
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/LICENSE +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/MANIFEST.in +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/README.md +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/setup.cfg +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/__init__.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_kb_client.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_license_matched.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_merge.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_parsing_scanoss_file.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_scan_item.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/run_manifest_extractor.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/run_spdx_extractor.py +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source.egg-info/dependency_links.txt +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source.egg-info/top_level.txt +0 -0
- {fosslight_source-2.3.4 → fosslight_source-2.3.6}/tests/test_tox.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fosslight_source
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
4
4
|
Summary: FOSSLight Source Scanner
|
|
5
5
|
Author: LG Electronics
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -24,7 +24,6 @@ Requires-Dist: fosslight_util>=2.2.2
|
|
|
24
24
|
Requires-Dist: PyYAML
|
|
25
25
|
Requires-Dist: wheel>=0.38.1
|
|
26
26
|
Requires-Dist: intbitset
|
|
27
|
-
Requires-Dist: fosslight_binary>=5.1.22
|
|
28
27
|
Requires-Dist: scancode-toolkit>=32.0.2
|
|
29
28
|
Requires-Dist: lxml>=6.0.1
|
|
30
29
|
Requires-Dist: cryptography<49; platform_system == "Darwin" and platform_machine == "x86_64"
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "fosslight_source"
|
|
10
|
-
version = "2.3.
|
|
10
|
+
version = "2.3.6"
|
|
11
11
|
description = "FOSSLight Source Scanner"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
license = "Apache-2.0"
|
|
@@ -33,7 +33,6 @@ dependencies = [
|
|
|
33
33
|
"PyYAML",
|
|
34
34
|
"wheel>=0.38.1",
|
|
35
35
|
"intbitset",
|
|
36
|
-
"fosslight_binary>=5.1.22",
|
|
37
36
|
"scancode-toolkit>=32.0.2",
|
|
38
37
|
"lxml>=6.0.1",
|
|
39
38
|
# cryptography 49.x does not provide macOS x86_64 wheels, causing source builds to require OpenSSL/pkg-config.
|
|
@@ -56,6 +55,9 @@ Download = "https://github.com/fosslight/fosslight_source_scanner"
|
|
|
56
55
|
[project.scripts]
|
|
57
56
|
fosslight_source = "fosslight_source.cli:main"
|
|
58
57
|
|
|
58
|
+
[project.entry-points."scancode_pre_scan"]
|
|
59
|
+
ignore-binaries = "fosslight_source._scancode_ignore_binaries:IgnoreBinaries"
|
|
60
|
+
|
|
59
61
|
[tool.setuptools]
|
|
60
62
|
package-dir = {"" = "src"}
|
|
61
63
|
|
|
@@ -37,7 +37,7 @@ _HELP_MESSAGE_SOURCE_SCANNER = f"""
|
|
|
37
37
|
────────────────────────────────────────────────────────────────────
|
|
38
38
|
-s <mode> Choose mode: scancode, scanoss, kb, or all(default)
|
|
39
39
|
-c <number> Number of CPU cores/threads to use for scanning
|
|
40
|
-
-t <seconds> Timeout in seconds for ScanCode scanning
|
|
40
|
+
-t <seconds> Timeout in seconds for ScanCode and SCANOSS scanning
|
|
41
41
|
-j Generate raw scanner results in JSON format
|
|
42
42
|
--no_merge Keep source paths file-based without folder merge
|
|
43
43
|
--no_correction Skip OSS information correction with sbom-info.yaml
|
|
@@ -73,7 +73,9 @@ def get_error_from_header(header_item: list) -> Tuple[bool, str]:
|
|
|
73
73
|
return has_error, str_error
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
def parsing_scancode_32_earlier(
|
|
76
|
+
def parsing_scancode_32_earlier(
|
|
77
|
+
scancode_file_list: list, has_error: bool = False, ui_mode: bool = False
|
|
78
|
+
) -> Tuple[bool, list, list, dict]:
|
|
77
79
|
rc = True
|
|
78
80
|
msg = []
|
|
79
81
|
scancode_file_item = []
|
|
@@ -118,7 +120,10 @@ def parsing_scancode_32_earlier(scancode_file_list: list, has_error: bool = Fals
|
|
|
118
120
|
|
|
119
121
|
# Set the license value
|
|
120
122
|
license_detected = []
|
|
121
|
-
if
|
|
123
|
+
if not licenses:
|
|
124
|
+
licenses = []
|
|
125
|
+
# Keep license and/or copyright findings; UI keeps finding-less files too.
|
|
126
|
+
if not licenses and not copyright_value_list and not ui_mode:
|
|
122
127
|
continue
|
|
123
128
|
|
|
124
129
|
license_expression_list = file.get("license_expressions", {})
|
|
@@ -191,6 +196,9 @@ def parsing_scancode_32_earlier(scancode_file_list: list, has_error: bool = Fals
|
|
|
191
196
|
set(license_expression_list))
|
|
192
197
|
result_item.comment = ','.join(license_expression_list)
|
|
193
198
|
|
|
199
|
+
scancode_file_item.append(result_item)
|
|
200
|
+
elif copyright_value_list or ui_mode:
|
|
201
|
+
result_item.copyright = copyright_value_list
|
|
194
202
|
scancode_file_item.append(result_item)
|
|
195
203
|
except Exception as ex:
|
|
196
204
|
msg.append(f"Error Parsing item: {ex}")
|
|
@@ -223,7 +231,7 @@ def get_license_expression_spdx(license_expression: str) -> str:
|
|
|
223
231
|
|
|
224
232
|
|
|
225
233
|
def parsing_scancode_32_later(
|
|
226
|
-
scancode_file_list: list, has_error: bool = False
|
|
234
|
+
scancode_file_list: list, has_error: bool = False, ui_mode: bool = False
|
|
227
235
|
) -> Tuple[bool, list, list, dict]:
|
|
228
236
|
rc = True
|
|
229
237
|
msg = []
|
|
@@ -258,9 +266,10 @@ def parsing_scancode_32_later(
|
|
|
258
266
|
copyright_value_list.append(copyright_data)
|
|
259
267
|
license_detected = []
|
|
260
268
|
licenses = file.get("license_detections", [])
|
|
261
|
-
|
|
269
|
+
# Keep license and/or copyright findings; UI keeps finding-less files too.
|
|
270
|
+
if not licenses and not copyright_value_list and not ui_mode:
|
|
262
271
|
continue
|
|
263
|
-
for lic in licenses:
|
|
272
|
+
for lic in licenses or []:
|
|
264
273
|
matched_lic_list = lic.get("matches", [])
|
|
265
274
|
for matched_lic in matched_lic_list:
|
|
266
275
|
found_lic_list = matched_lic.get("license_expression", "")
|
|
@@ -322,7 +331,8 @@ def parsing_scancode_32_later(
|
|
|
322
331
|
|
|
323
332
|
|
|
324
333
|
def parsing_file_item(
|
|
325
|
-
scancode_file_list: list, has_error: bool, need_matched_license: bool = False
|
|
334
|
+
scancode_file_list: list, has_error: bool, need_matched_license: bool = False,
|
|
335
|
+
ui_mode: bool = False
|
|
326
336
|
) -> Tuple[bool, list, list, dict]:
|
|
327
337
|
|
|
328
338
|
rc = True
|
|
@@ -330,9 +340,13 @@ def parsing_file_item(
|
|
|
330
340
|
|
|
331
341
|
first_item = next(iter(scancode_file_list or []), {})
|
|
332
342
|
if "licenses" in first_item:
|
|
333
|
-
rc, scancode_file_item, msg, license_list = parsing_scancode_32_earlier(
|
|
343
|
+
rc, scancode_file_item, msg, license_list = parsing_scancode_32_earlier(
|
|
344
|
+
scancode_file_list, has_error, ui_mode
|
|
345
|
+
)
|
|
334
346
|
else:
|
|
335
|
-
rc, scancode_file_item, msg, license_list = parsing_scancode_32_later(
|
|
347
|
+
rc, scancode_file_item, msg, license_list = parsing_scancode_32_later(
|
|
348
|
+
scancode_file_list, has_error, ui_mode
|
|
349
|
+
)
|
|
336
350
|
if not need_matched_license:
|
|
337
351
|
license_list = {}
|
|
338
352
|
return rc, scancode_file_item, msg, license_list
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Copyright (c) 2018 nexB Inc. and others.
|
|
2
|
+
# Copyright (c) 2026 LG Electronics Inc.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
# Vendored from scancode-ignore-binaries (aboutcode-org/scancode-plugins)
|
|
6
|
+
# so PyPI installs do not need a GitHub git dependency.
|
|
7
|
+
# SPDX-PackageDownloadLocation: https://github.com/aboutcode-org/scancode-plugins/tree/main/misc/scancode-ignore-binaries
|
|
8
|
+
|
|
9
|
+
from plugincode.pre_scan import PreScanPlugin
|
|
10
|
+
from plugincode.pre_scan import pre_scan_impl
|
|
11
|
+
from commoncode.cliutils import PluggableCommandLineOption
|
|
12
|
+
from commoncode.cliutils import PRE_SCAN_GROUP
|
|
13
|
+
from typecode.contenttype import get_type
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pre_scan_impl
|
|
17
|
+
class IgnoreBinaries(PreScanPlugin):
|
|
18
|
+
"""
|
|
19
|
+
Ignore binary files.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
options = [
|
|
23
|
+
PluggableCommandLineOption(
|
|
24
|
+
('--ignore-binaries',),
|
|
25
|
+
is_flag=True,
|
|
26
|
+
help='Ignore binary files.',
|
|
27
|
+
sort_order=10,
|
|
28
|
+
help_group=PRE_SCAN_GROUP,
|
|
29
|
+
)
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
def is_enabled(self, ignore_binaries, **kwargs):
|
|
33
|
+
return ignore_binaries
|
|
34
|
+
|
|
35
|
+
def process_codebase(self, codebase, ignore_binaries, **kwargs):
|
|
36
|
+
"""
|
|
37
|
+
Remove binary Resources from the resource tree.
|
|
38
|
+
"""
|
|
39
|
+
if not ignore_binaries:
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
resources_to_remove = []
|
|
43
|
+
for resource in codebase.walk():
|
|
44
|
+
if not resource.is_file:
|
|
45
|
+
continue
|
|
46
|
+
if is_binary(resource.location):
|
|
47
|
+
resources_to_remove.append(resource)
|
|
48
|
+
|
|
49
|
+
for resource in resources_to_remove:
|
|
50
|
+
resource.remove(codebase)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def is_binary(location):
|
|
54
|
+
"""
|
|
55
|
+
Return True if the resource at location is a binary file.
|
|
56
|
+
"""
|
|
57
|
+
t = get_type(location)
|
|
58
|
+
return (
|
|
59
|
+
t.is_binary
|
|
60
|
+
or t.is_archive
|
|
61
|
+
or t.is_media
|
|
62
|
+
or t.is_office_doc
|
|
63
|
+
or t.is_compressed
|
|
64
|
+
or t.is_filesystem
|
|
65
|
+
or t.is_winexe
|
|
66
|
+
or t.is_elf
|
|
67
|
+
or t.is_java_class
|
|
68
|
+
or t.is_data
|
|
69
|
+
)
|
|
@@ -90,6 +90,7 @@ def main() -> None:
|
|
|
90
90
|
parser.add_argument('--kb_url', type=str, required=False, default="")
|
|
91
91
|
parser.add_argument('--kb_token', type=str, required=False, default="")
|
|
92
92
|
parser.add_argument('--no_merge', action='store_true', required=False)
|
|
93
|
+
parser.add_argument('--ui', action='store_true', required=False)
|
|
93
94
|
|
|
94
95
|
args = parser.parse_args()
|
|
95
96
|
|
|
@@ -121,6 +122,7 @@ def main() -> None:
|
|
|
121
122
|
kb_url = args.kb_url
|
|
122
123
|
kb_token = args.kb_token
|
|
123
124
|
merge_by_folder = not args.no_merge
|
|
125
|
+
ui_mode = args.ui
|
|
124
126
|
|
|
125
127
|
time_out = args.timeout
|
|
126
128
|
core = args.cores
|
|
@@ -130,7 +132,7 @@ def main() -> None:
|
|
|
130
132
|
print_matched_text, formats, time_out, correct_mode, correct_filepath,
|
|
131
133
|
selected_scanner, path_to_exclude, hide_progress=hide_progress,
|
|
132
134
|
kb_url=kb_url, kb_token=kb_token,
|
|
133
|
-
merge_by_folder=merge_by_folder)
|
|
135
|
+
merge_by_folder=merge_by_folder, ui_mode=ui_mode)
|
|
134
136
|
|
|
135
137
|
_result_log["Scan Result"] = result[1]
|
|
136
138
|
try:
|
|
@@ -149,7 +151,7 @@ def create_report_file(
|
|
|
149
151
|
output_path: str = "", output_files: list = [],
|
|
150
152
|
output_extensions: list = [], correct_mode: bool = True,
|
|
151
153
|
correct_filepath: str = "", path_to_scan: str = "", path_to_exclude: list = [],
|
|
152
|
-
formats: list = [],
|
|
154
|
+
formats: list = [], scanoss_skipped: bool = False, files_count: int = 0, final_output_path: str = "",
|
|
153
155
|
run_kb_msg: str = "", merge_by_folder: bool = True
|
|
154
156
|
) -> 'ScannerItem':
|
|
155
157
|
"""
|
|
@@ -216,10 +218,14 @@ def create_report_file(
|
|
|
216
218
|
else:
|
|
217
219
|
scan_item.set_cover_comment("(No OSS detected.)")
|
|
218
220
|
|
|
219
|
-
if
|
|
220
|
-
|
|
221
|
+
if scanoss_skipped:
|
|
222
|
+
is_kb_success = run_kb_msg != "" and run_kb_msg.endswith("Completed")
|
|
223
|
+
if is_kb_success:
|
|
224
|
+
scan_item.set_cover_comment("SCANOSS replaced with KB")
|
|
225
|
+
else:
|
|
226
|
+
scan_item.set_cover_comment("SCANOSS skipped")
|
|
221
227
|
|
|
222
|
-
if run_kb_msg:
|
|
228
|
+
if run_kb_msg and not run_kb_msg.endswith("Completed"):
|
|
223
229
|
scan_item.set_cover_comment(run_kb_msg)
|
|
224
230
|
display_mode = selected_scanner
|
|
225
231
|
if selected_scanner == ALL_MODE:
|
|
@@ -390,11 +396,20 @@ def _collect_kb_file_hashes(
|
|
|
390
396
|
def merge_results(
|
|
391
397
|
scancode_result: list = [], scanoss_result: list = [], spdx_downloads: dict = {},
|
|
392
398
|
path_to_scan: str = "", run_kb: bool = False, manifest_licenses: dict = {},
|
|
393
|
-
excluded_files: set = None, hide_progress: bool = False, kb_url: str = "", kb_token: str = ""
|
|
399
|
+
excluded_files: set = None, hide_progress: bool = False, kb_url: str = "", kb_token: str = "",
|
|
400
|
+
ui_mode: bool = False
|
|
394
401
|
) -> tuple[list, Optional[str], int, int]:
|
|
395
402
|
|
|
396
403
|
"""
|
|
397
404
|
Merge scanner results and spdx parsing result.
|
|
405
|
+
|
|
406
|
+
ScanOSS items for files already present in scancode_result (same path) do not
|
|
407
|
+
replace the ScanCode license; only OSS name, version, and download location
|
|
408
|
+
from ScanOSS are applied. ScanOSS-only files are appended in full.
|
|
409
|
+
|
|
410
|
+
When ui_mode is False, items with both empty download location and empty
|
|
411
|
+
license are removed after merging.
|
|
412
|
+
|
|
398
413
|
:param scancode_result: list of scancode results in SourceItem.
|
|
399
414
|
:param scanoss_result: list of scanoss results in SourceItem.
|
|
400
415
|
:param spdx_downloads: dictionary of spdx parsed results.
|
|
@@ -403,12 +418,27 @@ def merge_results(
|
|
|
403
418
|
:param excluded_files: set of relative paths to exclude from KB-only file discovery.
|
|
404
419
|
:param kb_url: KB API base URL.
|
|
405
420
|
:param kb_token: KB API bearer token.
|
|
421
|
+
:param ui_mode: if False, drop items with no download location and no license.
|
|
406
422
|
:return: (merged_result, kb failure message, requested file_hash count, returned match count).
|
|
407
423
|
"""
|
|
408
424
|
if excluded_files is None:
|
|
409
425
|
excluded_files = set()
|
|
410
426
|
|
|
411
|
-
|
|
427
|
+
# Merge ScanOSS into ScanCode results.
|
|
428
|
+
# When ScanCode already detected a license for the same file, keep that license
|
|
429
|
+
# and only load OSS name, version, and download location from ScanOSS.
|
|
430
|
+
# Files found only by ScanOSS are appended as-is (including ScanOSS licenses).
|
|
431
|
+
for scanoss_item in scanoss_result:
|
|
432
|
+
if scanoss_item in scancode_result:
|
|
433
|
+
scancode_item = scancode_result[scancode_result.index(scanoss_item)]
|
|
434
|
+
if scanoss_item.oss_name:
|
|
435
|
+
scancode_item.oss_name = scanoss_item.oss_name
|
|
436
|
+
if scanoss_item.oss_version:
|
|
437
|
+
scancode_item.oss_version = scanoss_item.oss_version
|
|
438
|
+
if scanoss_item.download_location:
|
|
439
|
+
scancode_item.download_location = scanoss_item.download_location
|
|
440
|
+
else:
|
|
441
|
+
scancode_result.append(scanoss_item)
|
|
412
442
|
|
|
413
443
|
# If download loc. in SPDX form found, overwrite the scanner result.
|
|
414
444
|
# If scanner result doesn't exist, create a new row.
|
|
@@ -424,19 +454,16 @@ def merge_results(
|
|
|
424
454
|
if manifest_licenses:
|
|
425
455
|
for file_name, licenses in manifest_licenses.items():
|
|
426
456
|
valid_licenses = [lic.strip() for lic in licenses if isinstance(lic, str) and lic.strip()]
|
|
427
|
-
|
|
457
|
+
# Non-UI: skip manifests with no extracted licenses.
|
|
458
|
+
# UI: keep/create the row and mark is_manifest_file even without licenses.
|
|
459
|
+
if not valid_licenses and not ui_mode:
|
|
428
460
|
continue
|
|
429
|
-
|
|
430
|
-
|
|
461
|
+
item = _get_or_append_source_item(scancode_result, file_name)
|
|
462
|
+
item.is_manifest_file = True
|
|
463
|
+
if valid_licenses:
|
|
431
464
|
# overwrite existing detected licenses with manifest-provided licenses
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
merged_result_item.is_manifest_file = True
|
|
435
|
-
else:
|
|
436
|
-
new_result_item = SourceItem(file_name)
|
|
437
|
-
new_result_item.licenses = valid_licenses
|
|
438
|
-
new_result_item.is_manifest_file = True
|
|
439
|
-
scancode_result.append(new_result_item)
|
|
465
|
+
item.licenses = [] # clear existing licenses (setter clears when value falsy)
|
|
466
|
+
item.licenses = valid_licenses
|
|
440
467
|
|
|
441
468
|
kb_origin_urls: dict[str, str] = {}
|
|
442
469
|
kb_status_message: Optional[str] = None
|
|
@@ -465,9 +492,36 @@ def merge_results(
|
|
|
465
492
|
if extra_item.download_location:
|
|
466
493
|
scancode_result.append(extra_item)
|
|
467
494
|
|
|
495
|
+
if not ui_mode:
|
|
496
|
+
scancode_result[:] = [
|
|
497
|
+
item for item in scancode_result
|
|
498
|
+
if not _has_empty_download_and_license(item)
|
|
499
|
+
]
|
|
500
|
+
|
|
468
501
|
return scancode_result, kb_status_message, kb_requested_count, kb_returned_count
|
|
469
502
|
|
|
470
503
|
|
|
504
|
+
def _get_or_append_source_item(scancode_result: list, file_name: str) -> SourceItem:
|
|
505
|
+
if file_name in scancode_result:
|
|
506
|
+
return scancode_result[scancode_result.index(file_name)]
|
|
507
|
+
item = SourceItem(file_name)
|
|
508
|
+
scancode_result.append(item)
|
|
509
|
+
return item
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _has_empty_download_and_license(item: SourceItem) -> bool:
|
|
513
|
+
downloads = item.download_location
|
|
514
|
+
if isinstance(downloads, str):
|
|
515
|
+
has_download = bool(downloads.strip())
|
|
516
|
+
else:
|
|
517
|
+
has_download = bool(downloads) and any(bool(d and str(d).strip()) for d in downloads)
|
|
518
|
+
|
|
519
|
+
licenses = item.licenses
|
|
520
|
+
has_license = bool(licenses) and any(bool(lic and str(lic).strip()) for lic in licenses)
|
|
521
|
+
|
|
522
|
+
return (not has_download) and (not has_license)
|
|
523
|
+
|
|
524
|
+
|
|
471
525
|
def _finalize_temp_output(
|
|
472
526
|
temp_output_path: str,
|
|
473
527
|
final_output_path: str,
|
|
@@ -502,7 +556,8 @@ def run_scanners(
|
|
|
502
556
|
correct_mode: bool = True, correct_filepath: str = "",
|
|
503
557
|
selected_scanner: str = ALL_MODE, path_to_exclude: list = [],
|
|
504
558
|
all_exclude_mode: tuple = (), hide_progress: bool = False,
|
|
505
|
-
kb_url: str = "", kb_token: str = "", merge_by_folder: bool = True
|
|
559
|
+
kb_url: str = "", kb_token: str = "", merge_by_folder: bool = True,
|
|
560
|
+
ui_mode: bool = False
|
|
506
561
|
) -> Tuple[bool, str, 'ScannerItem', list, list]:
|
|
507
562
|
"""
|
|
508
563
|
Run Scancode and scanoss.py for the given path.
|
|
@@ -532,7 +587,7 @@ def run_scanners(
|
|
|
532
587
|
spdx_downloads = {}
|
|
533
588
|
result_log = {}
|
|
534
589
|
scan_item = []
|
|
535
|
-
|
|
590
|
+
scanoss_skipped = False
|
|
536
591
|
kb_url, kb_token = resolve_kb_config(kb_url, kb_token)
|
|
537
592
|
|
|
538
593
|
success, msg, output_path, output_files, output_extensions, formats = check_output_formats_v2(output_file_name, formats)
|
|
@@ -582,12 +637,15 @@ def run_scanners(
|
|
|
582
637
|
print_matched_text, formats, called_by_cli, time_out, correct_mode,
|
|
583
638
|
correct_filepath, path_to_exclude,
|
|
584
639
|
hide_progress=hide_progress,
|
|
640
|
+
ui_mode=ui_mode,
|
|
585
641
|
)
|
|
586
642
|
excluded_files = set(excluded_files) if excluded_files else set()
|
|
587
643
|
if selected_scanner in ['scanoss', ALL_MODE]:
|
|
588
|
-
scanoss_result,
|
|
589
|
-
|
|
590
|
-
|
|
644
|
+
scanoss_result, scanoss_skipped = run_scanoss_py(
|
|
645
|
+
path_to_scan, output_path, formats, True, num_cores,
|
|
646
|
+
excluded_path_with_default_exclusion, excluded_files,
|
|
647
|
+
write_json_file, hide_progress, timeout=time_out
|
|
648
|
+
)
|
|
591
649
|
|
|
592
650
|
run_kb_msg = ""
|
|
593
651
|
if selected_scanner in SCANNER_TYPE:
|
|
@@ -602,6 +660,7 @@ def run_scanners(
|
|
|
602
660
|
scancode_result, scanoss_result, spdx_downloads,
|
|
603
661
|
path_to_scan, run_kb, manifest_licenses, excluded_files,
|
|
604
662
|
hide_progress, kb_url, kb_token,
|
|
663
|
+
ui_mode=ui_mode,
|
|
605
664
|
)
|
|
606
665
|
if kb_status_message:
|
|
607
666
|
run_kb_msg = f"KB({kb_url}) {kb_status_message}"
|
|
@@ -611,7 +670,7 @@ def run_scanners(
|
|
|
611
670
|
scan_item = create_report_file(start_time, merged_result, license_list, scanoss_result, selected_scanner,
|
|
612
671
|
print_matched_text, output_path, output_files, output_extensions, correct_mode,
|
|
613
672
|
correct_filepath, path_to_scan, excluded_path_without_dot, formats,
|
|
614
|
-
|
|
673
|
+
scanoss_skipped, cnt_file_except_skipped, final_output_path, run_kb_msg,
|
|
615
674
|
merge_by_folder)
|
|
616
675
|
else:
|
|
617
676
|
print_help_msg_source_scanner()
|
|
@@ -653,7 +712,7 @@ def metadata_collector(path_to_scan: str, excluded_files: set) -> dict:
|
|
|
653
712
|
|
|
654
713
|
- Traverse files with exclusions applied
|
|
655
714
|
- spdx_downloads: {rel_path: [download_urls]}
|
|
656
|
-
- manifest_licenses: {rel_path: [license_names]}
|
|
715
|
+
- manifest_licenses: {rel_path: [license_names]} (empty list if extraction failed)
|
|
657
716
|
|
|
658
717
|
:return: (spdx_downloads, manifest_licenses)
|
|
659
718
|
"""
|
|
@@ -673,9 +732,7 @@ def metadata_collector(path_to_scan: str, excluded_files: set) -> dict:
|
|
|
673
732
|
spdx_downloads[rel_path_file] = downloads
|
|
674
733
|
|
|
675
734
|
if is_manifest_file(file_path):
|
|
676
|
-
|
|
677
|
-
if licenses:
|
|
678
|
-
manifest_licenses[rel_path_file] = licenses
|
|
735
|
+
manifest_licenses[rel_path_file] = get_manifest_licenses(file_path) or []
|
|
679
736
|
|
|
680
737
|
return spdx_downloads, manifest_licenses
|
|
681
738
|
|
|
@@ -7,6 +7,8 @@ import os
|
|
|
7
7
|
import multiprocessing
|
|
8
8
|
import warnings
|
|
9
9
|
import logging
|
|
10
|
+
from typing import Tuple
|
|
11
|
+
|
|
10
12
|
from scancode import cli
|
|
11
13
|
import fosslight_util.constant as constant
|
|
12
14
|
from fosslight_util.set_log import init_log
|
|
@@ -14,15 +16,12 @@ from fosslight_util.time import current_timestamp_utc, timestamp_for_filename
|
|
|
14
16
|
from ._parsing_scancode_file_item import parsing_file_item
|
|
15
17
|
from ._parsing_scancode_file_item import get_error_from_header
|
|
16
18
|
from fosslight_util.output_format import check_output_formats_v2
|
|
17
|
-
from fosslight_binary.binary_analysis import check_binary
|
|
18
19
|
from fosslight_util.exclude import (
|
|
19
20
|
EXCLUDE_DIRECTORY,
|
|
20
21
|
EXCLUDE_FILE_EXTENSION,
|
|
21
22
|
EXCLUDE_FILENAME,
|
|
22
23
|
PACKAGE_DIRECTORY,
|
|
23
24
|
)
|
|
24
|
-
from commoncode.fileset import is_included
|
|
25
|
-
from typing import Callable, Tuple
|
|
26
25
|
|
|
27
26
|
logger = logging.getLogger(constant.LOGGER_NAME)
|
|
28
27
|
warnings.filterwarnings("ignore", category=FutureWarning)
|
|
@@ -68,8 +67,6 @@ _WILDCARD_EXTENSIONS = {
|
|
|
68
67
|
"zip", "tar", "tgz", "gz",
|
|
69
68
|
"bmp", "webp", "ico",
|
|
70
69
|
} | {ext.lower() for ext in EXCLUDE_FILE_EXTENSION}
|
|
71
|
-
_SKIP_DIR_NAMES = frozenset(name.lower() for name in PACKAGE_DIRECTORY + EXCLUDE_DIRECTORY)
|
|
72
|
-
_SKIP_EXTS = frozenset(ext.lower() for ext in EXCLUDE_FILE_EXTENSION)
|
|
73
70
|
|
|
74
71
|
|
|
75
72
|
def _normalize_custom_pattern(pattern: str, abs_path_to_scan: str) -> set:
|
|
@@ -160,14 +157,15 @@ def _directory_ignore_pattern(dir_name: str) -> str:
|
|
|
160
157
|
return f"**/{normalized}/**"
|
|
161
158
|
|
|
162
159
|
|
|
163
|
-
def
|
|
160
|
+
def _default_scancode_ignore_patterns(
|
|
164
161
|
path_to_exclude: list = None,
|
|
165
162
|
abs_path_to_scan: str = ""
|
|
166
|
-
) ->
|
|
163
|
+
) -> tuple:
|
|
167
164
|
"""
|
|
168
165
|
Coarse ignore patterns aligned with fosslight_util.get_excluded_paths() rules.
|
|
169
166
|
Directory names use path-based globs (e.g. **/tests/**) so they do not match
|
|
170
167
|
the scan root directory name itself.
|
|
168
|
+
Binary files are excluded separately via scancode --ignore-binaries.
|
|
171
169
|
"""
|
|
172
170
|
patterns = {".*"}
|
|
173
171
|
for name in PACKAGE_DIRECTORY + EXCLUDE_DIRECTORY:
|
|
@@ -182,87 +180,6 @@ def _default_scancode_coarse_ignore_patterns(
|
|
|
182
180
|
pattern = os.path.relpath(pattern, abs_path_to_scan)
|
|
183
181
|
patterns.update(_expand_custom_exclude_pattern(pattern, abs_path_to_scan))
|
|
184
182
|
|
|
185
|
-
return frozenset(patterns)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def _to_excludes_dict(patterns) -> dict:
|
|
189
|
-
return {pattern: "exclude" for pattern in patterns}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def _is_path_covered(rel_path: str, excludes: dict) -> bool:
|
|
193
|
-
return not is_included(rel_path, includes={}, excludes=excludes)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def _add_ignore_pattern(
|
|
197
|
-
patterns: set,
|
|
198
|
-
excludes: dict,
|
|
199
|
-
pattern: str,
|
|
200
|
-
*,
|
|
201
|
-
sample_path: str = None,
|
|
202
|
-
) -> bool:
|
|
203
|
-
if pattern in patterns:
|
|
204
|
-
return False
|
|
205
|
-
if sample_path and _is_path_covered(sample_path, excludes):
|
|
206
|
-
return False
|
|
207
|
-
patterns.add(pattern)
|
|
208
|
-
excludes[pattern] = "exclude"
|
|
209
|
-
return True
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def _make_pre_scan_skip_filter(
|
|
213
|
-
coarse_patterns: frozenset,
|
|
214
|
-
) -> Tuple[dict, Callable[[str, str], bool], Callable[[str, str], bool]]:
|
|
215
|
-
excludes = _to_excludes_dict(coarse_patterns)
|
|
216
|
-
|
|
217
|
-
def should_skip_dir(dir_name: str, rel_dir: str) -> bool:
|
|
218
|
-
if dir_name.startswith('.'):
|
|
219
|
-
return True
|
|
220
|
-
if dir_name.lower() in _SKIP_DIR_NAMES:
|
|
221
|
-
return True
|
|
222
|
-
return _is_path_covered(f"{rel_dir}/_", excludes)
|
|
223
|
-
|
|
224
|
-
def should_skip_file(file_name: str, rel_path: str) -> bool:
|
|
225
|
-
if file_name.startswith('.'):
|
|
226
|
-
return True
|
|
227
|
-
ext = os.path.splitext(file_name)[1].lstrip('.').lower()
|
|
228
|
-
if ext in _SKIP_EXTS:
|
|
229
|
-
return True
|
|
230
|
-
return _is_path_covered(rel_path, excludes)
|
|
231
|
-
|
|
232
|
-
return excludes, should_skip_dir, should_skip_file
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
def _add_binary_ignore_patterns(
|
|
236
|
-
patterns: set,
|
|
237
|
-
excludes: dict,
|
|
238
|
-
binary_paths: list,
|
|
239
|
-
) -> None:
|
|
240
|
-
extensions = set()
|
|
241
|
-
no_ext_paths = []
|
|
242
|
-
|
|
243
|
-
for rel_path in binary_paths:
|
|
244
|
-
ext = os.path.splitext(rel_path)[1].lstrip('.').lower()
|
|
245
|
-
if ext:
|
|
246
|
-
extensions.add(ext)
|
|
247
|
-
else:
|
|
248
|
-
no_ext_paths.append(rel_path)
|
|
249
|
-
|
|
250
|
-
for ext in extensions:
|
|
251
|
-
_add_ignore_pattern(patterns, excludes, f"*.{ext}")
|
|
252
|
-
|
|
253
|
-
for rel_path in no_ext_paths:
|
|
254
|
-
_add_ignore_pattern(
|
|
255
|
-
patterns, excludes, f"**/{rel_path}", sample_path=rel_path
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
def _build_scancode_ignore_patterns(
|
|
260
|
-
coarse_patterns: frozenset,
|
|
261
|
-
binary_paths: list,
|
|
262
|
-
) -> tuple:
|
|
263
|
-
patterns = set(coarse_patterns)
|
|
264
|
-
excludes = _to_excludes_dict(coarse_patterns)
|
|
265
|
-
_add_binary_ignore_patterns(patterns, excludes, binary_paths)
|
|
266
183
|
return tuple(sorted(patterns))
|
|
267
184
|
|
|
268
185
|
|
|
@@ -273,7 +190,8 @@ def run_scan(
|
|
|
273
190
|
formats: list = [], called_by_cli: bool = False,
|
|
274
191
|
time_out: int = 120, correct_mode: bool = True,
|
|
275
192
|
correct_filepath: str = "", path_to_exclude: list = [],
|
|
276
|
-
excluded_files: list = [], hide_progress: bool = False
|
|
193
|
+
excluded_files: list = [], hide_progress: bool = False,
|
|
194
|
+
ui_mode: bool = False
|
|
277
195
|
) -> Tuple[bool, str, list, list]:
|
|
278
196
|
if not called_by_cli:
|
|
279
197
|
global logger
|
|
@@ -331,38 +249,9 @@ def run_scan(
|
|
|
331
249
|
pretty_params["path_to_exclude"] = path_to_exclude
|
|
332
250
|
pretty_params["output_file"] = output_file_name
|
|
333
251
|
abs_path_to_scan = os.path.abspath(path_to_scan)
|
|
334
|
-
|
|
335
|
-
coarse_patterns = _default_scancode_coarse_ignore_patterns(
|
|
252
|
+
ignore_tuple = _default_scancode_ignore_patterns(
|
|
336
253
|
path_to_exclude, abs_path_to_scan
|
|
337
254
|
)
|
|
338
|
-
_, should_skip_dir, should_skip_file = _make_pre_scan_skip_filter(
|
|
339
|
-
coarse_patterns
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
for root, dirs, files in os.walk(path_to_scan):
|
|
343
|
-
rel_root = os.path.relpath(root, abs_path_to_scan).replace("\\", "/")
|
|
344
|
-
dirs[:] = [
|
|
345
|
-
d for d in dirs
|
|
346
|
-
if not should_skip_dir(
|
|
347
|
-
d, d if rel_root == "." else f"{rel_root}/{d}"
|
|
348
|
-
)
|
|
349
|
-
]
|
|
350
|
-
for name in files:
|
|
351
|
-
rel_path = name if rel_root == "." else f"{rel_root}/{name}"
|
|
352
|
-
if should_skip_file(name, rel_path):
|
|
353
|
-
continue
|
|
354
|
-
full_path = os.path.join(root, name)
|
|
355
|
-
try:
|
|
356
|
-
if not check_binary(full_path, True):
|
|
357
|
-
continue
|
|
358
|
-
except Exception:
|
|
359
|
-
continue
|
|
360
|
-
binary_paths.append(rel_path)
|
|
361
|
-
logger.debug(f"Excluded binary from scancode: {rel_path}")
|
|
362
|
-
|
|
363
|
-
ignore_tuple = _build_scancode_ignore_patterns(
|
|
364
|
-
coarse_patterns, binary_paths
|
|
365
|
-
)
|
|
366
255
|
logger.debug(f"Scancode ignore patterns: {len(ignore_tuple)}")
|
|
367
256
|
|
|
368
257
|
kwargs = {
|
|
@@ -374,12 +263,14 @@ def run_scan(
|
|
|
374
263
|
"processes": num_cores,
|
|
375
264
|
"pretty_params": pretty_params,
|
|
376
265
|
"output_json_pp": output_json_file,
|
|
377
|
-
|
|
266
|
+
# Non-UI: only files with findings (license/copyright). UI: all files.
|
|
267
|
+
"only_findings": not ui_mode,
|
|
378
268
|
"license_text": True,
|
|
379
269
|
"url": True,
|
|
380
270
|
"timeout": time_out,
|
|
381
271
|
"include": (),
|
|
382
272
|
"ignore": ignore_tuple,
|
|
273
|
+
"ignore_binaries": True,
|
|
383
274
|
"quiet": hide_progress
|
|
384
275
|
}
|
|
385
276
|
_apply_scancode_unset_workaround(kwargs)
|
|
@@ -395,8 +286,9 @@ def run_scan(
|
|
|
395
286
|
_result_log["Error_files"] = error_msg
|
|
396
287
|
msg = "Failed to analyze :" + error_msg
|
|
397
288
|
if "files" in results:
|
|
398
|
-
rc, result_list, parsing_msg, license_list = parsing_file_item(
|
|
399
|
-
|
|
289
|
+
rc, result_list, parsing_msg, license_list = parsing_file_item(
|
|
290
|
+
results["files"], has_error, need_license, ui_mode=ui_mode
|
|
291
|
+
)
|
|
400
292
|
if parsing_msg:
|
|
401
293
|
_result_log["Parsing Log"] = parsing_msg
|
|
402
294
|
if rc:
|
|
@@ -404,12 +296,6 @@ def run_scan(
|
|
|
404
296
|
success = True
|
|
405
297
|
result_list = sorted(
|
|
406
298
|
result_list, key=lambda row: (''.join(row.licenses)))
|
|
407
|
-
|
|
408
|
-
for scan_item in result_list:
|
|
409
|
-
if os.path.isdir(scan_item.source_name_or_path):
|
|
410
|
-
continue
|
|
411
|
-
if check_binary(os.path.join(path_to_scan, scan_item.source_name_or_path), True):
|
|
412
|
-
scan_item.exclude = True
|
|
413
299
|
except Exception as ex:
|
|
414
300
|
success = False
|
|
415
301
|
msg = str(ex)
|
|
@@ -31,7 +31,8 @@ def get_scanoss_extra_info(scanned_result: dict) -> list:
|
|
|
31
31
|
def run_scanoss_py(path_to_scan: str, output_path: str = "", format: list = [],
|
|
32
32
|
called_by_cli: bool = False, num_threads: int = -1,
|
|
33
33
|
path_to_exclude: list = [], excluded_files: set = None,
|
|
34
|
-
write_json_file: bool = False, hide_progress: bool = False
|
|
34
|
+
write_json_file: bool = False, hide_progress: bool = False,
|
|
35
|
+
timeout: int = 120) -> Tuple[list, bool]:
|
|
35
36
|
"""
|
|
36
37
|
Run scanoss.py for the given path.
|
|
37
38
|
|
|
@@ -40,23 +41,25 @@ def run_scanoss_py(path_to_scan: str, output_path: str = "", format: list = [],
|
|
|
40
41
|
:param format: Output file format (not being used except when calling check_output_format).
|
|
41
42
|
:param called_by_cli: if not called by cli, initialize logger.
|
|
42
43
|
:param write_json_file: if requested, keep the raw files.
|
|
44
|
+
:param timeout: timeout in seconds for SCANOSS API request.
|
|
43
45
|
:return scanoss_file_list: list of ScanItem (scanned result by files).
|
|
44
46
|
"""
|
|
45
47
|
|
|
46
48
|
scanoss_file_list = []
|
|
47
|
-
|
|
49
|
+
scanoss_skipped = False
|
|
48
50
|
try:
|
|
49
51
|
importlib_metadata.distribution("scanoss")
|
|
50
52
|
except Exception as error:
|
|
51
53
|
logger.warning(f"{error}. Skipping scan with scanoss.")
|
|
52
54
|
logger.warning("Please install scanoss and dataclasses before run fosslight_source with scanoss option.")
|
|
53
|
-
return scanoss_file_list,
|
|
55
|
+
return scanoss_file_list, scanoss_skipped
|
|
54
56
|
|
|
55
57
|
output_json_file = os.path.join(output_path, SCANOSS_OUTPUT_FILE)
|
|
56
58
|
output_wfp_file = os.path.join(output_path, SCANOSS_RESULT_FILE)
|
|
57
59
|
if os.path.exists(output_json_file):
|
|
58
60
|
os.remove(output_json_file)
|
|
59
61
|
|
|
62
|
+
output_buffer = io.StringIO()
|
|
60
63
|
try:
|
|
61
64
|
logger.debug(f"|---Running SCANOSS on {path_to_scan}")
|
|
62
65
|
scanoss_settings = ScanossSettings()
|
|
@@ -66,15 +69,77 @@ def run_scanoss_py(path_to_scan: str, output_path: str = "", format: list = [],
|
|
|
66
69
|
scan_output=output_json_file,
|
|
67
70
|
scan_options=ScanType.SCAN_SNIPPETS.value,
|
|
68
71
|
nb_threads=num_threads if num_threads > 0 else 10,
|
|
69
|
-
scanoss_settings=scanoss_settings
|
|
72
|
+
scanoss_settings=scanoss_settings,
|
|
73
|
+
timeout=timeout,
|
|
74
|
+
retry=0
|
|
70
75
|
)
|
|
71
|
-
|
|
76
|
+
|
|
77
|
+
# Check API connectivity & API Limit using dummy WFP POST
|
|
78
|
+
try:
|
|
79
|
+
logger.debug(f"|---Checking SCANOSS API connectivity to {scanner.scanoss_api.url}")
|
|
80
|
+
dummy_wfp = "file=72214db4e1e543018d1bafe86ea3b444,21,dummy.txt\nfh2=b200cd2eff5d535886e598b3a833aab5\n"
|
|
81
|
+
ping_response = scanner.scanoss_api.session.post(
|
|
82
|
+
scanner.scanoss_api.url,
|
|
83
|
+
files={'file': ('dummy.wfp', dummy_wfp)},
|
|
84
|
+
headers=scanner.scanoss_api.headers,
|
|
85
|
+
timeout=5
|
|
86
|
+
)
|
|
87
|
+
if ping_response.status_code != 200:
|
|
88
|
+
response_text = ping_response.text.lower()
|
|
89
|
+
is_limit = ping_response.status_code in [429, 503]
|
|
90
|
+
is_limit = is_limit or "rate limit" in response_text
|
|
91
|
+
is_limit = is_limit or "limits being exceeded" in response_text
|
|
92
|
+
if is_limit:
|
|
93
|
+
logger.debug(f"[SCANOSS] API Limit Exceeded: HTTP {ping_response.status_code}")
|
|
94
|
+
elif ping_response.status_code in [401, 403]:
|
|
95
|
+
logger.debug(f"[SCANOSS] Authentication Failed: HTTP {ping_response.status_code}")
|
|
96
|
+
else:
|
|
97
|
+
logger.debug(f"[SCANOSS] API is not ready: HTTP {ping_response.status_code}")
|
|
98
|
+
scanoss_skipped = True
|
|
99
|
+
return scanoss_file_list, scanoss_skipped
|
|
100
|
+
except Exception as ping_error:
|
|
101
|
+
logger.debug(f"[SCANOSS] Connection failed to {scanner.scanoss_api.url}: {ping_error}")
|
|
102
|
+
scanoss_skipped = True
|
|
103
|
+
return scanoss_file_list, scanoss_skipped
|
|
104
|
+
|
|
72
105
|
with contextlib.redirect_stdout(output_buffer), contextlib.redirect_stderr(output_buffer):
|
|
73
106
|
scanner.scan_folder_with_options(scan_dir=path_to_scan)
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
except Exception as error:
|
|
108
|
+
logger.debug(f"SCANOSS execution failed: {error}")
|
|
76
109
|
|
|
77
|
-
|
|
110
|
+
captured_output = output_buffer.getvalue()
|
|
111
|
+
if captured_output:
|
|
112
|
+
for line in captured_output.splitlines():
|
|
113
|
+
line_strip = line.strip()
|
|
114
|
+
if line_strip.startswith("ERROR:") or "rejected" in line_strip:
|
|
115
|
+
logger.debug(f"[SCANOSS] {line_strip}")
|
|
116
|
+
|
|
117
|
+
api_limit_patterns = [
|
|
118
|
+
"due to service limits being exceeded",
|
|
119
|
+
"service limits/rate limit being exceeded",
|
|
120
|
+
"Rate limit exceeded",
|
|
121
|
+
"HTTP 429"
|
|
122
|
+
]
|
|
123
|
+
timeout_patterns = [
|
|
124
|
+
"The SCANOSS API request timed out",
|
|
125
|
+
"Service unavailable (HTTP 503)",
|
|
126
|
+
"The SCANOSS API is currently unavailable",
|
|
127
|
+
"ConnectionError communicating with",
|
|
128
|
+
"The SCANOSS API request failed",
|
|
129
|
+
"Connection aborted",
|
|
130
|
+
"RemoteDisconnected"
|
|
131
|
+
]
|
|
132
|
+
api_limit_exceed = any(p in captured_output for p in api_limit_patterns)
|
|
133
|
+
timeout_occurred = any(p in captured_output for p in timeout_patterns)
|
|
134
|
+
if timeout_occurred or api_limit_exceed:
|
|
135
|
+
scanoss_skipped = True
|
|
136
|
+
if api_limit_exceed:
|
|
137
|
+
logger.debug("SCANOSS skipped (API Limit Exceeded)")
|
|
138
|
+
elif timeout_occurred:
|
|
139
|
+
logger.debug("SCANOSS skipped (Timeout)")
|
|
140
|
+
|
|
141
|
+
if os.path.isfile(output_json_file):
|
|
142
|
+
try:
|
|
78
143
|
logger.debug("|---SCANOSS Parsing")
|
|
79
144
|
with open(output_json_file, "r") as st_json:
|
|
80
145
|
st_python = json.load(st_json)
|
|
@@ -86,8 +151,8 @@ def run_scanoss_py(path_to_scan: str, output_path: str = "", format: list = [],
|
|
|
86
151
|
with open(output_json_file, "r") as st_json:
|
|
87
152
|
st_python = json.load(st_json)
|
|
88
153
|
scanoss_file_list = parsing_scan_result(st_python, excluded_files)
|
|
89
|
-
|
|
90
|
-
|
|
154
|
+
except Exception as error:
|
|
155
|
+
logger.debug(f"SCANOSS Parsing {path_to_scan}: {error}")
|
|
91
156
|
|
|
92
157
|
if not write_json_file:
|
|
93
158
|
if os.path.isfile(output_json_file):
|
|
@@ -97,4 +162,4 @@ def run_scanoss_py(path_to_scan: str, output_path: str = "", format: list = [],
|
|
|
97
162
|
|
|
98
163
|
logger.info(f"|---Number of files detected with SCANOSS: {(len(scanoss_file_list))}")
|
|
99
164
|
|
|
100
|
-
return scanoss_file_list,
|
|
165
|
+
return scanoss_file_list, scanoss_skipped
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fosslight_source
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
4
4
|
Summary: FOSSLight Source Scanner
|
|
5
5
|
Author: LG Electronics
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -24,7 +24,6 @@ Requires-Dist: fosslight_util>=2.2.2
|
|
|
24
24
|
Requires-Dist: PyYAML
|
|
25
25
|
Requires-Dist: wheel>=0.38.1
|
|
26
26
|
Requires-Dist: intbitset
|
|
27
|
-
Requires-Dist: fosslight_binary>=5.1.22
|
|
28
27
|
Requires-Dist: scancode-toolkit>=32.0.2
|
|
29
28
|
Requires-Dist: lxml>=6.0.1
|
|
30
29
|
Requires-Dist: cryptography<49; platform_system == "Darwin" and platform_machine == "x86_64"
|
|
@@ -10,6 +10,7 @@ src/fosslight_source/_merge.py
|
|
|
10
10
|
src/fosslight_source/_parsing_scancode_file_item.py
|
|
11
11
|
src/fosslight_source/_parsing_scanoss_file.py
|
|
12
12
|
src/fosslight_source/_scan_item.py
|
|
13
|
+
src/fosslight_source/_scancode_ignore_binaries.py
|
|
13
14
|
src/fosslight_source/cli.py
|
|
14
15
|
src/fosslight_source/run_manifest_extractor.py
|
|
15
16
|
src/fosslight_source/run_scancode.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/_parsing_scanoss_file.py
RENAMED
|
File without changes
|
|
File without changes
|
{fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/run_manifest_extractor.py
RENAMED
|
File without changes
|
{fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source/run_spdx_extractor.py
RENAMED
|
File without changes
|
{fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{fosslight_source-2.3.4 → fosslight_source-2.3.6}/src/fosslight_source.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|