From 391141c1f6be8d40b86624e5b7a1a47fea67a97c Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Mon, 16 Feb 2026 17:53:24 +0530 Subject: [PATCH] Group related advisories on basis of content Signed-off-by: Tushar Goel --- vulnerabilities/api_v2.py | 65 ++- .../migrations/0113_advisoryv2_precedence.py | 22 + vulnerabilities/models.py | 62 ++- vulnerabilities/pipelines/__init__.py | 2 + .../v2_importers/alpine_linux_importer.py | 2 + .../pipelines/v2_importers/aosp_importer.py | 2 + .../v2_importers/apache_httpd_importer.py | 2 + .../v2_importers/apache_kafka_importer.py | 2 + .../v2_importers/apache_tomcat_importer.py | 2 + .../v2_importers/archlinux_importer.py | 2 + .../pipelines/v2_importers/curl_importer.py | 2 + .../pipelines/v2_importers/debian_importer.py | 2 + .../v2_importers/elixir_security_importer.py | 2 + .../v2_importers/epss_importer_v2.py | 2 + .../v2_importers/fireeye_importer_v2.py | 2 + .../v2_importers/github_osv_importer.py | 2 + .../pipelines/v2_importers/gitlab_importer.py | 2 + .../pipelines/v2_importers/istio_importer.py | 2 + .../v2_importers/mattermost_importer.py | 2 + .../v2_importers/mozilla_importer.py | 2 + .../pipelines/v2_importers/nginx_importer.py | 2 + .../pipelines/v2_importers/npm_importer.py | 2 + .../pipelines/v2_importers/nvd_importer.py | 2 + .../v2_importers/openssl_importer.py | 2 + .../pipelines/v2_importers/oss_fuzz.py | 2 + .../v2_importers/postgresql_importer.py | 2 + .../project_kb_msr2019_importer.py | 2 + .../project_kb_statements_importer.py | 2 + .../pipelines/v2_importers/pysec_importer.py | 1 + .../pipelines/v2_importers/redhat_importer.py | 2 + .../v2_importers/retiredotnet_importer.py | 2 + .../pipelines/v2_importers/ruby_importer.py | 2 + .../v2_importers/ubuntu_osv_importer.py | 2 + .../v2_importers/vulnrichment_importer.py | 2 + .../pipelines/v2_importers/xen_importer.py | 2 + vulnerabilities/pipes/advisory.py | 2 + .../templates/package_details_v2.html | 50 +- .../v2_importers/test_redhat_importer_v2.py | 5 +- vulnerabilities/tests/test_api_v2.py | 22 +- .../openssl_advisoryv2-expected.json | 34 +- .../redhat/redhat_advisoryv2-expected.json | 522 +++++++++--------- .../ubuntu_osv_advisoryv2-expected.json | 56 +- vulnerabilities/utils.py | 37 +- vulnerabilities/views.py | 56 +- 44 files changed, 616 insertions(+), 380 deletions(-) create mode 100644 vulnerabilities/migrations/0113_advisoryv2_precedence.py diff --git a/vulnerabilities/api_v2.py b/vulnerabilities/api_v2.py index f10d4a8ab..74975b819 100644 --- a/vulnerabilities/api_v2.py +++ b/vulnerabilities/api_v2.py @@ -41,6 +41,7 @@ from vulnerabilities.models import VulnerabilitySeverity from vulnerabilities.models import Weakness from vulnerabilities.throttling import PermissionBasedUserRateThrottle +from vulnerabilities.utils import group_advisories_by_content class CharInFilter(filters.BaseInFilter, filters.CharFilter): @@ -361,19 +362,39 @@ def get_affected_by_vulnerabilities(self, package): latest_advisories = AdvisoryV2.objects.latest_for_avids(avids) advisory_by_avid = {adv.avid: adv for adv in latest_advisories} + impact_by_avid = {} - result = {} - + advisories = [] for impact in impacts: avid = impact.advisory.avid advisory = advisory_by_avid.get(avid) if not advisory: continue - fixed_by_packages = [pkg.purl for pkg in impact.fixed_by_packages.all()] - result[advisory.avid] = { - "advisory_id": advisory.avid, - "fixed_by_packages": fixed_by_packages, - } + advisories.append(advisory) + impact_by_avid[avid] = impact + + grouped_advisories = group_advisories_by_content(advisories=advisories) + + advs = [] + + for hash in grouped_advisories: + advs.append(grouped_advisories[hash]) + + result = [] + + for advisory in advs: + primary_advisory = advisory["primary"] + avid = primary_advisory.avid + impact = impact_by_avid.get(avid) + if not impact: + continue + result.append( + { + "advisory_id": primary_advisory.avid, + "fixed_by_packages": [pkg.purl for pkg in impact.fixed_by_packages.all()], + "duplicate_advisory_ids": [adv.avid for adv in advisory["secondary"]], + } + ) return result @@ -384,7 +405,25 @@ def get_fixing_vulnerabilities(self, package): latest_advisories = AdvisoryV2.objects.latest_for_avids(avids) - return [adv.avid for adv in latest_advisories] + grouped_advisories = group_advisories_by_content(advisories=latest_advisories) + + advs = [] + + for hash in grouped_advisories: + advs.append(grouped_advisories[hash]) + + result = [] + + for advisory in advs: + primary_advisory = advisory["primary"] + result.append( + { + "advisory_id": primary_advisory.avid, + "duplicate_advisory_ids": [adv.avid for adv in advisory["secondary"]], + } + ) + + return result def get_next_non_vulnerable_version(self, package): if next_non_vulnerable := package.get_non_vulnerable_versions()[0]: @@ -1078,14 +1117,14 @@ def list(self, request, *args, **kwargs): return self.get_paginated_response( { "packages": serializer.data, - "advisories": advisory_data, + "advisories_by_id": advisory_data, } ) return Response( { "packages": serializer.data, - "advisories": advisory_data, + "advisories_by_id": advisory_data, } ) @@ -1160,7 +1199,7 @@ def bulk_lookup(self, request): return Response( { "packages": package_data, - "advisories": advisory_data, + "advisories_by_id": advisory_data, } ) @@ -1254,7 +1293,7 @@ def bulk_search(self, request): return Response( { "packages": package_data, - "advisories": advisory_data, + "advisories_by_id": advisory_data, } ) @@ -1308,7 +1347,7 @@ def bulk_search(self, request): return Response( { "packages": package_data, - "advisories": advisory_data, + "advisories_by_id": advisory_data, } ) diff --git a/vulnerabilities/migrations/0113_advisoryv2_precedence.py b/vulnerabilities/migrations/0113_advisoryv2_precedence.py new file mode 100644 index 000000000..84c34b0ab --- /dev/null +++ b/vulnerabilities/migrations/0113_advisoryv2_precedence.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.25 on 2026-02-10 12:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("vulnerabilities", "0112_alter_advisoryseverity_scoring_system_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="advisoryv2", + name="precedence", + field=models.IntegerField( + blank=True, + help_text="Precedence indicates the priority of advisory from different datasources. It is determined based on the reliability of the datasource and how close it is to the source.", + null=True, + ), + ), + ] diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index e64599c96..6e3664034 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -10,6 +10,7 @@ import csv import datetime import hashlib +import json import logging import uuid import xml.etree.ElementTree as ET @@ -69,7 +70,9 @@ from vulnerabilities.severity_systems import EPSS from vulnerabilities.severity_systems import SCORING_SYSTEMS from vulnerabilities.utils import compute_patch_checksum +from vulnerabilities.utils import normalize_list from vulnerabilities.utils import normalize_purl +from vulnerabilities.utils import normalize_text from vulnerabilities.utils import purl_to_dict from vulnerablecode import __version__ as VULNERABLECODE_VERSION from vulnerablecode.settings import VULNERABLECODE_PIPELINE_TIMEOUT @@ -2988,11 +2991,11 @@ class AdvisoryV2(models.Model): help_text="Weighted severity is the highest value calculated by multiplying each severity by its corresponding weight, divided by 10.", ) - # precedence = models.IntegerField( - # null=True, - # blank=True, - # help_text="Precedence indicates the priority level of addressing a vulnerability based on its overall risk", - # ) + precedence = models.IntegerField( + null=True, + blank=True, + help_text="Precedence indicates the priority of advisory from different datasources. It is determined based on the reliability of the datasource and how close it is to the source.", + ) @property def risk_score(self): @@ -3038,16 +3041,20 @@ def to_advisory_data(self) -> "AdvisoryDataV2": return AdvisoryDataV2( advisory_id=self.advisory_id, - aliases=[item.alias for item in self.aliases.all()], + aliases=normalize_list([item.alias for item in self.aliases.all()]), summary=self.summary, - affected_packages=[ - impacted.to_affected_package_data() for impacted in self.impacted_packages.all() - ], - references=[ref.to_reference_v2_data() for ref in self.references.all()], - patches=[patch.to_patch_data() for patch in self.patches.all()], + affected_packages=normalize_list( + [impacted.to_affected_package_data() for impacted in self.impacted_packages.all()] + ), + references=normalize_list( + [ref.to_reference_v2_data() for ref in self.references.all()] + ), + patches=normalize_list([patch.to_patch_data() for patch in self.patches.all()]), date_published=self.date_published, - weaknesses=[weak.cwe_id for weak in self.weaknesses.all()], - severities=[sev.to_vulnerability_severity_data() for sev in self.severities.all()], + weaknesses=normalize_list([weak.cwe_id for weak in self.weaknesses.all()]), + severities=normalize_list( + [sev.to_vulnerability_severity_data() for sev in self.severities.all()] + ), url=self.url, ) @@ -3058,6 +3065,35 @@ def get_aliases(self): """ return self.aliases.all() + def compute_advisory_content(self): + """ + Compute a unique content hash for an advisory by normalizing its data and hashing it. + + :param advisory: An Advisory object + :return: SHA-256 hash digest as content hash + """ + normalized_data = { + "summary": normalize_text(self.summary), + "impacted_packages": sorted( + [impact.to_dict() for impact in self.impacted_packages.all()], + key=lambda x: json.dumps(x, sort_keys=True), + ), + "patches": sorted( + [patch.to_patch_data().to_dict() for patch in self.patches.all()], + key=lambda x: json.dumps(x, sort_keys=True), + ), + "severities": sorted( + [sev.to_vulnerability_severity_data().to_dict() for sev in self.severities.all()], + key=lambda x: (x.get("system"), x.get("value")), + ), + "weaknesses": normalize_list([weakness.cwe_id for weakness in self.weaknesses.all()]), + } + + normalized_json = json.dumps(normalized_data, separators=(",", ":"), sort_keys=True) + content_hash = hashlib.sha256(normalized_json.encode("utf-8")).hexdigest() + + return content_hash + alias = get_aliases diff --git a/vulnerabilities/pipelines/__init__.py b/vulnerabilities/pipelines/__init__.py index fc784e019..e563846fe 100644 --- a/vulnerabilities/pipelines/__init__.py +++ b/vulnerabilities/pipelines/__init__.py @@ -265,6 +265,7 @@ class VulnerableCodeBaseImporterPipelineV2(VulnerableCodePipeline): spdx_license_expression = None repo_url = None ignorable_versions = [] + precedence = 0 # Control how often progress log is shown (range: 1–100, higher value = less frequent log) progress_step = 10 @@ -318,6 +319,7 @@ def collect_and_store_advisories(self): advisory=advisory, pipeline_id=self.pipeline_id, logger=self.log, + precedence=self.precedence, ): collected_advisory_count += 1 except Exception as e: diff --git a/vulnerabilities/pipelines/v2_importers/alpine_linux_importer.py b/vulnerabilities/pipelines/v2_importers/alpine_linux_importer.py index 6e7b1ef0a..642a37435 100644 --- a/vulnerabilities/pipelines/v2_importers/alpine_linux_importer.py +++ b/vulnerabilities/pipelines/v2_importers/alpine_linux_importer.py @@ -38,6 +38,8 @@ class AlpineLinuxImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://secdb.alpinelinux.org/license.txt" repo_url = "git+https://github.com/aboutcode-org/aboutcode-mirror-alpine-secdb/" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/aosp_importer.py b/vulnerabilities/pipelines/v2_importers/aosp_importer.py index ac4ef4ed6..23bcda86f 100644 --- a/vulnerabilities/pipelines/v2_importers/aosp_importer.py +++ b/vulnerabilities/pipelines/v2_importers/aosp_importer.py @@ -31,6 +31,8 @@ class AospImporterPipeline(VulnerableCodeBaseImporterPipelineV2): spdx_license_expression = "Apache-2.0" license_url = "https://github.com/quarkslab/aosp_dataset/blob/master/LICENSE" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/apache_httpd_importer.py b/vulnerabilities/pipelines/v2_importers/apache_httpd_importer.py index 85856a794..51e4b5e77 100644 --- a/vulnerabilities/pipelines/v2_importers/apache_httpd_importer.py +++ b/vulnerabilities/pipelines/v2_importers/apache_httpd_importer.py @@ -152,6 +152,8 @@ class ApacheHTTPDImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://www.apache.org/licenses/LICENSE-2.0" base_url = "https://httpd.apache.org/security/json/" + precedence = 200 + links = [] ignorable_versions = frozenset( diff --git a/vulnerabilities/pipelines/v2_importers/apache_kafka_importer.py b/vulnerabilities/pipelines/v2_importers/apache_kafka_importer.py index feec010c3..d05ed757d 100644 --- a/vulnerabilities/pipelines/v2_importers/apache_kafka_importer.py +++ b/vulnerabilities/pipelines/v2_importers/apache_kafka_importer.py @@ -48,6 +48,8 @@ class ApacheKafkaImporterPipeline(VulnerableCodeBaseImporterPipelineV2): "CVE-2021-4104", ] + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/apache_tomcat_importer.py b/vulnerabilities/pipelines/v2_importers/apache_tomcat_importer.py index 2b9b93b6d..f55665c56 100644 --- a/vulnerabilities/pipelines/v2_importers/apache_tomcat_importer.py +++ b/vulnerabilities/pipelines/v2_importers/apache_tomcat_importer.py @@ -40,6 +40,8 @@ class ApacheTomcatImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://www.apache.org/licenses/LICENSE-2.0" base_url = "https://tomcat.apache.org/security" + precedence = 200 + def fetch_advisory_links(self): """ Yield the URLs of each Tomcat version security-related page. diff --git a/vulnerabilities/pipelines/v2_importers/archlinux_importer.py b/vulnerabilities/pipelines/v2_importers/archlinux_importer.py index f1645a41a..4f9096f09 100644 --- a/vulnerabilities/pipelines/v2_importers/archlinux_importer.py +++ b/vulnerabilities/pipelines/v2_importers/archlinux_importer.py @@ -28,6 +28,8 @@ class ArchLinuxImporterPipeline(VulnerableCodeBaseImporterPipelineV2): spdx_license_expression = "MIT" license_url = "https://github.com/archlinux/arch-security-tracker/blob/master/LICENSE" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/curl_importer.py b/vulnerabilities/pipelines/v2_importers/curl_importer.py index 64181aac2..23e62cbb9 100644 --- a/vulnerabilities/pipelines/v2_importers/curl_importer.py +++ b/vulnerabilities/pipelines/v2_importers/curl_importer.py @@ -37,6 +37,8 @@ class CurlImporterPipeline(VulnerableCodeBaseImporterPipelineV2): repo_url = "https://github.com/curl/curl-www/" url = "https://curl.se/docs/vuln.json" + precedence = 200 + @classmethod def steps(cls): return (cls.collect_and_store_advisories,) diff --git a/vulnerabilities/pipelines/v2_importers/debian_importer.py b/vulnerabilities/pipelines/v2_importers/debian_importer.py index 778cbb1e8..bdc0770ca 100644 --- a/vulnerabilities/pipelines/v2_importers/debian_importer.py +++ b/vulnerabilities/pipelines/v2_importers/debian_importer.py @@ -76,6 +76,8 @@ class DebianImporterPipeline(VulnerableCodeBaseImporterPipelineV2): Moritz """ + precedence = 200 + api_url = "https://security-tracker.debian.org/tracker/data/json" response = None diff --git a/vulnerabilities/pipelines/v2_importers/elixir_security_importer.py b/vulnerabilities/pipelines/v2_importers/elixir_security_importer.py index d673978d3..8d7cdfc9e 100644 --- a/vulnerabilities/pipelines/v2_importers/elixir_security_importer.py +++ b/vulnerabilities/pipelines/v2_importers/elixir_security_importer.py @@ -36,6 +36,8 @@ class ElixirSecurityImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/dependabot/elixir-security-advisories/blob/master/LICENSE.txt" repo_url = "git+https://github.com/dependabot/elixir-security-advisories" + precedence = 200 + @classmethod def steps(cls): return (cls.clone, cls.collect_and_store_advisories, cls.clean_downloads) diff --git a/vulnerabilities/pipelines/v2_importers/epss_importer_v2.py b/vulnerabilities/pipelines/v2_importers/epss_importer_v2.py index 4eb660725..e1d6f6eaf 100644 --- a/vulnerabilities/pipelines/v2_importers/epss_importer_v2.py +++ b/vulnerabilities/pipelines/v2_importers/epss_importer_v2.py @@ -30,6 +30,8 @@ class EPSSImporterPipeline(VulnerableCodeBaseImporterPipelineV2): spdx_license_expression = "unknown" importer_name = "EPSS Importer" + precedence = 200 + def advisories_count(self): return len(self.lines) diff --git a/vulnerabilities/pipelines/v2_importers/fireeye_importer_v2.py b/vulnerabilities/pipelines/v2_importers/fireeye_importer_v2.py index 1a2c91e39..89c3875ed 100644 --- a/vulnerabilities/pipelines/v2_importers/fireeye_importer_v2.py +++ b/vulnerabilities/pipelines/v2_importers/fireeye_importer_v2.py @@ -40,6 +40,8 @@ class FireeyeImporterPipeline(VulnerableCodeBaseImporterPipelineV2): repo_url = "git+https://github.com/mandiant/Vulnerability-Disclosures" pipeline_id = "fireeye_importer_v2" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/github_osv_importer.py b/vulnerabilities/pipelines/v2_importers/github_osv_importer.py index 91adbf1bd..cfe92d93f 100644 --- a/vulnerabilities/pipelines/v2_importers/github_osv_importer.py +++ b/vulnerabilities/pipelines/v2_importers/github_osv_importer.py @@ -31,6 +31,8 @@ class GithubOSVImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/github/advisory-database/blob/main/LICENSE.md" repo_url = "git+https://github.com/github/advisory-database/" + precedence = 100 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/gitlab_importer.py b/vulnerabilities/pipelines/v2_importers/gitlab_importer.py index f6ba10eb5..7c164b1c0 100644 --- a/vulnerabilities/pipelines/v2_importers/gitlab_importer.py +++ b/vulnerabilities/pipelines/v2_importers/gitlab_importer.py @@ -45,6 +45,8 @@ class GitLabImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://gitlab.com/gitlab-org/advisories-community/-/blob/main/LICENSE" repo_url = "git+https://gitlab.com/gitlab-org/advisories-community/" + precedence = 100 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/istio_importer.py b/vulnerabilities/pipelines/v2_importers/istio_importer.py index 696133c81..a9a3de881 100644 --- a/vulnerabilities/pipelines/v2_importers/istio_importer.py +++ b/vulnerabilities/pipelines/v2_importers/istio_importer.py @@ -43,6 +43,8 @@ class IstioImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/istio/istio.io/blob/master/LICENSE" repo_url = "git+https://github.com/istio/istio.io" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/mattermost_importer.py b/vulnerabilities/pipelines/v2_importers/mattermost_importer.py index cb963673a..538bb1d4d 100644 --- a/vulnerabilities/pipelines/v2_importers/mattermost_importer.py +++ b/vulnerabilities/pipelines/v2_importers/mattermost_importer.py @@ -40,6 +40,8 @@ class MattermostImporterPipeline(VulnerableCodeBaseImporterPipelineV2): _cached_data = None # Class-level cache + precedence = 200 + @classmethod def steps(cls): return (cls.collect_and_store_advisories,) diff --git a/vulnerabilities/pipelines/v2_importers/mozilla_importer.py b/vulnerabilities/pipelines/v2_importers/mozilla_importer.py index e84c98c15..66765766a 100644 --- a/vulnerabilities/pipelines/v2_importers/mozilla_importer.py +++ b/vulnerabilities/pipelines/v2_importers/mozilla_importer.py @@ -45,6 +45,8 @@ class MozillaImporterPipeline(VulnerableCodeBaseImporterPipelineV2): spdx_license_expression = "MPL-2.0" license_url = "https://github.com/mozilla/foundation-security-advisories/blob/master/LICENSE" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/nginx_importer.py b/vulnerabilities/pipelines/v2_importers/nginx_importer.py index 3b9797de3..81448166b 100644 --- a/vulnerabilities/pipelines/v2_importers/nginx_importer.py +++ b/vulnerabilities/pipelines/v2_importers/nginx_importer.py @@ -37,6 +37,8 @@ class NginxImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://nginx.org/LICENSE" url = "https://nginx.org/en/security_advisories.html" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/npm_importer.py b/vulnerabilities/pipelines/v2_importers/npm_importer.py index 50f4b769b..32eec2051 100644 --- a/vulnerabilities/pipelines/v2_importers/npm_importer.py +++ b/vulnerabilities/pipelines/v2_importers/npm_importer.py @@ -41,6 +41,8 @@ class NpmImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/nodejs/security-wg/blob/main/LICENSE.md" repo_url = "git+https://github.com/nodejs/security-wg" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/nvd_importer.py b/vulnerabilities/pipelines/v2_importers/nvd_importer.py index fae3b34b2..d689aaa05 100644 --- a/vulnerabilities/pipelines/v2_importers/nvd_importer.py +++ b/vulnerabilities/pipelines/v2_importers/nvd_importer.py @@ -71,6 +71,8 @@ class NVDImporterPipeline(VulnerableCodeBaseImporterPipelineV2): MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. """ + precedence = 100 + @classmethod def steps(cls): return (cls.collect_and_store_advisories,) diff --git a/vulnerabilities/pipelines/v2_importers/openssl_importer.py b/vulnerabilities/pipelines/v2_importers/openssl_importer.py index 751cabdc1..cd2ec2006 100644 --- a/vulnerabilities/pipelines/v2_importers/openssl_importer.py +++ b/vulnerabilities/pipelines/v2_importers/openssl_importer.py @@ -40,6 +40,8 @@ class OpenSSLImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/openssl/openssl/blob/master/LICENSE.txt" repo_url = "git+https://github.com/openssl/release-metadata/" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/oss_fuzz.py b/vulnerabilities/pipelines/v2_importers/oss_fuzz.py index 38338b4cb..a22e53b77 100644 --- a/vulnerabilities/pipelines/v2_importers/oss_fuzz.py +++ b/vulnerabilities/pipelines/v2_importers/oss_fuzz.py @@ -27,6 +27,8 @@ class OSSFuzzImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/google/oss-fuzz-vulns/blob/main/LICENSE" repo_url = "git+https://github.com/google/oss-fuzz-vulns" + precedence = 100 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/postgresql_importer.py b/vulnerabilities/pipelines/v2_importers/postgresql_importer.py index adb4b28d6..10a6136e7 100644 --- a/vulnerabilities/pipelines/v2_importers/postgresql_importer.py +++ b/vulnerabilities/pipelines/v2_importers/postgresql_importer.py @@ -38,6 +38,8 @@ class PostgreSQLImporterPipeline(VulnerableCodeBaseImporterPipelineV2): links = set() + precedence = 200 + @classmethod def steps(cls): return (cls.collect_and_store_advisories,) diff --git a/vulnerabilities/pipelines/v2_importers/project_kb_msr2019_importer.py b/vulnerabilities/pipelines/v2_importers/project_kb_msr2019_importer.py index e1117d582..7dd01233e 100644 --- a/vulnerabilities/pipelines/v2_importers/project_kb_msr2019_importer.py +++ b/vulnerabilities/pipelines/v2_importers/project_kb_msr2019_importer.py @@ -30,6 +30,8 @@ class ProjectKBMSR2019Pipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/SAP/project-kb/blob/main/LICENSE.txt" repo_url = "git+https://github.com/SAP/project-kb" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/project_kb_statements_importer.py b/vulnerabilities/pipelines/v2_importers/project_kb_statements_importer.py index 727991424..b67540bce 100644 --- a/vulnerabilities/pipelines/v2_importers/project_kb_statements_importer.py +++ b/vulnerabilities/pipelines/v2_importers/project_kb_statements_importer.py @@ -37,6 +37,8 @@ class ProjectKBStatementsPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/SAP/project-kb/blob/main/LICENSE.txt" repo_url = "git+https://github.com/SAP/project-kb@vulnerability-data" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/pysec_importer.py b/vulnerabilities/pipelines/v2_importers/pysec_importer.py index 3d6eb44b8..05614b961 100644 --- a/vulnerabilities/pipelines/v2_importers/pysec_importer.py +++ b/vulnerabilities/pipelines/v2_importers/pysec_importer.py @@ -29,6 +29,7 @@ class PyPIImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/pypa/advisory-database/blob/main/LICENSE" url = "https://osv-vulnerabilities.storage.googleapis.com/PyPI/all.zip" spdx_license_expression = "CC-BY-4.0" + precedence = 100 @classmethod def steps(cls): diff --git a/vulnerabilities/pipelines/v2_importers/redhat_importer.py b/vulnerabilities/pipelines/v2_importers/redhat_importer.py index 8d10d3f72..5dde4ce8f 100644 --- a/vulnerabilities/pipelines/v2_importers/redhat_importer.py +++ b/vulnerabilities/pipelines/v2_importers/redhat_importer.py @@ -46,6 +46,8 @@ class RedHatImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://access.redhat.com/security/data/" url = "https://security.access.redhat.com/data/csaf/v2/advisories/" + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/retiredotnet_importer.py b/vulnerabilities/pipelines/v2_importers/retiredotnet_importer.py index bb92ed884..478d31323 100644 --- a/vulnerabilities/pipelines/v2_importers/retiredotnet_importer.py +++ b/vulnerabilities/pipelines/v2_importers/retiredotnet_importer.py @@ -30,6 +30,8 @@ class RetireDotnetImporterPipeline(VulnerableCodeBaseImporterPipelineV2): pipeline_id = "retiredotnet_importer_v2" run_once = True + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/ruby_importer.py b/vulnerabilities/pipelines/v2_importers/ruby_importer.py index 690b4186b..db02f4823 100644 --- a/vulnerabilities/pipelines/v2_importers/ruby_importer.py +++ b/vulnerabilities/pipelines/v2_importers/ruby_importer.py @@ -57,6 +57,8 @@ class RubyImporterPipeline(VulnerableCodeBaseImporterPipelineV2): SOFTWARE. """ + precedence = 200 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/ubuntu_osv_importer.py b/vulnerabilities/pipelines/v2_importers/ubuntu_osv_importer.py index 6df27ea1f..9643e3c11 100644 --- a/vulnerabilities/pipelines/v2_importers/ubuntu_osv_importer.py +++ b/vulnerabilities/pipelines/v2_importers/ubuntu_osv_importer.py @@ -31,6 +31,8 @@ class UbuntuOSVImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/canonical/ubuntu-security-notices/blob/main/LICENSE" repo_url = "git+https://github.com/canonical/ubuntu-security-notices/" + precedence = 200 + progress_step = 1 @classmethod diff --git a/vulnerabilities/pipelines/v2_importers/vulnrichment_importer.py b/vulnerabilities/pipelines/v2_importers/vulnrichment_importer.py index ee7fc2c28..84846a1bf 100644 --- a/vulnerabilities/pipelines/v2_importers/vulnrichment_importer.py +++ b/vulnerabilities/pipelines/v2_importers/vulnrichment_importer.py @@ -33,6 +33,8 @@ class VulnrichImporterPipeline(VulnerableCodeBaseImporterPipelineV2): license_url = "https://github.com/cisagov/vulnrichment/blob/develop/LICENSE" repo_url = "git+https://github.com/cisagov/vulnrichment.git" + precedence = 100 + @classmethod def steps(cls): return ( diff --git a/vulnerabilities/pipelines/v2_importers/xen_importer.py b/vulnerabilities/pipelines/v2_importers/xen_importer.py index ed759eebd..8bd2d6453 100644 --- a/vulnerabilities/pipelines/v2_importers/xen_importer.py +++ b/vulnerabilities/pipelines/v2_importers/xen_importer.py @@ -55,6 +55,8 @@ class XenImporterPipeline(VulnerableCodeBaseImporterPipelineV2): -George """ + precedence = 200 + _cached_data = None # Class-level cache @classmethod diff --git a/vulnerabilities/pipes/advisory.py b/vulnerabilities/pipes/advisory.py index b571b2871..9d242c01f 100644 --- a/vulnerabilities/pipes/advisory.py +++ b/vulnerabilities/pipes/advisory.py @@ -292,6 +292,7 @@ def insert_advisory_v2( advisory: AdvisoryDataV2, pipeline_id: str, logger: Callable = None, + precedence: int = 0, ): from vulnerabilities.models import ImpactedPackage from vulnerabilities.models import PackageV2 @@ -315,6 +316,7 @@ def insert_advisory_v2( "date_collected": datetime.now(timezone.utc), "original_advisory_text": advisory.original_advisory_text, "url": advisory.url, + "precedence": precedence, } advisory_obj, created = AdvisoryV2.objects.get_or_create( diff --git a/vulnerabilities/templates/package_details_v2.html b/vulnerabilities/templates/package_details_v2.html index 1af0866ef..9bc1f8742 100644 --- a/vulnerabilities/templates/package_details_v2.html +++ b/vulnerabilities/templates/package_details_v2.html @@ -129,7 +129,7 @@
- Vulnerabilities affecting this package ({{ affected_by_advisories|length }}) + Vulnerabilities affecting this package ({{ affected_by_advisories_v2|length }})
@@ -144,18 +144,18 @@ - {% for advisory in affected_by_advisories %} + {% for advisory in affected_by_advisories_v2 %}
- {{advisory.avid }} + {{advisory.primary.avid }}
- {% if advisory.alias|length != 0 %} + {% if advisory.primary.alias|length != 0 %} Aliases: {% endif %}
- {% for alias in advisory.alias %} + {% for alias in advisory.primary.alias %} {% if alias.url %} {{ alias }} @@ -165,18 +165,27 @@
{% endif %} {% endfor %} + + {% if advisory.secondary|length != 0 %} +

Supporting advisories are listed below the primary advisory.

+ {% for secondary in advisory.secondary %} + + {{secondary.avid }} + + {% endfor %} + {% endif %}
- {{advisory.url}} + {{advisory.primary.url}} - {{advisory.date_published}} + {{advisory.primary.date_published}} - {{ advisory.summary }} + {{ advisory.primary.summary }} - {% with fixed=fixed_package_details|get_item:advisory.id %} + {% with fixed=fixed_package_details|get_item:advisory.primary.avid %} {% if fixed %} {% for item in fixed %}
@@ -207,7 +216,7 @@
- Vulnerabilities fixed by this package ({{ fixing_advisories|length }}) + Vulnerabilities fixed by this package ({{ fixing_advisories_v2|length }})
@@ -221,24 +230,33 @@ - {% for advisory in fixing_advisories %} + {% for advisory in fixing_advisories_v2 %}
- {{advisory.avid }} + {{advisory.primary.avid }} +
+ {% if advisory.secondary|length != 0 %} +

Supporting advisories are listed below the primary advisory.

+ {% for secondary in advisory.secondary %} + + {{secondary.avid }} + + {% endfor %} + {% endif %}
- {{advisory.url}} + {{advisory.primary.url}} - {{advisory.date_published}} + {{advisory.primary.date_published}} - {{ advisory.summary }} + {{ advisory.primary.summary }} - {% for alias in advisory.alias %} + {% for alias in advisory.primary.alias %} {% if alias.url %} {{ alias }} diff --git a/vulnerabilities/tests/pipelines/v2_importers/test_redhat_importer_v2.py b/vulnerabilities/tests/pipelines/v2_importers/test_redhat_importer_v2.py index deda45311..e9fce5869 100644 --- a/vulnerabilities/tests/pipelines/v2_importers/test_redhat_importer_v2.py +++ b/vulnerabilities/tests/pipelines/v2_importers/test_redhat_importer_v2.py @@ -17,6 +17,7 @@ from vulnerabilities.models import PackageV2 from vulnerabilities.pipelines.v2_importers.redhat_importer import RedHatImporterPipeline from vulnerabilities.tests import util_tests +from vulnerabilities.utils import normalize_list TEST_DATA = Path(__file__).parent.parent.parent / "test_data" / "redhat" / "csaf_2_0" @@ -31,5 +32,7 @@ def test_redhat_advisories_v2(self, mock_fetch): self.assertEqual(6, AdvisoryV2.objects.count()) self.assertEqual(93, PackageV2.objects.count()) expected_file = TEST_DATA.parent / "redhat_advisoryv2-expected.json" - result = [adv.to_advisory_data().to_dict() for adv in AdvisoryV2.objects.all()] + result = [adv.to_advisory_data() for adv in AdvisoryV2.objects.all()] + result = normalize_list(result) + result = [adv.to_dict() for adv in result] util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_api_v2.py b/vulnerabilities/tests/test_api_v2.py index 42df1c623..ea1a689e6 100644 --- a/vulnerabilities/tests/test_api_v2.py +++ b/vulnerabilities/tests/test_api_v2.py @@ -850,37 +850,39 @@ def setUp(self): ) self.package = PackageV2.objects.from_purl(purl="pkg:pypi/sample@1.0.0") - self.impact = ImpactedPackage.objects.create(advisory=self.advisory) + self.impact = ImpactedPackage.objects.create( + advisory=self.advisory, base_purl="pkg:pypi/sample" + ) self.impact.affecting_packages.add(self.package) self.client = APIClient(enforce_csrf_checks=True) def test_list_with_purl_filter(self): url = reverse("package-v3-list") - with self.assertNumQueries(23): + with self.assertNumQueries(29): response = self.client.get(url, {"purl": "pkg:pypi/sample@1.0.0"}) assert response.status_code == 200 assert "packages" in response.data["results"] - assert "advisories" in response.data["results"] - assert self.advisory.avid in response.data["results"]["advisories"] + assert "advisories_by_id" in response.data["results"] + assert self.advisory.avid in response.data["results"]["advisories_by_id"] def test_bulk_lookup(self): url = reverse("package-v3-bulk-lookup") - with self.assertNumQueries(22): + with self.assertNumQueries(28): response = self.client.post(url, {"purls": ["pkg:pypi/sample@1.0.0"]}, format="json") assert response.status_code == 200 assert "packages" in response.data - assert "advisories" in response.data - assert self.advisory.avid in response.data["advisories"] + assert "advisories_by_id" in response.data + assert self.advisory.avid in response.data["advisories_by_id"] def test_bulk_search_plain(self): url = reverse("package-v3-bulk-search") payload = {"purls": ["pkg:pypi/sample@1.0.0"], "plain_purl": True, "purl_only": False} - with self.assertNumQueries(22): + with self.assertNumQueries(28): response = self.client.post(url, payload, format="json") assert response.status_code == 200 assert "packages" in response.data - assert "advisories" in response.data + assert "advisories_by_id" in response.data def test_bulk_search_purl_only(self): url = reverse("package-v3-bulk-search") @@ -892,7 +894,7 @@ def test_bulk_search_purl_only(self): def test_lookup_single_package(self): url = reverse("package-v3-lookup") - with self.assertNumQueries(15): + with self.assertNumQueries(21): response = self.client.post(url, {"purl": "pkg:pypi/sample@1.0.0"}, format="json") assert response.status_code == 200 assert any(pkg["purl"] == "pkg:pypi/sample@1.0.0" for pkg in response.data) diff --git a/vulnerabilities/tests/test_data/openssl/release_metadata/openssl_advisoryv2-expected.json b/vulnerabilities/tests/test_data/openssl/release_metadata/openssl_advisoryv2-expected.json index 245026ea3..bfc0b7ce7 100644 --- a/vulnerabilities/tests/test_data/openssl/release_metadata/openssl_advisoryv2-expected.json +++ b/vulnerabilities/tests/test_data/openssl/release_metadata/openssl_advisoryv2-expected.json @@ -151,8 +151,8 @@ "qualifiers": "", "subpath": "" }, - "affected_version_range": "vers:openssl/>=1.0.1|<1.0.1k", - "fixed_version_range": "vers:openssl/1.0.1k", + "affected_version_range": "vers:openssl/>=1.0.0|<1.0.0p", + "fixed_version_range": "vers:openssl/1.0.0p", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -165,8 +165,8 @@ "qualifiers": "", "subpath": "" }, - "affected_version_range": "vers:openssl/>=1.0.0|<1.0.0p", - "fixed_version_range": "vers:openssl/1.0.0p", + "affected_version_range": "vers:openssl/>=1.0.1|<1.0.1k", + "fixed_version_range": "vers:openssl/1.0.1k", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] } @@ -225,7 +225,7 @@ "patches": [ { "patch_url": "https://github.com/openssl/openssl/commit/7725e7bfe6f2ce8146b6552b44e0d226be7638e7", - "patch_text": null, + "patch_text": "", "patch_checksum": null } ], @@ -256,13 +256,13 @@ "qualifiers": "", "subpath": "" }, - "affected_version_range": "vers:openssl/>=3.5.0|<3.5.4", - "fixed_version_range": "vers:openssl/3.5.4", + "affected_version_range": "vers:openssl/>=3.2.0|<3.2.6", + "fixed_version_range": "vers:openssl/3.2.6", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [ { "vcs_url": "https://github.com/openssl/openssl", - "commit_hash": "fc47a2ec078912b3e914fab5734535e76c4820c2", + "commit_hash": "cba616c26ac8e7b37de5e77762e505ba5ca51698", "patch_text": null, "patch_checksum": null } @@ -277,13 +277,13 @@ "qualifiers": "", "subpath": "" }, - "affected_version_range": "vers:openssl/>=3.4.0|<3.4.3", - "fixed_version_range": "vers:openssl/3.4.3", + "affected_version_range": "vers:openssl/>=3.3.0|<3.3.5", + "fixed_version_range": "vers:openssl/3.3.5", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [ { "vcs_url": "https://github.com/openssl/openssl", - "commit_hash": "eed5adc9f969d77c94f213767acbb41ff923b6f4", + "commit_hash": "567f64386e43683888212226824b6a179885a0fe", "patch_text": null, "patch_checksum": null } @@ -298,13 +298,13 @@ "qualifiers": "", "subpath": "" }, - "affected_version_range": "vers:openssl/>=3.3.0|<3.3.5", - "fixed_version_range": "vers:openssl/3.3.5", + "affected_version_range": "vers:openssl/>=3.4.0|<3.4.3", + "fixed_version_range": "vers:openssl/3.4.3", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [ { "vcs_url": "https://github.com/openssl/openssl", - "commit_hash": "567f64386e43683888212226824b6a179885a0fe", + "commit_hash": "eed5adc9f969d77c94f213767acbb41ff923b6f4", "patch_text": null, "patch_checksum": null } @@ -319,13 +319,13 @@ "qualifiers": "", "subpath": "" }, - "affected_version_range": "vers:openssl/>=3.2.0|<3.2.6", - "fixed_version_range": "vers:openssl/3.2.6", + "affected_version_range": "vers:openssl/>=3.5.0|<3.5.4", + "fixed_version_range": "vers:openssl/3.5.4", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [ { "vcs_url": "https://github.com/openssl/openssl", - "commit_hash": "cba616c26ac8e7b37de5e77762e505ba5ca51698", + "commit_hash": "fc47a2ec078912b3e914fab5734535e76c4820c2", "patch_text": null, "patch_checksum": null } diff --git a/vulnerabilities/tests/test_data/redhat/redhat_advisoryv2-expected.json b/vulnerabilities/tests/test_data/redhat/redhat_advisoryv2-expected.json index a15ab5984..ed5832f5b 100644 --- a/vulnerabilities/tests/test_data/redhat/redhat_advisoryv2-expected.json +++ b/vulnerabilities/tests/test_data/redhat/redhat_advisoryv2-expected.json @@ -17,9 +17,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan", + "name": "libreswan-debuginfo", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=aarch64", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -31,9 +31,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan", + "name": "libreswan-debuginfo", "version": "", - "qualifiers": "arch=aarch64", + "qualifiers": "arch=ppc64le", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -45,9 +45,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan-debugsource", + "name": "libreswan-debuginfo", "version": "", - "qualifiers": "arch=aarch64", + "qualifiers": "arch=s390x", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -61,7 +61,7 @@ "namespace": "redhat", "name": "libreswan-debuginfo", "version": "", - "qualifiers": "arch=aarch64", + "qualifiers": "arch=x86_64", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -73,9 +73,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan", + "name": "libreswan-debugsource", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=aarch64", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -101,9 +101,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan-debuginfo", + "name": "libreswan-debugsource", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=s390x", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -115,7 +115,7 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan", + "name": "libreswan-debugsource", "version": "", "qualifiers": "arch=x86_64", "subpath": "" @@ -129,9 +129,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan-debugsource", + "name": "libreswan", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=aarch64", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -143,9 +143,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan-debuginfo", + "name": "libreswan", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=ppc64le", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -171,9 +171,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan-debugsource", + "name": "libreswan", "version": "", - "qualifiers": "arch=s390x", + "qualifiers": "arch=src", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -185,9 +185,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "libreswan-debuginfo", + "name": "libreswan", "version": "", - "qualifiers": "arch=s390x", + "qualifiers": "arch=x86_64", "subpath": "" }, "affected_version_range": "vers:rpm/<4.6-3.el9_0.3", @@ -354,7 +354,7 @@ "namespace": "redhat", "name": "microcode_ctl", "version": "", - "qualifiers": "arch=src&epoch=4", + "qualifiers": "arch=noarch&epoch=4", "subpath": "" }, "affected_version_range": "vers:rpm/<20230808-2.20250211.1.el9_4", @@ -368,7 +368,7 @@ "namespace": "redhat", "name": "microcode_ctl", "version": "", - "qualifiers": "arch=noarch&epoch=4", + "qualifiers": "arch=src&epoch=4", "subpath": "" }, "affected_version_range": "vers:rpm/<20230808-2.20250211.1.el9_4", @@ -414,7 +414,7 @@ "namespace": "redhat", "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=ia64", + "qualifiers": "arch=i386", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", @@ -426,13 +426,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML", + "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=ia64", + "qualifiers": "arch=i386", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", - "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", + "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", + "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -440,13 +440,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML", + "name": "PyXML-debuginfo", "version": "", "qualifiers": "arch=ia64", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", - "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", + "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", + "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -470,21 +470,7 @@ "namespace": "redhat", "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=x86_64", - "subpath": "" - }, - "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", - "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", - "introduced_by_commit_patches": [], - "fixed_by_commit_patches": [] - }, - { - "package": { - "type": "rpm", - "namespace": "redhat", - "name": "PyXML", - "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=ppc", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", @@ -496,9 +482,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML", + "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=ppc", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", @@ -512,11 +498,11 @@ "namespace": "redhat", "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=s390", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", - "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", + "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", + "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -526,7 +512,7 @@ "namespace": "redhat", "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=i386", + "qualifiers": "arch=s390x", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", @@ -538,13 +524,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML", + "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=i386", + "qualifiers": "arch=s390x", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", - "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", + "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", + "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -552,13 +538,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML", + "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=i386", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", - "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", + "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", + "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -568,7 +554,7 @@ "namespace": "redhat", "name": "PyXML-debuginfo", "version": "", - "qualifiers": "arch=i386", + "qualifiers": "arch=x86_64", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", @@ -582,7 +568,7 @@ "namespace": "redhat", "name": "PyXML", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=i386", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", @@ -596,7 +582,7 @@ "namespace": "redhat", "name": "PyXML", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=i386", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", @@ -608,9 +594,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML-debuginfo", + "name": "PyXML", "version": "", - "qualifiers": "arch=ppc", + "qualifiers": "arch=ia64", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", @@ -624,11 +610,11 @@ "namespace": "redhat", "name": "PyXML", "version": "", - "qualifiers": "arch=ppc", + "qualifiers": "arch=ia64", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", - "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", + "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", + "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -641,8 +627,8 @@ "qualifiers": "arch=ppc", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", - "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", + "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", + "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -650,7 +636,7 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML-debuginfo", + "name": "PyXML", "version": "", "qualifiers": "arch=ppc", "subpath": "" @@ -664,9 +650,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML-debuginfo", + "name": "PyXML", "version": "", - "qualifiers": "arch=s390x", + "qualifiers": "arch=s390", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", @@ -706,9 +692,23 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML-debuginfo", + "name": "PyXML", "version": "", - "qualifiers": "arch=s390x", + "qualifiers": "arch=src", + "subpath": "" + }, + "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", + "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", + "introduced_by_commit_patches": [], + "fixed_by_commit_patches": [] + }, + { + "package": { + "type": "rpm", + "namespace": "redhat", + "name": "PyXML", + "version": "", + "qualifiers": "arch=src", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", @@ -720,9 +720,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "PyXML-debuginfo", + "name": "PyXML", "version": "", - "qualifiers": "arch=s390", + "qualifiers": "arch=x86_64", "subpath": "" }, "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", @@ -736,11 +736,11 @@ "namespace": "redhat", "name": "PyXML", "version": "", - "qualifiers": "arch=s390", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.8.3-6.el4_8.2", - "fixed_version_range": "vers:rpm/0.8.3-6.el4_8.2", + "affected_version_range": "vers:rpm/<0.8.4-4.el5_4.2", + "fixed_version_range": "vers:rpm/0.8.4-4.el5_4.2", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] } @@ -790,13 +790,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-nose-docs", + "name": "python-PyMySQL", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.3.7-30.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/1.3.7-30.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<0.10.1-2.module+el8.4.0+9657+a4b6a102", + "fixed_version_range": "vers:rpm/0.10.1-2.module+el8.4.0+9657+a4b6a102", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -804,13 +804,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-pymongo-doc", + "name": "python-distro", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<1.4.0-2.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/1.4.0-2.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -818,13 +818,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-sqlalchemy-doc", + "name": "python-docs", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", - "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "affected_version_range": "vers:rpm/<3.6.7-2.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/3.6.7-2.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -832,13 +832,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-virtualenv-doc", + "name": "python-docutils", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<15.1.0-19.module+el8.4.0+22650+01781d50.3", - "fixed_version_range": "vers:rpm/15.1.0-19.module+el8.4.0+22650+01781d50.3", + "affected_version_range": "vers:rpm/<0.14-12.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/0.14-12.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -846,13 +846,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-PyMySQL", + "name": "python-nose-docs", "version": "", "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.10.1-2.module+el8.4.0+9657+a4b6a102", - "fixed_version_range": "vers:rpm/0.10.1-2.module+el8.4.0+9657+a4b6a102", + "affected_version_range": "vers:rpm/<1.3.7-30.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/1.3.7-30.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -860,13 +860,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-distro", + "name": "python-nose", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.4.0-2.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/1.4.0-2.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<1.3.7-30.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/1.3.7-30.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -874,13 +874,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-docs", + "name": "python-pygments", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.7-2.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/3.6.7-2.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<2.2.0-20.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/2.2.0-20.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -888,13 +888,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-docutils", + "name": "python-pymongo-debuginfo", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.14-12.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/0.14-12.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -902,13 +902,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-nose", + "name": "python-pymongo-debuginfo", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.3.7-30.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/1.3.7-30.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -916,13 +916,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-pygments", + "name": "python-pymongo-debugsource", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<2.2.0-20.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/2.2.0-20.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -930,13 +930,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-virtualenv", + "name": "python-pymongo-debugsource", "version": "", - "qualifiers": "arch=noarch", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<15.1.0-19.module+el8.4.0+22650+01781d50.3", - "fixed_version_range": "vers:rpm/15.1.0-19.module+el8.4.0+22650+01781d50.3", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -944,13 +944,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-wheel", + "name": "python-pymongo-doc", "version": "", - "qualifiers": "arch=noarch&epoch=1", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.31.1-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/0.31.1-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -958,13 +958,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-wheel-wheel", + "name": "python-pymongo", "version": "", - "qualifiers": "arch=noarch&epoch=1", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.31.1-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/0.31.1-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -972,13 +972,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python36-rpm-macros", + "name": "python-sqlalchemy-doc", "version": "", "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -986,13 +986,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-PyMySQL", + "name": "python-sqlalchemy", "version": "", "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.10.1-2.module+el8.4.0+9657+a4b6a102", - "fixed_version_range": "vers:rpm/0.10.1-2.module+el8.4.0+9657+a4b6a102", + "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1000,13 +1000,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-distro", + "name": "python-virtualenv-doc", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.4.0-2.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/1.4.0-2.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<15.1.0-19.module+el8.4.0+22650+01781d50.3", + "fixed_version_range": "vers:rpm/15.1.0-19.module+el8.4.0+22650+01781d50.3", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1014,13 +1014,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-docs", + "name": "python-virtualenv", "version": "", "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.7-2.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/3.6.7-2.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<15.1.0-19.module+el8.4.0+22650+01781d50.3", + "fixed_version_range": "vers:rpm/15.1.0-19.module+el8.4.0+22650+01781d50.3", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1028,13 +1028,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-docutils", + "name": "python-wheel", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=src&epoch=1", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.14-12.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/0.14-12.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<0.31.1-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/0.31.1-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1042,13 +1042,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-nose", + "name": "python3-PyMySQL", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.3.7-30.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/1.3.7-30.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<0.10.1-2.module+el8.4.0+9657+a4b6a102", + "fixed_version_range": "vers:rpm/0.10.1-2.module+el8.4.0+9657+a4b6a102", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1056,13 +1056,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-pygments", + "name": "python3-bson-debuginfo", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<2.2.0-20.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/2.2.0-20.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1070,9 +1070,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-pymongo", + "name": "python3-bson-debuginfo", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=x86_64", "subpath": "" }, "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", @@ -1084,13 +1084,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-sqlalchemy", + "name": "python3-bson", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", - "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1098,13 +1098,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-virtualenv", + "name": "python3-bson", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<15.1.0-19.module+el8.4.0+22650+01781d50.3", - "fixed_version_range": "vers:rpm/15.1.0-19.module+el8.4.0+22650+01781d50.3", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1112,13 +1112,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-wheel", + "name": "python3-distro", "version": "", - "qualifiers": "arch=src&epoch=1", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<0.31.1-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/0.31.1-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<1.4.0-2.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/1.4.0-2.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1126,13 +1126,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python36", + "name": "python3-docs", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<3.6.7-2.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/3.6.7-2.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1140,13 +1140,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "scipy", + "name": "python3-docutils", "version": "", - "qualifiers": "arch=src", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<0.14-12.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/0.14-12.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1154,13 +1154,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-pymongo-debuginfo", + "name": "python3-nose", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<1.3.7-30.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/1.3.7-30.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1168,13 +1168,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-pymongo-debugsource", + "name": "python3-pygments", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<2.2.0-20.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/2.2.0-20.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1182,7 +1182,7 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-bson", + "name": "python3-pymongo-debuginfo", "version": "", "qualifiers": "arch=ppc64le", "subpath": "" @@ -1196,9 +1196,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-bson-debuginfo", + "name": "python3-pymongo-debuginfo", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=x86_64", "subpath": "" }, "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", @@ -1210,7 +1210,7 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-pymongo", + "name": "python3-pymongo-gridfs", "version": "", "qualifiers": "arch=ppc64le", "subpath": "" @@ -1224,9 +1224,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-pymongo-debuginfo", + "name": "python3-pymongo-gridfs", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=x86_64", "subpath": "" }, "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", @@ -1238,7 +1238,7 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-pymongo-gridfs", + "name": "python3-pymongo", "version": "", "qualifiers": "arch=ppc64le", "subpath": "" @@ -1252,13 +1252,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-scipy", + "name": "python3-pymongo", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", + "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1280,13 +1280,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-sqlalchemy", + "name": "python3-scipy-debuginfo", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", - "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1294,13 +1294,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python36", + "name": "python3-scipy", "version": "", "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1308,13 +1308,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python36-debug", + "name": "python3-scipy", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1322,13 +1322,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python36-devel", + "name": "python3-sqlalchemy", "version": "", "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1336,13 +1336,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "scipy-debugsource", + "name": "python3-sqlalchemy", "version": "", - "qualifiers": "arch=ppc64le", + "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1350,13 +1350,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-pymongo-debuginfo", + "name": "python3-virtualenv", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<15.1.0-19.module+el8.4.0+22650+01781d50.3", + "fixed_version_range": "vers:rpm/15.1.0-19.module+el8.4.0+22650+01781d50.3", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1364,13 +1364,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python-pymongo-debugsource", + "name": "python3-wheel-wheel", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=noarch&epoch=1", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<0.31.1-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/0.31.1-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1378,13 +1378,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-bson", + "name": "python3-wheel", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=noarch&epoch=1", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<0.31.1-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/0.31.1-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1392,13 +1392,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-bson-debuginfo", + "name": "python36-debug", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1406,13 +1406,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-pymongo", + "name": "python36-debug", "version": "", "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1420,13 +1420,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-pymongo-debuginfo", + "name": "python36-devel", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1434,13 +1434,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-pymongo-gridfs", + "name": "python36-devel", "version": "", "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.7.0-1.module+el8.4.0+9670+1849b5f9", - "fixed_version_range": "vers:rpm/3.7.0-1.module+el8.4.0+9670+1849b5f9", + "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1448,13 +1448,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-scipy", + "name": "python36-rpm-macros", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=noarch", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1462,13 +1462,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-scipy-debuginfo", + "name": "python36", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", - "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", + "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1476,13 +1476,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python3-sqlalchemy", + "name": "python36", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=src", "subpath": "" }, - "affected_version_range": "vers:rpm/<1.3.2-2.module+el8.4.0+20979+8ba06d83.1", - "fixed_version_range": "vers:rpm/1.3.2-2.module+el8.4.0+20979+8ba06d83.1", + "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1504,13 +1504,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python36-debug", + "name": "scipy-debugsource", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=ppc64le", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1518,13 +1518,13 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "python36-devel", + "name": "scipy-debugsource", "version": "", "qualifiers": "arch=x86_64", "subpath": "" }, - "affected_version_range": "vers:rpm/<3.6.8-2.module+el8.4.0+15040+36b018e7.1", - "fixed_version_range": "vers:rpm/3.6.8-2.module+el8.4.0+15040+36b018e7.1", + "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", + "fixed_version_range": "vers:rpm/1.0.0-20.module+el8.1.0+3334+5cb623d7", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -1532,9 +1532,9 @@ "package": { "type": "rpm", "namespace": "redhat", - "name": "scipy-debugsource", + "name": "scipy", "version": "", - "qualifiers": "arch=x86_64", + "qualifiers": "arch=src", "subpath": "" }, "affected_version_range": "vers:rpm/<1.0.0-20.module+el8.1.0+3334+5cb623d7", diff --git a/vulnerabilities/tests/test_data/ubuntu/ubuntu_osv_advisoryv2-expected.json b/vulnerabilities/tests/test_data/ubuntu/ubuntu_osv_advisoryv2-expected.json index 427669788..444c2093f 100644 --- a/vulnerabilities/tests/test_data/ubuntu/ubuntu_osv_advisoryv2-expected.json +++ b/vulnerabilities/tests/test_data/ubuntu/ubuntu_osv_advisoryv2-expected.json @@ -58,11 +58,11 @@ "namespace": "ubuntu", "name": "netqmail", "version": "", - "qualifiers": "arch=source&distro=trusty/esm", + "qualifiers": "arch=source&distro=bionic", "subpath": "" }, - "affected_version_range": "vers:deb/1.06-5", - "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.14.04.1+esm1", + "affected_version_range": "vers:deb/1.06-6", + "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.18.04.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -72,11 +72,11 @@ "namespace": "ubuntu", "name": "netqmail", "version": "", - "qualifiers": "arch=source&distro=xenial", + "qualifiers": "arch=source&distro=focal", "subpath": "" }, - "affected_version_range": "vers:deb/1.06-5", - "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.16.04.1", + "affected_version_range": "vers:deb/1.06-6.1", + "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.20.04.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -86,11 +86,11 @@ "namespace": "ubuntu", "name": "netqmail", "version": "", - "qualifiers": "arch=source&distro=bionic", + "qualifiers": "arch=source&distro=trusty/esm", "subpath": "" }, - "affected_version_range": "vers:deb/1.06-6", - "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.18.04.1", + "affected_version_range": "vers:deb/1.06-5", + "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.14.04.1+esm1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] }, @@ -100,11 +100,11 @@ "namespace": "ubuntu", "name": "netqmail", "version": "", - "qualifiers": "arch=source&distro=focal", + "qualifiers": "arch=source&distro=xenial", "subpath": "" }, - "affected_version_range": "vers:deb/1.06-6.1", - "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.20.04.1", + "affected_version_range": "vers:deb/1.06-5", + "fixed_version_range": "vers:deb/1.06-6.2~deb10u1build0.16.04.1", "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] } @@ -272,10 +272,10 @@ "namespace": "ubuntu", "name": "trafficserver", "version": "", - "qualifiers": "arch=source&distro=xenial", + "qualifiers": "arch=source&distro=bionic", "subpath": "" }, - "affected_version_range": "vers:deb/5.3.0-2ubuntu1|5.3.0-2ubuntu2", + "affected_version_range": "vers:deb/7.0.0-5|7.1.2+ds-2|7.1.2+ds-2build1|7.1.2+ds-3", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] @@ -286,10 +286,10 @@ "namespace": "ubuntu", "name": "trafficserver", "version": "", - "qualifiers": "arch=source&distro=bionic", + "qualifiers": "arch=source&distro=esm-apps/focal", "subpath": "" }, - "affected_version_range": "vers:deb/7.0.0-5|7.1.2+ds-2|7.1.2+ds-2build1|7.1.2+ds-3", + "affected_version_range": "vers:deb/8.0.5+ds-1|8.0.5+ds-2|8.0.5+ds-2build1|8.0.5+ds-2ubuntu1|8.0.5+ds-3|8.0.5+ds-3ubuntu0.1~esm1", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] @@ -300,10 +300,10 @@ "namespace": "ubuntu", "name": "trafficserver", "version": "", - "qualifiers": "arch=source&distro=esm-apps/focal", + "qualifiers": "arch=source&distro=esm-apps/jammy", "subpath": "" }, - "affected_version_range": "vers:deb/8.0.5+ds-1|8.0.5+ds-2|8.0.5+ds-2build1|8.0.5+ds-2ubuntu1|8.0.5+ds-3|8.0.5+ds-3ubuntu0.1~esm1", + "affected_version_range": "vers:deb/8.1.1+ds-1.1|9.1.1+ds-2build1|9.1.1+ds-2ubuntu0.1~esm1", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] @@ -314,10 +314,10 @@ "namespace": "ubuntu", "name": "trafficserver", "version": "", - "qualifiers": "arch=source&distro=esm-apps/jammy", + "qualifiers": "arch=source&distro=xenial", "subpath": "" }, - "affected_version_range": "vers:deb/8.1.1+ds-1.1|9.1.1+ds-2build1|9.1.1+ds-2ubuntu0.1~esm1", + "affected_version_range": "vers:deb/5.3.0-2ubuntu1|5.3.0-2ubuntu2", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] @@ -370,10 +370,10 @@ "namespace": "ubuntu", "name": "mongodb", "version": "", - "qualifiers": "arch=source&distro=trusty", + "qualifiers": "arch=source&distro=bionic", "subpath": "" }, - "affected_version_range": "vers:deb/1:2.4.6-0ubuntu5|1:2.4.6-0ubuntu6|1:2.4.8-1ubuntu1|1:2.4.8-2|1:2.4.9-1|1:2.4.9-1ubuntu1|1:2.4.9-1ubuntu2", + "affected_version_range": "vers:deb/1:3.4.7-1|1:3.4.7-1ubuntu1|1:3.4.7-1ubuntu2|1:3.4.7-1ubuntu4|1:3.4.14-3ubuntu1|1:3.4.14-3ubuntu2|1:3.6.3-0ubuntu1|1:3.6.3-0ubuntu1.1|1:3.6.3-0ubuntu1.3|1:3.6.3-0ubuntu1.4", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] @@ -384,10 +384,10 @@ "namespace": "ubuntu", "name": "mongodb", "version": "", - "qualifiers": "arch=source&distro=xenial", + "qualifiers": "arch=source&distro=focal", "subpath": "" }, - "affected_version_range": "vers:deb/1:2.6.10-0ubuntu1", + "affected_version_range": "vers:deb/1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu2|1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5|1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5.2|1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5.3", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] @@ -398,10 +398,10 @@ "namespace": "ubuntu", "name": "mongodb", "version": "", - "qualifiers": "arch=source&distro=bionic", + "qualifiers": "arch=source&distro=trusty", "subpath": "" }, - "affected_version_range": "vers:deb/1:3.4.7-1|1:3.4.7-1ubuntu1|1:3.4.7-1ubuntu2|1:3.4.7-1ubuntu4|1:3.4.14-3ubuntu1|1:3.4.14-3ubuntu2|1:3.6.3-0ubuntu1|1:3.6.3-0ubuntu1.1|1:3.6.3-0ubuntu1.3|1:3.6.3-0ubuntu1.4", + "affected_version_range": "vers:deb/1:2.4.6-0ubuntu5|1:2.4.6-0ubuntu6|1:2.4.8-1ubuntu1|1:2.4.8-2|1:2.4.9-1|1:2.4.9-1ubuntu1|1:2.4.9-1ubuntu2", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] @@ -412,10 +412,10 @@ "namespace": "ubuntu", "name": "mongodb", "version": "", - "qualifiers": "arch=source&distro=focal", + "qualifiers": "arch=source&distro=xenial", "subpath": "" }, - "affected_version_range": "vers:deb/1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu2|1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5|1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5.2|1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5.3", + "affected_version_range": "vers:deb/1:2.6.10-0ubuntu1", "fixed_version_range": null, "introduced_by_commit_patches": [], "fixed_by_commit_patches": [] diff --git a/vulnerabilities/utils.py b/vulnerabilities/utils.py index 465348288..82f29bcea 100644 --- a/vulnerabilities/utils.py +++ b/vulnerabilities/utils.py @@ -678,12 +678,16 @@ def compute_content_id_v2(advisory_data): normalized_data = { "aliases": normalize_list(advisory_data.aliases), "summary": normalize_text(advisory_data.summary), - "affected_packages": [ - pkg for pkg in normalize_list(advisory_data.affected_packages) if pkg - ], + "impacted_packages": sorted( + [impact.to_dict() for impact in advisory_data.impacted_packages.all()], + key=lambda x: json.dumps(x, sort_keys=True), + ), + "patches": sorted( + [patch.to_patch_data().to_dict() for patch in advisory_data.patches.all()], + key=lambda x: json.dumps(x, sort_keys=True), + ), "references": [ref for ref in normalize_list(advisory_data.references) if ref], "weaknesses": normalize_list(advisory_data.weaknesses), - "patches": normalize_list(advisory_data.patches), } normalized_data["url"] = advisory_data.url @@ -859,3 +863,28 @@ def compute_patch_checksum(patch_text: str): Compute SHA-512 checksum for patch text. """ return hashlib.sha512(patch_text.encode("utf-8")).hexdigest() + + +def group_advisories_by_content(advisories): + grouped = {} + + for advisory in advisories: + content_hash = advisory.compute_advisory_content() + + entry = grouped.setdefault( + content_hash, + {"primary": advisory, "secondary": set()}, + ) + + primary = entry["primary"] + + if advisory is primary: + continue + + if advisory.precedence > primary.precedence: + entry["primary"] = advisory + entry["secondary"].add(primary) + else: + entry["secondary"].add(advisory) + + return grouped diff --git a/vulnerabilities/views.py b/vulnerabilities/views.py index 8a867983e..e67033da9 100644 --- a/vulnerabilities/views.py +++ b/vulnerabilities/views.py @@ -15,6 +15,7 @@ from django.contrib.auth.views import LoginView from django.core.exceptions import ValidationError from django.core.mail import send_mail +from django.db.models import Count from django.db.models import F from django.db.models import Prefetch from django.http.response import Http404 @@ -40,6 +41,7 @@ from vulnerabilities.models import PipelineSchedule from vulnerabilities.severity_systems import EPSS from vulnerabilities.severity_systems import SCORING_SYSTEMS +from vulnerabilities.utils import group_advisories_by_content from vulnerablecode import __version__ as VULNERABLECODE_VERSION from vulnerablecode.settings import env @@ -196,11 +198,27 @@ def get_context_data(self, **kwargs): fixing_advisories, ) = self.get_fixed_package_details(package) + affected_avid_by_hash = {} + fixing_avid_by_hash = {} + + affected_avid_by_hash = group_advisories_by_content(affected_by_advisories) + fixing_avid_by_hash = group_advisories_by_content(fixing_advisories) + + affecting_advs = [] + + for hash in affected_avid_by_hash: + affecting_advs.append(affected_avid_by_hash[hash]) + + fixing_advs = [] + + for hash in fixing_avid_by_hash: + fixing_advs.append(fixing_avid_by_hash[hash]) + context["package"] = package context["next_non_vulnerable"] = next_non_vulnerable context["latest_non_vulnerable"] = latest_non_vulnerable - context["affected_by_advisories"] = affected_by_advisories - context["fixing_advisories"] = fixing_advisories + context["affected_by_advisories_v2"] = affecting_advs + context["fixing_advisories_v2"] = fixing_advs context["package_search_form"] = PackageSearchForm(self.request.GET) context["fixed_package_details"] = fixed_pkg_details @@ -208,7 +226,15 @@ def get_context_data(self, **kwargs): return context def get_fixed_package_details(self, package): - affected_impacts = package.affected_in_impacts.select_related("advisory") + affected_impacts = package.affected_in_impacts.select_related("advisory").prefetch_related( + Prefetch( + "fixed_by_packages", + queryset=( + models.PackageV2.objects.annotate(affected_count=Count("affected_in_impacts")) + ), + ) + ) + fixed_impacts = package.fixed_in_impacts.select_related("advisory") affected_avids = {impact.advisory.avid for impact in affected_impacts if impact.advisory_id} @@ -217,26 +243,22 @@ def get_fixed_package_details(self, package): all_avids = affected_avids | fixed_avids - latest_advisories = models.AdvisoryV2.objects.latest_for_avids(all_avids) - advisory_by_avid = {adv.avid: adv for adv in latest_advisories} + advisories = models.AdvisoryV2.objects.latest_for_avids(all_avids) + advisory_by_avid = {adv.avid: adv for adv in advisories} fixed_pkg_details = {} for impact in affected_impacts: - avid = impact.advisory.avid - advisory = advisory_by_avid.get(avid) + advisory = advisory_by_avid.get(impact.advisory.avid) if not advisory: continue - if avid not in fixed_pkg_details: - fixed_pkg_details[avid] = [] - fixed_pkg_details[avid].extend( - [ - { - "pkg": pkg, - "affected_count": pkg.affected_in_impacts.count(), - } - for pkg in impact.fixed_by_packages.all() - ] + + fixed_pkg_details.setdefault(impact.advisory.avid, []).extend( + { + "pkg": pkg, + "affected_count": pkg.affected_count, + } + for pkg in impact.fixed_by_packages.all() ) affected_by_advisories = {