scanoss 1.23.0__py3-none-any.whl → 1.25.0__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 @@ SPDX-License-Identifier: MIT
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- __version__ = '1.23.0'
25
+ __version__ = '1.25.0'
scanoss/cli.py CHANGED
@@ -31,6 +31,7 @@ from typing import List
31
31
 
32
32
  import pypac
33
33
 
34
+ from scanoss.cryptography import Cryptography, create_cryptography_config_from_args
34
35
  from scanoss.scanners.container_scanner import (
35
36
  DEFAULT_SYFT_COMMAND,
36
37
  DEFAULT_SYFT_TIMEOUT,
@@ -50,6 +51,7 @@ from scanoss.scanossgrpc import (
50
51
  from . import __version__
51
52
  from .components import Components
52
53
  from .constants import (
54
+ DEFAULT_API_TIMEOUT,
53
55
  DEFAULT_POST_SIZE,
54
56
  DEFAULT_RETRY,
55
57
  DEFAULT_TIMEOUT,
@@ -292,15 +294,6 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
292
294
  help='component sub-commands',
293
295
  )
294
296
 
295
- # Component Sub-command: component crypto
296
- c_crypto = comp_sub.add_parser(
297
- 'crypto',
298
- aliases=['cr'],
299
- description=f'Show Cryptographic algorithms: {__version__}',
300
- help='Retrieve cryptographic algorithms for the given components',
301
- )
302
- c_crypto.set_defaults(func=comp_crypto)
303
-
304
297
  # Component Sub-command: component vulns
305
298
  c_vulns = comp_sub.add_parser(
306
299
  'vulns',
@@ -361,18 +354,76 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
361
354
  c_versions.add_argument('--limit', '-l', type=int, help='Generic component search')
362
355
  c_versions.set_defaults(func=comp_versions)
363
356
 
357
+ # Sub-command: crypto
358
+ p_crypto = subparsers.add_parser(
359
+ 'crypto',
360
+ aliases=['cr'],
361
+ description=f'SCANOSS Crypto commands: {__version__}',
362
+ help='Crypto support commands',
363
+ )
364
+ crypto_sub = p_crypto.add_subparsers(
365
+ title='Crypto Commands',
366
+ dest='subparsercmd',
367
+ description='crypto sub-commands',
368
+ help='crypto sub-commands',
369
+ )
370
+
371
+ # GetAlgorithms and GetAlgorithmsInRange gRPC APIs
372
+ p_crypto_algorithms = crypto_sub.add_parser(
373
+ 'algorithms',
374
+ aliases=['alg'],
375
+ description=f'Show Cryptographic algorithms: {__version__}',
376
+ help='Retrieve cryptographic algorithms for the given components',
377
+ )
378
+ p_crypto_algorithms.add_argument(
379
+ '--with-range',
380
+ action='store_true',
381
+ help='Returns the list of versions in the specified range that contains cryptographic algorithms',
382
+ )
383
+ p_crypto_algorithms.set_defaults(func=crypto_algorithms)
384
+
385
+ # GetEncryptionHints and GetHintsInRange gRPC APIs
386
+ p_crypto_hints = crypto_sub.add_parser(
387
+ 'hints',
388
+ description=f'Show Encryption hints: {__version__}',
389
+ help='Retrieve encryption hints for the given components',
390
+ )
391
+ p_crypto_hints.add_argument(
392
+ '--with-range',
393
+ action='store_true',
394
+ help='Returns the list of versions in the specified range that contains encryption hints',
395
+ )
396
+ p_crypto_hints.set_defaults(func=crypto_hints)
397
+
398
+ p_crypto_versions_in_range = crypto_sub.add_parser(
399
+ 'versions-in-range',
400
+ aliases=['vr'],
401
+ description=f'Show versions in range: {__version__}',
402
+ help="Given a list of PURLS and version ranges, get a list of versions that do/don't contain crypto algorithms",
403
+ )
404
+ p_crypto_versions_in_range.set_defaults(func=crypto_versions_in_range)
405
+
364
406
  # Common purl Component sub-command options
365
- for p in [c_crypto, c_vulns, c_semgrep, c_provenance]:
407
+ for p in [c_vulns, c_semgrep, c_provenance, p_crypto_algorithms, p_crypto_hints, p_crypto_versions_in_range]:
366
408
  p.add_argument('--purl', '-p', type=str, nargs='*', help='Package URL - PURL to process.')
367
409
  p.add_argument('--input', '-i', type=str, help='Input file name')
368
410
 
369
411
  # Common Component sub-command options
370
- for p in [c_crypto, c_vulns, c_search, c_versions, c_semgrep, c_provenance]:
412
+ for p in [
413
+ c_vulns,
414
+ c_search,
415
+ c_versions,
416
+ c_semgrep,
417
+ c_provenance,
418
+ p_crypto_algorithms,
419
+ p_crypto_hints,
420
+ p_crypto_versions_in_range,
421
+ ]:
371
422
  p.add_argument(
372
423
  '--timeout',
373
424
  '-M',
374
425
  type=int,
375
- default=600,
426
+ default=DEFAULT_API_TIMEOUT,
376
427
  help='Timeout (in seconds) for API communication (optional - default 600)',
377
428
  )
378
429
 
@@ -588,7 +639,6 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
588
639
  p_dep,
589
640
  p_fc,
590
641
  p_cnv,
591
- c_crypto,
592
642
  c_vulns,
593
643
  c_search,
594
644
  c_versions,
@@ -597,6 +647,9 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
597
647
  p_c_dwnld,
598
648
  p_folder_scan,
599
649
  p_folder_hash,
650
+ p_crypto_algorithms,
651
+ p_crypto_hints,
652
+ p_crypto_versions_in_range,
600
653
  ]:
601
654
  p.add_argument('--output', '-o', type=str, help='Output result file name (optional - default stdout).')
602
655
 
@@ -674,7 +727,6 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
674
727
  # Global Scan/GRPC options
675
728
  for p in [
676
729
  p_scan,
677
- c_crypto,
678
730
  c_vulns,
679
731
  c_search,
680
732
  c_versions,
@@ -682,6 +734,9 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
682
734
  c_provenance,
683
735
  p_folder_scan,
684
736
  p_cs,
737
+ p_crypto_algorithms,
738
+ p_crypto_hints,
739
+ p_crypto_versions_in_range,
685
740
  ]:
686
741
  p.add_argument(
687
742
  '--key', '-k', type=str, help='SCANOSS API Key token (optional - not required for default OSSKB URL)'
@@ -708,7 +763,19 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
708
763
  )
709
764
 
710
765
  # Global GRPC options
711
- for p in [p_scan, c_crypto, c_vulns, c_search, c_versions, c_semgrep, c_provenance, p_folder_scan, p_cs]:
766
+ for p in [
767
+ p_scan,
768
+ c_vulns,
769
+ c_search,
770
+ c_versions,
771
+ c_semgrep,
772
+ c_provenance,
773
+ p_folder_scan,
774
+ p_cs,
775
+ p_crypto_algorithms,
776
+ p_crypto_hints,
777
+ p_crypto_versions_in_range,
778
+ ]:
712
779
  p.add_argument(
713
780
  '--api2url', type=str, help='SCANOSS gRPC API 2.0 URL (optional - default: https://api.osskb.org)'
714
781
  )
@@ -751,7 +818,6 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
751
818
  p_c_loc,
752
819
  p_c_dwnld,
753
820
  p_p_proxy,
754
- c_crypto,
755
821
  c_vulns,
756
822
  c_search,
757
823
  c_versions,
@@ -763,6 +829,9 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
763
829
  p_folder_scan,
764
830
  p_folder_hash,
765
831
  p_cs,
832
+ p_crypto_algorithms,
833
+ p_crypto_hints,
834
+ p_crypto_versions_in_range,
766
835
  ]:
767
836
  p.add_argument('--debug', '-d', action='store_true', help='Enable debug messages')
768
837
  p.add_argument('--trace', '-t', action='store_true', help='Enable trace messages, including API posts')
@@ -775,7 +844,9 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
775
844
  if not args.subparser:
776
845
  parser.print_help() # No sub command subcommand, print general help
777
846
  sys.exit(1)
778
- elif (args.subparser in ('utils', 'ut', 'component', 'comp', 'inspect', 'insp', 'ins')) and not args.subparsercmd:
847
+ elif (
848
+ args.subparser in ('utils', 'ut', 'component', 'comp', 'inspect', 'insp', 'ins', 'crypto', 'cr')
849
+ ) and not args.subparsercmd:
779
850
  parser.parse_args([args.subparser, '--help']) # Force utils helps to be displayed
780
851
  sys.exit(1)
781
852
  args.func(parser, args) # Execute the function associated with the sub-command
@@ -1393,9 +1464,9 @@ def get_pac_file(pac: str):
1393
1464
  return pac_file
1394
1465
 
1395
1466
 
1396
- def comp_crypto(parser, args):
1467
+ def crypto_algorithms(parser, args):
1397
1468
  """
1398
- Run the "component crypto" sub-command
1469
+ Run the "crypto algorithms" sub-command
1399
1470
  Parameters
1400
1471
  ----------
1401
1472
  parser: ArgumentParser
@@ -1410,22 +1481,112 @@ def comp_crypto(parser, args):
1410
1481
  if args.ca_cert and not os.path.exists(args.ca_cert):
1411
1482
  print_stderr(f'Error: Certificate file does not exist: {args.ca_cert}.')
1412
1483
  sys.exit(1)
1413
- pac_file = get_pac_file(args.pac)
1414
1484
 
1415
- comps = Components(
1416
- debug=args.debug,
1417
- trace=args.trace,
1418
- quiet=args.quiet,
1419
- grpc_url=args.api2url,
1420
- api_key=args.key,
1421
- ca_cert=args.ca_cert,
1422
- proxy=args.proxy,
1423
- grpc_proxy=args.grpc_proxy,
1424
- pac=pac_file,
1425
- timeout=args.timeout,
1426
- req_headers=process_req_headers(args.header),
1427
- )
1428
- if not comps.get_crypto_details(args.input, args.purl, args.output):
1485
+ try:
1486
+ config = create_cryptography_config_from_args(args)
1487
+ grpc_config = create_grpc_config_from_args(args)
1488
+ if args.pac:
1489
+ grpc_config.pac = get_pac_file(args.pac)
1490
+ if args.header:
1491
+ grpc_config.req_headers = process_req_headers(args.header)
1492
+ client = ScanossGrpc(**asdict(grpc_config))
1493
+
1494
+ cryptography = Cryptography(config=config, client=client)
1495
+ cryptography.get_algorithms()
1496
+ cryptography.present(output_file=args.output)
1497
+ except ScanossGrpcError as e:
1498
+ print_stderr(f'API ERROR: {e}')
1499
+ sys.exit(1)
1500
+ except Exception as e:
1501
+ if args.debug:
1502
+ import traceback
1503
+
1504
+ traceback.print_exc()
1505
+ print_stderr(f'ERROR: {e}')
1506
+ sys.exit(1)
1507
+
1508
+
1509
+ def crypto_hints(parser, args):
1510
+ """
1511
+ Run the "crypto hints" sub-command
1512
+ Parameters
1513
+ ----------
1514
+ parser: ArgumentParser
1515
+ command line parser object
1516
+ args: Namespace
1517
+ Parsed arguments
1518
+ """
1519
+ if (not args.purl and not args.input) or (args.purl and args.input):
1520
+ print_stderr('Please specify an input file or purl to decorate (--purl or --input)')
1521
+ parser.parse_args([args.subparser, args.subparsercmd, '-h'])
1522
+ sys.exit(1)
1523
+ if args.ca_cert and not os.path.exists(args.ca_cert):
1524
+ print_stderr(f'Error: Certificate file does not exist: {args.ca_cert}.')
1525
+ sys.exit(1)
1526
+
1527
+ try:
1528
+ config = create_cryptography_config_from_args(args)
1529
+ grpc_config = create_grpc_config_from_args(args)
1530
+ if args.pac:
1531
+ grpc_config.pac = get_pac_file(args.pac)
1532
+ if args.header:
1533
+ grpc_config.req_headers = process_req_headers(args.header)
1534
+ client = ScanossGrpc(**asdict(grpc_config))
1535
+
1536
+ cryptography = Cryptography(config=config, client=client)
1537
+ cryptography.get_encryption_hints()
1538
+ cryptography.present(output_file=args.output)
1539
+ except ScanossGrpcError as e:
1540
+ print_stderr(f'API ERROR: {e}')
1541
+ sys.exit(1)
1542
+ except Exception as e:
1543
+ if args.debug:
1544
+ import traceback
1545
+
1546
+ traceback.print_exc()
1547
+ print_stderr(f'ERROR: {e}')
1548
+ sys.exit(1)
1549
+
1550
+
1551
+ def crypto_versions_in_range(parser, args):
1552
+ """
1553
+ Run the "crypto versions-in-range" sub-command
1554
+ Parameters
1555
+ ----------
1556
+ parser: ArgumentParser
1557
+ command line parser object
1558
+ args: Namespace
1559
+ Parsed arguments
1560
+ """
1561
+ if (not args.purl and not args.input) or (args.purl and args.input):
1562
+ print_stderr('Please specify an input file or purl to decorate (--purl or --input)')
1563
+ parser.parse_args([args.subparser, args.subparsercmd, '-h'])
1564
+ sys.exit(1)
1565
+ if args.ca_cert and not os.path.exists(args.ca_cert):
1566
+ print_stderr(f'Error: Certificate file does not exist: {args.ca_cert}.')
1567
+ sys.exit(1)
1568
+
1569
+ try:
1570
+ config = create_cryptography_config_from_args(args)
1571
+ grpc_config = create_grpc_config_from_args(args)
1572
+ if args.pac:
1573
+ grpc_config.pac = get_pac_file(args.pac)
1574
+ if args.header:
1575
+ grpc_config.req_headers = process_req_headers(args.header)
1576
+ client = ScanossGrpc(**asdict(grpc_config))
1577
+
1578
+ cryptography = Cryptography(config=config, client=client)
1579
+ cryptography.get_versions_in_range()
1580
+ cryptography.present(output_file=args.output)
1581
+ except ScanossGrpcError as e:
1582
+ print_stderr(f'API ERROR: {e}')
1583
+ sys.exit(1)
1584
+ except Exception as e:
1585
+ if args.debug:
1586
+ import traceback
1587
+
1588
+ traceback.print_exc()
1589
+ print_stderr(f'ERROR: {e}')
1429
1590
  sys.exit(1)
1430
1591
 
1431
1592
 
scanoss/constants.py CHANGED
@@ -10,3 +10,5 @@ DEFAULT_NB_THREADS = 5
10
10
 
11
11
  DEFAULT_URL = 'https://api.osskb.org' # default free service URL
12
12
  DEFAULT_URL2 = 'https://api.scanoss.com' # default premium service URL
13
+
14
+ DEFAULT_API_TIMEOUT = 600
@@ -0,0 +1,274 @@
1
+ import json
2
+ from dataclasses import dataclass
3
+ from typing import Dict, List, Optional
4
+
5
+ from scanoss.scanossbase import ScanossBase
6
+ from scanoss.scanossgrpc import ScanossGrpc
7
+ from scanoss.utils.abstract_presenter import AbstractPresenter
8
+ from scanoss.utils.file import validate_json_file
9
+
10
+
11
+ class ScanossCryptographyError(Exception):
12
+ pass
13
+
14
+
15
+ MIN_SPLIT_PARTS = 2
16
+
17
+
18
+ @dataclass
19
+ class CryptographyConfig:
20
+ purl: List[str]
21
+ input_file: Optional[str] = None
22
+ output_file: Optional[str] = None
23
+ header: Optional[str] = None
24
+ debug: bool = False
25
+ trace: bool = False
26
+ quiet: bool = False
27
+ with_range: bool = False
28
+
29
+ def __post_init__(self):
30
+ """
31
+ Validate that the configuration is valid.
32
+ """
33
+ if self.purl:
34
+ if self.with_range:
35
+ for purl in self.purl:
36
+ parts = purl.split('@')
37
+ if not (len(parts) >= MIN_SPLIT_PARTS and parts[1]):
38
+ raise ScanossCryptographyError(
39
+ f'Invalid PURL format: "{purl}".' f'It must include a version (e.g., pkg:type/name@version)'
40
+ )
41
+ if self.input_file:
42
+ input_file_validation = validate_json_file(self.input_file)
43
+ if not input_file_validation.is_valid:
44
+ raise ScanossCryptographyError(
45
+ f'There was a problem with the purl input file. {input_file_validation.error}'
46
+ )
47
+ if (
48
+ not isinstance(input_file_validation.data, dict)
49
+ or 'purls' not in input_file_validation.data
50
+ or not isinstance(input_file_validation.data['purls'], list)
51
+ or not all(isinstance(p, dict) and 'purl' in p for p in input_file_validation.data['purls'])
52
+ ):
53
+ raise ScanossCryptographyError('The supplied input file is not in the correct PurlRequest format.')
54
+ purls = input_file_validation.data['purls']
55
+ purls_with_requirement = []
56
+ if self.with_range:
57
+ if any('requirement' not in p for p in purls):
58
+ raise ScanossCryptographyError(
59
+ f'One or more PURLs in "{self.input_file}" are missing the "requirement" field.'
60
+ )
61
+ else:
62
+ for purl in purls:
63
+ purls_with_requirement.append(f'{purl["purl"]}@{purl["requirement"]}')
64
+ else:
65
+ purls_with_requirement = purls
66
+ self.purl = purls_with_requirement
67
+
68
+
69
+ def create_cryptography_config_from_args(args) -> CryptographyConfig:
70
+ return CryptographyConfig(
71
+ debug=getattr(args, 'debug', False),
72
+ trace=getattr(args, 'trace', False),
73
+ quiet=getattr(args, 'quiet', False),
74
+ with_range=getattr(args, 'with_range', False),
75
+ purl=getattr(args, 'purl', []),
76
+ input_file=getattr(args, 'input', None),
77
+ output_file=getattr(args, 'output', None),
78
+ header=getattr(args, 'header', None),
79
+ )
80
+
81
+
82
+ class Cryptography:
83
+ """
84
+ Cryptography Class
85
+
86
+ This class is used to decorate purls with cryptography information.
87
+ """
88
+
89
+ def __init__(
90
+ self,
91
+ config: CryptographyConfig,
92
+ client: ScanossGrpc,
93
+ ):
94
+ """
95
+ Initialize the Cryptography.
96
+
97
+ Args:
98
+ config (CryptographyConfig): Configuration parameters for the cryptography.
99
+ client (ScanossGrpc): gRPC client for communicating with the scanning service.
100
+ """
101
+ self.base = ScanossBase(
102
+ debug=config.debug,
103
+ trace=config.trace,
104
+ quiet=config.quiet,
105
+ )
106
+ self.presenter = CryptographyPresenter(
107
+ self,
108
+ debug=config.debug,
109
+ trace=config.trace,
110
+ quiet=config.quiet,
111
+ )
112
+
113
+ self.client = client
114
+ self.config = config
115
+ self.purls_request = self._build_purls_request()
116
+ self.results = None
117
+
118
+ def get_algorithms(self) -> Optional[Dict]:
119
+ """
120
+ Get the cryptographic algorithms for the provided purl or input file.
121
+
122
+ Returns:
123
+ Optional[Dict]: The folder hash response from the gRPC client, or None if an error occurs.
124
+ """
125
+
126
+ if not self.purls_request:
127
+ raise ScanossCryptographyError('No PURLs supplied. Provide --purl or --input.')
128
+ self.base.print_stderr(
129
+ f'Getting cryptographic algorithms for {", ".join([p["purl"] for p in self.purls_request["purls"]])}'
130
+ )
131
+ if self.config.with_range:
132
+ response = self.client.get_crypto_algorithms_in_range_for_purl(self.purls_request)
133
+ else:
134
+ response = self.client.get_crypto_algorithms_for_purl(self.purls_request)
135
+ if response:
136
+ self.results = response
137
+
138
+ return self.results
139
+
140
+ def get_encryption_hints(self) -> Optional[Dict]:
141
+ """
142
+ Get the encryption hints for the provided purl or input file.
143
+
144
+ Returns:
145
+ Optional[Dict]: The encryption hints response from the gRPC client, or None if an error occurs.
146
+ """
147
+
148
+ if not self.purls_request:
149
+ raise ScanossCryptographyError('No PURLs supplied. Provide --purl or --input.')
150
+ self.base.print_stderr(
151
+ f'Getting encryption hints '
152
+ f'{"in range" if self.config.with_range else ""} '
153
+ f'for {", ".join([p["purl"] for p in self.purls_request["purls"]])}'
154
+ )
155
+ if self.config.with_range:
156
+ response = self.client.get_encryption_hints_in_range_for_purl(self.purls_request)
157
+ else:
158
+ response = self.client.get_encryption_hints_for_purl(self.purls_request)
159
+ if response:
160
+ self.results = response
161
+
162
+ return self.results
163
+
164
+ def get_versions_in_range(self) -> Optional[Dict]:
165
+ """
166
+ Given a list of PURLS and version ranges, get a list of versions that do/do not contain cryptographic algorithms
167
+
168
+ Returns:
169
+ Optional[Dict]: The versions in range response from the gRPC client, or None if an error occurs.
170
+ """
171
+
172
+ if not self.purls_request:
173
+ raise ScanossCryptographyError('No PURLs supplied. Provide --purl or --input.')
174
+
175
+ self.base.print_stderr(
176
+ f'Getting versions in range for {", ".join([p["purl"] for p in self.purls_request["purls"]])}'
177
+ )
178
+
179
+ response = self.client.get_versions_in_range_for_purl(self.purls_request)
180
+ if response:
181
+ self.results = response
182
+
183
+ return self.results
184
+
185
+ def _build_purls_request(
186
+ self,
187
+ ) -> Optional[dict]:
188
+ """
189
+ Load the specified purls from a JSON file or a list of PURLs and return a dictionary
190
+
191
+ Args:
192
+ json_file (Optional[str], optional): The JSON file containing the PURLs. Defaults to None.
193
+ purls (Optional[List[str]], optional): The list of PURLs. Defaults to None.
194
+
195
+ Returns:
196
+ Optional[dict]: The dictionary containing the PURLs
197
+ """
198
+ return {
199
+ 'purls': [
200
+ {
201
+ 'purl': p,
202
+ 'requirement': self._extract_version_from_purl(p),
203
+ }
204
+ for p in self.config.purl
205
+ ]
206
+ }
207
+
208
+ def _extract_version_from_purl(self, purl: str) -> str:
209
+ """
210
+ Extract version from purl
211
+
212
+ Args:
213
+ purl (str): The purl string to extract the version from
214
+
215
+ Returns:
216
+ str: The extracted version
217
+
218
+ Raises:
219
+ ScanossCryptographyError: If the purl is not in the correct format
220
+ """
221
+ try:
222
+ return purl.split('@')[-1]
223
+ except IndexError:
224
+ raise ScanossCryptographyError(f'Invalid purl format: {purl}')
225
+
226
+ def present(
227
+ self,
228
+ output_format: Optional[str] = None,
229
+ output_file: Optional[str] = None,
230
+ ):
231
+ """Present the results in the selected format"""
232
+ self.presenter.present(output_format=output_format, output_file=output_file)
233
+
234
+
235
+ class CryptographyPresenter(AbstractPresenter):
236
+ """
237
+ Cryptography presenter class
238
+ Handles the presentation of the cryptography results
239
+ """
240
+
241
+ def __init__(self, cryptography: Cryptography, **kwargs):
242
+ super().__init__(**kwargs)
243
+ self.cryptography = cryptography
244
+
245
+ def _format_json_output(self) -> str:
246
+ """
247
+ Format the scan output data into a JSON object
248
+
249
+ Returns:
250
+ str: The formatted JSON string
251
+ """
252
+ return json.dumps(self.cryptography.results, indent=2)
253
+
254
+ def _format_plain_output(self) -> str:
255
+ """
256
+ Format the scan output data into a plain text string
257
+ """
258
+ return (
259
+ json.dumps(self.cryptography.results, indent=2)
260
+ if isinstance(self.cryptography.results, dict)
261
+ else str(self.cryptography.results)
262
+ )
263
+
264
+ def _format_cyclonedx_output(self) -> str:
265
+ raise NotImplementedError('CycloneDX output is not implemented')
266
+
267
+ def _format_spdxlite_output(self) -> str:
268
+ raise NotImplementedError('SPDXlite output is not implemented')
269
+
270
+ def _format_csv_output(self) -> str:
271
+ raise NotImplementedError('CSV output is not implemented')
272
+
273
+ def _format_raw_output(self) -> str:
274
+ raise NotImplementedError('Raw output is not implemented')
@@ -1 +1 @@
1
- date: 20250425091203, utime: 1745572323
1
+ date: 20250610161304, utime: 1749571984
@@ -26,9 +26,10 @@ import json
26
26
  import os.path
27
27
  from abc import abstractmethod
28
28
  from enum import Enum
29
- from typing import Callable, List, Dict, Any
30
- from .utils.license_utils import LicenseUtil
29
+ from typing import Any, Callable, Dict, List
30
+
31
31
  from ..scanossbase import ScanossBase
32
+ from .utils.license_utils import LicenseUtil
32
33
 
33
34
 
34
35
  class PolicyStatus(Enum):
@@ -87,7 +88,7 @@ class PolicyCheck(ScanossBase):
87
88
 
88
89
  VALID_FORMATS = {'md', 'json', 'jira_md'}
89
90
 
90
- def __init__(
91
+ def __init__( # noqa: PLR0913
91
92
  self,
92
93
  debug: bool = False,
93
94
  trace: bool = True,
@@ -181,10 +182,9 @@ class PolicyCheck(ScanossBase):
181
182
  :param status: The new component status
182
183
  :return: The updated components dictionary
183
184
  """
184
-
185
185
  # Determine the component key and purl based on component type
186
186
  if id in [ComponentID.FILE.value, ComponentID.SNIPPET.value]:
187
- purl = new_component['purl'][0] # Take first purl for these component types
187
+ purl = new_component['purl'][0] # Take the first purl for these component types
188
188
  else:
189
189
  purl = new_component['purl']
190
190
 
@@ -195,14 +195,13 @@ class PolicyCheck(ScanossBase):
195
195
  'licenses': {},
196
196
  'status': status,
197
197
  }
