Skip to content

Commit 3f48d14

Browse files
committed
Migrate elixir security importer
Signed-off-by: Tushar Goel <[email protected]>
1 parent 8ba56bb commit 3f48d14

File tree

2 files changed

+80
-99
lines changed

2 files changed

+80
-99
lines changed

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from vulnerabilities.importers import debian
1414
from vulnerabilities.importers import debian_oval
1515
from vulnerabilities.importers import gentoo
16+
from vulnerabilities.importers import elixir_security
1617
from vulnerabilities.importers import github
1718
from vulnerabilities.importers import gitlab
1819
from vulnerabilities.importers import istio
@@ -53,6 +54,7 @@
5354
istio.IstioImporter,
5455
project_kb_msr2019.ProjectKBMSRImporter,
5556
suse_scores.SUSESeverityScoreImporter,
57+
elixir_security.ElixirSecurityImporter,
5658
]
5759

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

vulnerabilities/importers/elixir_security.py

Lines changed: 78 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -6,119 +6,98 @@
66
# See https://github.com/nexB/vulnerablecode for support or download.
77
# See https://aboutcode.org for more information about nexB OSS projects.
88
#
9-
import asyncio
9+
import logging
10+
from pathlib import Path
1011
from typing import Set
1112

1213
from packageurl import PackageURL
13-
from univers.version_range import VersionRange
14-
from univers.versions import SemverVersion
14+
from univers.version_constraint import VersionConstraint
15+
from univers.version_range import HexVersionRange
1516

1617
from vulnerabilities.importer import AdvisoryData
17-
from vulnerabilities.importer import GitImporter
18+
from vulnerabilities.importer import AffectedPackage
19+
from vulnerabilities.importer import Importer
1820
from vulnerabilities.importer import Reference
19-
from vulnerabilities.package_managers import HexVersionAPI
2021
from vulnerabilities.utils import load_yaml
21-
from vulnerabilities.utils import nearest_patched_package
2222

23+
logger = logging.getLogger(__name__)
2324

24-
class ElixirSecurityImporter(GitImporter):
25-
def __enter__(self):
26-
super(ElixirSecurityImporter, self).__enter__()
2725

28-
if not getattr(self, "_added_files", None):
29-
self._added_files, self._updated_files = self.file_changes(
30-
recursive=True, file_ext="yml", subdir="./packages"
31-
)
32-
self.pkg_manager_api = HexVersionAPI()
33-
self.set_api(self.collect_packages())
34-
35-
def set_api(self, packages):
36-
asyncio.run(self.pkg_manager_api.load_api(packages))
37-
38-
def updated_advisories(self) -> Set[AdvisoryData]:
39-
files = self._updated_files.union(self._added_files)
40-
advisories = []
41-
for f in files:
42-
processed_data = self.process_file(f)
43-
if processed_data:
44-
advisories.append(processed_data)
45-
return self.batch_advisories(advisories)
46-
47-
def collect_packages(self):
48-
packages = set()
49-
files = self._updated_files.union(self._added_files)
50-
for f in files:
51-
data = load_yaml(f)
52-
if data.get("package"):
53-
packages.add(data["package"])
54-
55-
return packages
56-
57-
def get_versions_for_pkg_from_range_list(self, version_range_list, pkg_name):
58-
# Takes a list of version ranges(pathced and unaffected) of a package
59-
# as parameter and returns a tuple of safe package versions and
60-
# vulnerable package versions
61-
62-
safe_pkg_versions = []
63-
vuln_pkg_versions = []
64-
all_version_list = self.pkg_manager_api.get(pkg_name).valid_versions
65-
if not version_range_list:
66-
return [], all_version_list
67-
version_ranges = [
68-
VersionRange.from_scheme_version_spec_string("semver", r) for r in version_range_list
69-
]
70-
for version in all_version_list:
71-
version_obj = SemverVersion(version)
72-
if any([version_obj in v for v in version_ranges]):
73-
safe_pkg_versions.append(version)
74-
75-
vuln_pkg_versions = set(all_version_list) - set(safe_pkg_versions)
76-
return safe_pkg_versions, vuln_pkg_versions
26+
class ElixirSecurityImporter(Importer):
27+
28+
repo_url = "git+https://github.com/dependabot/elixir-security-advisories"
29+
license_url = "https://github.com/dependabot/elixir-security-advisories/blob/master/LICENSE.txt"
30+
spdx_license_expression = "CC0-1.0"
31+
32+
def advisory_data(self) -> Set[AdvisoryData]:
33+
try:
34+
self.clone(self.repo_url)
35+
path = Path(self.vcs_response.dest_dir)
36+
vuln = path / "packages"
37+
for file in vuln.glob("**/*.yml"):
38+
yield from self.process_file(file)
39+
finally:
40+
if self.vcs_response:
41+
self.vcs_response.delete()
7742

