scanoss 1.12.2__py3-none-any.whl → 1.43.1__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.
Files changed (109) hide show
  1. protoc_gen_swagger/__init__.py +13 -13
  2. protoc_gen_swagger/options/__init__.py +13 -13
  3. protoc_gen_swagger/options/annotations_pb2.py +18 -12
  4. protoc_gen_swagger/options/annotations_pb2.pyi +48 -0
  5. protoc_gen_swagger/options/annotations_pb2_grpc.py +20 -0
  6. protoc_gen_swagger/options/openapiv2_pb2.py +110 -99
  7. protoc_gen_swagger/options/openapiv2_pb2.pyi +1317 -0
  8. protoc_gen_swagger/options/openapiv2_pb2_grpc.py +20 -0
  9. scanoss/__init__.py +18 -18
  10. scanoss/api/__init__.py +17 -17
  11. scanoss/api/common/__init__.py +17 -17
  12. scanoss/api/common/v2/__init__.py +17 -17
  13. scanoss/api/common/v2/scanoss_common_pb2.py +49 -20
  14. scanoss/api/common/v2/scanoss_common_pb2_grpc.py +25 -0
  15. scanoss/api/components/__init__.py +17 -17
  16. scanoss/api/components/v2/__init__.py +17 -17
  17. scanoss/api/components/v2/scanoss_components_pb2.py +68 -43
  18. scanoss/api/components/v2/scanoss_components_pb2_grpc.py +83 -22
  19. scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +136 -21
  20. scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +766 -13
  21. scanoss/api/dependencies/__init__.py +17 -17
  22. scanoss/api/dependencies/v2/__init__.py +17 -17
  23. scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +56 -29
  24. scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +94 -8
  25. scanoss/api/geoprovenance/__init__.py +23 -0
  26. scanoss/api/geoprovenance/v2/__init__.py +23 -0
  27. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +92 -0
  28. scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +381 -0
  29. scanoss/api/licenses/__init__.py +23 -0
  30. scanoss/api/licenses/v2/__init__.py +23 -0
  31. scanoss/api/licenses/v2/scanoss_licenses_pb2.py +84 -0
  32. scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +302 -0
  33. scanoss/api/scanning/__init__.py +17 -17
  34. scanoss/api/scanning/v2/__init__.py +17 -17
  35. scanoss/api/scanning/v2/scanoss_scanning_pb2.py +42 -13
  36. scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +86 -7
  37. scanoss/api/semgrep/__init__.py +17 -17
  38. scanoss/api/semgrep/v2/__init__.py +17 -17
  39. scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +50 -23
  40. scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +151 -16
  41. scanoss/api/vulnerabilities/__init__.py +17 -17
  42. scanoss/api/vulnerabilities/v2/__init__.py +17 -17
  43. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +78 -31
  44. scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +282 -18
  45. scanoss/cli.py +2359 -370
  46. scanoss/components.py +187 -94
  47. scanoss/constants.py +22 -0
  48. scanoss/cryptography.py +308 -0
  49. scanoss/csvoutput.py +91 -58
  50. scanoss/cyclonedx.py +221 -63
  51. scanoss/data/build_date.txt +1 -1
  52. scanoss/data/osadl-copyleft.json +133 -0
  53. scanoss/data/scanoss-settings-schema.json +254 -0
  54. scanoss/delta.py +197 -0
  55. scanoss/export/__init__.py +23 -0
  56. scanoss/export/dependency_track.py +227 -0
  57. scanoss/file_filters.py +582 -0
  58. scanoss/filecount.py +75 -69
  59. scanoss/gitlabqualityreport.py +214 -0
  60. scanoss/header_filter.py +563 -0
  61. scanoss/inspection/__init__.py +23 -0
  62. scanoss/inspection/policy_check/__init__.py +0 -0
  63. scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
  64. scanoss/inspection/policy_check/dependency_track/project_violation.py +479 -0
  65. scanoss/inspection/policy_check/policy_check.py +222 -0
  66. scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
  67. scanoss/inspection/policy_check/scanoss/copyleft.py +243 -0
  68. scanoss/inspection/policy_check/scanoss/undeclared_component.py +309 -0
  69. scanoss/inspection/summary/__init__.py +0 -0
  70. scanoss/inspection/summary/component_summary.py +170 -0
  71. scanoss/inspection/summary/license_summary.py +191 -0
  72. scanoss/inspection/summary/match_summary.py +341 -0
  73. scanoss/inspection/utils/file_utils.py +44 -0
  74. scanoss/inspection/utils/license_utils.py +123 -0
  75. scanoss/inspection/utils/markdown_utils.py +63 -0
  76. scanoss/inspection/utils/scan_result_processor.py +417 -0
  77. scanoss/osadl.py +125 -0
  78. scanoss/results.py +275 -0
  79. scanoss/scancodedeps.py +87 -38
  80. scanoss/scanner.py +431 -539
  81. scanoss/scanners/__init__.py +23 -0
  82. scanoss/scanners/container_scanner.py +476 -0
  83. scanoss/scanners/folder_hasher.py +358 -0
  84. scanoss/scanners/scanner_config.py +73 -0
  85. scanoss/scanners/scanner_hfh.py +252 -0
  86. scanoss/scanoss_settings.py +337 -0
  87. scanoss/scanossapi.py +140 -101
  88. scanoss/scanossbase.py +59 -22
  89. scanoss/scanossgrpc.py +799 -251
  90. scanoss/scanpostprocessor.py +294 -0
  91. scanoss/scantype.py +22 -21
  92. scanoss/services/dependency_track_service.py +132 -0
  93. scanoss/spdxlite.py +532 -174
  94. scanoss/threadeddependencies.py +148 -47
  95. scanoss/threadedscanning.py +53 -37
  96. scanoss/utils/__init__.py +23 -0
  97. scanoss/utils/abstract_presenter.py +103 -0
  98. scanoss/utils/crc64.py +96 -0
  99. scanoss/utils/file.py +84 -0
  100. scanoss/utils/scanoss_scan_results_utils.py +41 -0
  101. scanoss/utils/simhash.py +198 -0
  102. scanoss/winnowing.py +241 -63
  103. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/METADATA +18 -9
  104. scanoss-1.43.1.dist-info/RECORD +110 -0
  105. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/WHEEL +1 -1
  106. scanoss-1.12.2.dist-info/RECORD +0 -58
  107. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/entry_points.txt +0 -0
  108. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info/licenses}/LICENSE +0 -0
  109. {scanoss-1.12.2.dist-info → scanoss-1.43.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,294 @@
1
+ """
2
+ SPDX-License-Identifier: MIT
3
+
4
+ Copyright (c) 2024, 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
+ from typing import List, Tuple
26
+
27
+ from packageurl import PackageURL
28
+ from packageurl.contrib import purl2url
29
+
30
+ from .scanoss_settings import BomEntry, ScanossSettings
31
+ from .scanossbase import ScanossBase
32
+
33
+
34
+ def _get_match_type_message(result_path: str, bom_entry: BomEntry, action: str) -> str:
35
+ """
36
+ Compose message based on match type
37
+
38
+ Args:
39
+ result_path (str): Path of the scan result
40
+ bom_entry (BomEntry): BOM entry to compare with
41
+ action (str): Post processing action being performed
42
+
43
+ Returns:
44
+ str: The message to be printed
45
+ """
46
+ if bom_entry.get('path') and bom_entry.get('purl'):
47
+ message = f"{action} '{result_path}'. Full match found."
48
+ elif bom_entry.get('purl'):
49
+ message = f"{action} '{result_path}'. Found PURL match."
50
+ else:
51
+ message = f"{action} '{result_path}'. Found path match."
52
+ return message
53
+
54
+
55
+ def _is_full_match(result_path: str, result_purls: List[str], bom_entry: BomEntry) -> bool:
56
+ """
57
+ Check if path and purl matches fully with the bom entry
58
+
59
+ Args:
60
+ result_path (str): Scan result path
61
+ result_purls (List[str]): Scan result purls
62
+ bom_entry (BomEntry): BOM entry to compare with
63
+
64
+ Returns:
65
+ bool: True if the path and purl match, False otherwise
66
+ """
67
+ if not result_purls:
68
+ return False
69
+ return bool(
70
+ (bom_entry.get('purl') and bom_entry.get('path'))
71
+ and (bom_entry.get('path') == result_path)
72
+ and (bom_entry.get('purl') in result_purls)
73
+ )
74
+
75
+
76
+ class ScanPostProcessor(ScanossBase):
77
+ """
78
+ Handles post-processing of the scan results
79
+ """
80
+
81
+ def __init__(
82
+ self,
83
+ scan_settings: ScanossSettings,
84
+ debug: bool = False,
85
+ trace: bool = False,
86
+ quiet: bool = False,
87
+ results: dict = None,
88
+ ):
89
+ """
90
+ Args:
91
+ scan_settings (ScanossSettings): Scan settings object
92
+ debug (bool, optional): Debug mode. Defaults to False.
93
+ trace (bool, optional): Traces. Defaults to False.
94
+ quiet (bool, optional): Quiet mode. Defaults to False.
95
+ results (dict | str, optional): Results to be processed. Defaults to None.
96
+ """
97
+ super().__init__(debug, trace, quiet)
98
+ self.scan_settings = scan_settings
99
+ self.results: dict = results
100
+ self.component_info_map: dict = {}
101
+
102
+ def load_results(self, raw_results: dict):
103
+ """Load the raw results
104
+
105
+ Args:
106
+ raw_results (dict): Raw scan results
107
+ """
108
+ self.results = raw_results
109
+ self._load_component_info()
110
+ return self
111
+
112
+ def _load_component_info(self):
113
+ """Create a map of component information from scan results for faster lookup"""
114
+ if not self.results:
115
+ return
116
+ for _, result in self.results.items():
117
+ result = result[0] if isinstance(result, list) else result
118
+ purls = result.get('purl', [])
119
+ for purl in purls:
120
+ self.component_info_map[purl] = result
121
+
122
+ def post_process(self):
123
+ """
124
+ Post-process the scan results
125
+
126
+ Returns:
127
+ dict: Processed results
128
+ """
129
+ if self.scan_settings.is_legacy():
130
+ self.print_stderr(
131
+ 'Legacy settings file detected. Post-processing is not supported for legacy settings file.'
132
+ )
133
+ return self.results
134
+ self._remove_dismissed_files()
135
+ self._replace_purls()
136
+ return self.results
137
+
138
+ def _remove_dismissed_files(self):
139
+ """
140
+ Remove entries from the results based on files and/or purls specified in the SCANOSS settings file
141
+ """
142
+ to_remove_entries = self.scan_settings.get_bom_remove()
143
+ if not to_remove_entries:
144
+ return
145
+ self.results = {
146
+ result_path: result
147
+ for result_path, result in self.results.items()
148
+ if not self._should_remove_result(result_path, result, to_remove_entries)
149
+ }
150
+
151
+ def _replace_purls(self):
152
+ """
153
+ Replace purls in the results based on the SCANOSS settings file
154
+ """
155
+ to_replace_entries = self.scan_settings.get_bom_replace()
156
+ if not to_replace_entries:
157
+ return
158
+
159
+ for result_path, result in self.results.items():
160
+ result = result[0] if isinstance(result, list) else result
161
+ should_replace, to_replace_with_purl = self._should_replace_result(result_path, result, to_replace_entries)
162
+ if should_replace:
163
+ self.results[result_path] = [self._update_replaced_result(result, to_replace_with_purl)]
164
+
165
+ def _update_replaced_result(self, result: dict, to_replace_with_purl: str) -> dict:
166
+ """
167
+ Update the result with the new purl and component information if available,
168
+ otherwise removes the old component information
169
+
170
+ Args:
171
+ result (dict): The result to update
172
+ to_replace_with_purl (str): The purl to replace with
173
+
174
+ Returns:
175
+ dict: Updated result
176
+ """
177
+
178
+ if self.component_info_map.get(to_replace_with_purl):
179
+ result.update(self.component_info_map[to_replace_with_purl])
180
+ else:
181
+ try:
182
+ new_component = PackageURL.from_string(to_replace_with_purl).to_dict()
183
+ new_component_url = purl2url.get_repo_url(to_replace_with_purl)
184
+ except RuntimeError:
185
+ self.print_stderr(
186
+ f"ERROR: Issue while replacing: Invalid PURL '{to_replace_with_purl}' in settings file. Skipping."
187
+ )
188
+ return result
189
+
190
+ result['component'] = new_component.get('name')
191
+ result['url'] = new_component_url
192
+ result['vendor'] = new_component.get('namespace')
193
+
194
+ result.pop('licenses', None)
195
+ result.pop('file', None)
196
+ result.pop('file_hash', None)
197
+ result.pop('file_url', None)
198
+ result.pop('latest', None)
199
+ result.pop('release_date', None)
200
+ result.pop('source_hash', None)
201
+ result.pop('url_hash', None)
202
+ result.pop('url_stats', None)
203
+ result.pop('url_stats', None)
204
+ result.pop('version', None)
205
+
206
+ result['purl'] = [to_replace_with_purl]
207
+
208
+ return result
209
+
210
+ def _should_replace_result(
211
+ self, result_path: str, result: dict, to_replace_entries: List[BomEntry]
212
+ ) -> Tuple[bool, str]:
213
+ """
214
+ Check if a result should be replaced based on the SCANOSS settings
215
+
216
+ Args:
217
+ result_path (str): Path of the result data
218
+ result (dict): Result to check
219
+ to_replace_entries (List[BomEntry]): BOM entries to replace from the settings file
220
+
221
+ Returns:
222
+ bool: True if the result should be replaced, False otherwise
223
+ str: The purl to replace with
224
+ """
225
+ result_purls = result.get('purl', [])
226
+ for to_replace_entry in to_replace_entries:
227
+ to_replace_path = to_replace_entry.get('path')
228
+ to_replace_purl = to_replace_entry.get('purl')
229
+ to_replace_with = to_replace_entry.get('replace_with')
230
+
231
+ if not to_replace_path and not to_replace_purl or not to_replace_with:
232
+ continue
233
+ if (
234
+ _is_full_match(result_path, result_purls, to_replace_entry)
235
+ or (not to_replace_path and to_replace_purl in result_purls)
236
+ or (not to_replace_purl and to_replace_path == result_path)
237
+ ):
238
+ self._print_message(result_path, result_purls, to_replace_entry, 'Replacing')
239
+ return True, to_replace_with
240
+
241
+ return False, None
242
+
243
+ def _should_remove_result(self, result_path: str, result: dict, to_remove_entries: List[BomEntry]) -> bool:
244
+ """
245
+ Check if a result should be removed based on the SCANOSS settings
246
+
247
+ :param result_path: path of the result data
248
+ :param result: result to check
249
+ :param to_remove_entries: BOM entries to remove from the result
250
+ :return:
251
+ """
252
+ result = result[0] if isinstance(result, list) else result
253
+ result_purls = result.get('purl', [])
254
+
255
+ for to_remove_entry in to_remove_entries:
256
+ to_remove_path = to_remove_entry.get('path')
257
+ to_remove_purl = to_remove_entry.get('purl')
258
+
259
+ if not to_remove_path and not to_remove_purl:
260
+ continue
261
+ if (
262
+ _is_full_match(result_path, result_purls, to_remove_entry)
263
+ or (not to_remove_path and to_remove_purl in result_purls)
264
+ or (not to_remove_purl and to_remove_path == result_path)
265
+ ):
266
+ self._print_message(result_path, result_purls, to_remove_entry, 'Removing')
267
+ return True
268
+
269
+ return False
270
+
271
+ def _print_message(self, result_path: str, result_purls: List[str], bom_entry: BomEntry, action: str) -> None:
272
+ """
273
+ Print a message about replacing or removing a result
274
+
275
+ :param result_path:
276
+ :param result_purls:
277
+ :param bom_entry:
278
+ :param action:
279
+ :return:
280
+ """
281
+ message = (
282
+ f'{_get_match_type_message(result_path, bom_entry, action)} \n'
283
+ f'Details:\n'
284
+ f' - PURLs: {", ".join(result_purls)}\n'
285
+ f" - Path: '{result_path}'\n"
286
+ )
287
+ if action == 'Replacing':
288
+ message += f" - {action} with '{bom_entry.get('replace_with')}'"
289
+ self.print_debug(message)
290
+
291
+
292
+ #
293
+ # End of ScanPostProcessor Class
294
+ #
scanoss/scantype.py CHANGED
@@ -1,25 +1,25 @@
1
1
  """
2
- SPDX-License-Identifier: MIT
3
-
4
- Copyright (c) 2021, 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.
2
+ SPDX-License-Identifier: MIT
3
+
4
+ Copyright (c) 2021, 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
23
  """
24
24
 
25
25
  from enum import Enum
@@ -29,6 +29,7 @@ class ScanType(Enum):
29
29
  """
30
30
  Octal Enum class describing all the scanning options
31
31
  """
32
+
32
33
  SCAN_FILES = 1
33
34
  SCAN_SNIPPETS = 2
34
35
  SCAN_DEPENDENCIES = 4
@@ -0,0 +1,132 @@
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 requests
26
+
27
+ from ..scanossbase import ScanossBase
28
+
29
+ HTTP_OK = 200
30
+
31
+ class DependencyTrackService(ScanossBase):
32
+
33
+ def __init__(
34
+ self,
35
+ api_key: str,
36
+ url: str,
37
+ debug: bool = False,
38
+ trace: bool = False,
39
+ quiet: bool = False,
40
+ ):
41
+ super().__init__(debug=debug, trace=trace, quiet=quiet)
42
+ if not url:
43
+ raise ValueError("Error: Dependency Track URL is required")
44
+ self.url = url.strip().rstrip('/')
45
+ if not api_key:
46
+ raise ValueError("Error: Dependency Track API key is required")
47
+ self.api_key = api_key
48
+
49
+ def get_project_by_name_version(self, name, version):
50
+ """
51
+ Get project information by name and version from Dependency Track
52
+
53
+ Args:
54
+ name: Project name to search for
55
+ version: Project version to search for
56
+
57
+ Returns:
58
+ dict: Project data if found, None otherwise
59
+ """
60
+ if not name or not version:
61
+ self.print_stderr('Error: Missing name or version.')
62
+ return None
63
+ # Use the project search endpoint
64
+ params = {
65
+ 'name': name,
66
+ 'version': version
67
+ }
68
+ self.print_debug(f'Searching for project by: {params}')
69
+ return self.get_dep_track_data(f'{self.url}/api/v1/project/lookup', params)
70
+
71
+ def get_project_status(self, upload_token):
72
+ """
73
+ Get Dependency Track project processing status.
74
+
75
+ Queries the Dependency Track API to check if the project upload
76
+ processing is complete using the upload token.
77
+
78
+ Returns:
79
+ dict: Project status information or None if request fails
80
+ """
81
+ if not upload_token:
82
+ self.print_stderr('Error: Missing upload token. Cannot search for project status.')
83
+ return None
84
+ self.print_trace(f'URL: {self.url} Upload token: {upload_token}')
85
+ return self.get_dep_track_data(f'{self.url}/api/v1/event/token/{upload_token}')
86
+
87
+ def get_project_violations(self,project_id:str):
88
+ """
89
+ Get project violations from Dependency Track.
90
+
91
+ Waits for project processing to complete, then retrieves all policy
92
+ violations for the specified project ID.
93
+
94
+ Returns:
95
+ List of policy violations or None if the request fails
96
+ """
97
+ if not project_id:
98
+ self.print_stderr('Error: Missing project id. Cannot search for project violations.')
99
+ return None
100
+ # Return the result as-is - None indicates API failure, empty list means no violations
101
+ return self.get_dep_track_data(f'{self.url}/api/v1/violation/project/{project_id}')
102
+
103
+ def get_project_by_id(self, project_id:str):
104
+ """
105
+ Get a Dependency Track project by id.
106
+
107
+ Queries the Dependency Track API to get a project by id
108
+
109
+ Returns:
110
+ dict
111
+ """
112
+ if not project_id:
113
+ self.print_stderr('Error: Missing project id. Cannot search for project.')
114
+ return None
115
+ self.print_trace(f'URL: {self.url}, UUID: {project_id}')
116
+ return self.get_dep_track_data(f'{self.url}/api/v1/project/{project_id}')
117
+
118
+ def get_dep_track_data(self, uri, params=None):
119
+ if not uri:
120
+ self.print_stderr('Error: Missing URI. Cannot search for project.')
121
+ return None
122
+ req_headers = {'X-Api-Key': self.api_key, 'Content-Type': 'application/json'}
123
+ try:
124
+ if params:
125
+ response = requests.get(uri, headers=req_headers, params=params)
126
+ else:
127
+ response = requests.get(uri, headers=req_headers)
128
+ response.raise_for_status() # Raises an HTTPError for bad responses
129
+ return response.json()
130
+ except requests.exceptions.RequestException as e:
131
+ self.print_stderr(f"Error: Problem getting project data: {e}")
132
+ return None