198
-
199
198
  if not new_component.get('licenses'):
200
- self.print_stderr(f'WARNING: Results missing licenses. Skipping.')
199
+ self.print_debug(f'WARNING: Results missing licenses. Skipping: {new_component}')
201
200
  return components
202
201
  # Process licenses for this component
203
- for l in new_component['licenses']:
204
- if l.get('name'):
205
- spdxid = l['name']
202
+ for license_item in new_component['licenses']:
203
+ if license_item.get('name'):
204
+ spdxid = license_item['name']
206
205
  components[component_key]['licenses'][spdxid] = {
207
206
  'spdxid': spdxid,
208
207
  'copyleft': self.license_util.is_copyleft(spdxid),
@@ -210,71 +209,103 @@ class PolicyCheck(ScanossBase):
210
209
  }
211
210
  return components
212
211
 
213
- def _get_components_from_results(self, results: Dict[str, Any]) -> list or None:
212
+ def _get_components_data(self, results: Dict[str, Any], components: Dict[str, Any]) -> Dict[str, Any]:
214
213
  """
215
- Process the results dictionary to extract and format component information.
216
-
217
- This function iterates through the results dictionary, identifying components from
218
- different sources (files, snippets, and dependencies). It consolidates this information
219
- into a list of unique components, each with its associated licenses and other details.
214
+ Extract and process file and snippet components from results.
220
215
 
221
216
  :param results: A dictionary containing the raw results of a component scan
222
- :return: A list of dictionaries, each representing a unique component with its details
217
+ :param components: Existing components dictionary to update
218
+ :return: Updated components dictionary with file and snippet data
223
219
  """
