Skip to content

Commit 10f37f7

Browse files
committed
Migrate mozilla importer
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 6b0a4c2 commit 10f37f7

2 files changed

Lines changed: 59 additions & 63 deletions

File tree

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Version v31.1.0
66
----------------
77

88
- We re-enabled support for the NPM vulnerabilities advisories importer.
9-
9+
- We re-enabled support for the mozilla vulnerabilities advisories importer.
1010

1111
Version v31.0.0
1212
----------------

vulnerabilities/importers/mozilla.py

Lines changed: 58 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,72 @@
88
#
99

1010
import re
11+
import logging
12+
from pathlib import Path
13+
from typing import Iterable
1114
from typing import List
12-
from typing import Set
1315

1416
import yaml
1517
from bs4 import BeautifulSoup
1618
from markdown import markdown
1719
from packageurl import PackageURL
20+
from univers.versions import SemverVersion
1821

1922
from vulnerabilities import severity_systems
2023
from vulnerabilities.importer import AdvisoryData
21-
from vulnerabilities.importer import GitImporter
24+
from vulnerabilities.importer import AffectedPackage
25+
from vulnerabilities.importer import Importer
2226
from vulnerabilities.importer import Reference
2327
from vulnerabilities.importer import VulnerabilitySeverity
2428
from vulnerabilities.utils import is_cve
2529
from vulnerabilities.utils import split_markdown_front_matter
2630

27-
REPOSITORY = "mozilla/foundation-security-advisories"
2831
MFSA_FILENAME_RE = re.compile(r"mfsa(\d{4}-\d{2,3})\.(md|yml)$")
32+
logger = logging.getLogger(__name__)
2933

34+
class MozillaImporter(Importer):
35+
spdx_license_expression = "MPL-2.0"
36+
license_url = "https://github.com/mozilla/foundation-security-advisories/blob/master/LICENSE"
37+
repo_url = "git+https://github.com/mozilla/foundation-security-advisories/"
3038

31-
class MozillaImporter(GitImporter):
32-
def __enter__(self):
33-
super(MozillaImporter, self).__enter__()
39+
def advisory_data(self) -> Iterable[AdvisoryData]:
40+
try:
41+
self.clone(self.repo_url)
42+
path = Path(self.vcs_response.dest_dir)
3443

35-
if not getattr(self, "_added_files", None):
36-
self._added_files, self._updated_files = self.file_changes(
37-
recursive=True, subdir="announce"
38-
)
39-
40-
def updated_advisories(self) -> Set[AdvisoryData]:
41-
files = self._updated_files.union(self._added_files)
42-
files = [
43-
f for f in files if f.endswith(".md") or f.endswith(".yml")
44-
] # skip irrelevant files
45-
46-
advisories = []
47-
for path in files:
48-
advisories.extend(to_advisories(path))
49-
50-
return self.batch_advisories(advisories)
44+
vuln = path / "announce"
45+
paths = list(vuln.glob("**/*.yml")) + list(vuln.glob("**/*.md"))
46+
for file_path in paths:
47+
yield from to_advisories(file_path)
48+
finally:
49+
if self.vcs_response:
50+
self.vcs_response.delete()
5151

5252

5353
def to_advisories(path: str) -> List[AdvisoryData]:
5454
"""
5555
Convert a file to corresponding advisories.
5656
This calls proper method to handle yml/md files.
5757
"""
58+
path = str(path)
5859
mfsa_id = mfsa_id_from_filename(path)
5960
if not mfsa_id:
6061
return []
6162

6263
with open(path) as lines:
6364
if path.endswith(".md"):
64-
return get_advisories_from_md(mfsa_id, lines)
65+
yield from get_advisories_from_md(mfsa_id, lines)
6566
if path.endswith(".yml"):
66-
return get_advisories_from_yml(mfsa_id, lines)
67+
yield from get_advisories_from_yml(mfsa_id, lines)
6768

6869
return []
6970

7071

