scanoss 1.39.0__tar.gz → 1.40.1__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 (120) hide show
  1. {scanoss-1.39.0/src/scanoss.egg-info → scanoss-1.40.1}/PKG-INFO +1 -1
  2. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/__init__.py +1 -1
  3. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/cli.py +15 -18
  4. scanoss-1.40.1/src/scanoss/data/build_date.txt +1 -0
  5. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/gitlabqualityreport.py +33 -4
  6. scanoss-1.40.1/src/scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
  7. {scanoss-1.39.0/src/scanoss/inspection → scanoss-1.40.1/src/scanoss/inspection/policy_check}/dependency_track/project_violation.py +24 -24
  8. {scanoss-1.39.0/src/scanoss/inspection → scanoss-1.40.1/src/scanoss/inspection/policy_check}/policy_check.py +22 -18
  9. scanoss-1.40.1/src/scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
  10. {scanoss-1.39.0/src/scanoss/inspection/raw → scanoss-1.40.1/src/scanoss/inspection/policy_check/scanoss}/copyleft.py +35 -35
  11. {scanoss-1.39.0/src/scanoss/inspection/raw → scanoss-1.40.1/src/scanoss/inspection/policy_check/scanoss}/undeclared_component.py +30 -29
  12. scanoss-1.40.1/src/scanoss/inspection/summary/__init__.py +0 -0
  13. {scanoss-1.39.0/src/scanoss/inspection/raw → scanoss-1.40.1/src/scanoss/inspection/summary}/component_summary.py +34 -9
  14. {scanoss-1.39.0/src/scanoss/inspection/raw → scanoss-1.40.1/src/scanoss/inspection/summary}/license_summary.py +46 -44
  15. {scanoss-1.39.0/src/scanoss/inspection/raw → scanoss-1.40.1/src/scanoss/inspection/summary}/match_summary.py +51 -0
  16. scanoss-1.39.0/src/scanoss/inspection/raw/raw_base.py → scanoss-1.40.1/src/scanoss/inspection/utils/scan_result_processor.py +25 -48
  17. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanners/scanner_hfh.py +3 -1
  18. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanossgrpc.py +5 -2
  19. {scanoss-1.39.0 → scanoss-1.40.1/src/scanoss.egg-info}/PKG-INFO +1 -1
  20. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss.egg-info/SOURCES.txt +12 -9
  21. {scanoss-1.39.0 → scanoss-1.40.1}/tests/test_policy_inspect.py +61 -63
  22. scanoss-1.39.0/src/scanoss/data/build_date.txt +0 -1
  23. {scanoss-1.39.0 → scanoss-1.40.1}/LICENSE +0 -0
  24. {scanoss-1.39.0 → scanoss-1.40.1}/PACKAGE.md +0 -0
  25. {scanoss-1.39.0 → scanoss-1.40.1}/README.md +0 -0
  26. {scanoss-1.39.0 → scanoss-1.40.1}/pyproject.toml +0 -0
  27. {scanoss-1.39.0 → scanoss-1.40.1}/setup.cfg +0 -0
  28. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/__init__.py +0 -0
  29. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/options/__init__.py +0 -0
  30. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/options/annotations_pb2.py +0 -0
  31. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/options/annotations_pb2.pyi +0 -0
  32. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/options/annotations_pb2_grpc.py +0 -0
  33. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/options/openapiv2_pb2.py +0 -0
  34. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/options/openapiv2_pb2.pyi +0 -0
  35. {scanoss-1.39.0 → scanoss-1.40.1}/src/protoc_gen_swagger/options/openapiv2_pb2_grpc.py +0 -0
  36. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/__init__.py +0 -0
  37. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/common/__init__.py +0 -0
  38. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/common/v2/__init__.py +0 -0
  39. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/common/v2/scanoss_common_pb2.py +0 -0
  40. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/common/v2/scanoss_common_pb2_grpc.py +0 -0
  41. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/components/__init__.py +0 -0
  42. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/components/v2/__init__.py +0 -0
  43. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/components/v2/scanoss_components_pb2.py +0 -0
  44. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/components/v2/scanoss_components_pb2_grpc.py +0 -0
  45. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +0 -0
  46. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +0 -0
  47. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/dependencies/__init__.py +0 -0
  48. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/dependencies/v2/__init__.py +0 -0
  49. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +0 -0
  50. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +0 -0
  51. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/geoprovenance/__init__.py +0 -0
  52. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/geoprovenance/v2/__init__.py +0 -0
  53. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +0 -0
  54. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +0 -0
  55. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/licenses/__init__.py +0 -0
  56. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/licenses/v2/__init__.py +0 -0
  57. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2.py +0 -0
  58. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +0 -0
  59. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/scanning/__init__.py +0 -0
  60. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/scanning/v2/__init__.py +0 -0
  61. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2.py +0 -0
  62. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +0 -0
  63. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/semgrep/__init__.py +0 -0
  64. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/semgrep/v2/__init__.py +0 -0
  65. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +0 -0
  66. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +0 -0
  67. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/vulnerabilities/__init__.py +0 -0
  68. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/vulnerabilities/v2/__init__.py +0 -0
  69. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +0 -0
  70. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +0 -0
  71. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/components.py +0 -0
  72. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/constants.py +0 -0
  73. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/cryptography.py +0 -0
  74. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/csvoutput.py +0 -0
  75. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/cyclonedx.py +0 -0
  76. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/data/scanoss-settings-schema.json +0 -0
  77. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/data/spdx-exceptions.json +0 -0
  78. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/data/spdx-licenses.json +0 -0
  79. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/delta.py +0 -0
  80. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/export/__init__.py +0 -0
  81. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/export/dependency_track.py +0 -0
  82. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/file_filters.py +0 -0
  83. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/filecount.py +0 -0
  84. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/inspection/__init__.py +0 -0
  85. {scanoss-1.39.0/src/scanoss/inspection/raw → scanoss-1.40.1/src/scanoss/inspection/policy_check}/__init__.py +0 -0
  86. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/inspection/utils/file_utils.py +0 -0
  87. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/inspection/utils/license_utils.py +0 -0
  88. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/inspection/utils/markdown_utils.py +0 -0
  89. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/results.py +0 -0
  90. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scancodedeps.py +0 -0
  91. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanner.py +0 -0
  92. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanners/__init__.py +0 -0
  93. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanners/container_scanner.py +0 -0
  94. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanners/folder_hasher.py +0 -0
  95. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanners/scanner_config.py +0 -0
  96. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanoss_settings.py +0 -0
  97. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanossapi.py +0 -0
  98. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanossbase.py +0 -0
  99. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scanpostprocessor.py +0 -0
  100. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/scantype.py +0 -0
  101. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/services/dependency_track_service.py +0 -0
  102. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/spdxlite.py +0 -0
  103. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/threadeddependencies.py +0 -0
  104. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/threadedscanning.py +0 -0
  105. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/utils/__init__.py +0 -0
  106. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/utils/abstract_presenter.py +0 -0
  107. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/utils/crc64.py +0 -0
  108. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/utils/file.py +0 -0
  109. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/utils/scanoss_scan_results_utils.py +0 -0
  110. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/utils/simhash.py +0 -0
  111. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss/winnowing.py +0 -0
  112. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss.egg-info/dependency_links.txt +0 -0
  113. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss.egg-info/entry_points.txt +0 -0
  114. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss.egg-info/requires.txt +0 -0
  115. {scanoss-1.39.0 → scanoss-1.40.1}/src/scanoss.egg-info/top_level.txt +0 -0
  116. {scanoss-1.39.0 → scanoss-1.40.1}/tests/test_csv_output.py +0 -0
  117. {scanoss-1.39.0 → scanoss-1.40.1}/tests/test_file_filters.py +0 -0
  118. {scanoss-1.39.0 → scanoss-1.40.1}/tests/test_scan_post_processor.py +0 -0
  119. {scanoss-1.39.0 → scanoss-1.40.1}/tests/test_spdxlite.py +0 -0
  120. {scanoss-1.39.0 → scanoss-1.40.1}/tests/test_winnowing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scanoss