224
- if results is None:
225
- self.print_stderr(f'ERROR: Results cannot be empty')
226
- return None
227
- components = {}
228
220
  for component in results.values():
229
221
  for c in component:
230
222
  component_id = c.get('id')
231
223
  if not component_id:
232
- self.print_stderr(f'WARNING: Result missing id. Skipping.')
224
+ self.print_debug(f'WARNING: Result missing id. Skipping: {c}')
233
225
  continue
234
226
  status = c.get('status')
235
- if not component_id:
236
- self.print_stderr(f'WARNING: Result missing status. Skipping.')
227
+ if not status:
228
+ self.print_debug(f'WARNING: Result missing status. Skipping: {c}')
237
229
  continue
238
230
  if component_id in [ComponentID.FILE.value, ComponentID.SNIPPET.value]:
239
231
  if not c.get('purl'):
240
- self.print_stderr(f'WARNING: Result missing purl. Skipping.')
232
+ self.print_debug(f'WARNING: Result missing purl. Skipping: {c}')
241
233
  continue
242
234
  if len(c.get('purl')) <= 0:
243
- self.print_stderr(f'WARNING: Result missing purls. Skipping.')
235
+ self.print_debug(f'WARNING: Result missing purls. Skipping: {c}')
244
236
  continue
245
237
  if not c.get('version'):
