From d2a2167d14389a5681e6f4c0e688d86d1c1cf514 Mon Sep 17 00:00:00 2001 From: Hritik Vijay Date: Mon, 30 Aug 2021 04:40:25 +0530 Subject: [PATCH] [WIP] Adding a TimeTravel improver for nginx ---------------------NOT READY FOR REVIEW YET-------------------- This is not the best way. Maybe we want a generic time travel improver which could run on multiple data source. This is highly experimental and is meant to serve as proof of work for current model. Currently, this is incomplete and doesn't work. The classes and functions used in time travel have not been ported properly. This commit might need a full rewrite Signed-off-by: Hritik Vijay --- vulnerabilities/helpers.py | 13 +++-- vulnerabilities/importers/nginx.py | 84 +++++++++++++++++++++------ vulnerabilities/improvers/__init__.py | 6 +- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/vulnerabilities/helpers.py b/vulnerabilities/helpers.py index 95dc2d801..5239d3df3 100644 --- a/vulnerabilities/helpers.py +++ b/vulnerabilities/helpers.py @@ -124,7 +124,7 @@ def requests_with_5xx_retry(max_retries=5, backoff_factor=0.5): def nearest_patched_package( vulnerable_packages: List[PackageURL], resolved_packages: List[PackageURL] -) -> List[AffectedPackage]: +) -> List[Tuple]: class PackageURLWithVersionComparator: """ This class is used to get around bisect module's lack of supplying custom @@ -158,11 +158,12 @@ def __lt__(self, other): if patched_package_index < resolved_package_count: patched_package = resolved_packages[patched_package_index].package - affected_package_with_patched_package_objects.append( - AffectedPackage( - vulnerable_package=vulnerable_package.package, patched_package=patched_package - ) - ) +# affected_package_with_patched_package_objects.append( +# AffectedPackage( +# vulnerable_package=vulnerable_package.package, patched_package=patched_package +# ) +# ) + affected_package_with_patched_package_objects.append((vulnerable_package.package, patched_package)) return affected_package_with_patched_package_objects diff --git a/vulnerabilities/importers/nginx.py b/vulnerabilities/importers/nginx.py index ef22d5095..1a9deeb7a 100644 --- a/vulnerabilities/importers/nginx.py +++ b/vulnerabilities/importers/nginx.py @@ -36,10 +36,12 @@ from vulnerabilities.data_source import DataSource from vulnerabilities.data_source import DataSourceConfiguration from vulnerabilities.data_source import Reference +from vulnerabilities.data_inference import Inference +from vulnerabilities.data_inference import Improver from vulnerabilities.package_managers import GitHubTagsAPI from vulnerabilities.package_managers import Version from vulnerabilities.helpers import nearest_patched_package - +from vulnerabilities.models import Advisory @dataclasses.dataclass class NginxDataSourceConfiguration(DataSourceConfiguration): @@ -51,21 +53,6 @@ class NginxDataSource(DataSource): url = "http://nginx.org/en/security_advisories.html" - def set_api(self): - self.version_api = GitHubTagsAPI() - asyncio.run(self.version_api.load_api(["nginx/nginx"])) - - # For some reason nginx tags it's releases are in the form of `release-1.2.3` - # Chop off the `release-` part here. - normalized_versions = set() - while self.version_api.cache["nginx/nginx"]: - version = self.version_api.cache["nginx/nginx"].pop() - normalized_version = Version( - version.value.replace("release-", ""), version.release_date - ) - normalized_versions.add(normalized_version) - self.version_api.cache["nginx/nginx"] = normalized_versions - def advisory_data(self) -> List[AdvisoryData]: adv_data = [] # self.set_api() @@ -175,7 +162,9 @@ def extract_vuln_pkgs(self, vuln_info): if "-" not in version_info: # These are discrete versions version_ranges.append( - VersionSpecifier.from_scheme_version_spec_string("semver", version_info[0]) + VersionSpecifier.from_scheme_version_spec_string( + "semver", version_info[0] + ) ) continue @@ -200,6 +189,67 @@ def extract_vuln_pkgs(self, vuln_info): ] +class NginxTimeTravel(Improver): + def infer(self): + self.set_api() + advisories = Advisory.objects.filter( + source="vulnerabilities.importers.nginx.NginxDataSource" + ) + inferences = [] + for advisory in advisories: + advisory_data = AdvisoryData.from_json(advisory.data) + + affected_package_ranges = [ + pkg.version_specifier for pkg in advisory_data.affected_packages + ] + affected_package_versions = find_valid_versions( + self.version_api.get("nginx/nginx").valid_versions, + affected_package_ranges, + ) + affected_packages = [] + for pkg in advisory_data.affected_packages: + for + affected_packages.extend([]) + affected_packages = [advisory_data.affected_packages] + + fixed_package_ranges = [ + pkg.version_specifier for pkg in advisory_data.affected_packages + ] + fixed_packages = find_valid_versions( + self.version_api.get("nginx/nginx").valid_versions, fixed_package_ranges + ) + + pkgs = nearest_patched_package(affected_package_versions, fixed_package_ranges) + for pkg in pkgs: + print(pkg) + print(type(pkg)) + inferences.append( + Inference( + confidence=90, # TODO: Decide properly + vulnerability_id=advisory_data.vulnerability_id, + affected_packages=pkg[0], + fixed_packages=pkg[1], + ) + ) + + return inferences + + def set_api(self): + self.version_api = GitHubTagsAPI() + asyncio.run(self.version_api.load_api(["nginx/nginx"])) + + # For some reason nginx tags it's releases are in the form of `release-1.2.3` + # Chop off the `release-` part here. + normalized_versions = set() + while self.version_api.cache["nginx/nginx"]: + version = self.version_api.cache["nginx/nginx"].pop() + normalized_version = Version( + version.value.replace("release-", ""), version.release_date + ) + normalized_versions.add(normalized_version) + self.version_api.cache["nginx/nginx"] = normalized_versions + + def find_valid_versions(versions, version_ranges): valid_versions = set() for version in versions: diff --git a/vulnerabilities/improvers/__init__.py b/vulnerabilities/improvers/__init__.py index a2ad6e35e..66822ff0d 100644 --- a/vulnerabilities/improvers/__init__.py +++ b/vulnerabilities/improvers/__init__.py @@ -1,5 +1,9 @@ +from vulnerabilities.importers.nginx import NginxTimeTravel from . import default -IMPROVER_REGISTRY = [default.DefaultImprover] +IMPROVER_REGISTRY = [ + default.DefaultImprover, + NginxTimeTravel, +] improver_mapping = {f"{x.__module__}.{x.__name__}": x for x in IMPROVER_REGISTRY}