@@ -118,32 +118,34 @@ def get_vulnerabilities_package_name_batch(packages: list) -> list:
118
118
return results
119
119
120
120
@staticmethod
121
- def call_osv_query_api ( query_data : dict ) -> list :
122
- """Query the OSV (Open Source Vulnerability) knowledge base API with the given data .
121
+ def get_osv_url ( endpoint : str ) -> str :
122
+ """Construct a full API URL for a given OSV endpoint using values from the .ini configuration .
123
123
124
- This method sends a POST request to the OSV API and processes the response to extract
125
- information about vulnerabilities based on the provided query data.
124
+ The configuration is expected to be in a section named `[osv_dev]` within the defaults object,
125
+ and must include the following keys:
126
+
127
+ - `url_netloc`: The base domain of the API.
128
+ - `url_scheme` (optional): The scheme (e.g., "https"). Defaults to "https" if not provided.
129
+ - A key matching the provided `endpoint` argument (e.g., "query_endpoint"), which defines the URL path.
126
130
127
131
Parameters
128
132
----------
129
- query_data : dict
130
- A dictionary containing the query parameters to be sent to the OSV API.
131
- The query data should conform to the format expected by the OSV API for querying vulnerabilities.
133
+ endpoint: str
134
+ The key name of the endpoint in the `[osv_dev]` section to construct the URL path.
132
135
133
136
Returns
134
137
-------
135
- list
136
- A list of vulnerabilities under the key "vulns" if the query is successful
137
- and the response is valid.
138
+ str
139
+ The fully constructed API URL.
138
140
139
141
Raises
140
142
------
141
143
APIAccessError
142
- If there are issues with the API URL construction, missing configuration values, or invalid responses .
144
+ If required keys are missing from the configuration or if the URL cannot be constructed .
143
145
"""
144
146
section_name = "osv_dev"
145
147
if not defaults .has_section (section_name ):
146
- return []
148
+ raise APIAccessError ( f"The section [ { section_name } ] is missing in the .ini configuration file." )
147
149
section = defaults [section_name ]
148
150
149
151
url_netloc = section .get ("url_netloc" )
@@ -152,13 +154,13 @@ def call_osv_query_api(query_data: dict) -> list:
152
154
f'The "url_netloc" key is missing in section [{ section_name } ] of the .ini configuration file.'
153
155
)
154
156
url_scheme = section .get ("url_scheme" , "https" )
155
- query_endpoint = section .get ("query_endpoint" )
157
+ query_endpoint = section .get (endpoint )
156
158
if not query_endpoint :
157
159
raise APIAccessError (
158
160
f'The "query_endpoint" key is missing in section [{ section_name } ] of the .ini configuration file.'
159
161
)
160
162
try :
161
- url = urllib .parse .urlunsplit (
163
+ return urllib .parse .urlunsplit (
162
164
urllib .parse .SplitResult (
163
165
scheme = url_scheme ,
164
166
netloc = url_netloc ,
@@ -170,6 +172,34 @@ def call_osv_query_api(query_data: dict) -> list:
170
172
except ValueError as error :
171
173
raise APIAccessError ("Failed to construct the API URL." ) from error
172
174
175
+ @staticmethod
176
+ def call_osv_query_api (query_data : dict ) -> list :
177
+ """Query the OSV (Open Source Vulnerability) knowledge base API with the given data.
178
+
179
+ This method sends a POST request to the OSV API and processes the response to extract
180
+ information about vulnerabilities based on the provided query data.
181
+
182
+ Parameters
183
+ ----------
184
+ query_data : dict
185
+ A dictionary containing the query parameters to be sent to the OSV API.
186
+ The query data should conform to the format expected by the OSV API for querying vulnerabilities.
187
+
188
+ Returns
189
+ -------
190
+ list
191
+ A list of vulnerabilities under the key "vulns" if the query is successful
192
+ and the response is valid.
193
+
194
+ Raises
195
+ ------
196
+ APIAccessError
197
+ If there are issues with the API URL construction, missing configuration values, or invalid responses.
198
+ """
199
+ try :
200
+ url = OSVDevService .get_osv_url ("query_endpoint" )
201
+ except APIAccessError as error :
202
+ raise error
173
203
response = send_post_http_raw (url , json_data = query_data , headers = None )
174
204
res_obj = None
175
205
if response :
@@ -209,8 +239,7 @@ def call_osv_querybatch_api(query_data: dict, expected_size: int | None = None)
209
239
-------
210
240
list
211
241
A list of results from the OSV API containing the vulnerability data that matches
212
- the query parameters. If no valid response is received or the results are
213
- improperly formatted, an empty list is returned.
242
+ the query parameters.
214
243
215
244
Raises
216
245
------
@@ -219,34 +248,10 @@ def call_osv_querybatch_api(query_data: dict, expected_size: int | None = None)
219
248
fails, or if the response from the OSV API is invalid or the number of results
220
249
does not match the expected size.
221
250
"""
222
- section_name = "osv_dev"
223
- if not defaults .has_section (section_name ):
224
- return []
225
- section = defaults [section_name ]
226
-
227
- url_netloc = section .get ("url_netloc" )
228
- if not url_netloc :
229
- raise APIAccessError (
230
- f'The "url_netloc" key is missing in section [{ section_name } ] of the .ini configuration file.'
231
- )
232
- url_scheme = section .get ("url_scheme" , "https" )
233
- query_endpoint = section .get ("querybatch_endpoint" )
234
- if not query_endpoint :
235
- raise APIAccessError (
236
- f'The "query_endpoint" key is missing in section [{ section_name } ] of the .ini configuration file.'
237
- )
238
251
try :
239
- url = urllib .parse .urlunsplit (
240
- urllib .parse .SplitResult (
241
- scheme = url_scheme ,
242
- netloc = url_netloc ,
243
- path = query_endpoint ,
244
- query = "" ,
245
- fragment = "" ,
246
- )
247
- )
248
- except ValueError as error :
249
- raise APIAccessError ("Failed to construct the API URL." ) from error
252
+ url = OSVDevService .get_osv_url ("querybatch_endpoint" )
253
+ except APIAccessError as error :
254
+ raise error
250
255
251
256
response = send_post_http_raw (url , json_data = query_data , headers = None )
252
257
res_obj = None
@@ -261,11 +266,13 @@ def call_osv_querybatch_api(query_data: dict, expected_size: int | None = None)
261
266
if isinstance (results , list ):
262
267
if expected_size :
263
268
if len (results ) != expected_size :
264
- raise APIAccessError (f"Unable to get a valid result from { url } " )
269
+ raise APIAccessError (
270
+ f"Failed to retrieve a valid result from { url } : result count does not match the expected count."
271
+ )
265
272
266
273
return results
267
274
268
- return []
275
+ raise APIAccessError ( f"The response from { url } does not contain a valid 'results' list." )
269
276
270
277
@staticmethod
271
278
def is_version_affected (
@@ -326,9 +333,13 @@ def is_version_affected(
326
333
pkg_version = tag
327
334
break
328
335
336
+ # If we were not able to find a tag for the commit hash, raise an exception.
337
+ if is_commit_hash (pkg_version ):
338
+ raise APIAccessError (f"Failed to find a tag for { pkg_name } @{ pkg_version } ." )
339
+
329
340
affected = json_extract (vuln , ["affected" ], list )
330
341
if not affected :
331
- raise APIAccessError (f"Failed to extracted info for { pkg_name } @{ pkg_version } ." )
342
+ raise APIAccessError (f"Received invalid response for { pkg_name } @{ pkg_version } ." )
332
343
333
344
affected_ranges : list | None = None
334
345
for rec in affected :
@@ -342,12 +353,12 @@ def is_version_affected(
342
353
break
343
354
344
355
if not affected_ranges :
345
- raise APIAccessError (f"Failed to extracted affected versions for { pkg_name } @{ pkg_version } ." )
356
+ raise APIAccessError (f"Failed to extract affected versions for { pkg_name } @{ pkg_version } ." )
346
357
347
358
for affected_range in affected_ranges :
348
359
events = json_extract (affected_range , ["events" ], list )
349
360
if not events :
350
- raise APIAccessError (f"Failed to extracted affected versions for { pkg_name } @{ pkg_version } ." )
361
+ raise APIAccessError (f"Failed to extract affected versions for { pkg_name } @{ pkg_version } ." )
351
362
352
363
introduced = None
353
364
fixed = None
0 commit comments