246
- self.print_stderr(f'WARNING: Result missing version. Skipping.')
238
+ self.print_msg(f'WARNING: Result missing version. Skipping: {c}')
247
239
  continue
248
240
  component_key = f'{c["purl"][0]}@{c["version"]}'
249
- # Initialize or update the component entry
250
241
  if component_key not in components:
251
242
  components = self._append_component(components, c, component_id, status)
243
+ # End component loop
244
+ # End components loop
245
+ return components
252
246
 
253
- if c['id'] == ComponentID.DEPENDENCY.value:
247
+ def _get_dependencies_data(self, results: Dict[str, Any], components: Dict[str, Any]) -> Dict[str, Any]:
248
+ """
249
+ Extract and process dependency components from results.
250
+
251
+ :param results: A dictionary containing the raw results of a component scan
252
+ :param components: Existing components dictionary to update
253
+ :return: Updated components dictionary with dependency data
254
+ """
255
+ for component in results.values():
256
+ for c in component:
257
+ component_id = c.get('id')
258
+ if not component_id:
259
+ self.print_debug(f'WARNING: Result missing id. Skipping: {c}')
260
+ continue
261
+ status = c.get('status')
262
+ if not status:
263
+ self.print_debug(f'WARNING: Result missing status. Skipping: {c}')
264
+ continue
265
+ if component_id == ComponentID.DEPENDENCY.value:
254
266
  if c.get('dependencies') is None:
