scanoss 1.17.2__py3-none-any.whl → 1.17.4__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 +3 -2
- scanoss/data/build_date.txt +1 -1
- scanoss/inspection/copyleft.py +1 -1
- scanoss/inspection/policy_check.py +3 -2
- scanoss/inspection/undeclared_component.py +2 -2
- scanoss/inspection/utils/license_utils.py +1 -1
- scanoss/scanner.py +59 -47
- scanoss/winnowing.py +9 -4
- {scanoss-1.17.2.dist-info → scanoss-1.17.4.dist-info}/METADATA +1 -1
- {scanoss-1.17.2.dist-info → scanoss-1.17.4.dist-info}/RECORD +15 -17
- scanoss/inspection/utils/markdown_utils.py +0 -23
- scanoss/inspection/utils/result_utils.py +0 -79
- {scanoss-1.17.2.dist-info → scanoss-1.17.4.dist-info}/LICENSE +0 -0
- {scanoss-1.17.2.dist-info → scanoss-1.17.4.dist-info}/WHEEL +0 -0
- {scanoss-1.17.2.dist-info → scanoss-1.17.4.dist-info}/entry_points.txt +0 -0
- {scanoss-1.17.2.dist-info → scanoss-1.17.4.dist-info}/top_level.txt +0 -0
scanoss/__init__.py
CHANGED
scanoss/cli.py
CHANGED
|
@@ -26,8 +26,9 @@ import os
|
|
|
26
26
|
from pathlib import Path
|
|
27
27
|
import sys
|
|
28
28
|
import pypac
|
|
29
|
-
|
|
30
|
-
from
|
|
29
|
+
|
|
30
|
+
from .inspection.copyleft import Copyleft
|
|
31
|
+
from .inspection.undeclared_component import UndeclaredComponent
|
|
31
32
|
from .threadeddependencies import SCOPE
|
|
32
33
|
from .scanoss_settings import ScanossSettings
|
|
33
34
|
from .scancodedeps import ScancodeDeps
|
scanoss/data/build_date.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
date:
|
|
1
|
+
date: 20241109095319, utime: 1731145999
|
scanoss/inspection/copyleft.py
CHANGED
|
@@ -26,8 +26,9 @@ import os.path
|
|
|
26
26
|
from abc import abstractmethod
|
|
27
27
|
from enum import Enum
|
|
28
28
|
from typing import Callable, List, Dict, Any
|
|
29
|
-
from
|
|
30
|
-
from
|
|
29
|
+
from .utils.license_utils import LicenseUtil
|
|
30
|
+
from ..scanossbase import ScanossBase
|
|
31
|
+
|
|
31
32
|
|
|
32
33
|
class PolicyStatus(Enum):
|
|
33
34
|
"""
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"""
|
|
24
24
|
import json
|
|
25
25
|
from typing import Dict, Any
|
|
26
|
-
from
|
|
26
|
+
from .policy_check import PolicyCheck, PolicyStatus
|
|
27
27
|
|
|
28
28
|
class UndeclaredComponent(PolicyCheck):
|
|
29
29
|
"""
|
|
@@ -115,7 +115,7 @@ class UndeclaredComponent(PolicyCheck):
|
|
|
115
115
|
'summary': self._get_summary(components),
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
def _generate_sbom_file(self, components: list) -> dict
|
|
118
|
+
def _generate_sbom_file(self, components: list) -> dict:
|
|
119
119
|
"""
|
|
120
120
|
Generate a list of PURLs for the SBOM file.
|
|
121
121
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
22
|
THE SOFTWARE.
|
|
23
23
|
"""
|
|
24
|
-
from
|
|
24
|
+
from ...scanossbase import ScanossBase
|
|
25
25
|
|
|
26
26
|
DEFAULT_COPYLEFT_LICENSES = {
|
|
27
27
|
'agpl-3.0-only', 'artistic-1.0', 'artistic-2.0', 'cc-by-sa-4.0', 'cddl-1.0', 'cddl-1.1', 'cecill-2.1',
|
scanoss/scanner.py
CHANGED
|
@@ -25,6 +25,7 @@ import json
|
|
|
25
25
|
import os
|
|
26
26
|
import sys
|
|
27
27
|
import datetime
|
|
28
|
+
from typing import Any, Dict, List, Optional
|
|
28
29
|
import importlib_resources
|
|
29
30
|
|
|
30
31
|
from progress.bar import Bar
|
|
@@ -490,66 +491,41 @@ class Scanner(ScanossBase):
|
|
|
490
491
|
success = False
|
|
491
492
|
return success
|
|
492
493
|
|
|
493
|
-
def __finish_scan_threaded(self, file_map:
|
|
494
|
-
"""
|
|
495
|
-
|
|
496
|
-
:
|
|
497
|
-
|
|
494
|
+
def __finish_scan_threaded(self, file_map: Optional[Dict[Any, Any]] = None) -> bool:
|
|
495
|
+
"""Wait for the threaded scan to complete and process the results
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
file_map: Mapping of obfuscated files back to originals
|
|
499
|
+
|
|
500
|
+
Returns:
|
|
501
|
+
bool: True if successful, False otherwise
|
|
502
|
+
|
|
503
|
+
Raises:
|
|
504
|
+
ValueError: If output format is invalid
|
|
498
505
|
"""
|
|
499
|
-
success = True
|
|
500
|
-
|
|
506
|
+
success: bool = True
|
|
507
|
+
scan_responses = None
|
|
501
508
|
dep_responses = None
|
|
502
509
|
if self.is_file_or_snippet_scan():
|
|
503
510
|
if not self.threaded_scan.complete(): # Wait for the scans to complete
|
|
504
511
|
self.print_stderr(f'Warning: Scanning analysis ran into some trouble.')
|
|
505
512
|
success = False
|
|
506
513
|
self.threaded_scan.complete_bar()
|
|
507
|
-
|
|
514
|
+
scan_responses = self.threaded_scan.responses
|
|
508
515
|
if self.is_dependency_scan():
|
|
509
516
|
self.print_msg('Retrieving dependency data...')
|
|
510
517
|
if not self.threaded_deps.complete():
|
|
511
|
-
self.print_stderr(
|
|
518
|
+
self.print_stderr(
|
|
519
|
+
f'Warning: Dependency analysis ran into some trouble.'
|
|
520
|
+
)
|
|
512
521
|
success = False
|
|
513
522
|
dep_responses = self.threaded_deps.responses
|
|
514
|
-
# self.print_stderr(f'Dep Data: {dep_responses}')
|
|
515
|
-
# TODO change to dictionary
|
|
516
|
-
raw_output = "{\n"
|
|
517
|
-
# TODO look into merging the two dictionaries. See https://favtutor.com/blogs/merge-dictionaries-python
|
|
518
|
-
if responses or dep_responses:
|
|
519
|
-
first = True
|
|
520
|
-
if responses:
|
|
521
|
-
for scan_resp in responses:
|
|
522
|
-
if scan_resp is not None:
|
|
523
|
-
for key, value in scan_resp.items():
|
|
524
|
-
if file_map: # We have a map for obfuscated files. Check if we can revert it
|
|
525
|
-
fm = file_map.get(key)
|
|
526
|
-
if fm:
|
|
527
|
-
key = fm # Replace the obfuscated filename
|
|
528
|
-
if first:
|
|
529
|
-
raw_output += " \"%s\":%s" % (key, json.dumps(value, indent=2))
|
|
530
|
-
first = False
|
|
531
|
-
else:
|
|
532
|
-
raw_output += ",\n \"%s\":%s" % (key, json.dumps(value, indent=2))
|
|
533
|
-
# End for loop
|
|
534
|
-
if dep_responses:
|
|
535
|
-
dep_files = dep_responses.get("files")
|
|
536
|
-
if dep_files and len(dep_files) > 0:
|
|
537
|
-
for dep_file in dep_files:
|
|
538
|
-
file = dep_file.pop("file", None)
|
|
539
|
-
if file is not None:
|
|
540
|
-
if first:
|
|
541
|
-
raw_output += " \"%s\":[%s]" % (file, json.dumps(dep_file, indent=2))
|
|
542
|
-
first = False
|
|
543
|
-
else:
|
|
544
|
-
raw_output += ",\n \"%s\":[%s]" % (file, json.dumps(dep_file, indent=2))
|
|
545
|
-
# End for loop
|
|
546
|
-
raw_output += "\n}"
|
|
547
|
-
try:
|
|
548
|
-
raw_results = json.loads(raw_output)
|
|
549
|
-
except Exception as e:
|
|
550
|
-
raise Exception(f'ERROR: Problem decoding parsed json: {e}')
|
|
551
523
|
|
|
552
|
-
|
|
524
|
+
raw_scan_results = self._merge_scan_results(
|
|
525
|
+
scan_responses, dep_responses, file_map
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
results = self.post_processor.load_results(raw_scan_results).post_process()
|
|
553
529
|
|
|
554
530
|
if self.output_format == 'plain':
|
|
555
531
|
self.__log_result(json.dumps(results, indent=2, sort_keys=True))
|
|
@@ -567,6 +543,42 @@ class Scanner(ScanossBase):
|
|
|
567
543
|
success = False
|
|
568
544
|
return success
|
|
569
545
|
|
|
546
|
+
def _merge_scan_results(
|
|
547
|
+
self,
|
|
548
|
+
scan_responses: Optional[List],
|
|
549
|
+
dep_responses: Optional[Dict[str,Any]],
|
|
550
|
+
file_map: Optional[Dict[str, Any]],
|
|
551
|
+
) -> Dict[str, Any]:
|
|
552
|
+
"""Merge scan and dependency responses into a single dictionary"""
|
|
553
|
+
results: Dict[str, Any] = {}
|
|
554
|
+
|
|
555
|
+
if scan_responses:
|
|
556
|
+
for response in scan_responses:
|
|
557
|
+
if response is not None:
|
|
558
|
+
if file_map:
|
|
559
|
+
response = self._deobfuscate_filenames(response, file_map)
|
|
560
|
+
results.update(response)
|
|
561
|
+
|
|
562
|
+
dep_files = dep_responses.get("files", None) if dep_responses else None
|
|
563
|
+
if dep_files:
|
|
564
|
+
for dep_file in dep_files:
|
|
565
|
+
file = dep_file.pop("file", None)
|
|
566
|
+
if file:
|
|
567
|
+
results[file] = dep_file
|
|
568
|
+
|
|
569
|
+
return results
|
|
570
|
+
|
|
571
|
+
def _deobfuscate_filenames(self, response: dict, file_map: dict) -> dict:
|
|
572
|
+
"""Convert obfuscated filenames back to original names"""
|
|
573
|
+
deobfuscated = {}
|
|
574
|
+
for key, value in response.items():
|
|
575
|
+
deobfuscated_name = file_map.get(key, None)
|
|
576
|
+
if deobfuscated_name:
|
|
577
|
+
deobfuscated[deobfuscated_name] = value
|
|
578
|
+
else:
|
|
579
|
+
deobfuscated[key] = value
|
|
580
|
+
return deobfuscated
|
|
581
|
+
|
|
570
582
|
def scan_file_with_options(self, file: str, deps_file: str = None, file_map: dict = None, dep_scope: SCOPE = None,
|
|
571
583
|
dep_scope_include: str = None, dep_scope_exclude: str = None) -> bool:
|
|
572
584
|
"""
|
scanoss/winnowing.py
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"""
|
|
30
30
|
import hashlib
|
|
31
31
|
import pathlib
|
|
32
|
+
import platform
|
|
32
33
|
import re
|
|
33
34
|
|
|
34
35
|
from crc32c import crc32c
|
|
@@ -307,11 +308,15 @@ class Winnowing(ScanossBase):
|
|
|
307
308
|
return ''
|
|
308
309
|
# Print file line
|
|
309
310
|
content_length = len(contents)
|
|
310
|
-
|
|
311
|
+
original_filename = file
|
|
312
|
+
|
|
313
|
+
if platform.system() == 'Windows':
|
|
314
|
+
original_filename = file.replace('\\', '/')
|
|
315
|
+
wfp_filename = repr(original_filename).strip("'") # return a utf-8 compatible version of the filename
|
|
311
316
|
if self.obfuscate: # hide the real size of the file and its name, but keep the suffix
|
|
312
|
-
wfp_filename = f'{self.ob_count}{pathlib.Path(
|
|
317
|
+
wfp_filename = f'{self.ob_count}{pathlib.Path(original_filename).suffix}'
|
|
313
318
|
self.ob_count = self.ob_count + 1
|
|
314
|
-
self.file_map[wfp_filename] =
|
|
319
|
+
self.file_map[wfp_filename] = original_filename # Save the file name map for later (reverse lookup)
|
|
315
320
|
|
|
316
321
|
wfp = 'file={0},{1},{2}\n'.format(file_md5, content_length, wfp_filename)
|
|
317
322
|
# We don't process snippets for binaries, or other uninteresting files, or if we're requested to skip
|
|
@@ -464,7 +469,7 @@ class Winnowing(ScanossBase):
|
|
|
464
469
|
crc = self.crc8_byte(crc, buffer[index])
|
|
465
470
|
crc ^= CRC8_MAXIM_DOW_FINAL # Bitwise OR (XOR) of crc in Maxim Dow Final
|
|
466
471
|
return crc
|
|
467
|
-
|
|
472
|
+
|
|
468
473
|
#
|
|
469
474
|
# End of Winnowing Class
|
|
470
475
|
#
|
|
@@ -4,15 +4,15 @@ protoc_gen_swagger/options/annotations_pb2.py,sha256=b25EDD6gssUWnFby9gxgcpLIROT
|
|
|
4
4
|
protoc_gen_swagger/options/annotations_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
5
5
|
protoc_gen_swagger/options/openapiv2_pb2.py,sha256=vYElGp8E1vGHszvWqX97zNG9GFJ7u2QcdK9ouq0XdyI,14939
|
|
6
6
|
protoc_gen_swagger/options/openapiv2_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
7
|
-
scanoss/__init__.py,sha256=
|
|
8
|
-
scanoss/cli.py,sha256=
|
|
7
|
+
scanoss/__init__.py,sha256=3jhR_mltPm870L27utBf37tiCtNy516_7MNyf6kVsjQ,1163
|
|
8
|
+
scanoss/cli.py,sha256=LSe8D3whEiDXgVyAtw2dsVqBd9BwQyB6POz4cH7kVf8,50652
|
|
9
9
|
scanoss/components.py,sha256=ZHZ1KA69shxOASZK7USD9yPTITpAc_RXL5q5zpDK23o,12590
|
|
10
10
|
scanoss/csvoutput.py,sha256=hBwr_Fc6mBdOdXgyQcdFrockYH-PJ0jblowlExJ6OPg,9925
|
|
11
11
|
scanoss/cyclonedx.py,sha256=JVBYeR3D-i4yP9cVSyWvm0_7Y8Kr2MC5GxMgRGAf8R0,12585
|
|
12
12
|
scanoss/filecount.py,sha256=o7xb6m387ucnsU4H1OXGzf_AdWsudhAHe49T8uX4Ieo,6660
|
|
13
13
|
scanoss/results.py,sha256=7G33QAYYI9qI61TCzXjSLYXMmg5CDtZS5e2QhnQfE74,9883
|
|
14
14
|
scanoss/scancodedeps.py,sha256=_9d7MAV20-FrET7mF7gW-BZiz2eHrtwudgrEcSX0oZQ,11321
|
|
15
|
-
scanoss/scanner.py,sha256=
|
|
15
|
+
scanoss/scanner.py,sha256=PEWOFFVxPRvT6Wp9CUi30owWQyZNc3Nf-8bRv5X2uuQ,51749
|
|
16
16
|
scanoss/scanoss_settings.py,sha256=QOpBDfu6vYL-K3WxkujPbXWwut1BHWpN2mJTm7yMD9c,6162
|
|
17
17
|
scanoss/scanossapi.py,sha256=TJxPctr-0DTn_26LfM__OAMfntaXzvheFTbdmU-5pnM,11953
|
|
18
18
|
scanoss/scanossbase.py,sha256=zMDRCLbrcoRvYEKQRuZXnBiVY4_Vsplmg_APbB65oaU,3084
|
|
@@ -22,7 +22,7 @@ scanoss/scantype.py,sha256=R2-ExLGOrYxaJFtIK2AEo2caD0XrN1zpF5q1qT9Zsyc,1326
|
|
|
22
22
|
scanoss/spdxlite.py,sha256=REChAWV-6qhp16jc4X2lMb1v7VvYiDH5nN9VDV3fDaQ,15828
|
|
23
23
|
scanoss/threadeddependencies.py,sha256=sOIAjiPTmxybKz2yhT4-ixXBeC4K8UQVq6JQj4e8mLc,9906
|
|
24
24
|
scanoss/threadedscanning.py,sha256=T0tL8W1IEX_hLY5ksrAl_iQqtxT_KbyDhTDHo6a7xFE,9387
|
|
25
|
-
scanoss/winnowing.py,sha256=
|
|
25
|
+
scanoss/winnowing.py,sha256=RCe5_EsyZ_iu6HhVTSEDeOw3MdNNlu_8m2Cz7BLl8RM,18926
|
|
26
26
|
scanoss/api/__init__.py,sha256=KlDD87JmyZP-10T-fuJo0_v2zt1gxWfTgs70wjky9xg,1139
|
|
27
27
|
scanoss/api/common/__init__.py,sha256=KlDD87JmyZP-10T-fuJo0_v2zt1gxWfTgs70wjky9xg,1139
|
|
28
28
|
scanoss/api/common/v2/__init__.py,sha256=KlDD87JmyZP-10T-fuJo0_v2zt1gxWfTgs70wjky9xg,1139
|
|
@@ -50,19 +50,17 @@ scanoss/api/vulnerabilities/__init__.py,sha256=FLQtiDiv85Q1Chk-sJ9ky9WOV1mulZhEK
|
|
|
50
50
|
scanoss/api/vulnerabilities/v2/__init__.py,sha256=FLQtiDiv85Q1Chk-sJ9ky9WOV1mulZhEKjiBihlwiaM,1139
|
|
51
51
|
scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py,sha256=CFhF80av8tenGvn9AIsGEtRJPuV2dC_syA5JLZb2lDw,5464
|
|
52
52
|
scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py,sha256=HlS4k4Zmx6RIAqaO9I96jD-eyF5yU6Xx04pVm7pdqOg,6864
|
|
53
|
-
scanoss/data/build_date.txt,sha256=
|
|
53
|
+
scanoss/data/build_date.txt,sha256=HTDHGlgxkIsYjJA8gub-HVdvIYCZTk3IBODvrsSr6o0,40
|
|
54
54
|
scanoss/data/spdx-exceptions.json,sha256=s7UTYxC7jqQXr11YBlIWYCNwN6lRDFTR33Y8rpN_dA4,17953
|
|
55
55
|
scanoss/data/spdx-licenses.json,sha256=A6Z0q82gaTLtnopBfzeIVZjJFxkdRW1g2TuumQc-lII,228794
|
|
56
56
|
scanoss/inspection/__init__.py,sha256=z62680zKq4OmBOugSODvgpSwdsloZL7bvcaMbnx3xgU,1139
|
|
57
|
-
scanoss/inspection/copyleft.py,sha256=
|
|
58
|
-
scanoss/inspection/policy_check.py,sha256=
|
|
59
|
-
scanoss/inspection/undeclared_component.py,sha256=
|
|
60
|
-
scanoss/inspection/utils/license_utils.py,sha256=
|
|
61
|
-
scanoss/
|
|
62
|
-
scanoss/
|
|
63
|
-
scanoss-1.17.
|
|
64
|
-
scanoss-1.17.
|
|
65
|
-
scanoss-1.17.
|
|
66
|
-
scanoss-1.17.
|
|
67
|
-
scanoss-1.17.2.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
|
|
68
|
-
scanoss-1.17.2.dist-info/RECORD,,
|
|
57
|
+
scanoss/inspection/copyleft.py,sha256=dkiLkgNYz7cbIQZCzy6zThiIyHkqrper_xruZ9PQhAI,6563
|
|
58
|
+
scanoss/inspection/policy_check.py,sha256=eo5VfEBwKoDSqIwRi0xwaVLy6EUR29HlH5Bl0Kpvx7I,14752
|
|
59
|
+
scanoss/inspection/undeclared_component.py,sha256=BxFhRrI_b_Ulu7cArm5ySphAXT7DfMmbrrGy121cz14,6808
|
|
60
|
+
scanoss/inspection/utils/license_utils.py,sha256=mIaoVWXMA6shkRQmgmiP2mWchjxX4ex8LWs91Nf6rq4,5093
|
|
61
|
+
scanoss-1.17.4.dist-info/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
|
|
62
|
+
scanoss-1.17.4.dist-info/METADATA,sha256=EaR6VFelUQH-AiXibOmz5UeqNFo5zGW0OhIsO-2CYD0,5936
|
|
63
|
+
scanoss-1.17.4.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
64
|
+
scanoss-1.17.4.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
|
|
65
|
+
scanoss-1.17.4.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
|
|
66
|
+
scanoss-1.17.4.dist-info/RECORD,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
def generate_table(headers, rows, centered_columns=None):
|
|
2
|
-
"""
|
|
3
|
-
Generate Markdown table
|
|
4
|
-
:param headers: List of headers
|
|
5
|
-
:param rows: Rows
|
|
6
|
-
:param centered_columns: List with centered columns
|
|
7
|
-
"""
|
|
8
|
-
COL_SEP = ' | '
|
|
9
|
-
centered_column_set = set(centered_columns or [])
|
|
10
|
-
def create_separator(header, index):
|
|
11
|
-
if centered_columns is None:
|
|
12
|
-
return '-'
|
|
13
|
-
return ':-:' if index in centered_column_set else '-'
|
|
14
|
-
|
|
15
|
-
row_separator = COL_SEP + COL_SEP.join(
|
|
16
|
-
create_separator(header, index) for index, header in enumerate(headers)
|
|
17
|
-
) + COL_SEP
|
|
18
|
-
|
|
19
|
-
table_rows = [COL_SEP + COL_SEP.join(headers) + COL_SEP]
|
|
20
|
-
table_rows.append(row_separator)
|
|
21
|
-
table_rows.extend(COL_SEP + COL_SEP.join(row) + COL_SEP for row in rows)
|
|
22
|
-
|
|
23
|
-
return '\n'.join(table_rows)
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
from typing import Dict, Any
|
|
3
|
-
|
|
4
|
-
from scanoss.inspection.utils.license_utils import license_util
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class ComponentID(Enum):
|
|
8
|
-
FILE = "file"
|
|
9
|
-
SNIPPET = "snippet"
|
|
10
|
-
DEPENDENCY = "dependency"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _append_component(components: Dict[str, Any], new_component: Dict[str, Any]) -> Dict[str, Any]:
|
|
14
|
-
"""
|
|
15
|
-
Append a new component to the components dictionary.
|
|
16
|
-
|
|
17
|
-
This function creates a new entry in the components dictionary for the given component,
|
|
18
|
-
or updates an existing entry if the component already exists. It also processes the
|
|
19
|
-
licenses associated with the component.
|
|
20
|
-
|
|
21
|
-
:param components: The existing dictionary of components
|
|
22
|
-
:param new_component: The new component to be added or updated
|
|
23
|
-
:return: The updated components dictionary
|
|
24
|
-
"""
|
|
25
|
-
component_key = f"{new_component['purl'][0]}@{new_component['version']}"
|
|
26
|
-
components[component_key] = {
|
|
27
|
-
'purl': new_component['purl'][0],
|
|
28
|
-
'version': new_component['version'],
|
|
29
|
-
'licenses': {},
|
|
30
|
-
'status': new_component['status'],
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
# Process licenses for this component
|
|
34
|
-
for l in new_component['licenses']:
|
|
35
|
-
spdxid = l['name']
|
|
36
|
-
components[component_key]['licenses'][spdxid] = {
|
|
37
|
-
'spdxid': spdxid,
|
|
38
|
-
'copyleft': license_util.is_copyleft(spdxid),
|
|
39
|
-
'url': l.get('url')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return components
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def get_components(results: Dict[str, Any]) -> list:
|
|
46
|
-
"""
|
|
47
|
-
Process the results dictionary to extract and format component information.
|
|
48
|
-
|
|
49
|
-
This function iterates through the results dictionary, identifying components from
|
|
50
|
-
different sources (files, snippets, and dependencies). It consolidates this information
|
|
51
|
-
into a list of unique components, each with its associated licenses and other details.
|
|
52
|
-
|
|
53
|
-
:param results: A dictionary containing the raw results of a component scan
|
|
54
|
-
:return: A list of dictionaries, each representing a unique component with its details
|
|
55
|
-
"""
|
|
56
|
-
components = {}
|
|
57
|
-
for component in results.values():
|
|
58
|
-
for c in component:
|
|
59
|
-
if c['id'] in [ComponentID.FILE.value, ComponentID.SNIPPET.value]:
|
|
60
|
-
component_key = f"{c['purl'][0]}@{c['version']}"
|
|
61
|
-
|
|
62
|
-
# Initialize or update the component entry
|
|
63
|
-
if component_key not in components:
|
|
64
|
-
components = _append_component(components, c)
|
|
65
|
-
|
|
66
|
-
if c['id'] == ComponentID.DEPENDENCY.value:
|
|
67
|
-
for d in c['dependencies']:
|
|
68
|
-
component_key = f"{d['purl'][0]}@{d['version']}"
|
|
69
|
-
|
|
70
|
-
if component_key not in components:
|
|
71
|
-
components = _append_component(components, d)
|
|
72
|
-
# End of for loop
|
|
73
|
-
# End if
|
|
74
|
-
# End if
|
|
75
|
-
results = list(components.values())
|
|
76
|
-
for component in results:
|
|
77
|
-
component['licenses'] = list(component['licenses'].values())
|
|
78
|
-
|
|
79
|
-
return results
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|