|
6 | 6 | # See https://github.com/nexB/vulnerablecode for support or download. |
7 | 7 | # See https://aboutcode.org for more information about nexB OSS projects. |
8 | 8 | # |
| 9 | +import logging |
9 | 10 | import re |
| 11 | +from datetime import datetime |
10 | 12 | from pathlib import Path |
| 13 | +from typing import Iterable |
| 14 | +from typing import List |
| 15 | +from typing import Mapping |
| 16 | +from typing import Optional |
11 | 17 | from typing import Set |
12 | 18 |
|
13 | 19 | import pytz |
14 | 20 | import saneyaml |
15 | 21 | from dateutil import parser |
| 22 | +from django.db.models.query import QuerySet |
16 | 23 | from packageurl import PackageURL |
17 | 24 | from univers.version_constraint import VersionConstraint |
18 | 25 | from univers.version_range import GitHubVersionRange |
|
23 | 30 | from vulnerabilities.importer import AffectedPackage |
24 | 31 | from vulnerabilities.importer import Importer |
25 | 32 | from vulnerabilities.importer import Reference |
| 33 | +from vulnerabilities.importer import UnMergeablePackageError |
| 34 | +from vulnerabilities.improver import Improver |
| 35 | +from vulnerabilities.improver import Inference |
| 36 | +from vulnerabilities.models import Advisory |
| 37 | +from vulnerabilities.package_managers import GitHubTagsAPI |
| 38 | +from vulnerabilities.package_managers import VersionAPI |
| 39 | +from vulnerabilities.utils import AffectedPackage as LegacyAffectedPackage |
| 40 | +from vulnerabilities.utils import get_affected_packages_by_patched_package |
| 41 | +from vulnerabilities.utils import nearest_patched_package |
| 42 | +from vulnerabilities.utils import resolve_version_range |
26 | 43 | from vulnerabilities.utils import split_markdown_front_matter |
27 | 44 |
|
28 | 45 | is_release = re.compile(r"^[\d.]+$", re.IGNORECASE).match |
29 | 46 |
|
| 47 | +logger = logging.getLogger(__name__) |
| 48 | + |
30 | 49 |
|
31 | 50 | class IstioImporter(Importer): |
32 | 51 | spdx_license_expression = "Apache-2.0" |
@@ -136,3 +155,70 @@ def get_data_from_md(self, path): |
136 | 155 | with open(path) as f: |
137 | 156 | front_matter, _ = split_markdown_front_matter(f.read()) |
138 | 157 | return saneyaml.load(front_matter) |
| 158 | + |
| 159 | + |
| 160 | +class IstioImprover(Improver): |
| 161 | + def __init__(self) -> None: |
| 162 | + self.versions_fetcher_by_purl: Mapping[str, VersionAPI] = {} |
| 163 | + self.vesions_by_purl = {} |
| 164 | + |
| 165 | + @property |
| 166 | + def interesting_advisories(self) -> QuerySet: |
| 167 | + return Advisory.objects.filter(created_by=IstioImporter.qualified_name) |
| 168 | + |
| 169 | + def get_package_versions( |
| 170 | + self, package_url: PackageURL, until: Optional[datetime] = None |
| 171 | + ) -> List[str]: |
| 172 | + """ |
| 173 | + Return a list of `valid_versions` for the `package_url` |
| 174 | + """ |
| 175 | + api_name = "istio/istio" |
| 176 | + versions_fetcher = GitHubTagsAPI() |
| 177 | + return versions_fetcher.get_until(package_name=api_name, until=until).valid_versions |
| 178 | + |
| 179 | + def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]: |
| 180 | + """ |
| 181 | + Yield Inferences for the given advisory data |
| 182 | + """ |
| 183 | + if not advisory_data.affected_packages: |
| 184 | + return |
| 185 | + for affected_package in advisory_data.affected_packages: |
| 186 | + purl = affected_package.package |
| 187 | + affected_version_range = affected_package.affected_version_range |
| 188 | + pkg_type = purl.type |
| 189 | + pkg_namespace = purl.namespace |
| 190 | + pkg_name = purl.name |
| 191 | + if not self.vesions_by_purl.get("istio/istio"): |
| 192 | + valid_versions = self.get_package_versions( |
| 193 | + package_url=purl, until=advisory_data.date_published |
| 194 | + ) |
| 195 | + self.vesions_by_purl["istio/istio"] = valid_versions |
| 196 | + valid_versions = self.vesions_by_purl["istio/istio"] |
| 197 | + aff_vers, unaff_vers = resolve_version_range( |
| 198 | + affected_version_range=affected_version_range, |
| 199 | + package_versions=valid_versions, |
| 200 | + ) |
| 201 | + affected_purls = [ |
| 202 | + PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version) |
| 203 | + for version in aff_vers |
| 204 | + ] |
| 205 | + |
| 206 | + unaffected_purls = [ |
| 207 | + PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version) |
| 208 | + for version in unaff_vers |
| 209 | + ] |
| 210 | + |
| 211 | + affected_packages: List[LegacyAffectedPackage] = nearest_patched_package( |
| 212 | + vulnerable_packages=affected_purls, resolved_packages=unaffected_purls |
| 213 | + ) |
| 214 | + |
| 215 | + for ( |
| 216 | + fixed_package, |
| 217 | + affected_packages, |
| 218 | + ) in get_affected_packages_by_patched_package(affected_packages).items(): |
| 219 | + yield Inference.from_advisory_data( |
| 220 | + advisory_data, |
| 221 | + confidence=100, # We are getting all valid versions to get this inference |
| 222 | + affected_purls=affected_packages, |
| 223 | + fixed_purl=fixed_package, |
| 224 | + ) |
0 commit comments