255
267
  continue
256
- for d in c['dependencies']:
257
- if not d.get('purl'):
258
- self.print_stderr(f'WARNING: Result missing purl. Skipping.')
259
- continue
260
- if len(d.get('purl')) <= 0:
261
- self.print_stderr(f'WARNING: Result missing purls. Skipping.')
268
+ for dependency in c['dependencies']:
269
+ if not dependency.get('purl'):
270
+ self.print_debug(f'WARNING: Dependency result missing purl. Skipping: {dependency}')
262
271
  continue
263
- if not d.get('version'):
264
- self.print_stderr(f'WARNING: Result missing version. Skipping.')
272
+ if not dependency.get('version'):
273
+ self.print_msg(f'WARNING: Dependency result missing version. Skipping: {dependency}')
265
274
  continue
266
- component_key = f'{d["purl"]}@{d["version"]}'
275
+ component_key = f'{dependency["purl"]}@{dependency["version"]}'
267
276
  if component_key not in components:
268
- components = self._append_component(components, d, component_id, status)
269
- # End of dependencies loop
270
- # End if
271
- # End of component loop
272
- # End of results loop
273
- results = list(components.values())
274
- for component in results:
277
+ components = self._append_component(components, dependency, component_id, status)
278
+ # End dependency loop
279
+ # End component loop
280
+ # End of result loop
281
+ return components
282
+
283
+ def _get_components_from_results(self, results: Dict[str, Any]) -> list or None:
284
+ """
285
+ Process the results dictionary to extract and format component information.
286
+
287
+ This function iterates through the results dictionary, identifying components from
288
+ different sources (files, snippets, and dependencies). It consolidates this information
289
+ into a list of unique components, each with its associated licenses and other details.
290
+
291
+ :param results: A dictionary containing the raw results of a component scan
292
+ :return: A list of dictionaries, each representing a unique component with its details
293
+ """
294
+ if results is None:
295
+ self.print_stderr('ERROR: Results cannot be empty')
296
+ return None
297
+
298
+ components = {}
299
+ # Extract file and snippet components
300
+ components = self._get_components_data(results, components)
301
+ # Extract dependency components
302
+ components = self._get_dependencies_data(results, components)
303
+ # Convert to list and process licenses
304
+ results_list = list(components.values())
305
+ for component in results_list:
275
306
  component['licenses'] = list(component['licenses'].values())
276
307
 
277
- return results
308
+ return results_list
278
309
 
279
310
  def generate_table(self, headers, rows, centered_columns=None):
280
311
  """
@@ -403,7 +434,6 @@ class PolicyCheck(ScanossBase):
403
434
  components = self._get_components_from_results(self.results)
404
435
  return components
405
436
 
406
-
407
437
  #
408
438
  # End of PolicyCheck Class
409
439
  #
scanoss/scanossgrpc.py CHANGED
@@ -54,7 +54,6 @@ from .api.components.v2.scanoss_components_pb2 import (
54
54
  CompVersionResponse,
55
55
  )
56
56
  from .api.components.v2.scanoss_components_pb2_grpc import ComponentsStub
57
- from .api.cryptography.v2.scanoss_cryptography_pb2 import AlgorithmResponse
58
57
  from .api.cryptography.v2.scanoss_cryptography_pb2_grpc import CryptographyStub
59
58
  from .api.dependencies.v2.scanoss_dependencies_pb2 import DependencyRequest
60
59
  from .api.dependencies.v2.scanoss_dependencies_pb2_grpc import DependenciesStub
@@ -312,36 +311,6 @@ class ScanossGrpc(ScanossBase):
312
311
  merged_response['status'] = response['status']
313
312
  return merged_response
314
313
 
315
- def get_crypto_json(self, purls: dict) -> dict:
316
- """
317
- Client function to call the rpc for Cryptography GetAlgorithms
318
- :param purls: Message to send to the service
319
- :return: Server response or None
320
- """
321
- if not purls:
322
- self.print_stderr('ERROR: No message supplied to send to gRPC service.')
323
- return None
324
- request_id = str(uuid.uuid4())
325
- resp: AlgorithmResponse
326
- try:
327
- request = ParseDict(purls, PurlRequest()) # Parse the JSON/Dict into the purl request object
328
- metadata = self.metadata[:]
329
- metadata.append(('x-request-id', request_id)) # Set a Request ID
330
- self.print_debug(f'Sending crypto data for decoration (rqId: {request_id})...')
331
- resp = self.crypto_stub.GetAlgorithms(request, metadata=metadata, timeout=self.timeout)
332
- except Exception as e:
333
- self.print_stderr(
334
- f'ERROR: {e.__class__.__name__} Problem encountered sending gRPC message (rqId: {request_id}): {e}'
335
- )
336
- else:
337
- if resp:
338
- if not self._check_status_response(resp.status, request_id):
339
- return None
340
- resp_dict = MessageToDict(resp, preserving_proto_field_name=True) # Convert gRPC response to a dict
341
- del resp_dict['status']
342
- return resp_dict
343
- return None
344
-
345
314
  def get_vulnerabilities_json(self, purls: dict) -> dict:
346
315
  """
