-
Notifications
You must be signed in to change notification settings - Fork 231
/
Copy pathelixir_security.py
122 lines (101 loc) · 3.99 KB
/
elixir_security.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#
# Copyright (c) nexB Inc. and others. All rights reserved.
# VulnerableCode is a trademark of nexB Inc.
# SPDX-License-Identifier: Apache-2.0
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
# See https://aboutcode.org for more information about nexB OSS projects.
#
import urllib.parse as urlparse
from pathlib import Path
from typing import Set
from dateutil import parser as dateparser
from packageurl import PackageURL
from univers.version_constraint import VersionConstraint
from univers.version_range import HexVersionRange
from vulnerabilities.importer import AdvisoryData
from vulnerabilities.importer import AffectedPackage
from vulnerabilities.importer import Importer
from vulnerabilities.importer import Reference
from vulnerabilities.utils import is_cve
from vulnerabilities.utils import load_yaml
class ElixirSecurityImporter(Importer):
repo_url = "git+https://github.com/dependabot/elixir-security-advisories"
license_url = "https://github.com/dependabot/elixir-security-advisories/blob/master/LICENSE.txt"
spdx_license_expression = "CC0-1.0"
importer_name = "Elixir Security Importer"
def advisory_data(self) -> Set[AdvisoryData]:
try:
self.clone(self.repo_url)
base_path = Path(self.vcs_response.dest_dir)
vuln = base_path / "packages"
for file in vuln.glob("**/*.yml"):
yield from self.process_file(file, base_path)
finally:
if self.vcs_response:
self.vcs_response.delete()
@classmethod
def get_advisory_id(cls, aliases: list[str]) -> str:
"""
Return the Advisory ID for the given aliases.
"""
return cls.get_cve_id(aliases)
def process_file(self, file, base_path):
relative_path = str(file.relative_to(base_path)).strip("/")
advisory_url = (
f"https://github.com/dependabot/elixir-security-advisories/blob/master/{relative_path}"
)
file = str(file)
yaml_file = load_yaml(file)
cve_id = ""
summary = yaml_file.get("description") or ""
pkg_name = yaml_file.get("package") or ""
cve = yaml_file.get("cve") or ""
if cve and not cve.startswith("CVE-"):
cve_id = f"CVE-{cve}"
if not cve_id:
return []
if not is_cve(cve_id):
return []
references = []
link = yaml_file.get("link") or ""
if link:
references.append(
Reference(
url=link,
)
)
affected_packages = []
unaffected_versions = yaml_file.get("unaffected_versions") or []
patched_versions = yaml_file.get("patched_versions") or []
constraints = []
vrc = HexVersionRange.version_class
for version in unaffected_versions:
constraints.append(VersionConstraint.from_string(version_class=vrc, string=version))
for version in patched_versions:
if version.startswith("~>"):
version = version[2:]
constraints.append(
VersionConstraint.from_string(version_class=vrc, string=version).invert()
)
if pkg_name:
affected_packages.append(
AffectedPackage(
package=PackageURL(
type="hex",
name=pkg_name,
),
affected_version_range=HexVersionRange(constraints=constraints),
)
)
date_published = None
if yaml_file.get("disclosure_date"):
date_published = dateparser.parse(yaml_file.get("disclosure_date"))
yield AdvisoryData(
aliases=[cve_id],
summary=summary,
references=references,
affected_packages=affected_packages,
url=advisory_url,
date_published=date_published,
)