Skip to content

Commit 1e2a495

Browse files
authored
Merge pull request #1043 from TG1999/migrate/mozilla
Migrate mozilla importer
2 parents a11871c + 6de1484 commit 1e2a495

File tree

10 files changed

+741
-150
lines changed

10 files changed

+741
-150
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ Release notes
22
=============
33

44

5+
Next release
6+
----------------
7+
8+
- We re-enabled support for the mozilla vulnerabilities advisories importer.
9+
10+
511
Version v31.1.1
612
---------------
713

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from vulnerabilities.importers import debian_oval
1515
from vulnerabilities.importers import github
1616
from vulnerabilities.importers import gitlab
17+
from vulnerabilities.importers import mozilla
1718
from vulnerabilities.importers import nginx
1819
from vulnerabilities.importers import npm
1920
from vulnerabilities.importers import nvd
@@ -43,6 +44,7 @@
4344
npm.NpmImporter,
4445
retiredotnet.RetireDotnetImporter,
4546
apache_httpd.ApacheHTTPDImporter,
47+
mozilla.MozillaImporter,
4648
]
4749

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

vulnerabilities/importers/mozilla.py

Lines changed: 61 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,74 @@
77
# See https://aboutcode.org for more information about nexB OSS projects.
88
#
99

10+
import logging
1011
import re
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

3034

31-
class MozillaImporter(GitImporter):
32-
def __enter__(self):
33-
super(MozillaImporter, self).__enter__()
35+
class MozillaImporter(Importer):
36+
spdx_license_expression = "MPL-2.0"
37+
license_url = "https://github.com/mozilla/foundation-security-advisories/blob/master/LICENSE"
38+
repo_url = "git+https://github.com/mozilla/foundation-security-advisories/"
3439

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
40+
def advisory_data(self) -> Iterable[AdvisoryData]:
41+
try:
42+
self.clone(self.repo_url)
43+
path = Path(self.vcs_response.dest_dir)
4544

46-
advisories = []
47-
for path in files:
48-
advisories.extend(to_advisories(path))
45+
vuln = path / "announce"
46+
paths = list(vuln.glob("**/*.yml")) + list(vuln.glob("**/*.md"))
47+
for file_path in paths:
48+
yield from to_advisories(file_path)
49+
finally:
50+
if self.vcs_response:
51+
self.vcs_response.delete()
4952

50-
return self.batch_advisories(advisories)
5153

52-
53-
def to_advisories(path: str) -> List[AdvisoryData]:
54+
def to_advisories(path: Path) -> List[AdvisoryData]:
5455
"""
5556
Convert a file to corresponding advisories.
5657
This calls proper method to handle yml/md files.
5758
"""
59+
path = str(path)
5860
mfsa_id = mfsa_id_from_filename(path)
5961
if not mfsa_id:
6062
return []
6163

6264
with open(path) as lines:
6365
if path.endswith(".md"):
64-
return get_advisories_from_md(mfsa_id, lines)
66+
yield from get_advisories_from_md(mfsa_id, lines)
6567
if path.endswith(".yml"):
66-
return get_advisories_from_yml(mfsa_id, lines)
68+
yield from get_advisories_from_yml(mfsa_id, lines)
6769

6870
return []
6971

7072

7173
def get_advisories_from_yml(mfsa_id, lines) -> List[AdvisoryData]:
72-
advisories = []
7374
data = yaml.safe_load(lines)
7475
data["mfsa_id"] = mfsa_id
7576

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

7980
if not data.get("advisories"):
@@ -82,47 +83,35 @@ def get_advisories_from_yml(mfsa_id, lines) -> List[AdvisoryData]:
8283
for cve, advisory in data["advisories"].items():
8384
# These may contain HTML tags
8485
summary = BeautifulSoup(advisory.get("description", ""), features="lxml").get_text()
85-
86-
advisories.append(
87-
AdvisoryData(
86+
if is_cve(cve):
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,28 @@ 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+
name, _, version = pkg.rpartition(" ")
144+
if version and name:
145+
try:
146+
# count no of "." in version
147+
# if 3, then it is not a valid semver version
148+
if version.count(".") == 3:
149+
continue
150+
fixed_version = SemverVersion(version)
151+
yield AffectedPackage(
152+
package=PackageURL(
153+
type="mozilla",
154+
name=name,
155+
),
156+
fixed_version=fixed_version,
157+
)
158+
except Exception:
159+
logger.exception(f"Error parsing version {version} for {name}")
161160

162161

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

vulnerabilities/tests/conftest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def no_rmtree(monkeypatch):
3232
"test_gentoo.py",
3333
"test_istio.py",
3434
"test_models.py",
35-
"test_mozilla.py",
3635
"test_msr2019.py",
3736
"test_package_managers.py",
3837
"test_ruby.py",
-35.2 KB
Binary file not shown.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
[
2+
{
3+
"aliases": [
4+
"CVE-2006-0294"
5+
],
6+
"summary": "Dynamically changing the style of an element from position:relative\nto position:static can cause Gecko to operate on freed memory.\nIt may be possible to exploit this in order to run arbitrary\ncode.This flaw was introduced during Firefox 1.5 and SeaMonkey 1.0\ndevelopment and does not affect Firefox 1.0 or the Mozilla Suite 1.7Thunderbird 1.5 could be vulnerable if JavaScript is\nenabled in mail. This is not the default setting and we strongly\ndiscourage users from turning on JavaScript in mail. Thunderbird\nis not vulnerable in its default configuration.Update (13 April 2006)\nThis flaw has been fixed in Thunderbird 1.5.0.2",
7+
"affected_packages": [
8+
{
9+
"package": {
10+
"type": "mozilla",
11+
"namespace": null,
12+
"name": "SeaMonkey",
13+
"version": null,
14+
"qualifiers": null,
15+
"subpath": null
16+
},
17+
"affected_version_range": null,
18+
"fixed_version": "1.0.0"
19+
}
20+
],
21+
"references": [
22+
{
23+
"reference_id": "mfsa2006-02",
24+
"url": "https://www.mozilla.org/en-US/security/advisories/mfsa2006-02",
25+
"severities": [
26+
{
27+
"system": "generic_textual",
28+
"value": "none",
29+
"scoring_elements": ""
30+
}
31+
]
32+
},
33+
{
34+
"reference_id": "CVE-2006-0294",
35+
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-0294",
36+
"severities": []
37+
}
38+
],
39+
"date_published": null
40+
}
41+
]

0 commit comments

Comments
 (0)