3
- Version: 1.39.0
3
+ Version: 1.40.1
4
4
  Summary: Simple Python library to leverage the SCANOSS APIs
5
5
  Home-page: https://scanoss.com
6
6
  Author: SCANOSS
@@ -22,4 +22,4 @@ SPDX-License-Identifier: MIT
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- __version__ = '1.39.0'
25
+ __version__ = '1.40.1'
@@ -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,
@@ -75,8 +69,14 @@ from .csvoutput import CsvOutput
75
69
  from .cyclonedx import CycloneDx
76
70
  from .filecount import FileCount
77
71
  from .gitlabqualityreport import GitLabQualityReport
78
- from .inspection.raw.copyleft import Copyleft
79
- from .inspection.raw.undeclared_component import UndeclaredComponent
72
+ from .inspection.policy_check.dependency_track.project_violation import (
73
+ DependencyTrackProjectViolationPolicyCheck,
74
+ )
75
+ from .inspection.policy_check.scanoss.copyleft import Copyleft
76
+ from .inspection.policy_check.scanoss.undeclared_component import UndeclaredComponent
77
+ from .inspection.summary.component_summary import ComponentSummary
78
+ from .inspection.summary.license_summary import LicenseSummary
79
+ from .inspection.summary.match_summary import MatchSummary
80
80
  from .results import Results