347
316
  Client function to call the rpc for Vulnerability GetVulnerabilities
@@ -607,6 +576,91 @@ class ScanossGrpc(ScanossBase):
607
576
  'Sending data for provenance origin decoration (rqId: {rqId})...',
608
577
  )
609
578
 
579
+ def get_crypto_algorithms_for_purl(self, request: Dict) -> Optional[Dict]:
580
+ """
581
+ Client function to call the rpc for GetAlgorithms for a list of purls
582
+
583
+ Args:
584
+ request (Dict): PurlRequest
585
+
586
+ Returns:
587
+ Optional[Dict]: AlgorithmResponse, or None if the request was not successfull
588
+ """
589
+ return self._call_rpc(
590
+ self.crypto_stub.GetAlgorithms,
591
+ request,
592
+ PurlRequest,
593
+ 'Sending data for cryptographic algorithms decoration (rqId: {rqId})...',
594
+ )
595
+
596
+ def get_crypto_algorithms_in_range_for_purl(self, request: Dict) -> Optional[Dict]:
597
+ """
598
+ Client function to call the rpc for GetAlgorithmsInRange for a list of purls
599
+
600
+ Args:
601
+ request (Dict): PurlRequest
602
+
603
+ Returns:
604
+ Optional[Dict]: AlgorithmsInRangeResponse, or None if the request was not successfull
605
+ """
606
+ return self._call_rpc(
607
+ self.crypto_stub.GetAlgorithmsInRange,
608
+ request,
609
+ PurlRequest,
610
+ 'Sending data for cryptographic algorithms in range decoration (rqId: {rqId})...',
611
+ )
612
+
613
+ def get_encryption_hints_for_purl(self, request: Dict) -> Optional[Dict]:
614
+ """
615
+ Client function to call the rpc for GetEncryptionHints for a list of purls
616
+
617
+ Args:
618
+ request (Dict): PurlRequest
619
+
620
+ Returns:
621
+ Optional[Dict]: HintsResponse, or None if the request was not successfull
622
+ """
623
+ return self._call_rpc(
624
+ self.crypto_stub.GetEncryptionHints,
625
+ request,
626
+ PurlRequest,
627
+ 'Sending data for encryption hints decoration (rqId: {rqId})...',
628
+ )
629
+
630
+ def get_encryption_hints_in_range_for_purl(self, request: Dict) -> Optional[Dict]:
631
+ """
632
+ Client function to call the rpc for GetHintsInRange for a list of purls
633
+
634
+ Args:
635
+ request (Dict): PurlRequest
636
+
637
+ Returns:
638
+ Optional[Dict]: HintsInRangeResponse, or None if the request was not successfull
639
+ """
640
+ return self._call_rpc(
641
+ self.crypto_stub.GetHintsInRange,
642
+ request,
643
+ PurlRequest,
644
+ 'Sending data for encryption hints in range decoration (rqId: {rqId})...',
645
+ )
646
+
647
+ def get_versions_in_range_for_purl(self, request: Dict) -> Optional[Dict]:
648
+ """
649
+ Client function to call the rpc for GetVersionsInRange for a list of purls
650
+
651
+ Args:
652
+ request (Dict): PurlRequest
653
+
654
+ Returns:
655
+ Optional[Dict]: VersionsInRangeResponse, or None if the request was not successfull
656
+ """
657
+ return self._call_rpc(
658
+ self.crypto_stub.GetVersionsInRange,
659
+ request,
660
+ PurlRequest,
661
+ 'Sending data for cryptographic versions in range decoration (rqId: {rqId})...',
662
+ )
663
+
610
664
  def load_generic_headers(self):
611
665
  """
612
666
  Adds custom headers from req_headers to metadata.
@@ -637,10 +691,11 @@ class GrpcConfig:
637
691
  quiet: Optional[bool] = False
638
692
  ver_details: Optional[str] = None
639
693
  ca_cert: Optional[str] = None
640
- pac: Optional[PACFile] = None
641
694
  timeout: Optional[int] = DEFAULT_TIMEOUT
642
695
  proxy: Optional[str] = None
643
696
  grpc_proxy: Optional[str] = None
697
+ pac: Optional[PACFile] = None
698
+ req_headers: Optional[dict] = None
644
699
 
645
700
 
646
701
  def create_grpc_config_from_args(args) -> GrpcConfig:
@@ -652,7 +707,6 @@ def create_grpc_config_from_args(args) -> GrpcConfig:
652
707
  quiet=getattr(args, 'quiet', False),
653
708
  ver_details=getattr(args, 'ver_details', None),
654
709
  ca_cert=getattr(args, 'ca_cert', None),
655
- pac=getattr(args, 'pac', None),
656
710
  timeout=getattr(args, 'timeout', DEFAULT_TIMEOUT),
657
711
  proxy=getattr(args, 'proxy', None),
658
712
  grpc_proxy=getattr(args, 'grpc_proxy', None),
scanoss/utils/file.py CHANGED
@@ -49,8 +49,8 @@ def validate_json_file(json_file_path: str) -> JsonValidation:
49
49
  json_file_path (str): The JSON file to validate
50
50
 
51
51
  Returns:
52
- Tuple[bool, str]: A tuple containing a boolean indicating if the file is valid and a message
53
- """
52
+ JsonValidation: A JsonValidation object containing a boolean indicating if the file is valid, the data, error, and error code
53
+ """ # noqa: E501
54
54
  if not json_file_path:
55
55
  return JsonValidation(is_valid=False, error='No JSON file specified')
56
56
  if not os.path.isfile(json_file_path):
scanoss/winnowing.py CHANGED
@@ -32,9 +32,10 @@ import hashlib
32
32
  import pathlib
33
33
  import platform
34
34
  import re
35
+ from typing import Tuple
35
36
 
36
- from crc32c import crc32c
37
37
  from binaryornot.check import is_binary
38
+ from crc32c import crc32c
38
39
 
39
40
  from .scanossbase import ScanossBase
40
41
 
@@ -157,7 +158,7 @@ class Winnowing(ScanossBase):
157
158
  a list of WFP fingerprints with their corresponding line numbers.
158
159
  """
159
160
 
