Skip to content

Commit 5152a97

Browse files
authored
Merge pull request #421 from nexB/drop_dephell_specifier
Drop dephell specifier
2 parents 1221e29 + 0ca5e16 commit 5152a97

24 files changed

Lines changed: 510 additions & 265 deletions

requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
aiohttp==3.7.4
22
asgiref==3.2.7
3-
attrs==19.3.0
3+
attrs==20.3.0
44
backcall==0.1.0
55
beautifulsoup4==4.7.1
66
cached-property==1.5.1
77
cffi==1.14.0
88
contextlib2==0.5.5
99
decorator==4.4.2
10-
dephell-specifier==0.2.1
10+
univers==21.4.8
1111
dj-database-url==0.4.2
1212
Django==3.0.13
1313
django-filter==2.2.0
@@ -22,7 +22,7 @@ jedi==0.17.0
2222
lxml==4.6.3
2323
more-itertools==8.0.2
2424
packageurl-python==0.9.3
25-
packaging==19.2
25+
packaging==20.9
2626
parso==0.7.0
2727
pexpect==4.8.0
2828
pickleshare==0.7.5

vulnerabilities/data_source.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141

4242
import pygit2
4343
from packageurl import PackageURL
44+
from univers.version_specifier import VersionSpecifier
45+
from univers.versions import version_class_by_package_type
4446

