Skip to content

Commit f809a89

Browse files
committed
added HexVersionAPI and mock test it
Signed-off-by: Tushar912 <tushar.912u@gmail.com>
1 parent 70ac4cd commit f809a89

3 files changed

Lines changed: 91 additions & 21 deletions

File tree

vulnerabilities/importers/elixir_security.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,22 @@
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

23+
import asyncio
2324
import yaml
24-
import re
2525
import json
2626
import requests
27+
import re
2728
from typing import Set
2829
from typing import List
30+
2931
from dephell_specifier import RangeSpecifier
3032
from packageurl import PackageURL
3133

3234
from vulnerabilities.data_source import GitDataSource
3335
from vulnerabilities.data_source import GitDataSourceConfiguration
3436
from vulnerabilities.data_source import Advisory
3537
from vulnerabilities.data_source import Reference
36-
38+
from vulnerabilities.package_managers import HexVersionAPI
3739

3840
class ElixirSecurityDataSource(GitDataSource):
3941
def __enter__(self):
@@ -43,6 +45,11 @@ def __enter__(self):
4345
self._added_files, self._updated_files = self.file_changes(
4446
recursive=True, file_ext="yml", subdir="./packages"
4547
)
48+
self.pkg_manager_api = HexVersionAPI()
49+
self.set_api(self.collect_packages())
50+
51+
def set_api(self, packages):
52+
asyncio.run(self.pkg_manager_api.load_api(packages))
4653

4754
def updated_advisories(self) -> Set[Advisory]:
4855
files = self._updated_files
@@ -62,17 +69,25 @@ def added_advisories(self) -> Set[Advisory]:
6269
advisories.append(processed_data)
6370
return self.batch_advisories(advisories)
6471

65-
@staticmethod
66-
def generate_all_version_list(pkg_name):
67-
resp = requests.get(f"https://hex.pm/api/packages/{pkg_name}")
68-
resp = resp.content
69-
json_resp = json.loads(resp)
70-
version_list = []
71-
for release in json_resp["releases"]:
72-
version_list.append(release["version"])
72+
def collect_packages(self):
73+
packages = set()
74+
files = self._updated_files.union(self._added_files)
75+
for f in files:
76+
with open(f) as file:
77+
data = yaml.safe_load(file)
78+
if data.get("package"):
79+
packages.add(data["package"])
80+
81+
return packages
82+
83+
def generate_all_version_list(self,pkg_name):
84+
if not getattr(self, 'pkg_manager_api', None):
85+
self.pkg_manager_api = HexVersionAPI()
86+
version_list = self.pkg_manager_api.get(
87+
pkg_name)
7388
return version_list
7489

