Skip to content

Commit d1bc81c

Browse files
committed
Migrate github importer to importer-improver model
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 8c69661 commit d1bc81c

30 files changed

Lines changed: 5119 additions & 578 deletions

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ django-widget-tweaks>=1.4.8
88
packageurl-python>=0.9.4
99
binaryornot>=0.4.4
1010
GitPython>=3.1.17
11-
univers>=30.1.0
11+
univers>=30.3.1
1212
saneyaml>=0.5.2
1313
beautifulsoup4>=4.9.3
1414
python-dateutil>=2.8.1

vulnerabilities/helpers.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import dataclasses
2525
import json
2626
import re
27+
from functools import total_ordering
2728
from typing import List
2829
from typing import Optional
2930
from typing import Tuple
@@ -34,6 +35,7 @@
3435
import toml
3536
import urllib3
3637
from packageurl import PackageURL
38+
from univers.version_range import RANGE_CLASS_BY_SCHEMES
3739

3840
# TODO add logging here
3941

@@ -133,32 +135,36 @@ def requests_with_5xx_retry(max_retries=5, backoff_factor=0.5):
133135
return session
134136

135137

136-
def nearest_patched_package(
137-
vulnerable_packages: List[PackageURL], resolved_packages: List[PackageURL]
138-
) -> List[AffectedPackage]:
139-
class PackageURLWithVersionComparator:
140-
"""
141-
This class is used to get around bisect module's lack of supplying custom
142-
compartor. Get rid of this once we use python 3.10 which supports this.
143-
See https://github.com/python/cpython/pull/20556
144-
"""
138+
@total_ordering
139+
class VersionedPackage:
140+
"""
141+
A PackageURL with a Version class.
142+
This class is used to get around bisect module's lack of supplying custom
143+
comparator. Get rid of this once we use python 3.10 which supports this.
144+
See https://github.com/python/cpython/pull/20556
145+
"""
145146

146-
def __init__(self, package):
147-
self.package = package
148-
self.version_object = version_class_by_package_type[package.type](package.version)
147+
def __init__(self, purl: PackageURL):
148+
self.purl = purl
149+
vrc = RANGE_CLASS_BY_SCHEMES.get(purl.type)
150+
self.version = vrc.version_class(purl.version)
149151

150-
def __eq__(self, other):
151-
return self.version_object == other.version_object
152+
def __eq__(self, other):
153+
return self.version == other.version
152154

153-
def __lt__(self, other):
154-
return self.version_object < other.version_object
155+
def __lt__(self, other):
156+
return self.version < other.version
155157

156-
vulnerable_packages = sorted(
157-
[PackageURLWithVersionComparator(package) for package in vulnerable_packages]
158-
)
159-
resolved_packages = sorted(
160-
[PackageURLWithVersionComparator(package) for package in resolved_packages]
161-
)
158+
159+
def nearest_patched_package(
160+
vulnerable_packages: List[PackageURL], resolved_packages: List[PackageURL]
161+
) -> List[AffectedPackage]:
162+
"""
163+
Return a list of Affected
164+
"""
165+
166+
vulnerable_packages = sorted([VersionedPackage(package) for package in vulnerable_packages])
167+
resolved_packages = sorted([VersionedPackage(package) for package in resolved_packages])
162168

163169
resolved_package_count = len(resolved_packages)
164170
affected_package_with_patched_package_objects = []
@@ -167,11 +173,11 @@ def __lt__(self, other):
167173
patched_package_index = bisect.bisect_right(resolved_packages, vulnerable_package)
168174
patched_package = None
169175
if patched_package_index < resolved_package_count:
170-
patched_package = resolved_packages[patched_package_index].package
176+
patched_package = resolved_packages[patched_package_index]
171177

172178
affected_package_with_patched_package_objects.append(
173179
AffectedPackage(
174-
vulnerable_package=vulnerable_package.package, patched_package=patched_package
180+
vulnerable_package=vulnerable_package.purl, patched_package=patched_package.purl
175181
)
176182
)
177183

vulnerabilities/importer.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,18 @@ def from_dict(cls, ref: dict):
113113
)
114114

115115

116+
class UnMergeablePackageError(Exception):
117+
"""
118+
Raised when a package cannot be merged with another one.
119+
"""
120+
121+
122+
class NoAffectedPackages(Exception):
123+
"""
124+
Raised when there were no affected packages found.
125+
"""
126+
127+
116128
@dataclasses.dataclass(order=True, frozen=True)
117129
class AffectedPackage:
118130
"""
@@ -155,18 +167,23 @@ def merge(cls, affected_packages: Iterable):
155167
affected_version_range: set(VersionRange)
156168
fixed_versions: set(Version)
157169
"""
158-
affected_version_ranges = set()
159-
fixed_versions = set()
170+
affected_packages = list(affected_packages)
171+
if not affected_packages:
172+
raise NoAffectedPackages("No affected packages found")
173+
affected_version_ranges = list()
174+
fixed_versions = list()
160175
purls = set()
161176
for pkg in affected_packages:
162177
if pkg.affected_version_range:
163-
affected_version_ranges.add(pkg.affected_version_range)
178+
if pkg.affected_version_range not in affected_version_ranges:
179+
affected_version_ranges.append(pkg.affected_version_range)
164180
if pkg.fixed_version:
165-
fixed_versions.add(pkg.fixed_version)
181+
if pkg.fixed_version not in fixed_versions:
182+
fixed_versions.append(pkg.fixed_version)
166183
purls.add(pkg.package)
167184
if len(purls) > 1:
168-
raise TypeError("Cannot merge with different purls", purls)
169-
return purls.pop(), affected_version_ranges, fixed_versions
185+
raise UnMergeablePackageError("Cannot merge with different purls", purls)
186+
return purls.pop(), sorted(affected_version_ranges), sorted(fixed_versions)
170187

171188
def to_dict(self):
172189
"""
@@ -230,6 +247,15 @@ def __post_init__(self):
230247
if self.date_published and not self.date_published.tzinfo:
231248
logger.warn(f"AdvisoryData with no tzinfo: {self!r}")
232249

250+
def to_dict(self):
251+
return {
252+
"aliases": self.aliases,
253+
"summary": self.summary,
254+
"affected_packages": [pkg.to_dict() for pkg in self.affected_packages],
255+
"references": [ref.to_dict() for ref in self.references],
256+
"date_published": self.date_published.isoformat() if self.date_published else None,
257+
}
258+
233259

234260
class NoLicenseError(Exception):
235261
pass

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
# VulnerableCode is a free software code scanning tool from nexB Inc. and others.
2121
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
2222
from vulnerabilities.importers import alpine_linux
23+
from vulnerabilities.importers import github
2324
from vulnerabilities.importers import nginx
2425

25-
IMPORTERS_REGISTRY = [nginx.NginxImporter, alpine_linux.AlpineImporter]
26+
IMPORTERS_REGISTRY = [nginx.NginxImporter, alpine_linux.AlpineImporter, github.GitHubAPIImporter]
2627

2728
IMPORTERS_REGISTRY = {x.qualified_name: x for x in IMPORTERS_REGISTRY}

0 commit comments

Comments
 (0)