Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions scanpipe/pipes/vulnerablecode.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ def fetch_vulnerabilities(

for purls_batch in chunked(get_purls(packages), chunk_size):
response_data = bulk_search_by_purl(purls_batch)
if not response_data:
logger(
f"VulnerableCode did not return data for a batch of "
f"{len(purls_batch)} PURLs. Skipping batch."
)
continue
for vulnerability_data in response_data:
vulnerabilities_by_purl[vulnerability_data["purl"]] = vulnerability_data

Expand Down
18 changes: 18 additions & 0 deletions scanpipe/tests/pipes/test_vulnerablecode.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,24 @@ def test_scanpipe_pipes_vulnerablecode_fetch_vulnerabilities(
django_5_0.refresh_from_db()
self.assertEqual(1, len(django_5_0.affected_by_vulnerabilities))

@mock.patch("scanpipe.pipes.vulnerablecode.bulk_search_by_purl")
def test_fetch_vulnerabilities_handles_none_response(self, mock_search_by_purl):
"""
fetch_vulnerabilities must not crash when bulk_search_by_purl returns None.

This happens when VulnerableCode is unreachable or returns an error,
causing request_post() to return None implicitly.
"""
package = make_package(self.project1, "pkg:pypi/requests@2.31.0")
mock_search_by_purl.return_value = None

buffer = io.StringIO()
fetch_vulnerabilities(packages=[package], logger=buffer.write)

package.refresh_from_db()
self.assertEqual([], package.affected_by_vulnerabilities)
self.assertIn("VulnerableCode did not return data", buffer.getvalue())

def test_scanpipe_pipes_vulnerablecode_filter_vulnerabilities(self):
data = self.data / "vulnerablecode/django-5.0_package_data.json"
package_data = json.loads(data.read_text())
Expand Down