160
- def __init__(
161
+ def __init__( # noqa: PLR0913
161
162
  self,
162
163
  size_limit: bool = False,
163
164
  debug: bool = False,
@@ -197,6 +198,7 @@ class Winnowing(ScanossBase):
197
198
  self.strip_hpsm_ids = strip_hpsm_ids
198
199
  self.strip_snippet_ids = strip_snippet_ids
199
200
  self.hpsm = hpsm
201
+ self.is_windows = platform.system() == 'Windows'
200
202
  if hpsm:
201
203
  self.crc8_maxim_dow_table = []
202
204
  self.crc8_generate_table()
@@ -218,11 +220,11 @@ class Winnowing(ScanossBase):
218
220
  return byte
219
221
  if byte >= ASCII_a:
220
222
  return byte
221
- if (byte >= 65) and (byte <= 90):
223
+ if (byte >= ASCII_A) and (byte <= ASCII_Z):
222
224
  return byte + 32
223
225
  return 0
224
226
 
225
- def __skip_snippets(self, file: str, src: str) -> bool:
227
+ def __skip_snippets(self, file: str, src: str) -> bool: # noqa: PLR0911
226
228
  """
227
229
  Determine files that are not of interest based on their content or file extension
228
230
  Parameters
@@ -351,7 +353,55 @@ class Winnowing(ScanossBase):
351
353
  self.print_debug(f'Stripped snippet ids from {file}')
352
354
  return wfp
353
355
 
354
- def wfp_for_contents(self, file: str, bin_file: bool, contents: bytes) -> str:
356
+ def __detect_line_endings(self, contents: bytes) -> Tuple[bool, bool, bool]:
357
+ """Detect the types of line endings present in file contents.
358
+
359
+ Args:
360
+ contents: File contents as bytes.
361
+
362
+ Returns:
363
+ Tuple of (has_crlf, has_lf_only, has_cr_only, has_mixed) indicating which line ending types are present.
364
+ """
365
+ has_crlf = b'\r\n' in contents
366
+ # For LF detection, we need to find LF that's not part of CRLF
367
+ content_without_crlf = contents.replace(b'\r\n', b'')
368
+ has_standalone_lf = b'\n' in content_without_crlf
369
+ # For CR detection, we need to find CR that's not part of CRLF
370
+ has_standalone_cr = b'\r' in content_without_crlf
371
+
372
+ return has_crlf, has_standalone_lf, has_standalone_cr
373
+
374
+ def __calculate_opposite_line_ending_hash(self, contents: bytes):
375
+ """Calculate hash for contents with opposite line endings.
376
+
377
+ If the file is primarily Unix (LF), calculates Windows (CRLF) hash.
378
+ If the file is primarily Windows (CRLF), calculates Unix (LF) hash.
379
+
380
+ Args:
381
+ contents: File contents as bytes.
382
+
383
+ Returns:
384
+ Hash with opposite line endings as hex string, or None if no line endings detected.
385
+ """
386
+ has_crlf, has_standalone_lf, has_standalone_cr = self.__detect_line_endings(contents)
387
+
388
+ if not has_crlf and not has_standalone_lf and not has_standalone_cr:
389
+ return None
390
+
391
+ # Normalize all line endings to LF first
392
+ normalized = contents.replace(b'\r\n', b'\n').replace(b'\r', b'\n')
393
+
394
+ # Determine the dominant line ending type
395
+ if has_crlf and not has_standalone_lf and not has_standalone_cr:
396
+ # File is Windows (CRLF) - produce Unix (LF) hash
397
+ opposite_contents = normalized
398
+ else:
399
+ # File is Unix (LF/CR) or mixed - produce Windows (CRLF) hash
400
+ opposite_contents = normalized.replace(b'\n', b'\r\n')
401
+
402
+ return hashlib.md5(opposite_contents).hexdigest()
403
+
404
+ def wfp_for_contents(self, file: str, bin_file: bool, contents: bytes) -> str: # noqa: PLR0912, PLR0915
355
405
  """
356
406
  Generate a Winnowing fingerprint (WFP) for the given file contents
357
407
  Parameters
@@ -371,7 +421,7 @@ class Winnowing(ScanossBase):
371
421
  content_length = len(contents)
372
422
  original_filename = file
373
423
 
374
- if platform.system() == 'Windows':
424
+ if self.is_windows:
375
425
  original_filename = file.replace('\\', '/')
376
426
  wfp_filename = repr(original_filename).strip("'") # return a utf-8 compatible version of the filename
377
427
  if self.obfuscate: # hide the real size of the file and its name, but keep the suffix
@@ -380,6 +430,13 @@ class Winnowing(ScanossBase):
380
430
  self.file_map[wfp_filename] = original_filename # Save the file name map for later (reverse lookup)
381
431
 
382
432
  wfp = 'file={0},{1},{2}\n'.format(file_md5, content_length, wfp_filename)
433
+
434
+ # Add opposite line ending hash based on line ending analysis
435
+ if not bin_file:
436
+ opposite_hash = self.__calculate_opposite_line_ending_hash(contents)
437
+ if opposite_hash is not None:
438
+ wfp += f'fh2={opposite_hash}\n'
439
+
383
440
  # We don't process snippets for binaries, or other uninteresting files, or if we're requested to skip
384
441
  if bin_file or self.skip_snippets or self.__skip_snippets(file, contents.decode('utf-8', 'ignore')):
385
442
  return wfp
@@ -467,7 +524,7 @@ class Winnowing(ScanossBase):
467
524
  for i, byte in enumerate(content):
468
525
  c = byte
469
526
  if c == ASCII_LF: # When there is a new line
470
- if len(list_normalized):
527
+ if list_normalized:
471
528
  crc_lines.append(self.crc8_buffer(list_normalized))
472
529
  list_normalized = []
473
530
  elif last_line + 1 == i:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scanoss
3
- Version: 1.23.0
3
+ Version: 1.25.0
4
4
  Summary: Simple Python library to leverage the SCANOSS APIs
5
5
  Home-page: https://scanoss.com
6
6
  Author: SCANOSS
@@ -4,10 +4,11 @@ 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=CqMRnCqzsM3UDMfg2QSfEduDTsshr2swd5eqsOOvrv4,1146
8
- scanoss/cli.py,sha256=fdByS-OjSYQbzoZpkT7cez4qkSILpkHPJtRfmSbqDHU,63706
7
+ scanoss/__init__.py,sha256=T3J1XVw8Hyutct8ClOLn1tqQ_NnHlMfxUSVYfLSuVJg,1146
8
+ scanoss/cli.py,sha256=SAB0xuHjEEw20YtYAPSZwrQaVf4JUsm8NRcVArMzd4U,69099
9
9
  scanoss/components.py,sha256=b0R9DdKuXqyQiw5nZZwjQ6NJXBr1U9gyx1RI2FP9ozA,14511
10
- scanoss/constants.py,sha256=9cJxbaXMP4GUIMVn0O1eEyT7Qfq4Jl6BWgZsYQqxQ20,293
10
+ scanoss/constants.py,sha256=FWCZG8gQputKwV7XwvW1GuwDXL4wDLQyVRGdwygg578,320
11
+ scanoss/cryptography.py,sha256=Q39MOCscP-OFvrnPXaPOMFFkc8OKnf3mC3SgZYEtCog,9407
11
12
  scanoss/csvoutput.py,sha256=qNKRwcChSkgIwLm00kZiVX6iHVQUF4Apl-sMbzJ5Taw,10192
12
13
  scanoss/cyclonedx.py,sha256=UktDuqZUbXSggdt864Pg8ziTD7sdEQtLxfYL7vd_ZCE,12756
13
14
  scanoss/file_filters.py,sha256=_1Ehb_rLnHw_-6N5Zhh4Es2lz6rlx0LozGPn-u52cok,20338
@@ -18,13 +19,13 @@ scanoss/scanner.py,sha256=ZL8I8KtVyXgUMcl7Ccbip_Q1IlU9WTgI-mFtSpF1JWw,45223
18
19
  scanoss/scanoss_settings.py,sha256=393JnWLsEZhvMg5tPUGgxmqnBKp8AcLxYsDRbLP7aV4,10650
19
20
  scanoss/scanossapi.py,sha256=v4D9i9Impa82Enw-5hZ7KLlscDIpaILNbGOMj3MJXqs,13067
20
21
  scanoss/scanossbase.py,sha256=Dkpwxa8NH8XN1iRl03NM_Mkvby0JQ4qfvCiiUrJ5ul0,3163
21
- scanoss/scanossgrpc.py,sha256=2SzIEi9tt5REZ6XVR_TrpP2E8wYVZDBKircTIlY8npw,28614
22
+ scanoss/scanossgrpc.py,sha256=B0rl676-B-ZxqXRp7blXnqbAGPC5rqLAQHG28NoC32E,30004
22
23
  scanoss/scanpostprocessor.py,sha256=-JsThlxrU70r92GHykTMERnicdd-6jmwNsE4PH0MN2o,11063
23
24
  scanoss/scantype.py,sha256=gFmyVmKQpHWogN2iCmMj032e_sZo4T92xS3_EH5B3Tc,1310
24
25
  scanoss/spdxlite.py,sha256=MQqFgQhIO-yrbRwEAQS77HmRgP5GDxff-2JYLVoceA0,28946
25
26
  scanoss/threadeddependencies.py,sha256=CAeZnoYd3d1ayoRvfm_aVjYbaTDLsk6DMWDxkoBPvq0,9866
26
27
  scanoss/threadedscanning.py,sha256=38ryN_kZGpzmrd_hkuiY9Sb3tOG248canGCDQDmGEwI,9317
27
- scanoss/winnowing.py,sha256=68_AL3iI-yR8GMfOD1LemToA_rvbNHeghKTX5-AK698,19254
28
+ scanoss/winnowing.py,sha256=RsR9jRTR3TzS1pEeKQ2RuYlIG8Q7RnUQFfgPLog6B-A,21679
28
29
  scanoss/api/__init__.py,sha256=hx-P78xbDsh6WQIigewkJ7Y7y1fqc_eYnyHC5IZTKmo,1122
29
30
  scanoss/api/common/__init__.py,sha256=hx-P78xbDsh6WQIigewkJ7Y7y1fqc_eYnyHC5IZTKmo,1122
30
31
  scanoss/api/common/v2/__init__.py,sha256=hx-P78xbDsh6WQIigewkJ7Y7y1fqc_eYnyHC5IZTKmo,1122
@@ -56,13 +57,13 @@ scanoss/api/vulnerabilities/__init__.py,sha256=IFrDk_DTJgKSZmmU-nuLXuq_s8sQZlrSC
56
57
  scanoss/api/vulnerabilities/v2/__init__.py,sha256=IFrDk_DTJgKSZmmU-nuLXuq_s8sQZlrSCHhIDMJT4r0,1122
57
58
  scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py,sha256=CFhF80av8tenGvn9AIsGEtRJPuV2dC_syA5JLZb2lDw,5464
58
59
  scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py,sha256=HlS4k4Zmx6RIAqaO9I96jD-eyF5yU6Xx04pVm7pdqOg,6864
59
- scanoss/data/build_date.txt,sha256=G5TAnGlLvFVpqBLQYKD1FtFr0kpQDLVD8zI69TRvA90,40
60
+ scanoss/data/build_date.txt,sha256=3dG3QmOR7re5yDaUAaCa2noLbeaUxqlhd4ZYGn2-eo0,40
60
61
  scanoss/data/scanoss-settings-schema.json,sha256=ClkRYAkjAN0Sk704G8BE_Ok006oQ6YnIGmX84CF8h9w,8798
61
62
  scanoss/data/spdx-exceptions.json,sha256=s7UTYxC7jqQXr11YBlIWYCNwN6lRDFTR33Y8rpN_dA4,17953
62
63
  scanoss/data/spdx-licenses.json,sha256=A6Z0q82gaTLtnopBfzeIVZjJFxkdRW1g2TuumQc-lII,228794
63
64
  scanoss/inspection/__init__.py,sha256=0hjb5ktavp7utJzFhGMPImPaZiHWgilM2HwvTp5lXJE,1122
64
65
  scanoss/inspection/copyleft.py,sha256=VynluuCUVxR7uQUz026sp4_yE0Eh3dwpPK0YJ6FNFFw,7579
65
- scanoss/inspection/policy_check.py,sha256=NYBGKElaeHXKsMOFfSS4IKCiS71h3CKoL20uwhdjaoU,15685
66
+ scanoss/inspection/policy_check.py,sha256=wUUQD2WTBibjf3MOPXKOmB8jFGIX0cd1K3flU0pjTjc,17295
66
67
  scanoss/inspection/undeclared_component.py,sha256=0YEiWQ4Q1xu7j4YHLPsS3b5Mf9fplHJdHRXgUoQyF2w,10102
67
68
  scanoss/inspection/utils/license_utils.py,sha256=Zb6QLmVJb86lKCwZyBsmwakyAtY1SXa54kUyyKmWMqA,5093
68
69
  scanoss/scanners/__init__.py,sha256=D4C0lWLuNp8k_BjQZEc07WZcUgAvriVwQWOk063b0ZU,1122
@@ -73,11 +74,11 @@ scanoss/scanners/scanner_hfh.py,sha256=KksrC1XnOv7mXSlGyo_AJXwtPfKjqffkttdPoNDc-
73
74
  scanoss/utils/__init__.py,sha256=0hjb5ktavp7utJzFhGMPImPaZiHWgilM2HwvTp5lXJE,1122
74
75
  scanoss/utils/abstract_presenter.py,sha256=teiDTxBj5jBMCk2T8i4l1BJPf_u4zBLWrtCTFHSSECM,3148
75
76
  scanoss/utils/crc64.py,sha256=TMrwQimSdE6imhFOUL7oAG6Kxu-8qMpGWMuMg8QpSVs,3169
76
- scanoss/utils/file.py,sha256=yVyv7C7xLWtFNfUrv3r6W8tkIqEuPjZ7_mgIT01IjJs,2933
77
+ scanoss/utils/file.py,sha256=62cA9a17TU9ZvfA3FY5HY4-QOajJeSrc8S6xLA_f-3M,2980
77
78
  scanoss/utils/simhash.py,sha256=6iu8DOcecPAY36SZjCOzrrLMT9oIE7-gI6QuYwUQ7B0,5793
78
- scanoss-1.23.0.dist-info/licenses/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
79
- scanoss-1.23.0.dist-info/METADATA,sha256=pdGRVPWFrt8Xw86e3X3IXd1_n7mrbyTmGY340ybaCRw,6060
80
- scanoss-1.23.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
81
- scanoss-1.23.0.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
82
- scanoss-1.23.0.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
83
- scanoss-1.23.0.dist-info/RECORD,,
79
+ scanoss-1.25.0.dist-info/licenses/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
80
+ scanoss-1.25.0.dist-info/METADATA,sha256=mvajvjxQSlBpII6nFUq44pp7-Kedgf9hXGblfCjqjWU,6060
81
+ scanoss-1.25.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
82
+ scanoss-1.25.0.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
83
+ scanoss-1.25.0.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
84
+ scanoss-1.25.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5