4547
from vulnerabilities.oval_parser import OvalParser
4648
from vulnerabilities.severity_systems import ScoringSystem
@@ -514,7 +516,7 @@ def get_data_from_xml_doc(self, xml_doc: ET.ElementTree, pkg_metadata={}) -> Lis
514516
"type" key.
515517
516518
Example value of pkg_metadata:
517-
{"type":"deb","qualifiers":{"distro":"buster"} }
519+
{"type":"deb","qualifiers":{"distro":"buster"} }
518520
"""
519521

520522
all_adv = []
@@ -536,7 +538,14 @@ def get_data_from_xml_doc(self, xml_doc: ET.ElementTree, pkg_metadata={}) -> Lis
536538
for package_name in test_data["package_list"]:
537539
if package_name and len(package_name) >= 50:
538540
continue
539-
aff_ver_range = test_data["version_ranges"] or set()
541+
542+
affected_version_range = test_data["version_ranges"] or set()
543+
version_class = version_class_by_package_type[pkg_metadata["type"]]
544+
version_scheme = version_class.scheme
545+
546+
affected_version_range = VersionSpecifier.from_scheme_version_spec_string(
547+
version_scheme, affected_version_range
548+
)
540549
all_versions = self.pkg_manager_api.get(package_name)
541550

542551
# FIXME: what is this 50 DB limit? that's too small for versions
@@ -546,7 +555,9 @@ def get_data_from_xml_doc(self, xml_doc: ET.ElementTree, pkg_metadata={}) -> Lis
546555
all_versions = set(filter(lambda x: len(x) < 50, all_versions))
547556
if not all_versions:
548557
continue
549-
affected_versions = set(filter(lambda x: x in aff_ver_range, all_versions))
558+
affected_versions = set(
559+
filter(lambda x: version_class(x) in affected_version_range, all_versions)
560+
)
550561
safe_versions = all_versions - affected_versions
551562

552563
for version in affected_versions:

vulnerabilities/helpers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,11 @@ def requests_with_5xx_retry(max_retries=5, backoff_factor=0.5):
9898
session.mount("https://", adapter)
9999
session.mount("http://", adapter)
100100
return session
101+
102+
103+
def contains_alpha(string):
104+
"""
105+
Return True if the input 'string' contains any alphabet
106+
"""
107+
108+
return any([c.isalpha() for c in string])

vulnerabilities/importers/apache_kafka.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424

2525
import requests
2626
from bs4 import BeautifulSoup
27-
from dephell_specifier import RangeSpecifier
2827
from packageurl import PackageURL
28+
from univers.versions import MavenVersion
29+
from univers.version_specifier import VersionSpecifier
2930

3031
from vulnerabilities.data_source import Advisory
3132
from vulnerabilities.data_source import DataSource
@@ -71,13 +72,23 @@ def to_advisory(self, advisory_page):
7172
fixed_packages = [
7273
PackageURL(type="apache", name="kafka", version=version)
7374
for version in self.version_api.get("apache/kafka")
74-
if any([version in version_range for version_range in fixed_version_ranges])
75+
if any(
76+
[
77+
MavenVersion(version) in version_range
78+
for version_range in fixed_version_ranges
79+
]
80+
)
7581
]
7682

7783
affected_packages = [
7884
PackageURL(type="apache", name="kafka", version=version)
7985
for version in self.version_api.get("apache/kafka")
80-
if any([version in version_range for version_range in affected_version_ranges])
86+
if any(
87+
[
88+
MavenVersion(version) in version_range
89+
for version_range in affected_version_ranges
90+
]
91+
)
8192
]
8293

8394
advisories.append(
@@ -107,14 +118,22 @@ def to_version_ranges(version_range_text):
107118
lower_bound, upper_bound = range_expression.split("to")
108119
lower_bound = f">={lower_bound}"
109120
upper_bound = f"<={upper_bound}"
110-
version_ranges.append(RangeSpecifier(f"{lower_bound},{upper_bound}"))
121+
version_ranges.append(
122+
VersionSpecifier.from_scheme_version_spec_string(
123+
"maven", f"{lower_bound},{upper_bound}"
124+
)
125+
)
111126

112127
elif "and later" in range_expression:
113128
# eg range_expression == "2.1.1 and later"
114129
range_expression = range_expression.replace("and later", "")
115-
version_ranges.append(RangeSpecifier(f">={range_expression}"))
130+
version_ranges.append(
131+
VersionSpecifier.from_scheme_version_spec_string("maven", f">={range_expression}")
132+
)
116133

117134
else:
118135
# eg range_expression == "3.0.0"
119-
version_ranges.append(RangeSpecifier(range_expression))
136+
version_ranges.append(
137+
VersionSpecifier.from_scheme_version_spec_string("maven", range_expression)
138+
)
120139
return version_ranges

vulnerabilities/importers/apache_tomcat.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727

2828
import requests
2929
from bs4 import BeautifulSoup
30-
from dephell_specifier import RangeSpecifier
30+
from univers.version_specifier import VersionSpecifier
31+
from univers.versions import MavenVersion
3132
from packageurl import PackageURL
3233

3334
from vulnerabilities.data_source import Advisory
@@ -101,7 +102,7 @@ def to_advisories(self, apache_tomcat_advisory_html):
101102
type="maven", namespace="apache", name="tomcat", version=version
102103
)
103104
for version in self.version_api.get("org.apache.tomcat:tomcat")
104-
if version in version_range
105+
if MavenVersion(version) in version_range
105106
]
106107
)
107108

@@ -126,16 +127,21 @@ def to_advisories(self, apache_tomcat_advisory_html):
126127

127128
def parse_version_ranges(string):
128129
"""
129-
This method yields Rangespecifier objects obtained by
130+
This method yields VersionSpecifier objects obtained by
130131
parsing `string`.
131-
>> list(parse_version_ranges("Affects: 9.0.0.M1 to 9.0.0.M9"))
132-
[RangeSpecifier(<=9.0.0.M9,>=9.0.0.M1)]
133-
134-
>> list(parse_version_ranges("Affects: 9.0.0.M1"))
135-
[RangeSpecifier(>=9.0.0.M1<=9.0.0.M1)]
136-
137-
>> list(parse_version_ranges("Affects: 9.0.0.M1 to 9.0.0.M9, 1.2.3 to 3.4.5"))
138-
[RangeSpecifier(<=9.0.0.M9,>=9.0.0.M1), RangeSpecifier(<=3.4.5,>=1.2.3)]
132+
>>> list(parse_version_ranges("Affects: 9.0.0.M1 to 9.0.0.M9")) == [
133+
... VersionSpecifier.from_scheme_version_spec_string('maven','<=9.0.0.M9,>=9.0.0.M1')
134+
... ]
135+
True
136+
>>> list(parse_version_ranges("Affects: 9.0.0.M1")) == [
137+
... VersionSpecifier.from_scheme_version_spec_string('maven','>=9.0.0.M1,<=9.0.0.M1')
138+
... ]
139+
True
140+
>>> list(parse_version_ranges("Affects: 9.0.0.M1 to 9.0.0.M9, 1.2.3 to 3.4.5")) == [
141+
... VersionSpecifier.from_scheme_version_spec_string('maven','<=9.0.0.M9,>=9.0.0.M1'),
142+
... VersionSpecifier.from_scheme_version_spec_string('maven','<=3.4.5,>=1.2.3')
143+
... ]
144+
True
139145
"""
140146
version_rng_txt = string.split("Affects:")[-1].strip()
141147
version_ranges = version_rng_txt.split(",")
@@ -147,4 +153,6 @@ def parse_version_ranges(string):
147153
else:
148154
lower_bound = upper_bound = version_range
149155

150-
yield RangeSpecifier(">=" + lower_bound + "<=" + upper_bound)
156+
yield VersionSpecifier.from_scheme_version_spec_string(
157+
"maven", f">={lower_bound},<={upper_bound}"
158+
)

vulnerabilities/importers/elixir_security.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
from typing import List, Set
2525

2626
import yaml
27-
from dephell_specifier import RangeSpecifier
27+
from univers.version_specifier import VersionSpecifier
28+
from univers.versions import SemverVersion
2829
from packageurl import PackageURL
2930

3031
from vulnerabilities.data_source import GitDataSource
@@ -86,9 +87,13 @@ def get_versions_for_pkg_from_range_list(self, version_range_list, pkg_name):
8687
all_version_list = self.pkg_manager_api.get(pkg_name)
8788
if not version_range_list:
8889
return [], all_version_list
89-
version_ranges = {RangeSpecifier(r) for r in version_range_list}
90+
version_ranges = [
91+
VersionSpecifier.from_scheme_version_spec_string("semver", r)
92+
for r in version_range_list
93+
]
9094
for version in all_version_list:
91-
if any([version in v for v in version_ranges]):
95+
version_obj = SemverVersion(version)
96+
if any([version_obj in v for v in version_ranges]):
9297
safe_pkg_versions.append(version)
9398

9499
vuln_pkg_versions = set(all_version_list) - set(safe_pkg_versions)

vulnerabilities/importers/github.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131
from typing import Optional
3232

3333
import requests
34-
from dephell_specifier import RangeSpecifier
3534
from packageurl import PackageURL
35+
from univers.version_specifier import VersionSpecifier
36+
from univers.versions import version_class_by_package_type
3637

3738
from vulnerabilities.data_source import Advisory
3839
from vulnerabilities.data_source import DataSource
@@ -200,7 +201,7 @@ def process_response(self) -> List[Advisory]:
200201
ns, pkg_name = self.process_name(ecosystem, name)
201202
aff_range = adv["node"]["vulnerableVersionRange"]
202203
aff_vers, unaff_vers = self.categorize_versions(
203-
aff_range, self.version_api.get(name)
204+
self.version_api.package_type, aff_range, self.version_api.get(name)
204205
)
205206
affected_purls = {
206207
PackageURL(name=pkg_name, namespace=ns, version=version, type=pkg_type)
@@ -252,8 +253,14 @@ def process_response(self) -> List[Advisory]:
252253

253254
@staticmethod
254255
def categorize_versions(
255-
version_range: str, all_versions: Set[str]
256-
) -> Tuple[Set[str], Set[str]]: # nopep8
257-
version_range = RangeSpecifier(version_range)
258-
affected_versions = {version for version in all_versions if version in version_range}
256+
package_type: str, version_range: str, all_versions: Set[str]
257+
) -> Tuple[Set[str], Set[str]]:
258+
version_class = version_class_by_package_type[package_type]
259+
version_scheme = version_class.scheme
260+
version_range = VersionSpecifier.from_scheme_version_spec_string(
261+
version_scheme, version_range
262+
)
263+
affected_versions = {
264+
version for version in all_versions if version_class(version) in version_range
265+
}
259266
return (affected_versions, all_versions - affected_versions)

vulnerabilities/importers/istio.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@
2323
import asyncio
2424
import re
2525
from typing import List, Set
26-
2726
import yaml
2827

29-
from dephell_specifier import RangeSpecifier
28+
from univers.version_specifier import VersionSpecifier
29+
from univers.versions import SemverVersion
3030
from packageurl import PackageURL
31+
3132
from vulnerabilities.data_source import Advisory, GitDataSource, Reference
3233
from vulnerabilities.package_managers import GitHubTagsAPI
3334

@@ -64,9 +65,13 @@ def get_pkg_versions_from_ranges(self, version_range_list):
6465
all_version = self.version_api.get("istio/istio")
6566
safe_pkg_versions = []
6667
vuln_pkg_versions = []
67-
version_ranges = [RangeSpecifier(r) for r in version_range_list]
68+
version_ranges = [
69+
VersionSpecifier.from_scheme_version_spec_string("semver", r)
70+
for r in version_range_list
71+
]
6872
for version in all_version:
69-
if any([version in v for v in version_ranges]):
73+
version_obj = SemverVersion(version)
74+
if any([version_obj in v for v in version_ranges]):
7075
vuln_pkg_versions.append(version)
7176

7277
safe_pkg_versions = set(all_version) - set(vuln_pkg_versions)

vulnerabilities/importers/nginx.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
import requests
2828
from packageurl import PackageURL
2929
from bs4 import BeautifulSoup
30-
from dephell_specifier import RangeSpecifier
30+
from univers.version_specifier import VersionSpecifier
31+
from univers.versions import SemverVersion
3132

3233
from vulnerabilities.data_source import Advisory
3334
from vulnerabilities.data_source import DataSource
@@ -58,10 +59,10 @@ def set_api(self):
5859

5960
def updated_advisories(self):
6061
advisories = []
61-
if create_etag(data_src=self, url=self.url, etag_key="ETag"):
62-
self.set_api()
63-
data = requests.get(self.url).content
64-
advisories.extend(self.to_advisories(data))
62+
# if create_etag(data_src=self, url=self.url, etag_key="ETag"):
63+
self.set_api()
64+
data = requests.get(self.url).content
65+
advisories.extend(self.to_advisories(data))
6566
return self.batch_advisories(advisories)
6667

6768
def to_advisories(self, data):
@@ -126,13 +127,15 @@ def extract_fixed_pkgs(self, vuln_info):
126127
raw_ranges = version_info.split(",")
127128
version_ranges = []
128129
for rng in raw_ranges:
129-
# Eg. "1.7.3+" gets converted to RangeSpecifier("^1.7.3")
130+
# Eg. "1.7.3+" gets converted to VersionSpecifier.from_scheme_version_spec_string("semver","^1.7.3")
130131
# The advisory in this case uses `+` in the sense that any version
131132
# with greater or equal `minor` version satisfies the range.
132133
# "1.7.4" satisifes "1.7.3+", but "1.8.4" does not. "1.7.3+" has same
133134
# semantics as that of "^1.7.3"
134135

135-
version_ranges.append(RangeSpecifier("^" + rng[:-1]))
136+
version_ranges.append(
137+
VersionSpecifier.from_scheme_version_spec_string("semver", "^" + rng[:-1])
138+
)
136139

137140
valid_versions = find_valid_versions(self.version_api.get("nginx/nginx"), version_ranges)
138141

@@ -148,16 +151,26 @@ def extract_vuln_pkgs(self, vuln_info):
148151
version_ranges = []
149152
windows_only = False
150153
for version_info in version_infos.split(", "):
154+
if version_info == "all":
155+
# This is misleading since eventually some version get fixed.
156+
continue
157+
151158
if "-" not in version_info:
152159
# These are discrete versions
153-
version_ranges.append(RangeSpecifier(version_info[0]))
160+
version_ranges.append(
161+
VersionSpecifier.from_scheme_version_spec_string("semver", version_info[0])
162+
)
154163
continue
155164

156165
windows_only = "nginx/Windows" in version_info
157166
version_info = version_info.replace("nginx/Windows", "")
158167
lower_bound, upper_bound = version_info.split("-")
159168

160-
version_ranges.append(RangeSpecifier(f">={lower_bound},<={upper_bound}"))
169+
version_ranges.append(
170+
VersionSpecifier.from_scheme_version_spec_string(
171+
"semver", f">={lower_bound},<={upper_bound}"
172+
)
173+
)
161174

162175
valid_versions = find_valid_versions(self.version_api.get("nginx/nginx"), version_ranges)
163176
qualifiers = {}
@@ -173,7 +186,8 @@ def extract_vuln_pkgs(self, vuln_info):
173186
def find_valid_versions(versions, version_ranges):
174187
valid_versions = set()
175188
for version in versions:
176-
if any([version in ver_range for ver_range in version_ranges]):
189+
version_obj = SemverVersion(version)
190+
if any([version_obj in ver_range for ver_range in version_ranges]):
177191
valid_versions.add(version)
178192

179193
return valid_versions

0 commit comments

Comments
 (0)