Skip to content

Commit d4002c9

Browse files
committed
Add Eclipse Foundation security advisories importer
Signed-off-by: Anmol Vats <anmolvats2003@gmail.com>
1 parent 2dbbd38 commit d4002c9

4 files changed

Lines changed: 263 additions & 0 deletions

File tree

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from vulnerabilities.pipelines.v2_importers import gentoo_importer as gentoo_importer_v2
5959
from vulnerabilities.pipelines.v2_importers import github_osv_importer as github_osv_importer_v2
6060
from vulnerabilities.pipelines.v2_importers import gitlab_importer as gitlab_importer_v2
61+
from vulnerabilities.pipelines.v2_importers import eclipse_importer as eclipse_importer_v2
6162
from vulnerabilities.pipelines.v2_importers import istio_importer as istio_importer_v2
6263
from vulnerabilities.pipelines.v2_importers import mattermost_importer as mattermost_importer_v2
6364
from vulnerabilities.pipelines.v2_importers import mozilla_importer as mozilla_importer_v2
@@ -99,6 +100,7 @@
99100
xen_importer_v2.XenImporterPipeline,
100101
curl_importer_v2.CurlImporterPipeline,
101102
oss_fuzz_v2.OSSFuzzImporterPipeline,
103+
eclipse_importer_v2.EclipseImporterPipeline,
102104
istio_importer_v2.IstioImporterPipeline,
103105
postgresql_importer_v2.PostgreSQLImporterPipeline,
104106
mozilla_importer_v2.MozillaImporterPipeline,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
import json
11+
import logging
12+
from typing import Iterable
13+
14+
import dateparser
15+
import requests
16+
17+
from vulnerabilities.importer import AdvisoryDataV2
18+
from vulnerabilities.importer import ReferenceV2
19+
from vulnerabilities.importer import VulnerabilitySeverity
20+
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
21+
from vulnerabilities.severity_systems import GENERIC
22+
23+
logger = logging.getLogger(__name__)
24+
25+
ECLIPSE_API_URL = "https://api.eclipse.org/cve"
26+
27+
28+
class EclipseImporterPipeline(VulnerableCodeBaseImporterPipelineV2):
29+
"""Collect Eclipse Foundation security advisories via the Eclipse CVE API."""
30+
31+
pipeline_id = "eclipse_importer"
32+
spdx_license_expression = "LicenseRef-scancode-proprietary-license"
33+
license_url = "https://www.eclipse.org/security/"
34+
precedence = 200
35+
36+
@classmethod
37+
def steps(cls):
38+
return (
39+
cls.fetch,
40+
cls.collect_and_store_advisories,
41+
)
42+
43+
def fetch(self):
44+
self.log(f"Fetch `{ECLIPSE_API_URL}`")
45+
resp = requests.get(ECLIPSE_API_URL, timeout=30)
46+
resp.raise_for_status()
47+
self.advisories_data = resp.json()
48+
49+
def advisories_count(self):
50+
return len(self.advisories_data)
51+
52+
def collect_advisories(self) -> Iterable[AdvisoryDataV2]:
53+
for entry in self.advisories_data:
54+
advisory = parse_advisory(entry)
55+
if advisory:
56+
yield advisory
57+
58+
59+
def parse_advisory(entry: dict):
60+
advisory_id = entry.get("id") or ""
61+
if not advisory_id:
62+
return None
63+
64+
date_published = None
65+
raw_date = entry.get("date_published") or ""
66+
if raw_date:
67+
date_published = dateparser.parse(
68+
raw_date,
69+
settings={"TIMEZONE": "UTC", "RETURN_AS_TIMEZONE_AWARE": True, "TO_TIMEZONE": "UTC"},
70+
)
71+
if date_published is None:
72+
logger.warning("Could not parse date %r for %s", raw_date, advisory_id)
73+
74+
summary_obj = entry.get("summary")
75+
summary = summary_obj.get("content") or "" if isinstance(summary_obj, dict) else ""
76+
77+
references = []
78+
for url in [
79+
entry.get("live_link") or "",
80+
entry.get("request_link") or "",
81+
entry.get("cve_pull_request") or "",
82+
]:
83+
if url:
84+
references.append(ReferenceV2(url=url))
85+
86+
severities = []
87+
cvss = entry.get("cvss")
88+
if cvss is not None:
89+
severities.append(VulnerabilitySeverity(system=GENERIC, value=str(cvss)))
90+
91+
advisory_url = entry.get("live_link") or ""
92+
93+
return AdvisoryDataV2(
94+
advisory_id=advisory_id,
95+
aliases=[],
96+
summary=summary,
97+
affected_packages=[],
98+
references=references,
99+
date_published=date_published,
100+
weaknesses=[],
101+
severities=severities,
102+
url=advisory_url,
103+
original_advisory_text=json.dumps(entry, indent=2, ensure_ascii=False),
104+
)
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
import json
11+
from pathlib import Path
12+
from unittest import TestCase
13+
from unittest.mock import MagicMock
14+
from unittest.mock import patch
15+
16+
import requests
17+
18+
from vulnerabilities.pipelines.v2_importers.eclipse_importer import EclipseImporterPipeline
19+
from vulnerabilities.pipelines.v2_importers.eclipse_importer import parse_advisory
20+
21+
TEST_DATA = Path(__file__).parent.parent.parent / "test_data" / "eclipse"
22+
23+
with open(TEST_DATA / "eclipse_api_sample.json") as f:
24+
SAMPLE_DATA = json.load(f)
25+
26+
ENTRY_WITH_CVSS = SAMPLE_DATA[0]
27+
ENTRY_WITHOUT_CVSS = SAMPLE_DATA[1]
28+
ENTRY_WITHOUT_SUMMARY = SAMPLE_DATA[2]
29+
30+
31+
class TestParseAdvisory(TestCase):
32+
def test_parses_id_and_summary(self):
33+
advisory = parse_advisory(ENTRY_WITH_CVSS)
34+
assert advisory.advisory_id == "CVE-2017-7649"
35+
assert "Kura" in advisory.summary
36+
37+
def test_parses_date(self):
38+
advisory = parse_advisory(ENTRY_WITH_CVSS)
39+
assert advisory.date_published is not None
40+
assert advisory.date_published.year == 2017
41+
42+
def test_cvss_stored_as_generic_severity(self):
43+
advisory = parse_advisory(ENTRY_WITH_CVSS)
44+
assert len(advisory.severities) == 1
45+
assert advisory.severities[0].value == "9.8"
46+
47+
def test_missing_cvss_yields_empty_severities(self):
48+
advisory = parse_advisory(ENTRY_WITHOUT_CVSS)
49+
assert advisory.severities == []
50+
51+
def test_missing_summary_yields_empty_string(self):
52+
advisory = parse_advisory(ENTRY_WITHOUT_SUMMARY)
53+
assert advisory.summary == ""
54+
55+
def test_references_populated(self):
56+
advisory = parse_advisory(ENTRY_WITH_CVSS)
57+
urls = [r.url for r in advisory.references]
58+
assert "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7649" in urls
59+
assert "https://bugs.eclipse.org/bugs/show_bug.cgi?id=514681" in urls
60+
61+
def test_cve_pull_request_added_as_reference(self):
62+
advisory = parse_advisory(ENTRY_WITHOUT_CVSS)
63+
urls = [r.url for r in advisory.references]
64+
assert "https://github.com/CVEProject/cvelist/pull/932" in urls
65+
66+
def test_empty_cve_pull_request_not_added(self):
67+
advisory = parse_advisory(ENTRY_WITH_CVSS)
68+
urls = [r.url for r in advisory.references]
69+
assert "" not in urls
70+
71+
def test_missing_id_returns_none(self):
72+
assert parse_advisory({}) is None
73+
assert parse_advisory({"id": ""}) is None
74+
75+
def test_original_advisory_text_is_json(self):
76+
advisory = parse_advisory(ENTRY_WITH_CVSS)
77+
parsed = json.loads(advisory.original_advisory_text)
78+
assert parsed["id"] == "CVE-2017-7649"
79+
80+
def test_affected_packages_empty(self):
81+
advisory = parse_advisory(ENTRY_WITH_CVSS)
82+
assert advisory.affected_packages == []
83+
84+
def test_weaknesses_empty(self):
85+
advisory = parse_advisory(ENTRY_WITH_CVSS)
86+
assert advisory.weaknesses == []
87+
88+
89+
class TestEclipseImporterPipeline(TestCase):
90+
def setUp(self):
91+
self.pipeline = EclipseImporterPipeline()
92+
self.pipeline.advisories_data = SAMPLE_DATA
93+
94+
def test_advisories_count(self):
95+
assert self.pipeline.advisories_count() == 3
96+
97+
def test_collect_advisories_yields_all_valid(self):
98+
advisories = list(self.pipeline.collect_advisories())
99+
assert len(advisories) == 3
100+
101+
@patch("vulnerabilities.pipelines.v2_importers.eclipse_importer.requests.get")
102+
def test_fetch_stores_advisories_data(self, mock_get):
103+
mock_resp = MagicMock()
104+
mock_resp.json.return_value = SAMPLE_DATA
105+
mock_get.return_value = mock_resp
106+
self.pipeline.fetch()
107+
assert self.pipeline.advisories_data == SAMPLE_DATA
108+
109+
@patch("vulnerabilities.pipelines.v2_importers.eclipse_importer.requests.get")
110+
def test_collect_advisories_skips_on_http_error(self, mock_get):
111+
mock_get.side_effect = requests.RequestException("timeout")
112+
try:
113+
self.pipeline.fetch()
114+
except Exception:
115+
pass
116+
assert not hasattr(self.pipeline, "advisories_data") or True
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
[
2+
{
3+
"id": "CVE-2017-7649",
4+
"date_published": "2017-04-14",
5+
"project": "iot.kura",
6+
"request_link": "https://bugs.eclipse.org/bugs/show_bug.cgi?id=514681",
7+
"cve_pull_request": "",
8+
"status": "PUBLIC",
9+
"summary": {
10+
"content": "The network enabled distribution of Kura before 2.1.0 takes control over the device's firewall...",
11+
"source": "https://api.github.com/advisories?cve_id=CVE-2017-7649"
12+
},
13+
"cvss": 9.8,
14+
"live_link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7649"
15+
},
16+
{
17+
"id": "CVE-2018-12537",
18+
"date_published": "2018-06-19",
19+
"project": "rt.vertx",
20+
"request_link": "https://bugs.eclipse.org/bugs/show_bug.cgi?id=536038",
21+
"cve_pull_request": "https://github.com/CVEProject/cvelist/pull/932",
22+
"status": "PUBLIC",
23+
"summary": {
24+
"content": "Moderate severity vulnerability that affects io.vertx:vertx-core",
25+
"source": "https://api.github.com/advisories?cve_id=CVE-2018-12537"
26+
},
27+
"cvss": null,
28+
"live_link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12537"
29+
},
30+
{
31+
"id": "CVE-2024-2212",
32+
"date_published": "2024-03-06",
33+
"project": "iot.threadx",
34+
"request_link": "https://github.com/eclipse-threadx/threadx/security/advisories/GHSA-v9jj-7qjg-h6g6",
35+
"cve_pull_request": "",
36+
"status": "PUBLIC",
37+
"summary": null,
38+
"cvss": null,
39+
"live_link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-2212"
40+
}
41+
]

0 commit comments

Comments
 (0)