7172
def get_advisories_from_yml(mfsa_id, lines) -> List[AdvisoryData]:
72-
advisories = []
7373
data = yaml.safe_load(lines)
7474
data["mfsa_id"] = mfsa_id
7575

76-
fixed_package_urls = get_package_urls(data.get("fixed_in"))
76+
affected_packages = get_affected_packages(data.get("fixed_in") or [])
7777
references = get_yml_references(data)
7878

7979
if not data.get("advisories"):
@@ -82,47 +82,36 @@ def get_advisories_from_yml(mfsa_id, lines) -> List[AdvisoryData]:
8282
for cve, advisory in data["advisories"].items():
8383
# These may contain HTML tags
8484
summary = BeautifulSoup(advisory.get("description", ""), features="lxml").get_text()
85-
86-
advisories.append(
87-
AdvisoryData(
85+
alias = is_cve(cve)
86+
if alias:
87+
yield AdvisoryData(
8888
summary=summary,
89-
vulnerability_id=cve if is_cve(cve) else "",
90-
impacted_package_urls=[],
91-
resolved_package_urls=fixed_package_urls,
89+
aliases=[cve],
9290
references=references,
91+
affected_packages=list(affected_packages),
9392
)
94-
)
95-
96-
return advisories
9793

9894

9995
def get_advisories_from_md(mfsa_id, lines) -> List[AdvisoryData]:
10096
yamltext, mdtext = split_markdown_front_matter(lines.read())
10197
data = yaml.safe_load(yamltext)
10298
data["mfsa_id"] = mfsa_id
10399

104-
fixed_package_urls = get_package_urls(data.get("fixed_in"))
100+
affected_packages = get_affected_packages(data.get("fixed_in") or [])
105101
references = get_yml_references(data)
106102
cves = re.findall(r"CVE-\d+-\d+", yamltext + mdtext, re.IGNORECASE)
103+
description = html_get_p_under_h3(markdown(mdtext), "description")
107104
for cve in cves:
108-
references.append(
109-
Reference(
110-
reference_id=cve,
111-
url=f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve}",
112-
)
105+
cve_ref = Reference(
106+
reference_id=cve,
107+
url=f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve}",
113108
)
114-
115-
description = html_get_p_under_h3(markdown(mdtext), "description")
116-
117-
return [
118-
AdvisoryData(
109+
yield AdvisoryData(
119110
summary=description,
120-
vulnerability_id="",
121-
impacted_package_urls=[],
122-
resolved_package_urls=fixed_package_urls,
123-
references=references,
111+
aliases=[cve],
112+
affected_packages=list(affected_packages),
113+
references=references + [cve_ref],
124114
)
125-
]
126115

127116

128117
def html_get_p_under_h3(html, h3: str):
@@ -146,18 +135,25 @@ def mfsa_id_from_filename(filename):
146135
return None
147136

148137

149-
def get_package_urls(pkgs: List[str]) -> List[PackageURL]:
150-
package_urls = [
151-
PackageURL(
152-
type="mozilla",
138+
def get_affected_packages(pkgs: List[str]) -> List[PackageURL]:
139+
for pkg in pkgs:
140+
if not pkg:
141+
continue
153142
# pkg is of the form "Firefox ESR 1.21" or "Thunderbird 2.21"
154-
name=pkg.rsplit(None, 1)[0],
155-
version=pkg.rsplit(None, 1)[1],
156-
)
157-
for pkg in pkgs
158-
if pkg
159-
]
160-
return package_urls
143+
version = pkg.rsplit(None, 1)[1]
144+
name = pkg.rsplit(None, 1)[0]
145+
if version and name:
146+
try:
147+
fixed_version=SemverVersion(version)
148+
yield AffectedPackage(
149+
package=PackageURL(
150+
type="mozilla",
151+
name=name,
152+
),
153+
fixed_version=fixed_version,
154+
)
155+
except Exception:
156+
logger.exception(f"Error parsing version {version} for {name}")
161157

162158

163159
def get_yml_references(data: any) -> List[Reference]:

0 commit comments

Comments
 (0)