fosslight-source 2.3.3__tar.gz → 2.3.5__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.3/src/fosslight_source.egg-info → fosslight_source-2.3.5}/PKG-INFO +2 -2
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/pyproject.toml +5 -2
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/_help.py +1 -1
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/_parsing_scancode_file_item.py +22 -8
- fosslight_source-2.3.5/src/fosslight_source/_scancode_ignore_binaries.py +69 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/cli.py +89 -35
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/run_scancode.py +104 -85
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/run_scanoss.py +15 -5
- {fosslight_source-2.3.3 → fosslight_source-2.3.5/src/fosslight_source.egg-info}/PKG-INFO +2 -2
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source.egg-info/SOURCES.txt +1 -0
- fosslight_source-2.3.5/src/fosslight_source.egg-info/entry_points.txt +5 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source.egg-info/requires.txt +1 -1
- fosslight_source-2.3.3/src/fosslight_source.egg-info/entry_points.txt +0 -2
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/LICENSE +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/MANIFEST.in +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/README.md +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/setup.cfg +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/__init__.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/_kb_client.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/_license_matched.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/_merge.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/_parsing_scanoss_file.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/_scan_item.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/run_manifest_extractor.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/run_spdx_extractor.py +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source.egg-info/dependency_links.txt +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source.egg-info/top_level.txt +0 -0
- {fosslight_source-2.3.3 → fosslight_source-2.3.5}/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.5
|
|
4
4
|
Summary: FOSSLight Source Scanner
|
|
5
5
|
Author: LG Electronics
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -24,8 +24,8 @@ 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
|
|
28
|
+
Requires-Dist: lxml>=6.0.1
|
|
29
29
|
Requires-Dist: cryptography<49; platform_system == "Darwin" and platform_machine == "x86_64"
|
|
30
30
|
Requires-Dist: fingerprints==1.2.3
|
|
31
31
|
Requires-Dist: normality==2.6.1
|
|
@@ -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.5"
|
|
11
11
|
description = "FOSSLight Source Scanner"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
license = "Apache-2.0"
|
|
@@ -33,8 +33,8 @@ 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",
|
|
37
|
+
"lxml>=6.0.1",
|
|
38
38
|
# cryptography 49.x does not provide macOS x86_64 wheels, causing source builds to require OpenSSL/pkg-config.
|
|
39
39
|
"cryptography<49; platform_system == 'Darwin' and platform_machine == 'x86_64'",
|
|
40
40
|
"fingerprints==1.2.3",
|
|
@@ -55,6 +55,9 @@ Download = "https://github.com/fosslight/fosslight_source_scanner"
|
|
|
55
55
|
[project.scripts]
|
|
56
56
|
fosslight_source = "fosslight_source.cli:main"
|
|
57
57
|
|
|
58
|
+
[project.entry-points."scancode_pre_scan"]
|
|
59
|
+
ignore-binaries = "fosslight_source._scancode_ignore_binaries:IgnoreBinaries"
|
|
60
|
+
|
|
58
61
|
[tool.setuptools]
|
|
59
62
|
package-dir = {"" = "src"}
|
|
60
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,8 +218,12 @@ 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 "Completed" in run_kb_msg
|
|
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
228
|
if run_kb_msg:
|
|
223
229
|
scan_item.set_cover_comment(run_kb_msg)
|
|
@@ -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)
|
|
@@ -572,7 +627,7 @@ def run_scanners(
|
|
|
572
627
|
excluded_path_without_dot,
|
|
573
628
|
excluded_files,
|
|
574
629
|
cnt_file_except_skipped) = get_excluded_paths(path_to_scan, path_to_exclude_with_filename)
|
|
575
|
-
logger.debug(f"Skipped paths: {excluded_path_with_default_exclusion}")
|
|
630
|
+
logger.debug(f"Skipped paths count: {len(excluded_path_with_default_exclusion)}")
|
|
576
631
|
|
|
577
632
|
if not selected_scanner:
|
|
578
633
|
selected_scanner = ALL_MODE
|
|
@@ -580,14 +635,17 @@ def run_scanners(
|
|
|
580
635
|
success, result_log[RESULT_KEY], scancode_result, license_list = run_scan(
|
|
581
636
|
path_to_scan, output_file_name, write_json_file, num_cores, True,
|
|
582
637
|
print_matched_text, formats, called_by_cli, time_out, correct_mode,
|
|
583
|
-
correct_filepath,
|
|
584
|
-
|
|
638
|
+
correct_filepath, path_to_exclude,
|
|
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:
|
|
@@ -598,23 +656,21 @@ def run_scanners(
|
|
|
598
656
|
run_kb_msg = f"KB({kb_url}) Unreachable"
|
|
599
657
|
|
|
600
658
|
spdx_downloads, manifest_licenses = metadata_collector(path_to_scan, excluded_files)
|
|
601
|
-
merged_result, kb_status_message, kb_requested_count,
|
|
659
|
+
merged_result, kb_status_message, kb_requested_count, _ = merge_results(
|
|
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}"
|
|
608
667
|
elif run_kb and kb_requested_count > 0:
|
|
609
|
-
run_kb_msg = (
|
|
610
|
-
f"KB({kb_url}) response : {kb_returned_count}/"
|
|
611
|
-
f" requested: {kb_requested_count}"
|
|
612
|
-
)
|
|
668
|
+
run_kb_msg = f"KB({kb_url}) : Completed"
|
|
613
669
|
mark_oss_info_correction_files_as_excluded(merged_result)
|
|
614
670
|
scan_item = create_report_file(start_time, merged_result, license_list, scanoss_result, selected_scanner,
|
|
615
671
|
print_matched_text, output_path, output_files, output_extensions, correct_mode,
|
|
616
672
|
correct_filepath, path_to_scan, excluded_path_without_dot, formats,
|
|
617
|
-
|
|
673
|
+
scanoss_skipped, cnt_file_except_skipped, final_output_path, run_kb_msg,
|
|
618
674
|
merge_by_folder)
|
|
619
675
|
else:
|
|
620
676
|
print_help_msg_source_scanner()
|
|
@@ -656,7 +712,7 @@ def metadata_collector(path_to_scan: str, excluded_files: set) -> dict:
|
|
|
656
712
|
|
|
657
713
|
- Traverse files with exclusions applied
|
|
658
714
|
- spdx_downloads: {rel_path: [download_urls]}
|
|
659
|
-
- manifest_licenses: {rel_path: [license_names]}
|
|
715
|
+
- manifest_licenses: {rel_path: [license_names]} (empty list if extraction failed)
|
|
660
716
|
|
|
661
717
|
:return: (spdx_downloads, manifest_licenses)
|
|
662
718
|
"""
|
|
@@ -676,9 +732,7 @@ def metadata_collector(path_to_scan: str, excluded_files: set) -> dict:
|
|
|
676
732
|
spdx_downloads[rel_path_file] = downloads
|
|
677
733
|
|
|
678
734
|
if is_manifest_file(file_path):
|
|
679
|
-
|
|
680
|
-
if licenses:
|
|
681
|
-
manifest_licenses[rel_path_file] = licenses
|
|
735
|
+
manifest_licenses[rel_path_file] = get_manifest_licenses(file_path) or []
|
|
682
736
|
|
|
683
737
|
return spdx_downloads, manifest_licenses
|
|
684
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 Tuple, Iterable
|
|
26
25
|
|
|
27
26
|
logger = logging.getLogger(constant.LOGGER_NAME)
|
|
28
27
|
warnings.filterwarnings("ignore", category=FutureWarning)
|
|
@@ -62,42 +61,53 @@ def _apply_scancode_unset_workaround(kwargs: dict) -> None:
|
|
|
62
61
|
logger.debug("scancode UNSET workaround skipped: %s", ex)
|
|
63
62
|
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
64
|
+
_WILDCARD_EXTENSIONS = {
|
|
65
|
+
"png", "mp3", "wav", "comp", "bin", "o", "db", "tflite",
|
|
66
|
+
"ttf", "exe", "dll", "jpg", "jpeg", "gif",
|
|
67
|
+
"zip", "tar", "tgz", "gz",
|
|
68
|
+
"bmp", "webp", "ico",
|
|
69
|
+
} | {ext.lower() for ext in EXCLUDE_FILE_EXTENSION}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _normalize_custom_pattern(pattern: str, abs_path_to_scan: str) -> set:
|
|
73
|
+
pat = pattern.replace('\\', '/').strip()
|
|
74
|
+
if not pat:
|
|
75
|
+
return set()
|
|
76
|
+
|
|
77
|
+
patterns_to_add = {pat}
|
|
78
|
+
|
|
79
|
+
if pat.endswith("/**"):
|
|
80
|
+
base = pat[:-3].rstrip("/")
|
|
81
|
+
if base:
|
|
82
|
+
patterns_to_add.add(base)
|
|
83
|
+
elif pat.endswith("/*"):
|
|
84
|
+
base = pat[:-2].rstrip("/")
|
|
85
|
+
if base:
|
|
86
|
+
patterns_to_add.add(base)
|
|
87
|
+
patterns_to_add.add(f"{base}/**")
|
|
88
|
+
elif pat.endswith("/"):
|
|
89
|
+
base = pat.rstrip("/")
|
|
90
|
+
if base:
|
|
91
|
+
patterns_to_add.add(base)
|
|
92
|
+
patterns_to_add.add(f"{base}/**")
|
|
93
|
+
patterns_to_add.add(f"{base}/*")
|
|
94
|
+
else:
|
|
95
|
+
full_path = os.path.join(abs_path_to_scan, pat)
|
|
96
|
+
if os.path.isdir(full_path):
|
|
97
|
+
patterns_to_add.add(f"{pat}/**")
|
|
98
|
+
patterns_to_add.add(f"{pat}/*")
|
|
87
99
|
|
|
100
|
+
return patterns_to_add
|
|
88
101
|
|
|
89
|
-
def _is_covered_by_coarse_ignore(rel_path: str, coarse_patterns: Iterable[str]) -> bool:
|
|
90
|
-
excludes = {pattern: "" for pattern in coarse_patterns}
|
|
91
|
-
return not is_included(rel_path, includes={}, excludes=excludes)
|
|
92
102
|
|
|
103
|
+
def _expand_custom_exclude_pattern(pattern: str, abs_path_to_scan: str) -> set:
|
|
104
|
+
exclude_path_normalized = os.path.normpath(
|
|
105
|
+
pattern.replace('\\', '/').strip()
|
|
106
|
+
).replace("\\", "/")
|
|
107
|
+
if not exclude_path_normalized:
|
|
108
|
+
return set()
|
|
93
109
|
|
|
94
|
-
|
|
95
|
-
patterns: set,
|
|
96
|
-
exclude_path: str,
|
|
97
|
-
abs_path_to_scan: str,
|
|
98
|
-
coarse_patterns: frozenset,
|
|
99
|
-
) -> None:
|
|
100
|
-
exclude_path_normalized = os.path.normpath(exclude_path).replace("\\", "/")
|
|
110
|
+
patterns = set(_normalize_custom_pattern(exclude_path_normalized, abs_path_to_scan))
|
|
101
111
|
|
|
102
112
|
if exclude_path_normalized.endswith("/**"):
|
|
103
113
|
base_dir = exclude_path_normalized[:-3].rstrip("/")
|
|
@@ -105,17 +115,15 @@ def _add_path_to_exclude_pattern(
|
|
|
105
115
|
full_exclude_path = os.path.join(abs_path_to_scan, base_dir)
|
|
106
116
|
if os.path.isdir(full_exclude_path):
|
|
107
117
|
patterns.add(base_dir)
|
|
108
|
-
|
|
109
|
-
else:
|
|
110
|
-
patterns.add(exclude_path_normalized)
|
|
118
|
+
patterns.add(exclude_path_normalized)
|
|
111
119
|
else:
|
|
112
120
|
patterns.add(exclude_path_normalized)
|
|
113
|
-
return
|
|
121
|
+
return patterns
|
|
114
122
|
|
|
115
123
|
has_glob_chars = any(char in exclude_path_normalized for char in ['*', '?', '['])
|
|
116
124
|
if has_glob_chars:
|
|
117
125
|
patterns.add(exclude_path_normalized)
|
|
118
|
-
return
|
|
126
|
+
return patterns
|
|
119
127
|
|
|
120
128
|
full_exclude_path = os.path.join(abs_path_to_scan, exclude_path_normalized)
|
|
121
129
|
if os.path.isdir(full_exclude_path):
|
|
@@ -126,29 +134,51 @@ def _add_path_to_exclude_pattern(
|
|
|
126
134
|
else:
|
|
127
135
|
patterns.add(exclude_path_normalized)
|
|
128
136
|
elif os.path.isfile(full_exclude_path):
|
|
129
|
-
|
|
137
|
+
ext = os.path.splitext(exclude_path_normalized)[1].lstrip('.').lower()
|
|
138
|
+
if ext in _WILDCARD_EXTENSIONS:
|
|
139
|
+
patterns.add(f"*.{ext}")
|
|
140
|
+
else:
|
|
130
141
|
patterns.add(f"**/{exclude_path_normalized}")
|
|
131
142
|
else:
|
|
132
|
-
|
|
143
|
+
ext = os.path.splitext(exclude_path_normalized)[1].lstrip('.').lower()
|
|
144
|
+
if ext in _WILDCARD_EXTENSIONS:
|
|
145
|
+
patterns.add(f"*.{ext}")
|
|
146
|
+
else:
|
|
147
|
+
patterns.add(exclude_path_normalized)
|
|
133
148
|
|
|
149
|
+
return patterns
|
|
134
150
|
|
|
135
|
-
def _build_scancode_ignore_patterns(
|
|
136
|
-
path_to_exclude: list,
|
|
137
|
-
abs_path_to_scan: str,
|
|
138
|
-
binary_paths: list,
|
|
139
|
-
) -> tuple:
|
|
140
|
-
coarse_patterns = _default_scancode_coarse_ignore_patterns()
|
|
141
|
-
patterns = set(coarse_patterns)
|
|
142
151
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
def _directory_ignore_pattern(dir_name: str) -> str:
|
|
153
|
+
"""Path-based glob for a directory name (avoids matching the scan root itself)."""
|
|
154
|
+
normalized = dir_name.strip().strip("/").replace("\\", "/")
|
|
155
|
+
if not normalized:
|
|
156
|
+
return dir_name
|
|
157
|
+
return f"**/{normalized}/**"
|
|
149
158
|
|
|
150
|
-
|
|
151
|
-
|
|
159
|
+
|
|
160
|
+
def _default_scancode_ignore_patterns(
|
|
161
|
+
path_to_exclude: list = None,
|
|
162
|
+
abs_path_to_scan: str = ""
|
|
163
|
+
) -> tuple:
|
|
164
|
+
"""
|
|
165
|
+
Coarse ignore patterns aligned with fosslight_util.get_excluded_paths() rules.
|
|
166
|
+
Directory names use path-based globs (e.g. **/tests/**) so they do not match
|
|
167
|
+
the scan root directory name itself.
|
|
168
|
+
Binary files are excluded separately via scancode --ignore-binaries.
|
|
169
|
+
"""
|
|
170
|
+
patterns = {".*"}
|
|
171
|
+
for name in PACKAGE_DIRECTORY + EXCLUDE_DIRECTORY:
|
|
172
|
+
patterns.add(_directory_ignore_pattern(name))
|
|
173
|
+
for ext in EXCLUDE_FILE_EXTENSION:
|
|
174
|
+
patterns.add(f"*.{ext}")
|
|
175
|
+
for name in EXCLUDE_FILENAME:
|
|
176
|
+
patterns.add(name)
|
|
177
|
+
|
|
178
|
+
for pattern in path_to_exclude or []:
|
|
179
|
+
if os.path.isabs(pattern):
|
|
180
|
+
pattern = os.path.relpath(pattern, abs_path_to_scan)
|
|
181
|
+
patterns.update(_expand_custom_exclude_pattern(pattern, abs_path_to_scan))
|
|
152
182
|
|
|
153
183
|
return tuple(sorted(patterns))
|
|
154
184
|
|
|
@@ -160,7 +190,8 @@ def run_scan(
|
|
|
160
190
|
formats: list = [], called_by_cli: bool = False,
|
|
161
191
|
time_out: int = 120, correct_mode: bool = True,
|
|
162
192
|
correct_filepath: str = "", path_to_exclude: list = [],
|
|
163
|
-
excluded_files: list = [], hide_progress: bool = False
|
|
193
|
+
excluded_files: list = [], hide_progress: bool = False,
|
|
194
|
+
ui_mode: bool = False
|
|
164
195
|
) -> Tuple[bool, str, list, list]:
|
|
165
196
|
if not called_by_cli:
|
|
166
197
|
global logger
|
|
@@ -198,8 +229,13 @@ def run_scan(
|
|
|
198
229
|
output_json_file = ""
|
|
199
230
|
|
|
200
231
|
if not called_by_cli:
|
|
201
|
-
|
|
202
|
-
|
|
232
|
+
log_file_path = os.path.join(
|
|
233
|
+
output_path, f"fosslight_log_src_{_file_time}.txt"
|
|
234
|
+
)
|
|
235
|
+
logger, _result_log = init_log(
|
|
236
|
+
log_file_path, True, logging.INFO, logging.DEBUG,
|
|
237
|
+
_PKG_NAME, path_to_scan, path_to_exclude
|
|
238
|
+
)
|
|
203
239
|
|
|
204
240
|
logger.info(f"Tool Info : {_result_log['Tool Info']}")
|
|
205
241
|
|
|
@@ -213,22 +249,8 @@ def run_scan(
|
|
|
213
249
|
pretty_params["path_to_exclude"] = path_to_exclude
|
|
214
250
|
pretty_params["output_file"] = output_file_name
|
|
215
251
|
abs_path_to_scan = os.path.abspath(path_to_scan)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
for name in files:
|
|
219
|
-
full_path = os.path.join(root, name)
|
|
220
|
-
try:
|
|
221
|
-
if not check_binary(full_path, True):
|
|
222
|
-
continue
|
|
223
|
-
except Exception:
|
|
224
|
-
continue
|
|
225
|
-
rel_path = os.path.relpath(full_path, abs_path_to_scan)
|
|
226
|
-
rel_norm = os.path.normpath(rel_path).replace("\\", "/")
|
|
227
|
-
binary_paths.append(rel_norm)
|
|
228
|
-
logger.debug(f"Excluded binary from scancode: {rel_norm}")
|
|
229
|
-
|
|
230
|
-
ignore_tuple = _build_scancode_ignore_patterns(
|
|
231
|
-
path_to_exclude, abs_path_to_scan, binary_paths
|
|
252
|
+
ignore_tuple = _default_scancode_ignore_patterns(
|
|
253
|
+
path_to_exclude, abs_path_to_scan
|
|
232
254
|
)
|
|
233
255
|
logger.debug(f"Scancode ignore patterns: {len(ignore_tuple)}")
|
|
234
256
|
|
|
@@ -241,12 +263,14 @@ def run_scan(
|
|
|
241
263
|
"processes": num_cores,
|
|
242
264
|
"pretty_params": pretty_params,
|
|
243
265
|
"output_json_pp": output_json_file,
|
|
244
|
-
|
|
266
|
+
# Non-UI: only files with findings (license/copyright). UI: all files.
|
|
267
|
+
"only_findings": not ui_mode,
|
|
245
268
|
"license_text": True,
|
|
246
269
|
"url": True,
|
|
247
270
|
"timeout": time_out,
|
|
248
271
|
"include": (),
|
|
249
272
|
"ignore": ignore_tuple,
|
|
273
|
+
"ignore_binaries": True,
|
|
250
274
|
"quiet": hide_progress
|
|
251
275
|
}
|
|
252
276
|
_apply_scancode_unset_workaround(kwargs)
|
|
@@ -262,8 +286,9 @@ def run_scan(
|
|
|
262
286
|
_result_log["Error_files"] = error_msg
|
|
263
287
|
msg = "Failed to analyze :" + error_msg
|
|
264
288
|
if "files" in results:
|
|
265
|
-
rc, result_list, parsing_msg, license_list = parsing_file_item(
|
|
266
|
-
|
|
289
|
+
rc, result_list, parsing_msg, license_list = parsing_file_item(
|
|
290
|
+
results["files"], has_error, need_license, ui_mode=ui_mode
|
|
291
|
+
)
|
|
267
292
|
if parsing_msg:
|
|
268
293
|
_result_log["Parsing Log"] = parsing_msg
|
|
269
294
|
if rc:
|
|
@@ -271,12 +296,6 @@ def run_scan(
|
|
|
271
296
|
success = True
|
|
272
297
|
result_list = sorted(
|
|
273
298
|
result_list, key=lambda row: (''.join(row.licenses)))
|
|
274
|
-
|
|
275
|
-
for scan_item in result_list:
|
|
276
|
-
if os.path.isdir(scan_item.source_name_or_path):
|
|
277
|
-
continue
|
|
278
|
-
if check_binary(os.path.join(path_to_scan, scan_item.source_name_or_path), True):
|
|
279
|
-
scan_item.exclude = True
|
|
280
299
|
except Exception as ex:
|
|
281
300
|
success = False
|
|
282
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,17 +41,18 @@ 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)
|
|
@@ -66,13 +68,21 @@ def run_scanoss_py(path_to_scan: str, output_path: str = "", format: list = [],
|
|
|
66
68
|
scan_output=output_json_file,
|
|
67
69
|
scan_options=ScanType.SCAN_SNIPPETS.value,
|
|
68
70
|
nb_threads=num_threads if num_threads > 0 else 10,
|
|
69
|
-
scanoss_settings=scanoss_settings
|
|
71
|
+
scanoss_settings=scanoss_settings,
|
|
72
|
+
timeout=timeout
|
|
70
73
|
)
|
|
71
74
|
output_buffer = io.StringIO()
|
|
72
75
|
with contextlib.redirect_stdout(output_buffer), contextlib.redirect_stderr(output_buffer):
|
|
73
76
|
scanner.scan_folder_with_options(scan_dir=path_to_scan)
|
|
74
77
|
captured_output = output_buffer.getvalue()
|
|
75
78
|
api_limit_exceed = "due to service limits being exceeded" in captured_output
|
|
79
|
+
timeout_occurred = "The SCANOSS API request timed out" in captured_output
|
|
80
|
+
if timeout_occurred or api_limit_exceed:
|
|
81
|
+
scanoss_skipped = True
|
|
82
|
+
if timeout_occurred:
|
|
83
|
+
logger.debug("SCANOSS skipped (Timeout)")
|
|
84
|
+
elif api_limit_exceed:
|
|
85
|
+
logger.debug("SCANOSS skipped (API Limit Exceeded)")
|
|
76
86
|
|
|
77
87
|
if os.path.isfile(output_json_file):
|
|
78
88
|
logger.debug("|---SCANOSS Parsing")
|
|
@@ -97,4 +107,4 @@ def run_scanoss_py(path_to_scan: str, output_path: str = "", format: list = [],
|
|
|
97
107
|
|
|
98
108
|
logger.info(f"|---Number of files detected with SCANOSS: {(len(scanoss_file_list))}")
|
|
99
109
|
|
|
100
|
-
return scanoss_file_list,
|
|
110
|
+
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.5
|
|
4
4
|
Summary: FOSSLight Source Scanner
|
|
5
5
|
Author: LG Electronics
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -24,8 +24,8 @@ 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
|
|
28
|
+
Requires-Dist: lxml>=6.0.1
|
|
29
29
|
Requires-Dist: cryptography<49; platform_system == "Darwin" and platform_machine == "x86_64"
|
|
30
30
|
Requires-Dist: fingerprints==1.2.3
|
|
31
31
|
Requires-Dist: normality==2.6.1
|
|
@@ -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.3 → fosslight_source-2.3.5}/src/fosslight_source/_parsing_scanoss_file.py
RENAMED
|
File without changes
|
|
File without changes
|
{fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/run_manifest_extractor.py
RENAMED
|
File without changes
|
{fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source/run_spdx_extractor.py
RENAMED
|
File without changes
|
{fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{fosslight_source-2.3.3 → fosslight_source-2.3.5}/src/fosslight_source.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|