75-
def get_pkg_from_range(self, version_list, pkg_name):
90+
def get_versions_from_range(self, version_list, pkg_name):
7691
pkg_versions = []
7792
all_version_list = self.generate_all_version_list(pkg_name)
7893
if version_list is None:
@@ -89,15 +104,16 @@ def process_file(self, path):
89104
pkg_name = yaml_file["package"]
90105
safe_pkg_versions = []
91106
if yaml_file.get("unaffected_versions"):
92-
safe_pkg_versions = self.get_pkg_from_range(
107+
safe_pkg_versions = self.get_versions_from_range(
93108
yaml_file["patched_versions"] + yaml_file["unaffected_versions"],
94109
pkg_name,
95110
)
96111
else:
97-
safe_pkg_versions = self.get_pkg_from_range(
112+
safe_pkg_versions = self.get_versions_from_range(
98113
yaml_file["patched_versions"], pkg_name
99114
)
100-
cve_id = yaml_file["cve"]
115+
116+
cve_id = "CVE-"+str(yaml_file["cve"])
101117
safe_purls = []
102118
if safe_pkg_versions is not None:
103119
safe_purls = {
@@ -107,6 +123,7 @@ def process_file(self, path):
107123

108124
vuln_reference = [
109125
Reference(
126+
reference_id=yaml_file["id"],
110127
url=yaml_file["link"],
111128
)
112129
]

vulnerabilities/package_managers.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,24 @@ async def fetch(self, owner_repo: str, session) -> None:
321321
resp = await resp.json()
322322
print(resp)
323323
self.cache[owner_repo] = [release["ref"].split("/")[-1] for release in resp]
324+
325+
class HexVersionAPI(VersionAPI):
326+
async def load_api(self, pkg_set):
327+
async with ClientSession(raise_for_status=True) as session:
328+
await asyncio.gather(
329+
*[self.fetch(pkg, session) for pkg in pkg_set if pkg not in self.cache]
330+
)
331+
332+
async def fetch(self, pkg, session):
333+
url = f"https://hex.pm/api/packages/{pkg}"
334+
versions = set()
335+
try:
336+
response = await session.request(method="GET", url=url)
337+
response = await response.json()
338+
for release in response["releases"]:
339+
versions.add(release["version"])
340+
except (ClientResponseError, JSONDecodeError):
341+
pass
342+
343+
self.cache[pkg] = versions
344+

vulnerabilities/tests/test_elixir_security.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@
2222

2323
import os
2424
from unittest import TestCase
25+
from unittest.mock import patch
2526
from collections import OrderedDict
2627

2728
from vulnerabilities.data_source import Reference
2829
from packageurl import PackageURL
2930

30-
from vulnerabilities.importers.elixir_security import ElixirSecurityDataSource
3131
from vulnerabilities.data_source import Advisory
32+
from vulnerabilities.importers.elixir_security import ElixirSecurityDataSource
33+
from vulnerabilities.package_managers import HexVersionAPI
34+
3235

3336
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
3437

@@ -37,11 +40,26 @@ class TestElixirSecurityDataSource(TestCase):
3740
@classmethod
3841
def setUpClass(cls):
3942
data_source_cfg = {
40-
"repository_url": "https://test.net",
43+
"repository_url": 'https://github.com/dependabot/elixir-security-advisories',
4144
}
4245
cls.data_src = ElixirSecurityDataSource(1, config=data_source_cfg)
46+
cls.data_src.pkg_manager_api = HexVersionAPI()
4347

44-
def test_generate_all_version_list(self):
48+
@patch('vulnerabilities.package_managers.HexVersionAPI.get',
49+
return_value=[
50+
"0.5.2",
51+
"0.5.1",
52+
"0.5.0",
53+
"0.4.0",
54+
"0.3.1",
55+
"0.3.0",
56+
"0.2.0",
57+
"0.1.3",
58+
"0.1.2",
59+
"0.1.1",
60+
"0.1.0",
61+
])
62+
def test_generate_all_version_list(self,mock_write):
4563
package = "coherence"
4664
actual_list = self.data_src.generate_all_version_list(package)
4765
expected_list = [
@@ -58,8 +76,21 @@ def test_generate_all_version_list(self):
5876
"0.1.0",
5977
]
6078
assert actual_list == expected_list
61-
62-
def test_process_file(self):
79+
@patch('vulnerabilities.package_managers.HexVersionAPI.get',
80+
return_value=[
81+
"0.5.2",
82+
"0.5.1",
83+
"0.5.0",
84+
"0.4.0",
85+
"0.3.1",
86+
"0.3.0",
87+
"0.2.0",
88+
"0.1.3",
89+
"0.1.2",
90+
"0.1.1",
91+
"0.1.0",
92+
])
93+
def test_process_file(self,mock_write):
6394

6495
path = os.path.join(BASE_DIR, "test_data/elixir_security/test_file.yml")
6596
expected_data = Advisory(
@@ -75,9 +106,10 @@ def test_process_file(self):
75106
),
76107
},
77108
vuln_references=[
78-
Reference(url="https://github.com/smpallen99/coherence/issues/270")
109+
Reference(reference_id='2aae6e3a-24a3-4d5f-86ff-b964eaf7c6d1',
110+
url="https://github.com/smpallen99/coherence/issues/270")
79111
],
80-
cve_id="2018-20301",
112+
cve_id="CVE-2018-20301",
81113
)
82114

83115
found_data = self.data_src.process_file(path)

0 commit comments

Comments
 (0)