Skip to content

Commit a3ade32

Browse files
committed
Migrate alpine importer to importer improver model
Current alpine importer models need to be refactored to give AdvisoryData instead of Advisory, also add some validation to parse license expression and make affected_version_range optional Add tests to test scraping of webpages and parsing of data Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent f9f4bc0 commit a3ade32

18 files changed

Lines changed: 948 additions & 197 deletions

File tree

AUTHORS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ The following organizations or individuals have contributed to this repo:
1313
- Navonil Das @NavonilDas
1414
- Tushar Upadhyay @tushar912
1515
- Hritik Vijay @hritik14
16+
- Tushar Goel @TG1999

requirements.txt

Lines changed: 2 additions & 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.0.0
11+
univers>=30.1.0
1212
saneyaml>=0.5.2
1313
beautifulsoup4>=4.9.3
1414
python-dateutil>=2.8.1
@@ -17,5 +17,6 @@ lxml>=4.6.4
1717
gunicorn>=20.1.0
1818
django-environ==0.4.5
1919
defusedxml==0.7.1
20+
license-expression>=21.6.14
2021

2122
Markdown==3.3.4

vulnerabilities/importer.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from binaryornot.helpers import is_binary_string
3939
from git import DiffIndex
4040
from git import Repo
41+
from license_expression import Licensing
4142
from packageurl import PackageURL
4243
from univers.version_range import VersionRange
4344
from univers.versions import Version
@@ -117,22 +118,28 @@ class AffectedPackage:
117118
"""
118119
Contains a range of affected versions and a fixed version of a given package
119120
The PackageURL supplied must *not* have a version
121+
It must contain either `affected_version_range` or `fixed_version`
120122
"""
121123

122124
package: PackageURL
123-
affected_version_range: VersionRange
125+
affected_version_range: Optional[VersionRange] = None
124126
fixed_version: Optional[Version] = None
125127

126128
def __post_init__(self):
127129
if self.package.version:
128-
raise ValueError
130+
raise ValueError("The PackageURL supplied must not have a version")
131+
if not (self.affected_version_range or self.fixed_version):
132+
raise ValueError(
133+
"Affected Package should at least have either a fixed version or affected version range"
134+
)
129135

130136
def get_fixed_purl(self):
131137
"""
132138
Return PackageURL corresponding to object's fixed_version
133139
"""
134-
fixed_version = self.fixed_version
135-
fixed_purl = self.package._replace(version=str(fixed_version))
140+
if not self.fixed_version:
141+
raise ValueError("Affected package should have a fixed version")
142+
fixed_purl = self.package._replace(version=str(self.fixed_version))
136143
return fixed_purl
137144

138145
@classmethod
@@ -152,7 +159,8 @@ def merge(cls, affected_packages: Iterable):
152159
fixed_versions = set()
153160
purls = set()
154161
for pkg in affected_packages:
155-
affected_version_ranges.add(pkg.affected_version_range)
162+
if pkg.affected_version_range:
163+
affected_version_ranges.add(pkg.affected_version_range)
156164
if pkg.fixed_version:
157165
fixed_versions.add(pkg.fixed_version)
158166
purls.add(pkg.package)
@@ -164,9 +172,12 @@ def to_dict(self):
164172
"""
165173
Return a serializable dict that can be converted back using self.from_dict
166174
"""
175+
affected_version_range = None
176+
if self.affected_version_range:
177+
affected_version_range = str(self.affected_version_range)
167178
return {
168179
"package": self.package.to_dict(),
169-
"affected_version_range": str(self.affected_version_range),
180+
"affected_version_range": affected_version_range,
170181
"fixed_version": str(self.fixed_version) if self.fixed_version else None,
171182
}
172183

@@ -176,9 +187,13 @@ def from_dict(cls, affected_pkg: dict):
176187
Return an AffectedPackage object from dict generated by self.to_dict
177188
"""
178189
package = PackageURL(**affected_pkg["package"])
179-
affected_version_range = VersionRange.from_string(affected_pkg["affected_version_range"])
190+
affected_version_range = None
191+
if affected_pkg["affected_version_range"]:
192+
affected_version_range = VersionRange.from_string(
193+
affected_pkg["affected_version_range"]
194+
)
180195
fixed_version = affected_pkg["fixed_version"]
181-
if fixed_version:
196+
if fixed_version and affected_version_range:
182197
# TODO: revisit after https://github.com/nexB/univers/issues/10
183198
fixed_version = affected_version_range.version_class(fixed_version)
184199

@@ -203,7 +218,7 @@ class AdvisoryData:
203218
"""
204219

205220
aliases: List[str] = dataclasses.field(default_factory=list)
206-
summary: str = None
221+
summary: Optional[str] = None
207222
affected_packages: List[AffectedPackage] = dataclasses.field(default_factory=list)
208223
references: List[Reference] = dataclasses.field(default_factory=list)
209224
date_published: Optional[datetime.datetime] = None
@@ -217,17 +232,29 @@ class NoLicenseError(Exception):
217232
pass
218233

219234

235+
class InvalidSPDXLicense(Exception):
236+
pass
237+
238+
220239
class Importer:
221240
"""
222241
An Importer collects data from various upstreams and returns corresponding AdvisoryData objects
223242
in its advisory_data method. Subclass this class to implement an importer
224243
"""
225244

226245
spdx_license_expression = ""
246+
license_url = ""
227247

228248
def __init__(self):
229249
if not self.spdx_license_expression:
230250
raise Exception(f"Cannot run importer {self!r} without a license")
251+
licensing = Licensing()
252+
try:
253+
licensing.parse(self.spdx_license_expression)
254+
except InvalidSPDXLicense as e:
255+
raise ValueError(
256+
f"{self.spdx_license_expression!r} is not a valid SPDX license expression"
257+
) from e
231258

232259
@classproperty
233260
def qualified_name(cls):

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
# for any legal advice.
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.
22+
from vulnerabilities.importers import alpine_linux
2223
from vulnerabilities.importers import nginx
2324

24-
IMPORTERS_REGISTRY = [nginx.NginxImporter]
25+
IMPORTERS_REGISTRY = [nginx.NginxImporter, alpine_linux.AlpineImporter]
2526

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

0 commit comments

Comments
 (0)