81
81
  from .scancodedeps import ScancodeDeps
82
82
  from .scanner import FAST_WINNOWING, Scanner
@@ -796,7 +796,6 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
796
796
  help='Timeout (in seconds) for API communication (optional - default 300 sec)',
797
797
  )
798
798
 
799
-
800
799
  # ==============================================================================
801
800
  # GitLab Integration Parser
802
801
  # ==============================================================================
@@ -830,11 +829,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
830
829
 
831
830
  # Input file argument - SCANOSS scan results in JSON format
832
831
  p_gl_inspect_matches.add_argument(
833
- '-i',
834
- '--input',
835
- required=True,
836
- type=str,
837
- help='Path to SCANOSS scan results file (JSON format) to analyze'
832
+ '-i', '--input', required=True, type=str, help='Path to SCANOSS scan results file (JSON format) to analyze'
838
833
  )
839
834
 
840
835
  # Line range prefix for GitLab file navigation
@@ -844,7 +839,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
844
839
  '--line-range-prefix',
845
840
  required=True,
846
841
  type=str,
847
- help='Base URL prefix for GitLab file links with line ranges (e.g., https://gitlab.com/org/project/-/blob/main)'
842
+ help='Base URL prefix for GitLab file links with line ranges (e.g., https://gitlab.com/org/project/-/blob/main)',
848
843
  )
849
844
 
850
845
  # Output file argument - where to save the generated Markdown report
@@ -853,7 +848,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
853
848
  '-o',
854
849
  required=False,
855
850
  type=str,
856
- help='Output file path for the generated Markdown report (default: stdout)'
851
+ help='Output file path for the generated Markdown report (default: stdout)',
857
852
  )
858
853
 
859
854
  # TODO Move to the command call def location
@@ -1189,6 +1184,7 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
1189
1184
  c_search,
1190
1185
  c_versions,
1191
1186
  c_licenses,
1187
+ p_folder_scan,
1192
1188
  ]:
1193
1189
  p.add_argument('--grpc', action='store_true', default=True, help='Use gRPC (default)')
1194
1190
  p.add_argument('--rest', action='store_true', dest='rest', help='Use REST instead of gRPC')
@@ -1757,7 +1753,6 @@ def inspect_copyleft(parser, args):
1757
1753
  exclude=args.exclude, # Licenses to ignore
1758
1754
  explicit=args.explicit, # Explicit license list
1759
1755
  )
1760
-
1761
1756
  # Execute inspection and exit with appropriate status code
1762
1757
  status, _ = i_copyleft.run()
1763
1758
  sys.exit(status)
@@ -1975,7 +1970,7 @@ def inspect_dep_track_project_violations(parser, args):
1975
1970
  sys.exit(1)
1976
1971
 
1977
1972
 
