@@ -54,6 +54,7 @@ def setUp(self):
5454 url = "https://example.com/ref2" , reference_type = "exploit" , reference_id = "REF-2"
5555 )
5656 self .reference2 .vulnerabilities .add (self .vuln2 )
57+
5758 self .
user = ApiUser .
objects .
create_api_user (
username = "[email protected] " )
5859 self .auth = f"Token { self .user .auth_token .key } "
5960 self .client = APIClient (enforce_csrf_checks = True )
@@ -62,13 +63,16 @@ def setUp(self):
6263 def test_list_vulnerabilities (self ):
6364 """
6465 Test listing vulnerabilities without filters.
65- Should return a list of vulnerabilities with IDs and URLs .
66+ Should return a paginated response with vulnerabilities dictionary .
6667 """
6768 url = reverse ("vulnerability-v2-list" )
6869 response = self .client .get (url , format = "json" )
6970 self .assertEqual (response .status_code , status .HTTP_200_OK )
71+ self .assertIn ("results" , response .data )
7072 self .assertIn ("vulnerabilities" , response .data ["results" ])
7173 self .assertEqual (len (response .data ["results" ]["vulnerabilities" ]), 2 )
74+ self .assertIn ("VCID-1234" , response .data ["results" ]["vulnerabilities" ])
75+ self .assertIn ("VCID-5678" , response .data ["results" ]["vulnerabilities" ])
7276 self .assertTrue ("url" in response .data ["results" ]["vulnerabilities" ]["VCID-1234" ])
7377
7478 def test_retrieve_vulnerability_detail (self ):
@@ -100,6 +104,8 @@ def test_filter_vulnerability_by_alias(self):
100104 url = reverse ("vulnerability-v2-list" )
101105 response = self .client .get (url , {"alias" : "CVE-2021-5678" }, format = "json" )
102106 self .assertEqual (response .status_code , status .HTTP_200_OK )
107+ self .assertIn ("results" , response .data )
108+ self .assertIn ("vulnerabilities" , response .data ["results" ])
103109 self .assertEqual (
104110 response .data ["results" ]["vulnerabilities" ]["VCID-5678" ]["vulnerability_id" ],
105111 "VCID-5678" ,
@@ -159,9 +165,13 @@ def test_list_vulnerabilities_pagination(self):
159165 response = self .client .get (url , format = "json" )
160166 self .assertEqual (response .status_code , status .HTTP_200_OK )
161167 self .assertIn ("results" , response .data )
168+ self .assertIn ("vulnerabilities" , response .data ["results" ])
162169 self .assertIn ("next" , response .data )
163170 self .assertIn ("previous" , response .data )
164- self .assertEqual (len (response .data ["results" ]), 1 ) # Assuming default page size is 10
171+ # The 'vulnerabilities' dictionary should contain vulnerabilities up to the page limit
172+ self .assertEqual (
173+ len (response .data ["results" ]["vulnerabilities" ]), 10
174+ ) # Assuming default page size is 10
165175
166176
167177class PackageV2ViewSetTest (APITestCase ):
@@ -185,6 +195,7 @@ def setUp(self):
185195 # Associate packages with vulnerabilities
186196 self .package1 .affected_by_vulnerabilities .add (self .vuln1 )
187197 self .package2 .fixing_vulnerabilities .add (self .vuln2 )
198+
188199 self .
user = ApiUser .
objects .
create_api_user (
username = "[email protected] " )
189200 self .auth = f"Token { self .user .auth_token .key } "
190201 self .client = APIClient (enforce_csrf_checks = True )
@@ -193,13 +204,24 @@ def setUp(self):
193204 def test_list_packages (self ):
194205 """
195206 Test listing packages without filters.
196- Should return a list of packages with their details.
207+ Should return a list of packages with their details and associated vulnerabilities .
197208 """
198209 url = reverse ("package-v2-list" )
199210 response = self .client .get (url , format = "json" )
200211 self .assertEqual (response .status_code , status .HTTP_200_OK )
212+ self .assertIn ("results" , response .data )
201213 self .assertIn ("packages" , response .data ["results" ])
214+ self .assertIn ("vulnerabilities" , response .data ["results" ])
202215 self .assertEqual (len (response .data ["results" ]["packages" ]), 2 )
216+ # Verify that vulnerabilities are included
217+ self .assertIsInstance (response .data ["results" ]["vulnerabilities" ], dict )
218+ package_vulns = set ()
219+ for package in response .data ["results" ]["packages" ]:
220+ package_vulns .update (package ["affected_by_vulnerabilities" ])
221+ package_vulns .update (package ["fixing_vulnerabilities" ])
222+ self .assertTrue (
223+ all (vuln_id in response .data ["results" ]["vulnerabilities" ] for vuln_id in package_vulns )
224+ )
203225
204226 def test_filter_packages_by_purl (self ):
205227 """
@@ -264,9 +286,13 @@ def test_list_packages_pagination(self):
264286 response = self .client .get (url , format = "json" )
265287 self .assertEqual (response .status_code , status .HTTP_200_OK )
266288 self .assertIn ("results" , response .data )
289+ self .assertIn ("packages" , response .data ["results" ])
290+ self .assertIn ("vulnerabilities" , response .data ["results" ])
267291 self .assertIn ("next" , response .data )
268292 self .assertIn ("previous" , response .data )
269- self .assertEqual (len (response .data ["results" ]), 1 ) # Assuming default page size is 10
293+ self .assertEqual (
294+ len (response .data ["results" ]["packages" ]), 10
295+ ) # Assuming default page size is 10
270296
271297 def test_invalid_vulnerability_filter (self ):
272298 """
@@ -309,16 +335,27 @@ def test_get_fixing_vulnerabilities(self):
309335 def test_bulk_lookup_with_valid_purls (self ):
310336 """
311337 Test bulk lookup with valid PURLs.
338+ Should return packages and their associated vulnerabilities.
312339 """
313340 url = reverse ("package-v2-bulk-lookup" )
314341 data = {
"purls" : [
"pkg:pypi/[email protected] " ,
"pkg:npm/[email protected] " ]}
315342 response = self .client .post (url , data , format = "json" )
316343 self .assertEqual (response .status_code , status .HTTP_200_OK )
317- self .assertEqual (len (response .data ), 2 )
344+ self .assertIn ("packages" , response .data )
345+ self .assertIn ("vulnerabilities" , response .data )
346+ self .assertEqual (len (response .data ["packages" ]), 2 )
318347 # Verify that the returned data matches the packages
319- purls = [package ["purl" ] for package in response .data ]
348+ purls = [package ["purl" ] for package in response .data [ "packages" ] ]
320349 self .
assertIn (
"pkg:pypi/[email protected] " ,
purls )
321350 self .
assertIn (
"pkg:npm/[email protected] " ,
purls )
351+ # Verify that vulnerabilities are included
352+ package_vulns = set ()
353+ for package in response .data ["packages" ]:
354+ package_vulns .update (package ["affected_by_vulnerabilities" ])
355+ package_vulns .update (package ["fixing_vulnerabilities" ])
356+ self .assertTrue (
357+ all (vuln_id in response .data ["vulnerabilities" ] for vuln_id in package_vulns )
358+ )
322359
323360 def test_bulk_lookup_with_invalid_purls (self ):
324361 """
@@ -329,7 +366,8 @@ def test_bulk_lookup_with_invalid_purls(self):
329366 response = self .client .post (url , data , format = "json" )
330367 self .assertEqual (response .status_code , status .HTTP_200_OK )
331368 # Since the packages don't exist, the response should be empty
332- self .assertEqual (len (response .data ), 0 )
369+ self .assertEqual (len (response .data ["packages" ]), 0 )
370+ self .assertEqual (len (response .data ["vulnerabilities" ]), 0 )
333371
334372 def test_bulk_lookup_with_empty_purls (self ):
335373 """
@@ -347,23 +385,37 @@ def test_bulk_lookup_with_empty_purls(self):
347385 def test_bulk_search_with_valid_purls (self ):
348386 """
349387 Test bulk search with valid PURLs.
388+ Should return packages and their associated vulnerabilities.
350389 """
351390 url = reverse ("package-v2-bulk-search" )
352391 data = {
"purls" : [
"pkg:pypi/[email protected] " ,
"pkg:npm/[email protected] " ]}
353392 response = self .client .post (url , data , format = "json" )
354393 self .assertEqual (response .status_code , status .HTTP_200_OK )
355- self .assertEqual (len (response .data ), 2 )
356- purls = [package ["purl" ] for package in response .data ]
394+ self .assertIn ("packages" , response .data )
395+ self .assertIn ("vulnerabilities" , response .data )
396+ self .assertEqual (len (response .data ["packages" ]), 2 )
397+ purls = [package ["purl" ] for package in response .data ["packages" ]]
357398 self .
assertIn (
"pkg:pypi/[email protected] " ,
purls )
358399 self .
assertIn (
"pkg:npm/[email protected] " ,
purls )
400+ # Verify that vulnerabilities are included
401+ package_vulns = set ()
402+ for package in response .data ["packages" ]:
403+ package_vulns .update (package ["affected_by_vulnerabilities" ])
404+ package_vulns .update (package ["fixing_vulnerabilities" ])
405+ self .assertTrue (
406+ all (vuln_id in response .data ["vulnerabilities" ] for vuln_id in package_vulns )
407+ )
359408
360409 def test_bulk_search_with_purl_only_true (self ):
361410 """
362411 Test bulk search with purl_only set to True.
363412 Should return only the PURLs of vulnerable packages.
364413 """
365414 url = reverse ("package-v2-bulk-search" )
366- data = {
"purls" : [
"pkg:pypi/[email protected] " ,
"pkg:npm/[email protected] " ],
"purl_only" :
True }
415+ data = {
416+ "purls" : [
"pkg:pypi/[email protected] " ,
"pkg:npm/[email protected] " ],
417+ "purl_only" : True ,
418+ }
367419 response = self .client .post (url , data , format = "json" )
368420 self .assertEqual (response .status_code , status .HTTP_200_OK )
369421 # Since purl_only=True, response should be a list of PURLs
@@ -375,15 +427,29 @@ def test_bulk_search_with_purl_only_true(self):
375427 def test_bulk_search_with_plain_purl_true (self ):
376428 """
377429 Test bulk search with plain_purl set to True.
378- """
430+ Should return packages grouped by plain PURLs.
431+ """
432+ # Create another package with the same name and version but different qualifiers
433+ Package .objects .create (
434+ name = "django" ,
435+ version = "3.2" ,
436+ type = "pypi" ,
437+ qualifiers = {"extension" : "tar.gz" },
438+ )
439+
379440 url = reverse ("package-v2-bulk-search" )
380- data = {
"purls" : [
"pkg:pypi/[email protected] " ,
"pkg:pypi/[email protected] " ],
"plain_purl" :
True }
441+ data = {
442+ "purls" : [
"pkg:pypi/[email protected] " ,
"pkg:pypi/[email protected] ?extension=tar.gz" ],
443+ "plain_purl" : True ,
444+ }
381445 response = self .client .post (url , data , format = "json" )
382446 self .assertEqual (response .status_code , status .HTTP_200_OK )
383- # Since plain_purl=True, packages with the same name and version are grouped
384- self .assertEqual (len (response .data ), 1 )
385- purls = [package ["purl" ] for package in response .data ]
386- self .
assertIn (
"pkg:pypi/[email protected] " ,
purls [
0 ]
or "pkg:pypi/[email protected] " in purls [
0 ])
447+ self .assertIn ("packages" , response .data )
448+ self .assertIn ("vulnerabilities" , response .data )
449+ # Since plain_purl=True, packages with the same type, namespace, name, version are grouped
450+ self .assertEqual (len (response .data ["packages" ]), 1 )
451+ purl = response .data ["packages" ][0 ]["purl" ]
452+ self .
assertTrue (
purl .
startswith (
"pkg:pypi/[email protected] " ))
387453
388454 def test_bulk_search_with_purl_only_and_plain_purl_true (self ):
389455 """
@@ -407,12 +473,15 @@ def test_bulk_search_with_purl_only_and_plain_purl_true(self):
407473 def test_bulk_search_with_invalid_purls (self ):
408474 """
409475 Test bulk search with invalid PURLs.
476+ Should return an empty response.
410477 """
411478 url = reverse ("package-v2-bulk-search" )
412479 data = {
"purls" : [
"pkg:pypi/[email protected] " ,
"pkg:npm/[email protected] " ]}
413480 response = self .client .post (url , data , format = "json" )
414481 self .assertEqual (response .status_code , status .HTTP_200_OK )
415- self .assertEqual (len (response .data ), 0 )
482+ # Since the packages don't exist, the response should be empty
483+ self .assertEqual (len (response .data ["packages" ]), 0 )
484+ self .assertEqual (len (response .data ["vulnerabilities" ]), 0 )
416485
417486 def test_bulk_search_with_empty_purls (self ):
418487 """
@@ -434,32 +503,39 @@ def test_all_vulnerable_packages(self):
434503 url = reverse ("package-v2-all" )
435504 response = self .client .get (url , format = "json" )
436505 self .assertEqual (response .status_code , status .HTTP_200_OK )
437- # Since package1 and package3 are vulnerable, they should be returned
506+ # Since package1 is vulnerable, it should be returned
438507 expected_purls = [
"pkg:pypi/[email protected] " ]
439508 self .assertEqual (sorted (response .data ), sorted (expected_purls ))
440509
441510 def test_lookup_with_valid_purl (self ):
442511 """
443512 Test the 'lookup' endpoint with a valid PURL.
513+ Should return the package and its associated vulnerabilities.
444514 """
445515 url = reverse ("package-v2-lookup" )
446516 data = {
"purl" :
"pkg:pypi/[email protected] " }
447517 response = self .client .post (url , data , format = "json" )
448518 self .assertEqual (response .status_code , status .HTTP_200_OK )
449- self .assertEqual (len (response .data ), 1 )
519+ self .assertEqual (1 , len (response .data ))
520+ self .assertIn ("purl" , response .data [0 ])
521+ self .assertIn ("affected_by_vulnerabilities" , response .data [0 ])
522+ self .assertIn ("fixing_vulnerabilities" , response .data [0 ])
523+ self .assertIn ("next_non_vulnerable_version" , response .data [0 ])
524+ self .assertIn ("latest_non_vulnerable_version" , response .data [0 ])
450525 self .
assertEqual (
response .
data [
0 ][
"purl" ],
"pkg:pypi/[email protected] " )
451526 self .assertEqual (response .data [0 ]["affected_by_vulnerabilities" ], ["VCID-1234" ])
527+ self .assertEqual (response .data [0 ]["fixing_vulnerabilities" ], [])
452528
453529 def test_lookup_with_invalid_purl (self ):
454530 """
455531 Test the 'lookup' endpoint with a PURL that does not exist.
456- Should return an empty list .
532+ Should return empty packages and vulnerabilities .
457533 """
458534 url = reverse ("package-v2-lookup" )
459535 data = {
"purl" :
"pkg:pypi/[email protected] " }
460536 response = self .client .post (url , data , format = "json" )
461537 self .assertEqual (response .status_code , status .HTTP_200_OK )
462- # No packages should be returned
538+ # No packages or vulnerabilities should be returned
463539 self .assertEqual (len (response .data ), 0 )
464540
465541 def test_lookup_with_missing_purl (self ):
@@ -478,9 +554,11 @@ def test_lookup_with_missing_purl(self):
478554 def test_lookup_with_invalid_purl_format (self ):
479555 """
480556 Test the 'lookup' endpoint with an invalid PURL format.
481- Should return 400 Bad Request .
557+ Should return empty packages and vulnerabilities .
482558 """
483559 url = reverse ("package-v2-lookup" )
484560 data = {"purl" : "invalid_purl_format" }
485561 response = self .client .post (url , data , format = "json" )
486562 self .assertEqual (response .status_code , status .HTTP_200_OK )
563+ # No packages or vulnerabilities should be returned
564+ self .assertEqual (len (response .data ), 0 )
0 commit comments