scanoss 1.40.0__tar.gz → 1.41.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. {scanoss-1.40.0/src/scanoss.egg-info → scanoss-1.41.0}/PKG-INFO +1 -1
  2. {scanoss-1.40.0 → scanoss-1.41.0}/README.md +5 -0
  3. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/__init__.py +1 -1
  4. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/cli.py +22 -9
  5. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/constants.py +3 -0
  6. scanoss-1.41.0/src/scanoss/data/build_date.txt +1 -0
  7. scanoss-1.41.0/src/scanoss/data/osadl-copyleft.json +133 -0
  8. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/filecount.py +37 -38
  9. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/gitlabqualityreport.py +33 -4
  10. scanoss-1.41.0/src/scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
  11. {scanoss-1.40.0/src/scanoss/inspection → scanoss-1.41.0/src/scanoss/inspection/policy_check}/dependency_track/project_violation.py +24 -24
  12. {scanoss-1.40.0/src/scanoss/inspection → scanoss-1.41.0/src/scanoss/inspection/policy_check}/policy_check.py +22 -18
  13. scanoss-1.41.0/src/scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
  14. {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/policy_check/scanoss}/copyleft.py +42 -36
  15. {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/policy_check/scanoss}/undeclared_component.py +30 -29
  16. scanoss-1.41.0/src/scanoss/inspection/summary/__init__.py +0 -0
  17. {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/summary}/component_summary.py +34 -9
  18. {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/summary}/license_summary.py +46 -44
  19. {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/summary}/match_summary.py +51 -0
  20. scanoss-1.41.0/src/scanoss/inspection/utils/license_utils.py +123 -0
  21. scanoss-1.40.0/src/scanoss/inspection/raw/raw_base.py → scanoss-1.41.0/src/scanoss/inspection/utils/scan_result_processor.py +47 -59
  22. scanoss-1.41.0/src/scanoss/osadl.py +125 -0
  23. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanner.py +191 -189
  24. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/folder_hasher.py +24 -24
  25. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/scanner_hfh.py +20 -15
  26. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/threadedscanning.py +10 -0
  27. {scanoss-1.40.0 → scanoss-1.41.0/src/scanoss.egg-info}/PKG-INFO +1 -1
  28. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/SOURCES.txt +15 -9
  29. scanoss-1.41.0/tests/test_osadl.py +102 -0
  30. {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_policy_inspect.py +316 -63
  31. scanoss-1.40.0/src/scanoss/data/build_date.txt +0 -1
  32. scanoss-1.40.0/src/scanoss/inspection/utils/license_utils.py +0 -137
  33. {scanoss-1.40.0 → scanoss-1.41.0}/LICENSE +0 -0
  34. {scanoss-1.40.0 → scanoss-1.41.0}/PACKAGE.md +0 -0
  35. {scanoss-1.40.0 → scanoss-1.41.0}/pyproject.toml +0 -0
  36. {scanoss-1.40.0 → scanoss-1.41.0}/setup.cfg +0 -0
  37. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/__init__.py +0 -0
  38. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/__init__.py +0 -0
  39. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/annotations_pb2.py +0 -0
  40. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/annotations_pb2.pyi +0 -0
  41. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/annotations_pb2_grpc.py +0 -0
  42. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/openapiv2_pb2.py +0 -0
  43. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/openapiv2_pb2.pyi +0 -0
  44. {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/openapiv2_pb2_grpc.py +0 -0
  45. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/__init__.py +0 -0
  46. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/__init__.py +0 -0
  47. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/v2/__init__.py +0 -0
  48. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/v2/scanoss_common_pb2.py +0 -0
  49. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/v2/scanoss_common_pb2_grpc.py +0 -0
  50. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/__init__.py +0 -0
  51. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/v2/__init__.py +0 -0
  52. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/v2/scanoss_components_pb2.py +0 -0
  53. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/v2/scanoss_components_pb2_grpc.py +0 -0
  54. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +0 -0
  55. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +0 -0
  56. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/__init__.py +0 -0
  57. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/v2/__init__.py +0 -0
  58. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +0 -0
  59. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +0 -0
  60. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/__init__.py +0 -0
  61. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/v2/__init__.py +0 -0
  62. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +0 -0
  63. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +0 -0
  64. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/__init__.py +0 -0
  65. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/v2/__init__.py +0 -0
  66. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2.py +0 -0
  67. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +0 -0
  68. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/__init__.py +0 -0
  69. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/v2/__init__.py +0 -0
  70. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2.py +0 -0
  71. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +0 -0
  72. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/__init__.py +0 -0
  73. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/v2/__init__.py +0 -0
  74. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +0 -0
  75. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +0 -0
  76. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/__init__.py +0 -0
  77. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/v2/__init__.py +0 -0
  78. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +0 -0
  79. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +0 -0
  80. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/components.py +0 -0
  81. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/cryptography.py +0 -0
  82. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/csvoutput.py +0 -0
  83. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/cyclonedx.py +0 -0
  84. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/data/scanoss-settings-schema.json +0 -0
  85. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/data/spdx-exceptions.json +0 -0
  86. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/data/spdx-licenses.json +0 -0
  87. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/delta.py +0 -0
  88. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/export/__init__.py +0 -0
  89. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/export/dependency_track.py +0 -0
  90. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/file_filters.py +0 -0
  91. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/inspection/__init__.py +0 -0
  92. {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/policy_check}/__init__.py +0 -0
  93. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/inspection/utils/file_utils.py +0 -0
  94. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/inspection/utils/markdown_utils.py +0 -0
  95. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/results.py +0 -0
  96. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scancodedeps.py +0 -0
  97. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/__init__.py +0 -0
  98. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/container_scanner.py +0 -0
  99. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/scanner_config.py +0 -0
  100. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanoss_settings.py +0 -0
  101. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanossapi.py +0 -0
  102. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanossbase.py +0 -0
  103. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanossgrpc.py +0 -0
  104. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanpostprocessor.py +0 -0
  105. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scantype.py +0 -0
  106. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/services/dependency_track_service.py +0 -0
  107. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/spdxlite.py +0 -0
  108. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/threadeddependencies.py +0 -0
  109. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/__init__.py +0 -0
  110. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/abstract_presenter.py +0 -0
  111. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/crc64.py +0 -0
  112. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/file.py +0 -0
  113. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/scanoss_scan_results_utils.py +0 -0
  114. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/simhash.py +0 -0
  115. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/winnowing.py +0 -0
  116. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/dependency_links.txt +0 -0
  117. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/entry_points.txt +0 -0
  118. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/requires.txt +0 -0
  119. {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/top_level.txt +0 -0
  120. {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_csv_output.py +0 -0
  121. {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_file_filters.py +0 -0
  122. {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_scan_post_processor.py +0 -0
  123. {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_spdxlite.py +0 -0
  124. {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_winnowing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scanoss
3
- Version: 1.40.0
3
+ Version: 1.41.0
4
4
  Summary: Simple Python library to leverage the SCANOSS APIs
5
5
  Home-page: https://scanoss.com
6
6
  Author: SCANOSS
@@ -135,3 +135,8 @@ Details of major changes to the library can be found in [CHANGELOG.md](CHANGELOG
135
135
 
136
136
  ## Background
137
137
  Details about the Winnowing algorithm used for scanning can be found [here](WINNOWING.md).
138
+
139
+ ## Dataset License Notice
140
+ This application is licensed under the MIT License. In addition, it includes an unmodified copy of the OSADL copyleft license dataset ([osadl-copyleft.json](src/scanoss/data/osadl-copyleft.json)) which is licensed under the [Creative Commons Attribution 4.0 International license (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/) by the [Open Source Automation Development Lab (OSADL) eG](https://www.osadl.org/).
141
+
142
+ **Attribution:** A project by the Open Source Automation Development Lab (OSADL) eG. Original source: [https://www.osadl.org/fileadmin/checklists/copyleft.json](https://www.osadl.org/fileadmin/checklists/copyleft.json)
@@ -22,4 +22,4 @@ SPDX-License-Identifier: MIT
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- __version__ = '1.40.0'
25
+ __version__ = '1.41.0'
@@ -35,12 +35,6 @@ import pypac
35
35
  from scanoss.cryptography import Cryptography, create_cryptography_config_from_args
36
36
  from scanoss.delta import Delta
37
37
  from scanoss.export.dependency_track import DependencyTrackExporter
38
- from scanoss.inspection.dependency_track.project_violation import (
39
- DependencyTrackProjectViolationPolicyCheck,
40
- )
41
- from scanoss.inspection.raw.component_summary import ComponentSummary
42
- from scanoss.inspection.raw.license_summary import LicenseSummary
43
- from scanoss.inspection.raw.match_summary import MatchSummary
44
38
  from scanoss.scanners.container_scanner import (
45
39
  DEFAULT_SYFT_COMMAND,
46
40
  DEFAULT_SYFT_TIMEOUT,
@@ -61,6 +55,7 @@ from . import __version__
61
55
  from .components import Components
62
56
  from .constants import (
63
57
  DEFAULT_API_TIMEOUT,
58
+ DEFAULT_COPYLEFT_LICENSE_SOURCES,
64
59
  DEFAULT_HFH_DEPTH,
65
60
  DEFAULT_HFH_MIN_ACCEPTED_SCORE,
66
61
  DEFAULT_HFH_RANK_THRESHOLD,
@@ -70,13 +65,20 @@ from .constants import (
70
65
  DEFAULT_TIMEOUT,
71
66
  MIN_TIMEOUT,
72
67
  PYTHON_MAJOR_VERSION,
68
+ VALID_LICENSE_SOURCES,
73
69
  )
74
70
  from .csvoutput import CsvOutput
75
71
  from .cyclonedx import CycloneDx
76
72
  from .filecount import FileCount
77
73
  from .gitlabqualityreport import GitLabQualityReport
78
- from .inspection.raw.copyleft import Copyleft
79
- from .inspection.raw.undeclared_component import UndeclaredComponent
74
+ from .inspection.policy_check.dependency_track.project_violation import (
75
+ DependencyTrackProjectViolationPolicyCheck,
76
+ )
77
+ from .inspection.policy_check.scanoss.copyleft import Copyleft
78
+ from .inspection.policy_check.scanoss.undeclared_component import UndeclaredComponent
79
+ from .inspection.summary.component_summary import ComponentSummary
80
+ from .inspection.summary.license_summary import LicenseSummary
81
+ from .inspection.summary.match_summary import MatchSummary
80
82
  from .results import Results
81
83
  from .scancodedeps import ScancodeDeps
82
84
  from .scanner import FAST_WINNOWING, Scanner
@@ -699,6 +701,17 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
699
701
  p.add_argument('--exclude', help='Licenses to exclude from analysis (comma-separated list)')
700
702
  p.add_argument('--explicit', help='Use only these specific licenses for analysis (comma-separated list)')
701
703
 
704
+ # License source filtering
705
+ for p in [p_inspect_raw_copyleft, p_inspect_legacy_copyleft]:
706
+ p.add_argument(
707
+ '-ls', '--license-sources',
708
+ action='extend',
709
+ nargs='+',
710
+ choices=VALID_LICENSE_SOURCES,
711
+ help=f'Specify which license sources to check for copyleft violations. Each license object in scan results '
712
+ f'has a source field indicating its origin. Default: {", ".join(DEFAULT_COPYLEFT_LICENSE_SOURCES)}',
713
+ )
714
+
702
715
  # Common options for (legacy) copyleft and undeclared component inspection
703
716
  for p in [p_inspect_raw_copyleft, p_inspect_raw_undeclared, p_inspect_legacy_copyleft, p_inspect_legacy_undeclared]:
704
717
  p.add_argument('-i', '--input', nargs='?', help='Path to scan results file to analyse')
@@ -1752,8 +1765,8 @@ def inspect_copyleft(parser, args):
1752
1765
  include=args.include, # Additional licenses to check
1753
1766
  exclude=args.exclude, # Licenses to ignore
1754
1767
  explicit=args.explicit, # Explicit license list
1768
+ license_sources=args.license_sources, # License sources to check (list)
1755
1769
  )
1756
-
1757
1770
  # Execute inspection and exit with appropriate status code
1758
1771
  status, _ = i_copyleft.run()
1759
1772
  sys.exit(status)
@@ -17,3 +17,6 @@ DEFAULT_HFH_RANK_THRESHOLD = 5
17
17
  DEFAULT_HFH_DEPTH = 1
18
18
  DEFAULT_HFH_RECURSIVE_THRESHOLD = 0.8
19
19
  DEFAULT_HFH_MIN_ACCEPTED_SCORE = 0.15
20
+
21
+ VALID_LICENSE_SOURCES = ['component_declared', 'license_file', 'file_header', 'file_spdx_tag', 'scancode']
22
+ DEFAULT_COPYLEFT_LICENSE_SOURCES = ['component_declared', 'license_file']
@@ -0,0 +1 @@
1
+ date: 20251117160705, utime: 1763395625
@@ -0,0 +1,133 @@
1
+ {
2
+ "title": "OSADL Open Source License Obligations Checklist (https:\/\/www.osadl.org\/Checklists)",
3
+ "license": "Creative Commons Attribution 4.0 International license (CC-BY-4.0)",
4
+ "attribution": "A project by the Open Source Automation Development Lab (OSADL) eG. For further information about the project see the description at www.osadl.org\/checklists.",
5
+ "copyright": "(C) 2017 - 2024 Open Source Automation Development Lab (OSADL) eG and contributors, info@osadl.org",
6
+ "disclaimer": "The checklists and particularly the copyleft data have been assembled with maximum diligence and care; however, the authors do not warrant nor can be held liable in any way for its correctness, usefulness, merchantibility or fitness for a particular purpose as far as permissible by applicable law. Anyone who uses the information does this on his or her sole responsibility. For any individual legal advice, it is recommended to contact a lawyer.",
7
+ "timeformat": "%Y-%m-%dT%H:%M:%S%z",
8
+ "timestamp": "2025-10-30T11:23:00+0000",
9
+ "copyleft":
10
+ {
11
+ "0BSD": "No",
12
+ "AFL-2.0": "No",
13
+ "AFL-2.1": "No",
14
+ "AFL-3.0": "No",
15
+ "AGPL-3.0-only": "Yes",
16
+ "AGPL-3.0-or-later": "Yes",
17
+ "Apache-1.0": "No",
18
+ "Apache-1.1": "No",
19
+ "Apache-2.0": "No",
20
+ "APSL-2.0": "Yes (restricted)",
21
+ "Artistic-1.0": "No",
22
+ "Artistic-1.0-Perl": "No",
23
+ "Artistic-2.0": "No",
24
+ "Bitstream-Vera": "No",
25
+ "blessing": "No",
26
+ "BlueOak-1.0.0": "No",
27
+ "BSD-1-Clause": "No",
28
+ "BSD-2-Clause": "No",
29
+ "BSD-2-Clause-Patent": "No",
30
+ "BSD-3-Clause": "No",
31
+ "BSD-3-Clause-Open-MPI": "No",
32
+ "BSD-4-Clause": "No",
33
+ "BSD-4-Clause-UC": "No",
34
+ "BSD-4.3TAHOE": "No",
35
+ "BSD-Source-Code": "No",
36
+ "BSL-1.0": "No",
37
+ "bzip2-1.0.5": "No",
38
+ "bzip2-1.0.6": "No",
39
+ "CC-BY-2.5": "No",
40
+ "CC-BY-3.0": "No",
41
+ "CDDL-1.0": "Yes (restricted)",
42
+ "CDDL-1.1": "Yes (restricted)",
43
+ "CPL-1.0": "Yes",
44
+ "curl": "No",
45
+ "ECL-1.0": "No",
46
+ "ECL-2.0": "No",
47
+ "EFL-2.0": "No",
48
+ "EPL-1.0": "Yes",
49
+ "EPL-2.0": "Yes (restricted)",
50
+ "EUPL-1.1": "Yes",
51
+ "EUPL-1.2": "Yes",
52
+ "FSFAP": "No",
53
+ "FSFUL": "No",
54
+ "FSFULLR": "No",
55
+ "FSFULLRWD": "No",
56
+ "FTL": "No",
57
+ "GPL-1.0-only": "Yes",
58
+ "GPL-1.0-or-later": "Yes",
59
+ "GPL-2.0-only": "Yes",
60
+ "GPL-2.0-only WITH Classpath-exception-2.0": "Yes (restricted)",
61
+ "GPL-2.0-or-later": "Yes",
62
+ "GPL-3.0-only": "Yes",
63
+ "GPL-3.0-or-later": "Yes",
64
+ "HPND": "No",
65
+ "IBM-pibs": "No",
66
+ "ICU": "No",
67
+ "IJG": "No",
68
+ "ImageMagick": "No",
69
+ "Info-ZIP": "No",
70
+ "IPL-1.0": "Yes",
71
+ "ISC": "No",
72
+ "JasPer-2.0": "No",
73
+ "LGPL-2.0-only": "Yes (restricted)",
74
+ "LGPL-2.0-or-later": "Yes (restricted)",
75
+ "LGPL-2.1-only": "Yes (restricted)",
76
+ "LGPL-2.1-or-later": "Yes (restricted)",
77
+ "LGPL-3.0-only": "Yes (restricted)",
78
+ "LGPL-3.0-or-later": "Yes (restricted)",
79
+ "Libpng": "No",
80
+ "libpng-2.0": "No",
81
+ "libtiff": "No",
82
+ "LicenseRef-scancode-bsla-no-advert": "No",
83
+ "LicenseRef-scancode-info-zip-2003-05": "No",
84
+ "LicenseRef-scancode-ppp": "No",
85
+ "Minpack": "No",
86
+ "MirOS": "No",
87
+ "MIT": "No",
88
+ "MIT-0": "No",
89
+ "MIT-CMU": "No",
90
+ "MPL-1.1": "Yes (restricted)",
91
+ "MPL-2.0": "Yes (restricted)",
92
+ "MPL-2.0-no-copyleft-exception": "Yes (restricted)",
93
+ "MS-PL": "Questionable",
94
+ "MS-RL": "Yes (restricted)",
95
+ "NBPL-1.0": "No",
96
+ "NCSA": "No",
97
+ "NTP": "No",
98
+ "OFL-1.1": "Yes (restricted)",
99
+ "OGC-1.0": "No",
100
+ "OLDAP-2.8": "No",
101
+ "OpenSSL": "Questionable",
102
+ "OSL-3.0": "Yes",
103
+ "PHP-3.01": "No",
104
+ "PostgreSQL": "No",
105
+ "PSF-2.0": "No",
106
+ "Python-2.0": "No",
107
+ "Qhull": "No",
108
+ "RSA-MD": "No",
109
+ "Saxpath": "No",
110
+ "SGI-B-2.0": "No",
111
+ "Sleepycat": "Yes",
112
+ "SMLNJ": "No",
113
+ "Spencer-86": "No",
114
+ "SSH-OpenSSH": "No",
115
+ "SSH-short": "No",
116
+ "SunPro": "No",
117
+ "Ubuntu-font-1.0": "Yes (restricted)",
118
+ "Unicode-3.0": "No",
119
+ "Unicode-DFS-2015": "No",
120
+ "Unicode-DFS-2016": "No",
121
+ "Unlicense": "No",
122
+ "UPL-1.0": "No",
123
+ "W3C": "No",
124
+ "W3C-19980720": "No",
125
+ "W3C-20150513": "No",
126
+ "WTFPL": "No",
127
+ "X11": "No",
128
+ "XFree86-1.1": "No",
129
+ "Zlib": "No",
130
+ "zlib-acknowledgement": "No",
131
+ "ZPL-2.0": "No"
132
+ }
133
+ }
@@ -26,6 +26,7 @@ import csv
26
26
  import os
27
27
  import pathlib
28
28
  import sys
29
+ from contextlib import nullcontext
29
30
 
30
31
  from progress.spinner import Spinner
31
32
 
@@ -105,48 +106,46 @@ class FileCount(ScanossBase):
105
106
  """
106
107
  success = True
107
108
  if not scan_dir:
108
- raise Exception(f'ERROR: Please specify a folder to scan')
109
+ raise Exception('ERROR: Please specify a folder to scan')
109
110
  if not os.path.exists(scan_dir) or not os.path.isdir(scan_dir):
110
111
  raise Exception(f'ERROR: Specified folder does not exist or is not a folder: {scan_dir}')
111
112
 
112
113
  self.print_msg(f'Searching {scan_dir} for files to count...')
113
- spinner = None
114
- if not self.quiet and self.isatty:
115
- spinner = Spinner('Searching ')
116
- file_types = {}
117
- file_count = 0
118
- file_size = 0
119
- for root, dirs, files in os.walk(scan_dir):
120
- self.print_trace(f'U Root: {root}, Dirs: {dirs}, Files {files}')
121
- dirs[:] = self.__filter_dirs(dirs) # Strip out unwanted directories
122
- filtered_files = self.__filter_files(files) # Strip out unwanted files
123
- self.print_trace(f'F Root: {root}, Dirs: {dirs}, Files {filtered_files}')
124
- for file in filtered_files: # Cycle through each filtered file
125
- path = os.path.join(root, file)
126
- f_size = 0
127
- try:
128
- f_size = os.stat(path).st_size
129
- except Exception as e:
130
- self.print_trace(f'Ignoring missing symlink file: {file} ({e})') # broken symlink
131
- if f_size > 0: # Ignore broken links and empty files
132
- file_count = file_count + 1
133
- file_size = file_size + f_size
134
- f_suffix = pathlib.Path(file).suffix
135
- if not f_suffix or f_suffix == '':
136
- f_suffix = 'no_suffix'
137
- self.print_trace(f'Counting {path} ({f_suffix} - {f_size})..')
138
- fc = file_types.get(f_suffix)
139
- if not fc:
140
- fc = [1, f_size]
141
- else:
142
- fc[0] = fc[0] + 1
143
- fc[1] = fc[1] + f_size
144
- file_types[f_suffix] = fc
145
- if spinner:
146
- spinner.next()
147
- # End for loop
148
- if spinner:
149
- spinner.finish()
114
+ spinner_ctx = Spinner('Searching ') if (not self.quiet and self.isatty) else nullcontext()
115
+
116
+ with spinner_ctx as spinner:
117
+ file_types = {}
118
+ file_count = 0
119
+ file_size = 0
120
+ for root, dirs, files in os.walk(scan_dir):
121
+ self.print_trace(f'U Root: {root}, Dirs: {dirs}, Files {files}')
122
+ dirs[:] = self.__filter_dirs(dirs) # Strip out unwanted directories
123
+ filtered_files = self.__filter_files(files) # Strip out unwanted files
124
+ self.print_trace(f'F Root: {root}, Dirs: {dirs}, Files {filtered_files}')
125
+ for file in filtered_files: # Cycle through each filtered file
126
+ path = os.path.join(root, file)
127
+ f_size = 0
128
+ try:
129
+ f_size = os.stat(path).st_size
130
+ except Exception as e:
131
+ self.print_trace(f'Ignoring missing symlink file: {file} ({e})') # broken symlink
132
+ if f_size > 0: # Ignore broken links and empty files
133
+ file_count = file_count + 1
134
+ file_size = file_size + f_size
135
+ f_suffix = pathlib.Path(file).suffix
136
+ if not f_suffix or f_suffix == '':
137
+ f_suffix = 'no_suffix'
138
+ self.print_trace(f'Counting {path} ({f_suffix} - {f_size})..')
139
+ fc = file_types.get(f_suffix)
140
+ if not fc:
141
+ fc = [1, f_size]
142
+ else:
143
+ fc[0] = fc[0] + 1
144
+ fc[1] = fc[1] + f_size
145
+ file_types[f_suffix] = fc
146
+ if spinner:
147
+ spinner.next()
148
+ # End for loop
150
149
  self.print_stderr(f'Found {file_count:,.0f} files with a total size of {file_size / (1 << 20):,.2f} MB.')
151
150
  if file_types:
152
151
  csv_dict = []
@@ -74,16 +74,21 @@ class GitLabQualityReport(ScanossBase):
74
74
  Initialise the GitLabCodeQuality class
75
75
  """
76
76
  super().__init__(debug, trace, quiet)
77
+ self.print_trace(f"GitLabQualityReport initialized with debug={debug}, trace={trace}, quiet={quiet}")
77
78
 
78
79
 
79
80
  def _get_code_quality(self, file_name: str, result: dict) -> CodeQuality or None:
81
+ self.print_trace(f"_get_code_quality called for file: {file_name}")
82
+ self.print_trace(f"Processing result: {result}")
83
+
80
84
  if not result.get('file_hash'):
81
85
  self.print_debug(f"Warning: no hash found for result: {result}")
82
86
  return None
83
87
 
84
88
  if result.get('id') == 'file':
89
+ self.print_debug(f"Processing file match for: {file_name}")
85
90
  description = f"File match found in: {file_name}"
86
- return CodeQuality(
91
+ code_quality = CodeQuality(
87
92
  description=description,
88
93
  check_name=file_name,
89
94
  fingerprint=result.get('file_hash'),
@@ -95,17 +100,21 @@ class GitLabQualityReport(ScanossBase):
95
100
  )
96
101
  )
97
102
  )
103
+ self.print_trace(f"Created file CodeQuality object: {code_quality}")
104
+ return code_quality
98
105
 
99
106
  if not result.get('lines'):
100
107
  self.print_debug(f"Warning: No lines found for result: {result}")
101
108
  return None
102
109
  lines = scanoss_scan_results_utils.get_lines(result.get('lines'))
110
+ self.print_trace(f"Extracted lines: {lines}")
103
111
  if len(lines) == 0:
104
112
  self.print_debug(f"Warning: empty lines for result: {result}")
105
113
  return None
106
114
  end_line = lines[len(lines) - 1] if len(lines) > 1 else lines[0]
107
115
  description = f"Snippet found in: {file_name} - lines {lines[0]}-{end_line}"
108
- return CodeQuality(
116
+ self.print_debug(f"Processing snippet match for: {file_name}, lines: {lines[0]}-{end_line}")
117
+ code_quality = CodeQuality(
109
118
  description=description,
110
119
  check_name=file_name,
111
120
  fingerprint=result.get('file_hash'),
@@ -117,35 +126,47 @@ class GitLabQualityReport(ScanossBase):
117
126
  )
118
127
  )
119
128
  )
129
+ self.print_trace(f"Created snippet CodeQuality object: {code_quality}")
130
+ return code_quality
120
131
 
121
132
  def _write_output(self, data: list[CodeQuality], output_file: str = None) -> bool:
122
133
  """Write the Gitlab Code Quality Report to output."""
134
+ self.print_trace(f"_write_output called with {len(data)} items, output_file: {output_file}")
123
135
  try:
124
136
  json_data = [item.to_dict() for item in data]
137
+ self.print_trace(f"JSON data: {json_data}")
125
138
  file = open(output_file, 'w') if output_file else sys.stdout
126
139
  print(json.dumps(json_data, indent=2), file=file)
127
140
  if output_file:
128
141
  file.close()
142
+ self.print_debug(f"Wrote output to file: {output_file}")
143
+ else:
144
+ self.print_debug("Wrote output to 'stdout'")
129
145
  return True
130
146
  except Exception as e:
131
147
  self.print_stderr(f'Error writing output: {str(e)}')
132
148
  return False
133
149
 
134
150
  def _produce_from_json(self, data: dict, output_file: str = None) -> bool:
151
+ self.print_trace(f"_produce_from_json called with output_file: {output_file}")
152
+ self.print_debug(f"Processing {len(data)} files from JSON data")
135
153
  code_quality = []
136
154
  for file_name, results in data.items():
155
+ self.print_trace(f"Processing file: {file_name} with {len(results)} results")
137
156
  for result in results:
138
157
  if not result.get('id'):
139
158
  self.print_debug(f"Warning: No ID found for result: {result}")
140
159
  continue
141
160
  if result.get('id') != 'snippet' and result.get('id') != 'file':
142
- self.print_debug(f"Skipping non-snippet/file match: {result}")
161
+ self.print_debug(f"Skipping non-snippet/file match: {file_name}, id: '{result['id']}'")
143
162
  continue
144
163
  code_quality_item = self._get_code_quality(file_name, result)
145
164
  if code_quality_item:
146
165
  code_quality.append(code_quality_item)
166
+ self.print_trace(f"Added code quality item for {file_name}")
147
167
  else:
148
168
  self.print_debug(f"Warning: No Code Quality found for result: {result}")
169
+ self.print_debug(f"Generated {len(code_quality)} code quality items")
149
170
  self._write_output(data=code_quality,output_file=output_file)
150
171
  return True
151
172
 
@@ -156,11 +177,15 @@ class GitLabQualityReport(ScanossBase):
156
177
  :param output_file: Output file (optional)
157
178
  :return: True if successful, False otherwise
158
179
  """
180
+ self.print_trace(f"_produce_from_str called with output_file: {output_file}")
159
181
  if not json_str:
160
182
  self.print_stderr('ERROR: No JSON string provided to parse.')
161
183
  return False
184
+ self.print_debug(f"Parsing JSON string of length: {len(json_str)}")
162
185
  try:
163
186
  data = json.loads(json_str)
187
+ self.print_debug("Successfully parsed JSON data")
188
+ self.print_trace(f"Parsed data structure: {type(data)}")
164
189
  except Exception as e:
165
190
  self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
166
191
  return False
@@ -174,12 +199,16 @@ class GitLabQualityReport(ScanossBase):
174
199
  :param output_file:
175
200
  :return: True if successful, False otherwise
176
201
  """
202
+ self.print_trace(f"produce_from_file called with json_file: {json_file}, output_file: {output_file}")
203
+ self.print_debug(f"Input JSON file: {json_file}, output_file: {output_file}")
177
204
  if not json_file:
178
205
  self.print_stderr('ERROR: No JSON file provided to parse.')
179
206
  return False
180
207
  if not os.path.isfile(json_file):
181
208
  self.print_stderr(f'ERROR: JSON file does not exist or is not a file: {json_file}')
182
209
  return False
210
+ self.print_debug(f"Reading JSON file: {json_file}")
183
211
  with open(json_file, 'r') as f:
184
- success = self._produce_from_str(f.read(), output_file)
212
+ json_content = f.read()
213
+ success = self._produce_from_str(json_content, output_file)
185
214
  return success
@@ -26,9 +26,9 @@ import time
26
26
  from datetime import datetime
27
27
  from typing import Any, Dict, List, Optional, TypedDict
28
28
 
29
- from ...services.dependency_track_service import DependencyTrackService
30
- from ..policy_check import PolicyCheck, PolicyStatus
31
- from ..utils.markdown_utils import generate_jira_table, generate_table
29
+ from ....services.dependency_track_service import DependencyTrackService
30
+ from ...utils.markdown_utils import generate_jira_table, generate_table
31
+ from ..policy_check import PolicyCheck, PolicyOutput, PolicyStatus
32
32
 
33
33
  # Constants
34
34
  PROCESSING_RETRY_DELAY = 5 # seconds
@@ -171,7 +171,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
171
171
  self.url = url.strip().rstrip('/') if url else None
172
172
  self.dep_track_service = DependencyTrackService(self.api_key, self.url, debug=debug, trace=trace, quiet=quiet)
173
173
 
174
- def _json(self, project_violations: list[PolicyViolationDict]) -> Dict[str, Any]:
174
+ def _json(self, project_violations: list[PolicyViolationDict]) -> PolicyOutput:
175
175
  """
176
176
  Format project violations as JSON.
177
177
 
@@ -181,12 +181,12 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
181
181
  Returns:
182
182
  Dictionary containing JSON formatted results and summary
183
183
  """
184
- return {
185
- "details": json.dumps(project_violations, indent=2),
186
- "summary": f'{len(project_violations)} policy violations were found.\n',
187
- }
184
+ return PolicyOutput(
185
+ details= json.dumps(project_violations, indent=2),
186
+ summary= f'{len(project_violations)} policy violations were found.\n',
187
+ )
188
188
 
189
- def _markdown(self, project_violations: list[PolicyViolationDict]) -> Dict[str, Any]:
189
+ def _markdown(self, project_violations: list[PolicyViolationDict]) -> PolicyOutput:
190
190
  """
191
191
  Format Dependency Track violations to Markdown format.
192
192
 
@@ -198,7 +198,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
198
198
  """
199
199
  return self._md_summary_generator(project_violations, generate_table)
200
200
 
201
- def _jira_markdown(self, data: list[PolicyViolationDict]) -> Dict[str, Any]:
201
+ def _jira_markdown(self, data: list[PolicyViolationDict]) -> PolicyOutput:
202
202
  """
203
203
  Format project violations for Jira Markdown.
204
204
 
@@ -357,8 +357,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
357
357
  self.print_stderr(f'Error: Failed to get project uuid from: {dt_project}')
358
358
  raise ValueError(f'Error: Project {self.project_name}@{self.project_version} does not have a valid UUID')
359
359
 
360
- @staticmethod
361
- def _sort_project_violations(violations: List[PolicyViolationDict]) -> List[PolicyViolationDict]:
360
+ def _sort_project_violations(self,violations: List[PolicyViolationDict]) -> List[PolicyViolationDict]:
362
361
  """
363
362
  Sort project violations by priority.
364
363
 
@@ -377,7 +376,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
377
376
  key=lambda x: -type_priority.get(x.get('type', 'OTHER'), 1)
378
377
  )
379
378
 
380
- def _md_summary_generator(self, project_violations: list[PolicyViolationDict], table_generator):
379
+ def _md_summary_generator(self, project_violations: list[PolicyViolationDict], table_generator) -> PolicyOutput:
381
380
  """
382
381
  Generates a Markdown summary of project policy violations.
383
382
 
@@ -396,10 +395,10 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
396
395
  """
397
396
  if project_violations is None:
398
397
  self.print_stderr('Warning: No project violations found. Returning empty results.')
399
- return {
400
- "details": "h3. Dependency Track Project Violations\n\nNo policy violations found.\n",
401
- "summary": "0 policy violations were found.\n",
402
- }
398
+ return PolicyOutput(
399
+ details= "h3. Dependency Track Project Violations\n\nNo policy violations found.\n",
400
+ summary= "0 policy violations were found.\n",
401
+ )
403
402
  headers = ['State', 'Risk Type', 'Policy Name', 'Component', 'Date']
404
403
  c_cols = [0, 1]
405
404
  rows: List[List[str]] = []
@@ -424,11 +423,11 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
424
423
  ]
425
424
  rows.append(row)
426
425
  # End for loop
427
- return {
428
- "details": f'### Dependency Track Project Violations\n{table_generator(headers, rows, c_cols)}\n\n'
426
+ return PolicyOutput(
427
+ details= f'### Dependency Track Project Violations\n{table_generator(headers, rows, c_cols)}\n\n'
429
428
  f'View project in Dependency Track [here]({self.url}/projects/{self.project_id}).\n',
430
- "summary": f'{len(project_violations)} policy violations were found.\n'
431
- }
429
+ summary= f'{len(project_violations)} policy violations were found.\n'
430
+ )
432
431
 
433
432
  def run(self) -> int:
434
433
  """
@@ -470,10 +469,11 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
470
469
  self.print_stderr('Error: Invalid format specified.')
471
470
  return PolicyStatus.ERROR.value
472
471
  # Format and output data - handle empty results gracefully
473
- data = formatter(self._sort_project_violations(dt_project_violations))
474
- self.print_to_file_or_stdout(data['details'], self.output)
475
- self.print_to_file_or_stderr(data['summary'], self.status)
472
+ policy_output = formatter(self._sort_project_violations(dt_project_violations))
473
+ self.print_to_file_or_stdout(policy_output.details, self.output)
474
+ self.print_to_file_or_stderr(policy_output.summary, self.status)
476
475
  # Return appropriate status based on violation count
477
476
  if len(dt_project_violations) > 0:
478
477
  return PolicyStatus.POLICY_FAIL.value
479
478
  return PolicyStatus.POLICY_SUCCESS.value
479
+