Skip to content

Commit e9943b0

Browse files
authored
Merge pull request #467 from nexB/time_travel_advisory
Time travel to the date of advisory publish time when importing
2 parents d94f4f6 + ca2293a commit e9943b0

31 files changed

Lines changed: 682 additions & 234 deletions

pytest.ini

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
[pytest]
2-
DJANGO_SETTINGS_MODULE = vulnerablecode.settings
2+
DJANGO_SETTINGS_MODULE = vulnerablecode.settings
3+
markers =
4+
webtest

vulnerabilities/importers/apache_httpd.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
from vulnerabilities.data_source import VulnerabilitySeverity
3838
from vulnerabilities.package_managers import GitHubTagsAPI
3939
from vulnerabilities.severity_systems import scoring_systems
40-
from vulnerabilities.helpers import create_etag
4140
from vulnerabilities.helpers import nearest_patched_package
4241

4342

@@ -106,7 +105,7 @@ def to_advisory(self, data):
106105
fixed_packages.extend(
107106
[
108107
PackageURL(type="apache", name="httpd", version=version)
109-
for version in self.version_api.get("apache/httpd")
108+
for version in self.version_api.get("apache/httpd").valid_versions
110109
if MavenVersion(version) in version_range
111110
]
112111
)
@@ -115,7 +114,7 @@ def to_advisory(self, data):
115114
affected_packages.extend(
116115
[
117116
PackageURL(type="apache", name="httpd", version=version)
118-
for version in self.version_api.get("apache/httpd")
117+
for version in self.version_api.get("apache/httpd").valid_versions
119118
if MavenVersion(version) in version_range
120119
]
121120
)

vulnerabilities/importers/apache_kafka.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def to_advisory(self, advisory_page):
7272