1978
- def inspect_gitlab_matches(parser,args):
1973
+ def inspect_gitlab_matches(parser, args):
1979
1974
  """
1980
1975
  Handle GitLab matches the summary inspection command.
1981
1976
 
@@ -2038,6 +2033,7 @@ def inspect_gitlab_matches(parser,args):
2038
2033
  traceback.print_exc()
2039
2034
  sys.exit(1)
2040
2035
 
2036
+
2041
2037
  # =============================================================================
2042
2038
  # END INSPECT COMMAND HANDLERS
2043
2039
  # =============================================================================
@@ -2673,6 +2669,7 @@ def folder_hashing_scan(parser, args):
2673
2669
  depth=args.depth,
2674
2670
  recursive_threshold=args.recursive_threshold,
2675
2671
  min_accepted_score=args.min_accepted_score,
2672
+ use_grpc=args.grpc,
2676
2673
  )
2677
2674
 
2678
2675
  if scanner.scan():
@@ -0,0 +1 @@
1
+ date: 20251029145743, utime: 1761749863
@@ -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
+
@@ -22,12 +22,12 @@ SPDX-License-Identifier: MIT
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- from abc import abstractmethod
25
+ from abc import ABC, abstractmethod
26
26
  from enum import Enum
27
- from typing import Any, Callable, Dict, Generic, List, TypeVar
27
+ from typing import Callable, Dict, Generic, List, NamedTuple, TypeVar
28
28
 
29
- from ..scanossbase import ScanossBase
30
- from .utils.license_utils import LicenseUtil
29
+ from ...scanossbase import ScanossBase
30
+ from ..utils.license_utils import LicenseUtil
31
31
 
32
32
 
33
33
  class PolicyStatus(Enum):
@@ -46,9 +46,13 @@ class PolicyStatus(Enum):
46
46
  # End of PolicyStatus Class
47
47
  #
48
48
 
49
+ class PolicyOutput(NamedTuple):
50
+ details: str
51
+ summary: str
52
+
49
53
  T = TypeVar('T')
50
54
 
51
- class PolicyCheck(ScanossBase, Generic[T]):
55
+ class PolicyCheck(ScanossBase, Generic[T], ABC):
52
56
  """
53
57
  A base class for implementing various software policy checks.
54
58
 
@@ -80,7 +84,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
80
84
  self.output = output
81
85
 
82
86
  @abstractmethod
83
- def run(self):
87
+ def run(self)-> tuple[int,PolicyOutput]:
84
88
  """
85
89
  Execute the policy check process.
86
90
 
@@ -91,14 +95,14 @@ class PolicyCheck(ScanossBase, Generic[T]):
91
95
  3. Formatting the results
92
96
  4. Saving the output to files if required
93
97
 
94
- :return: A tuple containing:
98
+ :return: A named tuple containing two elements:
95
99
  - First element: PolicyStatus enum value (SUCCESS, FAIL, or ERROR)
96
- - Second element: Dictionary containing the inspection results
100
+ - Second element: PolicyOutput A tuple containing the policy results.
97
101
  """
98
102
  pass
99
103
 
100
104
  @abstractmethod
101
- def _json(self, data: list[T]) -> Dict[str, Any]:
105
+ def _json(self, data: list[T]) -> PolicyOutput:
102
106
  """
103
107
  Format the policy checks results as JSON.
104
108
  This method should be implemented by subclasses to create a Markdown representation
@@ -112,7 +116,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
112
116
  pass
113
117
 
114
118
  @abstractmethod
115
- def _markdown(self, data: list[T]) -> Dict[str, Any]:
119
+ def _markdown(self, data: list[T]) -> PolicyOutput:
116
120
  """
117
121
  Generate Markdown output for the policy check results.
118
122
 
@@ -125,7 +129,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
125
129
  pass
126
130
 
127
131
  @abstractmethod
128
- def _jira_markdown(self, data: list[T]) -> Dict[str, Any]:
132
+ def _jira_markdown(self, data: list[T]) -> PolicyOutput:
129
133
  """
130
134
  Generate Markdown output for the policy check results.
131
135
 
@@ -137,7 +141,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
137
141
  """
138
142
  pass
