From 6df203d6a04d8ddfce76acdfc6bdcbf911d02e36 Mon Sep 17 00:00:00 2001 From: shivamshrma09 Date: Thu, 5 Feb 2026 15:48:57 +0530 Subject: [PATCH 1/2] fix #1754: newest-first sorting for packages + vulnerabilities - packages: version_rank desc - vulns: vulnerability_id desc - advisories: advisory_id desc --- vulnerabilities/views.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/vulnerabilities/views.py b/vulnerabilities/views.py index 8a867983e..ec8d3cc69 100644 --- a/vulnerabilities/views.py +++ b/vulnerabilities/views.py @@ -64,13 +64,14 @@ def get_queryset(self, query=None): Return a Package queryset for the ``query``. Make a best effort approach to find matching packages either based on exact purl, partial purl or just name and namespace. + Results are sorted by version rank (newest first). """ query = query or self.request.GET.get("search") or "" return ( self.model.objects.search(query) .with_vulnerability_counts() .prefetch_related() - .order_by("package_url") + .order_by("-version_rank", "package_url") ) @@ -92,13 +93,14 @@ def get_queryset(self, query=None): Return a Package queryset for the ``query``. Make a best effort approach to find matching packages either based on exact purl, partial purl or just name and namespace. + Results are sorted by version rank (newest first). """ query = query or self.request.GET.get("search") or "" return ( self.model.objects.search(query) .with_vulnerability_counts() .prefetch_related() - .order_by("package_url") + .order_by("-version_rank", "package_url") ) @@ -117,7 +119,7 @@ def get_context_data(self, **kwargs): def get_queryset(self, query=None): query = query or self.request.GET.get("search") or "" - return self.model.objects.search(query=query).with_package_counts() + return self.model.objects.search(query=query).with_package_counts().order_by("-vulnerability_id") class AdvisorySearch(ListView): @@ -135,7 +137,7 @@ def get_context_data(self, **kwargs): def get_queryset(self, query=None): query = query or self.request.GET.get("search") or "" - return self.model.objects.search(query=query).with_package_counts() + return self.model.objects.search(query=query).with_package_counts().order_by("-advisory_id") class PackageDetails(DetailView): From 93a14a508af09476f48e1c1e3b7198787e79557f Mon Sep 17 00:00:00 2001 From: shivamshrma09 Date: Sat, 28 Mar 2026 11:44:11 +0530 Subject: [PATCH 2/2] fix: skip withdrawn OSV advisories in parse_advisory_data_v3 Withdrawn advisories were being imported by the v2 pipeline because parse_advisory_data_v3 in pipes/osv_v2.py had no withdrawn check. This caused packages like pkg:pypi/py@1.11.0 to appear as affected even though the advisory (e.g. GHSA-w596-4wvx-j9j6) was withdrawn. Add a withdrawn check at the top of parse_advisory_data_v3, consistent with the existing check in parse_advisory_data (v1). This fix covers all 5 pipelines that use parse_advisory_data_v3: github_osv_importer, oss_fuzz, pypa_importer, pysec_importer, ubuntu_osv_importer. Add tests for both withdrawn and non-withdrawn cases in test_osv_v2.py. Fixes #2238 Signed-off-by: shivamshrma09 --- vulnerabilities/importers/github_osv.py | 4 +- vulnerabilities/importers/osv.py | 4 ++ vulnerabilities/pipes/osv_v2.py | 3 ++ vulnerabilities/tests/pipes/test_osv_v2.py | 45 ++++++++++++++++++++++ vulnerabilities/tests/test_osv.py | 42 ++++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) diff --git a/vulnerabilities/importers/github_osv.py b/vulnerabilities/importers/github_osv.py index f0490044e..030e4ef17 100644 --- a/vulnerabilities/importers/github_osv.py +++ b/vulnerabilities/importers/github_osv.py @@ -50,7 +50,9 @@ def advisory_data(self) -> Iterable[AdvisoryData]: ) with open(file) as f: raw_data = json.load(f) - yield parse_advisory_data(raw_data, supported_ecosystems, advisory_url) + advisory = parse_advisory_data(raw_data, supported_ecosystems, advisory_url) + if advisory: + yield advisory finally: if self.vcs_response: self.vcs_response.delete() diff --git a/vulnerabilities/importers/osv.py b/vulnerabilities/importers/osv.py index 3658c57b3..d30290880 100644 --- a/vulnerabilities/importers/osv.py +++ b/vulnerabilities/importers/osv.py @@ -54,7 +54,11 @@ def parse_advisory_data( """ Return an AdvisoryData build from a ``raw_data`` mapping of OSV advisory and a ``supported_ecosystem`` string. + Return None if the advisory has been withdrawn. """ + if raw_data.get("withdrawn"): + logger.info(f"Skipping withdrawn advisory: {raw_data.get('id')!r}") + return None raw_id = raw_data.get("id") or "" summary = raw_data.get("summary") or "" details = raw_data.get("details") or "" diff --git a/vulnerabilities/pipes/osv_v2.py b/vulnerabilities/pipes/osv_v2.py index e70ba4a4a..ed16f1f1e 100644 --- a/vulnerabilities/pipes/osv_v2.py +++ b/vulnerabilities/pipes/osv_v2.py @@ -65,6 +65,9 @@ def parse_advisory_data_v3( Return an AdvisoryData build from a ``raw_data`` mapping of OSV advisory and a ``supported_ecosystem`` string. """ + if raw_data.get("withdrawn"): + logger.info(f"Skipping withdrawn advisory: {raw_data.get('id')!r}") + return None advisory_id = raw_data.get("id") or "" if not advisory_id: logger.error(f"Missing advisory id in OSV data: {raw_data}") diff --git a/vulnerabilities/tests/pipes/test_osv_v2.py b/vulnerabilities/tests/pipes/test_osv_v2.py index 50f865697..b191cdf57 100644 --- a/vulnerabilities/tests/pipes/test_osv_v2.py +++ b/vulnerabilities/tests/pipes/test_osv_v2.py @@ -23,6 +23,51 @@ from vulnerabilities.pipes.osv_v2 import parse_advisory_data_v3 from vulnerabilities.tests import util_tests + +def test_parse_advisory_data_v3_withdrawn_returns_none(): + raw_data = { + "id": "GHSA-w596-4wvx-j9j6", + "published": "2022-10-16T12:00:23Z", + "withdrawn": "2025-08-01T20:34:11Z", + "aliases": ["CVE-2022-42969"], + "summary": "Withdrawn Advisory: ReDoS in py library", + "affected": [ + { + "package": {"ecosystem": "PyPI", "name": "py"}, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [{"introduced": "0"}, {"last_affected": "1.11.0"}], + } + ], + } + ], + } + result = parse_advisory_data_v3( + raw_data, + supported_ecosystems=["pypi"], + advisory_url="https://github.com/github/advisory-database/blob/main/advisories/GHSA-w596-4wvx-j9j6.json", + advisory_text="", + ) + assert result is None + + +def test_parse_advisory_data_v3_not_withdrawn_returns_advisory(): + raw_data = { + "id": "GHSA-j3f7-7rmc-6wqj", + "published": "2022-01-10T14:12:00Z", + "aliases": ["CVE-2022-0001"], + "summary": "Some valid advisory", + "affected": [], + } + result = parse_advisory_data_v3( + raw_data, + supported_ecosystems=["pypi"], + advisory_url="https://github.com/github/advisory-database/blob/main/advisories/GHSA-j3f7-7rmc-6wqj.json", + advisory_text="", + ) + assert result is not None + TEST_DATA = Path(__file__).parent.parent / "test_data" / "osv_test" diff --git a/vulnerabilities/tests/test_osv.py b/vulnerabilities/tests/test_osv.py index e7505510c..87c7dbf24 100644 --- a/vulnerabilities/tests/test_osv.py +++ b/vulnerabilities/tests/test_osv.py @@ -24,6 +24,7 @@ from vulnerabilities.importers.osv import get_published_date from vulnerabilities.importers.osv import get_references from vulnerabilities.importers.osv import get_severities +from vulnerabilities.importers.osv import parse_advisory_data from vulnerabilities.severity_systems import SCORING_SYSTEMS @@ -397,3 +398,44 @@ def test_get_fixed_versions4(self): ) assert results == [SemverVersion("6.5.4")] + + def test_parse_advisory_data_withdrawn_returns_none(self): + raw_data = { + "id": "GHSA-w596-4wvx-j9j6", + "published": "2022-10-16T12:00:23Z", + "withdrawn": "2025-08-01T20:34:11Z", + "aliases": ["CVE-2022-42969"], + "summary": "Withdrawn Advisory: ReDoS in py library", + "affected": [ + { + "package": {"ecosystem": "PyPI", "name": "py"}, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [{"introduced": "0"}, {"last_affected": "1.11.0"}], + } + ], + } + ], + } + result = parse_advisory_data( + raw_data, + supported_ecosystems=["pypi"], + advisory_url="https://github.com/github/advisory-database/blob/main/advisories/GHSA-w596-4wvx-j9j6.json", + ) + assert result is None + + def test_parse_advisory_data_not_withdrawn_returns_advisory(self): + raw_data = { + "id": "GHSA-j3f7-7rmc-6wqj", + "published": "2022-01-10T14:12:00Z", + "aliases": ["CVE-2022-0001"], + "summary": "Some valid advisory", + "affected": [], + } + result = parse_advisory_data( + raw_data, + supported_ecosystems=["pypi"], + advisory_url="https://github.com/github/advisory-database/blob/main/advisories/GHSA-j3f7-7rmc-6wqj.json", + ) + assert result is not None