scanoss 1.25.2__py3-none-any.whl → 1.26.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.25.2'
25
+ __version__ = '1.26.0'
scanoss/cli.py CHANGED
@@ -32,6 +32,8 @@ from typing import List
32
32
  import pypac
33
33
 
34
34
  from scanoss.cryptography import Cryptography, create_cryptography_config_from_args
35
+ from scanoss.inspection.component_summary import ComponentSummary
36
+ from scanoss.inspection.license_summary import LicenseSummary
35
37
  from scanoss.scanners.container_scanner import (
36
38
  DEFAULT_SYFT_COMMAND,
37
39
  DEFAULT_SYFT_TIMEOUT,
@@ -531,6 +533,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
531
533
  )
532
534
  p_results.set_defaults(func=results)
533
535
 
536
+ ########################################### INSPECT SUBCOMMAND ###########################################
534
537
  # Sub-command: inspect
535
538
  p_inspect = subparsers.add_parser(
536
539
  'inspect', aliases=['insp', 'ins'], description=f'Inspect results: {__version__}', help='Inspect results'
@@ -539,24 +542,26 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
539
542
  p_inspect_sub = p_inspect.add_subparsers(
540
543
  title='Inspect Commands', dest='subparsercmd', description='Inspect sub-commands', help='Inspect sub-commands'
541
544
  )
545
+
546
+ ####### INSPECT: Copyleft ######
542
547
  # Inspect Sub-command: inspect copyleft
543
548
  p_copyleft = p_inspect_sub.add_parser(
544
549
  'copyleft', aliases=['cp'], description='Inspect for copyleft licenses', help='Inspect for copyleft licenses'
545
550
  )
546
- p_copyleft.add_argument(
547
- '--include',
548
- help='List of Copyleft licenses to append to the default list. Provide licenses as a comma-separated list.',
549
- )
550
- p_copyleft.add_argument(
551
- '--exclude',
552
- help='List of Copyleft licenses to remove from default list. Provide licenses as a comma-separated list.',
551
+
552
+ ####### INSPECT: License Summary ######
553
+ # Inspect Sub-command: inspect license summary
554
+ p_license_summary = p_inspect_sub.add_parser(
555
+ 'license-summary', aliases=['lic-summary', 'licsum'], description='Get license summary',
556
+ help='Get detected license summary from scan results'
553
557
  )
554
- p_copyleft.add_argument(
555
- '--explicit',
556
- help='Explicit list of Copyleft licenses to consider. Provide licenses as a comma-separated list.s',
558
+
559
+ p_component_summary = p_inspect_sub.add_parser(
560
+ 'component-summary', aliases=['comp-summary', 'compsum'], description='Get component summary',
561
+ help='Get detected component summary from scan results'
557
562
  )
558
- p_copyleft.set_defaults(func=inspect_copyleft)
559
563
 
564
+ ####### INSPECT: Undeclared components ######
560
565
  # Inspect Sub-command: inspect undeclared
561
566
  p_undeclared = p_inspect_sub.add_parser(
562
567
  'undeclared',
@@ -571,7 +576,33 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
571
576
  default='settings',
572
577
  help='Sbom format for status output',
573
578
  )
579
+
580
+ # Add common commands for inspect copyleft and license summary
581
+ for p in [p_copyleft, p_license_summary]:
582
+ p.add_argument(
583
+ '--include',
584
+ help='List of Copyleft licenses to append to the default list. Provide licenses as a comma-separated list.',
585
+ )
586
+ p.add_argument(
587
+ '--exclude',
588
+ help='List of Copyleft licenses to remove from default list. Provide licenses as a comma-separated list.',
589
+ )
590
+ p.add_argument(
591
+ '--explicit',
592
+ help='Explicit list of Copyleft licenses to consider. Provide licenses as a comma-separated list.s',
593
+ )
594
+
595
+ # Add common commands for inspect copyleft and license summary
596
+ for p in [p_license_summary, p_component_summary]:
597
+ p.add_argument('-i', '--input', nargs='?', help='Path to results file')
598
+ p.add_argument('-o', '--output', type=str, help='Save summary into a file')
599
+
574
600
  p_undeclared.set_defaults(func=inspect_undeclared)
601
+ p_copyleft.set_defaults(func=inspect_copyleft)
602
+ p_license_summary.set_defaults(func=inspect_license_summary)
603
+ p_component_summary.set_defaults(func=inspect_component_summary)
604
+
605
+ ########################################### END INSPECT SUBCOMMAND ###########################################
575
606
 
576
607
  # Sub-command: folder-scan
577
608
  p_folder_scan = subparsers.add_parser(
@@ -825,6 +856,8 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
825
856
  p_results,
826
857
  p_undeclared,
827
858
  p_copyleft,
859
+ p_license_summary,
860
+ p_component_summary,
828
861
  c_provenance,
829
862
  p_folder_scan,
830
863
  p_folder_hash,
@@ -1279,7 +1312,7 @@ def convert(parser, args):
1279
1312
  if not success:
1280
1313
  sys.exit(1)
1281
1314
 
1282
-
1315
+ ################################ INSPECT handlers ################################
1283
1316
  def inspect_copyleft(parser, args):
1284
1317
  """
1285
1318
  Run the "inspect" sub-command
@@ -1356,13 +1389,73 @@ def inspect_undeclared(parser, args):
1356
1389
  status, _ = i_undeclared.run()
1357
1390
  sys.exit(status)
1358
1391
 
1392
+ def inspect_license_summary(parser, args):
1393
+ """
1394
+ Run the "inspect" sub-command
1395
+ Parameters
1396
+ ----------
1397
+ parser: ArgumentParser
1398
+ command line parser object
1399
+ args: Namespace
1400
+ Parsed arguments
1401
+ """
1402
+ if args.input is None:
1403
+ print_stderr('Please specify an input file to inspect')
1404
+ parser.parse_args([args.subparser, args.subparsercmd, '-h'])
1405
+ sys.exit(1)
1406
+ output: str = None
1407
+ if args.output:
1408
+ output = args.output
1409
+ open(output, 'w').close()
1410
+
1411
+ i_license_summary = LicenseSummary(
1412
+ debug=args.debug,
1413
+ trace=args.trace,
1414
+ quiet=args.quiet,
1415
+ filepath=args.input,
1416
+ output=output,
1417
+ include=args.include,
1418
+ exclude=args.exclude,
1419
+ explicit=args.explicit,
1420
+ )
1421
+ i_license_summary.run()
1422
+
1423
+ def inspect_component_summary(parser, args):
1424
+ """
1425
+ Run the "inspect" sub-command
1426
+ Parameters
1427
+ ----------
1428
+ parser: ArgumentParser
1429
+ command line parser object
1430
+ args: Namespace
1431
+ Parsed arguments
1432
+ """
1433
+ if args.input is None:
1434
+ print_stderr('Please specify an input file to inspect')
1435
+ parser.parse_args([args.subparser, args.subparsercmd, '-h'])
1436
+ sys.exit(1)
1437
+ output: str = None
1438
+ if args.output:
1439
+ output = args.output
1440
+ open(output, 'w').close()
1441
+
1442
+ i_component_summary = ComponentSummary(
1443
+ debug=args.debug,
1444
+ trace=args.trace,
1445
+ quiet=args.quiet,
1446
+ filepath=args.input,
1447
+ output=output,
1448
+ )
1449
+ i_component_summary.run()
1450
+
1451
+ ################################ End inspect handlers ################################
1359
1452
 
1360
1453
  def utils_certloc(*_):
1361
1454
  """
1362
1455
  Run the "utils certloc" sub-command
1363
1456
  :param _: ignored/unused
1364
1457
  """
1365
- import certifi
1458
+ import certifi # noqa: PLC0415,I001
1366
1459
 
1367
1460
  print(f'CA Cert File: {certifi.where()}')
1368
1461
 
@@ -1373,11 +1466,11 @@ def utils_cert_download(_, args): # pylint: disable=PLR0912 # noqa: PLR0912
1373
1466
  :param _: ignore/unused
1374
1467
  :param args: Parsed arguments
1375
1468
  """
1376
- import socket
1377
- import traceback
1378
- from urllib.parse import urlparse
1469
+ import socket # noqa: PLC0415,I001
1470
+ import traceback # noqa: PLC0415,I001
1471
+ from urllib.parse import urlparse # noqa: PLC0415,I001
1379
1472
 
1380
- from OpenSSL import SSL, crypto
1473
+ from OpenSSL import SSL, crypto # noqa: PLC0415,I001
1381
1474
 
1382
1475
  file = sys.stdout
1383
1476
  if args.output:
@@ -1425,7 +1518,7 @@ def utils_pac_proxy(_, args):
1425
1518
  :param _: ignore/unused
1426
1519
  :param args: Parsed arguments
1427
1520
  """
1428
- from pypac.resolver import ProxyResolver
1521
+ from pypac.resolver import ProxyResolver # noqa: PLC0415,I001
1429
1522
 
1430
1523
  if not args.pac:
1431
1524
  print_stderr('Error: No pac file option specified.')
@@ -1499,7 +1592,7 @@ def crypto_algorithms(parser, args):
1499
1592
  sys.exit(1)
1500
1593
  except Exception as e:
1501
1594
  if args.debug:
1502
- import traceback
1595
+ import traceback # noqa: PLC0415,I001
1503
1596
 
1504
1597
  traceback.print_exc()
1505
1598
  print_stderr(f'ERROR: {e}')
@@ -1541,7 +1634,7 @@ def crypto_hints(parser, args):
1541
1634
  sys.exit(1)
1542
1635
  except Exception as e:
1543
1636
  if args.debug:
1544
- import traceback
1637
+ import traceback # noqa: PLC0415,I001
1545
1638
 
1546
1639
  traceback.print_exc()
1547
1640
  print_stderr(f'ERROR: {e}')
@@ -1583,7 +1676,7 @@ def crypto_versions_in_range(parser, args):
1583
1676
  sys.exit(1)
1584
1677
  except Exception as e:
1585
1678
  if args.debug:
1586
- import traceback
1679
+ import traceback # noqa: PLC0415,I001
1587
1680
 
1588
1681
  traceback.print_exc()
1589
1682
  print_stderr(f'ERROR: {e}')
@@ -1 +1 @@
1
- date: 20250618150502, utime: 1750259102
1
+ date: 20250620142757, utime: 1750429677
@@ -1,7 +1,7 @@
1
1
  """
2
2
  SPDX-License-Identifier: MIT
3
3
 
4
- Copyright (c) 2024, SCANOSS
4
+ Copyright (c) 2025, SCANOSS
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,85 @@
1
+ """
2
+ SPDX-License-Identifier: MIT
3
+
4
+ Copyright (c) 2025, SCANOSS
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ """
24
+
25
+ import json
26
+
27
+ from .inspect_base import InspectBase
28
+
29
+
30
+ class ComponentSummary(InspectBase):
31
+ def _get_component_summary_from_components(self, scan_components: list)-> dict:
32
+ """
33
+ Get a component summary from detected components.
34
+
35
+ :param components: List of all components
36
+ :return: Dict with license summary information
37
+ """
38
+ components: list = []
39
+ undeclared_components = 0
40
+ total_components = 0
41
+ for component in scan_components:
42
+ total_components += component['count']
43
+ undeclared_components += component['undeclared']
44
+ components.append({
45
+ 'purl': component['purl'],
46
+ 'version': component['version'],
47
+ 'count': component['count'],
48
+ 'undeclared': component['undeclared'],
49
+ 'declared': component['count'] - component['undeclared'],
50
+ })
51
+ ## End for loop components
52
+ return {
53
+ 'components': components,
54
+ 'total': total_components,
55
+ 'undeclared': undeclared_components,
56
+ 'declared': total_components - undeclared_components,
57
+ }
58
+
59
+ def _get_components(self):
60
+ """
61
+ Extract and process components from results and their dependencies.
62
+
63
+ This method performs the following steps:
64
+ 1. Validates that `self.results` is loaded. Returns `None` if not.
65
+ 2. Extracts file, snippet, and dependency components into a dictionary.
66
+ 3. Converts components to a list and processes their licenses.
67
+
68
+ :return: A list of processed components with license data, or `None` if `self.results` is not set.
69
+ """
70
+ if self.results is None:
71
+ return None
72
+
73
+ components: dict = {}
74
+ # Extract component and license data from file and dependency results. Both helpers mutate `components`
75
+ self._get_components_data(self.results, components)
76
+ return self._convert_components_to_list(components)
77
+
78
+ def run(self):
79
+ components = self._get_components()
80
+ component_summary = self._get_component_summary_from_components(components)
81
+ self.print_to_file_or_stdout(json.dumps(component_summary, indent=2), self.output)
82
+ return component_summary
83
+ #
84
+ # End of ComponentSummary Class
85
+ #
@@ -1,7 +1,7 @@
1
1
  """
2
2
  SPDX-License-Identifier: MIT
3
3
 
4
- Copyright (c) 2024, SCANOSS
4
+ Copyright (c) 2025, SCANOSS
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
@@ -151,7 +151,15 @@ class Copyleft(PolicyCheck):
151
151
  for component in components:
152
152
  copyleft_licenses = [lic for lic in component['licenses'] if lic['copyleft']]
153
153
  if copyleft_licenses:
154
+ # Remove unused keys
155
+ del component['count']
156
+ del component['declared']
157
+ del component['undeclared']
154
158
  filtered_component = component
159
+ # Remove 'count' from each license using pop
160
+ for lic in copyleft_licenses:
161
+ lic.pop('count', None) # None is default value if key doesn't exist
162
+
155
163
  filtered_component['licenses'] = copyleft_licenses
156
164
  del filtered_component['status']
157
165
  filtered_components.append(filtered_component)