139
143
 
140
- def _get_formatter(self) -> Callable[[List[dict]], Dict[str, Any]] or None:
144
+ def _get_formatter(self) -> Callable[[List[dict]], PolicyOutput]:
141
145
  """
142
146
  Get the appropriate formatter function based on the specified format.
143
147
 
@@ -145,7 +149,7 @@ class PolicyCheck(ScanossBase, Generic[T]):
145
149
  """
146
150
  valid_format = self._is_valid_format()
147
151
  if not valid_format:
148
- return None
152
+ raise ValueError('Invalid format specified')
149
153
  # a map of which format function to return
150
154
  function_map = {
151
155
  'json': self._json,
@@ -205,14 +209,14 @@ class PolicyCheck(ScanossBase, Generic[T]):
205
209
  if formatter is None:
206
210
  return PolicyStatus.ERROR.value, {}
207
211
  # Format the results
208
- data = formatter(components)
212
+ policy_output = formatter(components)
209
213
  ## Save outputs if required
210
- self.print_to_file_or_stdout(data['details'], self.output)
211
- self.print_to_file_or_stderr(data['summary'], self.status)
214
+ self.print_to_file_or_stdout(policy_output.details, self.output)
215
+ self.print_to_file_or_stderr(policy_output.summary, self.status)
212
216
  # Check to see if we have policy violations
213
217
  if len(components) > 0:
214
- return PolicyStatus.POLICY_FAIL.value, data
215
- return PolicyStatus.POLICY_SUCCESS.value, data
218
+ return PolicyStatus.POLICY_FAIL.value, policy_output
219
+ return PolicyStatus.POLICY_SUCCESS.value, policy_output
216
220
  #
217
221
  # End of PolicyCheck Class
218
222
  #
@@ -24,11 +24,11 @@ SPDX-License-Identifier: MIT
24
24
 
25
25
  import json
26
26
  from dataclasses import dataclass
27
- from typing import Any, Dict, List
27
+ from typing import Dict, List
28
28
 
29
- from ..policy_check import PolicyStatus
30
- from ..utils.markdown_utils import generate_jira_table, generate_table
31
- from .raw_base import RawBase
29
+ from ...policy_check.policy_check import PolicyCheck, PolicyOutput, PolicyStatus
30
+ from ...utils.markdown_utils import generate_jira_table, generate_table
31
+ from ...utils.scan_result_processor import ScanResultProcessor
32
32
 
33
33
 
34
34
  @dataclass
@@ -45,7 +45,7 @@ class Component:
45
45
  licenses: List[License]
46
46
  status: str
47
47
 
48
- class Copyleft(RawBase[Component]):
48
+ class Copyleft(PolicyCheck[Component]):
49
49
  """
50
50
  SCANOSS Copyleft class
51
51
  Inspects components for copyleft licenses
@@ -78,17 +78,23 @@ class Copyleft(RawBase[Component]):
78
78
  :param exclude: Licenses to exclude from the analysis
79
79
  :param explicit: Explicitly defined licenses
80
80
  """
81
- super().__init__(debug, trace, quiet, format_type,filepath, output ,status, name='Copyleft Policy')
81
+ super().__init__(
82
+ debug, trace, quiet, format_type, status, name='Copyleft Policy', output=output
83
+ )
82
84
  self.license_util.init(include, exclude, explicit)
83
85
  self.filepath = filepath
84
- self.format = format
85
86
  self.output = output
86
87
  self.status = status
87
- self.include = include
88
- self.exclude = exclude
89
- self.explicit = explicit
88
+ self.results_processor = ScanResultProcessor(
89
+ self.debug,
90
+ self.trace,
91
+ self.quiet,
92
+ self.filepath,
93
+ include,
94
+ exclude,
95
+ explicit)
90
96
 
91
- def _json(self, components: list[Component]) -> Dict[str, Any]:
97
+ def _json(self, components: list[Component]) -> PolicyOutput:
92
98
  """
93
99
  Format the components with copyleft licenses as JSON.
94
100
 
@@ -96,16 +102,16 @@ class Copyleft(RawBase[Component]):
96
102
  :return: Dictionary with formatted JSON details and summary
97
103
  """
98
104
  # A component is considered unique by its combination of PURL (Package URL) and license
99
- component_licenses = self._group_components_by_license(components)
105
+ component_licenses = self.results_processor.group_components_by_license(components)
100
106
  details = {}
101
107
  if len(components) > 0:
102
108
  details = {'components': components}
103
- return {
104
- 'details': f'{json.dumps(details, indent=2)}\n',
105
- 'summary': f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
106
- }
109
+ return PolicyOutput(
110
+ details= f'{json.dumps(details, indent=2)}\n',
111
+ summary= f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
112
+ )
107
113
 
108
- def _markdown(self, components: list[Component]) -> Dict[str, Any]:
114
+ def _markdown(self, components: list[Component]) -> PolicyOutput:
109
115
  """
110
116
  Format the components with copyleft licenses as Markdown.
111
117
 
@@ -114,7 +120,7 @@ class Copyleft(RawBase[Component]):
114
120
  """
115
121
  return self._md_summary_generator(components, generate_table)
116
122
 
117
- def _jira_markdown(self, components: list[Component]) -> Dict[str, Any]:
123
+ def _jira_markdown(self, components: list[Component]) -> PolicyOutput:
118
124
  """
119
125
  Format the components with copyleft licenses as Markdown.
120
126
 
@@ -123,7 +129,7 @@ class Copyleft(RawBase[Component]):
123
129
  """
124
130
  return self._md_summary_generator(components, generate_jira_table)
125
131
 
126
- def _md_summary_generator(self, components: list[Component], table_generator):
132
+ def _md_summary_generator(self, components: list[Component], table_generator) -> PolicyOutput:
127
133
  """
128
134
  Generates a Markdown summary for components with a focus on copyleft licenses.
129
135
 
@@ -138,15 +144,10 @@ class Copyleft(RawBase[Component]):
138
144
  A callable function to generate tabular data for components.
139
145
 
140
146
  Returns:
141
- dict
142
- A dictionary containing two keys:
143
- - 'details': A detailed Markdown representation including a table of components
144
- and associated copyleft license data.
145
- - 'summary': A textual summary highlighting the total number of components
146
- with copyleft licenses.
147
+ PolicyOutput
147
148
  """
148
149
  # A component is considered unique by its combination of PURL (Package URL) and license
149
- component_licenses = self._group_components_by_license(components)
150
+ component_licenses = self.results_processor.group_components_by_license(components)
150
151
  headers = ['Component', 'License', 'URL', 'Copyleft']
151
152
  centered_columns = [1, 4]
152
153
  rows = []
@@ -160,10 +161,10 @@ class Copyleft(RawBase[Component]):
160
161
  rows.append(row)
161
162
  # End license loop
162
163
  # End component loop
163
- return {
164
- 'details': f'### Copyleft Licenses\n{table_generator(headers, rows, centered_columns)}',
165
- 'summary': f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
166
- }
164
+ return PolicyOutput(
165
+ details= f'### Copyleft Licenses\n{table_generator(headers, rows, centered_columns)}',
166
+ summary= f'{len(component_licenses)} component(s) with copyleft licenses were found.\n',
167
+ )
167
168
 
168
169
  def _get_components_with_copyleft_licenses(self, components: list) -> list[Dict]:
169
170
  """
@@ -202,14 +203,13 @@ class Copyleft(RawBase[Component]):
202
203
 
203
204
  :return: A list of processed components with license data, or `None` if `self.results` is not set.
204
205
  """
205
- if self.results is None:
206
+ if self.results_processor.get_results() is None:
206
207
  return None
207
-
208
208
  components: dict = {}
209
209
  # Extract component and license data from file and dependency results. Both helpers mutate `components`
210
- self._get_components_data(self.results, components)
211
- self._get_dependencies_data(self.results, components)
212
- return self._convert_components_to_list(components)
210
+ self.results_processor.get_components_data(components)
211
+ self.results_processor.get_dependencies_data(components)
212
+ return self.results_processor.convert_components_to_list(components)
213
213
 
214
214
  def run(self):
215
215
  """