7843
def process_file(self, path):
44+
path = str(path)
7945
yaml_file = load_yaml(path)
80-
pkg_name = yaml_file["package"]
81-
safe_pkg_versions = []
82-
vuln_pkg_versions = []
83-
if not yaml_file.get("patched_versions"):
84-
yaml_file["patched_versions"] = []
85-
86-
if not yaml_file.get("unaffected_versions"):
87-
yaml_file["unaffected_versions"] = []
88-
89-
safe_pkg_versions, vuln_pkg_versions = self.get_versions_for_pkg_from_range_list(
90-
yaml_file["patched_versions"] + yaml_file["unaffected_versions"],
91-
pkg_name,
46+
cve_id = ""
47+
summary = yaml_file.get("description") or ""
48+
pkg_name = yaml_file.get("package") or ""
49+
if not pkg_name:
50+
return []
51+
52+
cve = yaml_file.get("cve") or ""
53+
54+
if cve and not cve.startswith("CVE-"):
55+
cve = yaml_file["cve"]
56+
cve_id = f"CVE-{cve}"
57+
58+
references = []
59+
link = yaml_file.get("link") or ""
60+
if link:
61+
references.append(
62+
Reference(
63+
url=link,
64+
)
65+
)
66+
67+
affected_packages = []
68+
69+
unaffected_versions = yaml_file.get("unaffected_versions") or []
70+
patched_versions = yaml_file.get("patched_versions") or []
71+
72+
constraints = []
73+
vrc = HexVersionRange.version_class
74+
75+
for version in unaffected_versions:
76+
constraints.append(VersionConstraint.from_string(version_class=vrc, string=version))
77+
78+
for version in patched_versions:
79+
if version.startswith("~>"):
80+
version = version[2:]
81+
constraints.append(
82+
VersionConstraint.from_string(version_class=vrc, string=version).invert()
83+
)
84+
85+
affected_packages.append(
86+
AffectedPackage(
87+
package=PackageURL(
88+
type="hex",
89+
name=pkg_name,
90+
),
91+
affected_version_range=HexVersionRange(constraints=constraints),
92+
)
9293
)
9394

94-
if yaml_file.get("cve"):
95-
cve_id = "CVE-" + yaml_file["cve"]
96-
else:
97-
cve_id = ""
98-
99-
safe_purls = []
100-
vuln_purls = []
101-
102-
safe_purls = [
103-
PackageURL(name=pkg_name, type="hex", version=version) for version in safe_pkg_versions
104-
]
105-
106-
vuln_purls = [
107-
PackageURL(name=pkg_name, type="hex", version=version) for version in vuln_pkg_versions
108-
]
109-
110-
references = [
111-
Reference(
112-
reference_id=yaml_file["id"],
113-
),
114-
Reference(
115-
url=yaml_file["link"],
116-
),
117-
]
118-
119-
return AdvisoryData(
120-
summary=yaml_file["description"],
121-
affected_packages=nearest_patched_package(vuln_purls, safe_purls),
122-
vulnerability_id=cve_id,
95+
if not cve_id:
96+
return []
97+
98+
yield AdvisoryData(
99+
aliases=[cve_id],
100+
summary=summary,
123101
references=references,
102+
affected_packages=affected_packages,
124103
)

0 commit comments

Comments
 (0)