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 CHANGED
@@ -22,4 +22,4 @@
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- __version__ = "1.17.2"
25
+ __version__ = "1.17.4"
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
- from scanoss.inspection.copyleft import Copyleft
30
- from scanoss.inspection.undeclared_component import UndeclaredComponent
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
@@ -1 +1 @@
1
- date: 20241104160508, utime: 1730736308
1
+ date: 20241109095319, utime: 1731145999
@@ -23,7 +23,7 @@
23
23
  """
24
24
  import json
25
25
  from typing import Dict, Any
26
- from scanoss.inspection.policy_check import PolicyCheck, PolicyStatus
26
+ from .policy_check import PolicyCheck, PolicyStatus
27
27
 
28
28
  class Copyleft(PolicyCheck):
29
29
  """
@@ -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 scanoss.inspection.utils.license_utils import LicenseUtil
30
- from scanoss.scanossbase import ScanossBase
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 scanoss.inspection.policy_check import PolicyCheck, PolicyStatus
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[str, list[dict[str, str]]]:
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 scanoss.scanossbase import ScanossBase
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: dict = None) -> bool:
494
- """
495
- Wait for the threaded scans to complete
496
- :param file_map: mapping of obfuscated files back into originals
497
- :return: True if successful, False otherwise
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
- responses = None
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
- responses = self.threaded_scan.responses
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(f'Warning: Dependency analysis ran into some trouble.')
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
- results = self.post_processor.load_results(raw_results).post_process()
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
- wfp_filename = repr(file).strip("'") # return a utf-8 compatible version of the filename
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(file).suffix}'
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] = file # Save the file name map for later (reverse lookup)
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
  #
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scanoss
3
- Version: 1.17.2
3
+ Version: 1.17.4
4
4
  Summary: Simple Python library to leverage the SCANOSS APIs
5
5
  Home-page: https://scanoss.com
6
6
  Author: SCANOSS
@@ -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=aru_CeXo9ujcKEUaT1zaTFbIBCn_pJNsNzmB2T4ewwU,1163
8
- scanoss/cli.py,sha256=6NlNf1PHlaWNpsDhXFXvrwdLUFiCHbPypj4eFl4rJ5U,50665
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=Boxk0A-AuS0DMB4UYArU0PWZ0yJlK4v1YgdeVnKmJck,52023
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=HzMWRYh1XB4so71br-DUPpV6OlmymDfsnU-EOCCObJM,18734
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=oPE2N3MmwjrcnboSRBZJnf0DruUMcoSRdYculyX05aA,40
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=B150gqqEvXZXq5toYtmNCKY7M0WkooBUWYiIeu48aek,6581
58
- scanoss/inspection/policy_check.py,sha256=r4qvY8j5Vz8FflVL1xZxkd7ufX_wLh5ocna6MoTkPWg,14775
59
- scanoss/inspection/undeclared_component.py,sha256=XUxNz6iaAoeLPPT4XnfeQlvOv-2thmf1MxP3KRmegQE,6853
60
- scanoss/inspection/utils/license_utils.py,sha256=iln0414t-cfmVktmAd7ANK7oOqpfRUvwRwIgrj-6GJA,5098
61
- scanoss/inspection/utils/markdown_utils.py,sha256=hCa7rqBvtRoAziz3wj0gbpUOrPJIEi3pTvIUrsZf6qc,808
62
- scanoss/inspection/utils/result_utils.py,sha256=OJRFznK4WCBMNvgX9kTus5WI5eTKjTXp_kWya7ixCyQ,2938
63
- scanoss-1.17.2.dist-info/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
64
- scanoss-1.17.2.dist-info/METADATA,sha256=xdt4go57mXTL7fodzNRcOSC7ywmvFHTW-Hs6F5SWoGc,5936
65
- scanoss-1.17.2.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
66
- scanoss-1.17.2.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
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