7373
fixed_packages = [
7474
PackageURL(type="apache", name="kafka", version=version)
75-
for version in self.version_api.get("apache/kafka")
75+
for version in self.version_api.get("apache/kafka").valid_versions
7676
if any(
7777
[
7878
MavenVersion(version) in version_range
@@ -83,7 +83,7 @@ def to_advisory(self, advisory_page):
8383

8484
affected_packages = [
8585
PackageURL(type="apache", name="kafka", version=version)
86-
for version in self.version_api.get("apache/kafka")
86+
for version in self.version_api.get("apache/kafka").valid_versions
8787
if any(
8888
[
8989
MavenVersion(version) in version_range

vulnerabilities/importers/apache_tomcat.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from bs4 import BeautifulSoup
3030
from univers.version_specifier import VersionSpecifier
3131
from univers.versions import MavenVersion
32+
from univers.versions import SemverVersion
3233
from packageurl import PackageURL
3334

3435
from vulnerabilities.data_source import Advisory
@@ -62,7 +63,11 @@ def updated_advisories(self):
6263
return self.batch_advisories(advisories)
6364

6465
def fetch_pages(self):
65-
tomcat_major_versions = {i[0] for i in self.version_api.get("org.apache.tomcat:tomcat")}
66+
# Here Semver is used because it has notion of major, minor versions.
67+
tomcat_major_versions = {
68+
SemverVersion(i).value.major
69+
for i in self.version_api.get("org.apache.tomcat:tomcat").valid_versions
70+
}
6671
for version in tomcat_major_versions:
6772
page_url = self.base_url.format(version)
6873
if create_etag(self, page_url, "ETag"):
@@ -102,7 +107,9 @@ def to_advisories(self, apache_tomcat_advisory_html):
102107
PackageURL(
103108
type="maven", namespace="apache", name="tomcat", version=version
104109
)
105-
for version in self.version_api.get("org.apache.tomcat:tomcat")
110+
for version in self.version_api.get(
111+
"org.apache.tomcat:tomcat"
112+
).valid_versions
106113
if MavenVersion(version) in version_range
107114
]
108115
)

vulnerabilities/importers/debian_oval.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,8 @@
2323

2424
import asyncio
2525
import dataclasses
26-
from typing import Iterable
27-
from typing import List
28-
from typing import Mapping
29-
from typing import Set
3026
import xml.etree.ElementTree as ET
3127

32-
from aiohttp import ClientSession
33-
from aiohttp.client_exceptions import ClientResponseError, ServerDisconnectedError
3428
import requests
3529

3630
from vulnerabilities.data_source import OvalDataSource, DataSourceConfiguration

vulnerabilities/importers/elixir_security.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def get_versions_for_pkg_from_range_list(self, version_range_list, pkg_name):
8383

8484
safe_pkg_versions = []
8585
vuln_pkg_versions = []
86-
all_version_list = self.pkg_manager_api.get(pkg_name)
86+
all_version_list = self.pkg_manager_api.get(pkg_name).valid_versions
8787
if not version_range_list:
8888
return [], all_version_list
8989
version_ranges = [

vulnerabilities/importers/github.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import asyncio
2424
import os
2525
import dataclasses
26-
import json
26+
from dateutil import parser as dateparser
2727
from typing import Set
2828
from typing import Tuple
2929
from typing import List
@@ -67,6 +67,7 @@
6767
url
6868
}
6969
severity
70+
publishedAt
7071
}
7172
package {
7273
name
@@ -185,7 +186,6 @@ def fetch(self) -> Mapping[str, List[Mapping]]:
185186
end_cursor_exp = ""
186187

187188
while True:
188-
189189
query_json = {"query": query % (ecosystem, end_cursor_exp)}
190190
resp = requests.post(self.config.endpoint, headers=headers, json=query_json).json()
191191
if resp.get("message") == "Bad credentials":
@@ -260,12 +260,16 @@ def process_response(self) -> List[Advisory]:
260260
for resp_page in self.advisories[ecosystem]:
261261
for adv in resp_page["data"]["securityVulnerabilities"]["edges"]:
262262
name = adv["node"]["package"]["name"]
263-
263+
cutoff_time = dateparser.parse(adv["node"]["advisory"]["publishedAt"])
264+
affected_purls = []
265+
unaffected_purls = []
264266
if self.process_name(ecosystem, name):
265267
ns, pkg_name = self.process_name(ecosystem, name)
266268
aff_range = adv["node"]["vulnerableVersionRange"]
267269
aff_vers, unaff_vers = self.categorize_versions(
268-
self.version_api.package_type, aff_range, self.version_api.get(name)
270+
self.version_api.package_type,
271+
aff_range,
272+
self.version_api.get(name, until=cutoff_time).valid_versions,
269273
)
270274
affected_purls = [
271275
PackageURL(name=pkg_name, namespace=ns, version=version, type=pkg_type)
@@ -276,10 +280,6 @@ def process_response(self) -> List[Advisory]:
276280
PackageURL(name=pkg_name, namespace=ns, version=version, type=pkg_type)
277281
for version in unaff_vers
278282
]
279-
else:
280-
affected_purls = []
281-
unaffected_purls = []
282-
283283
cve_ids = set()
284284
references = self.extract_references(adv["node"]["advisory"]["references"])
285285
vuln_desc = adv["node"]["advisory"]["summary"]

vulnerabilities/importers/istio.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
# VulnerableCode is a free software tool from nexB Inc. and others.
2121
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
2222
import asyncio
23+
import pytz
2324
import re
24-
from typing import List
25+
from dateutil import parser
2526
from typing import Set
2627

2728
import saneyaml
@@ -62,11 +63,11 @@ def updated_advisories(self) -> Set[Advisory]:
6263
advisories.extend(processed_data)
6364
return self.batch_advisories(advisories)
6465

65-
def get_pkg_versions_from_ranges(self, version_range_list):
66+
def get_pkg_versions_from_ranges(self, version_range_list, release_date):
6667
"""Takes a list of version ranges(affected) of a package
6768
as parameter and returns a tuple of safe package versions and
6869
vulnerable package versions"""
69-
all_version = self.version_api.get("istio/istio")
70+
all_version = self.version_api.get("istio/istio", release_date).valid_versions
7071
safe_pkg_versions = []
7172
vuln_pkg_versions = []
7273
version_ranges = [
@@ -86,6 +87,7 @@ def process_file(self, path):
8687
advisories = []
8788

8889
data = self.get_data_from_md(path)
90+
release_date = parser.parse(data["publishdate"]).replace(tzinfo=pytz.UTC)
8991

9092
releases = []
9193
if data.get("releases"):
@@ -128,7 +130,7 @@ def process_file(self, path):
128130
data["release_ranges"] = []
129131

130132
safe_pkg_versions, vuln_pkg_versions = self.get_pkg_versions_from_ranges(
131-
data["release_ranges"]
133+
data["release_ranges"], release_date
132134
)
133135

134136
affected_packages = []

vulnerabilities/importers/nginx.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ def set_api(self):
5353

5454
# For some reason nginx tags it's releases are in the form of `release-1.2.3`
5555
# Chop off the `release-` part here.
56-
for index, version in enumerate(self.version_api.cache["nginx/nginx"]):
57-
self.version_api.cache["nginx/nginx"][index] = version.replace("release-", "")
56+
for index, version in enumerate(self.version_api.cache["nginx/nginx"].valid_versions):
57+
self.version_api.cache["nginx/nginx"].valid_versions[index] = version.replace(
58+
"release-", ""
59+
)
5860

5961
def updated_advisories(self):
6062
advisories = []
@@ -134,7 +136,9 @@ def extract_fixed_pkgs(self, vuln_info):
134136
VersionSpecifier.from_scheme_version_spec_string("semver", "^" + rng[:-1])
135137
)
136138

137-
valid_versions = find_valid_versions(self.version_api.get("nginx/nginx"), version_ranges)
139+
valid_versions = find_valid_versions(
140+
self.version_api.get("nginx/nginx").valid_versions, version_ranges
141+
)
138142

139143
return [
140144
PackageURL(type="generic", name="nginx", version=version) for version in valid_versions
@@ -169,7 +173,9 @@ def extract_vuln_pkgs(self, vuln_info):
169173
)
170174
)
171175

172-
valid_versions = find_valid_versions(self.version_api.get("nginx/nginx"), version_ranges)
176+
valid_versions = find_valid_versions(
177+
self.version_api.get("nginx/nginx").valid_versions, version_ranges
178+
)
173179
qualifiers = {}
174180
if windows_only:
175181
qualifiers["os"] = "windows"

vulnerabilities/importers/npm.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,11 @@
2222
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
2323

2424
import asyncio
25-
from typing import Any
25+
import pytz
2626
from typing import List
27-
from typing import Mapping
2827
from typing import Set
2928
from typing import Tuple
30-
from urllib.error import HTTPError
3129
from urllib.parse import quote
32-
from urllib.request import urlopen
3330

3431
from dateutil.parser import parse
3532
from univers.version_specifier import VersionSpecifier
@@ -87,7 +84,11 @@ def process_file(self, file) -> List[Advisory]:
8784
record = load_json(file)
8885
advisories = []
8986
package_name = record["module_name"].strip()
90-
all_versions = self.versions.get(package_name)
87+
88+
publish_date = parse(record["updated_at"])
89+
publish_date.replace(tzinfo=pytz.UTC)
90+
91+
all_versions = self.versions.get(package_name, until=publish_date).valid_versions
9192
aff_range = record.get("vulnerable_versions")
9293
if not aff_range:
9394
aff_range = ""

0 commit comments

Comments
 (0)