Skip to content

Commit 5a2c07d

Browse files
authored
Merge branch 'main' into update_technologies
2 parents fd05fbf + 4a11509 commit 5a2c07d

File tree

5 files changed

+468
-0
lines changed

5 files changed

+468
-0
lines changed

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
from vulnerabilities.pipelines.v2_importers import pypa_importer as pypa_importer_v2
7474
from vulnerabilities.pipelines.v2_importers import pysec_importer as pysec_importer_v2
7575
from vulnerabilities.pipelines.v2_importers import redhat_importer as redhat_importer_v2
76+
from vulnerabilities.pipelines.v2_importers import retiredotnet_importer as retiredotnet_importer_v2
7677
from vulnerabilities.pipelines.v2_importers import ruby_importer as ruby_importer_v2
7778
from vulnerabilities.pipelines.v2_importers import ubuntu_osv_importer as ubuntu_osv_importer_v2
7879
from vulnerabilities.pipelines.v2_importers import vulnrichment_importer as vulnrichment_importer_v2
@@ -108,6 +109,7 @@
108109
debian_importer_v2.DebianImporterPipeline,
109110
mattermost_importer_v2.MattermostImporterPipeline,
110111
apache_tomcat_v2.ApacheTomcatImporterPipeline,
112+
retiredotnet_importer_v2.RetireDotnetImporterPipeline,
111113
ubuntu_osv_importer_v2.UbuntuOSVImporterPipeline,
112114
nvd_importer.NVDImporterPipeline,
113115
github_importer.GitHubAPIImporterPipeline,
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
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 re
12+
from collections import defaultdict
13+
from pathlib import Path
14+
15+
from fetchcode.vcs import fetch_via_vcs
16+
from packageurl import PackageURL
17+
from univers.version_range import NugetVersionRange
18+
19+
from vulnerabilities.importer import AdvisoryDataV2
20+
from vulnerabilities.importer import AffectedPackageV2
21+
from vulnerabilities.importer import ReferenceV2
22+
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
23+
from vulnerabilities.utils import get_advisory_url
24+
25+
26+
class RetireDotnetImporterPipeline(VulnerableCodeBaseImporterPipelineV2):
27+
license_url = "https://github.com/RetireNet/Packages/blob/master/LICENSE"
28+
spdx_license_expression = "MIT"
29+
repo_url = "git+https://github.com/RetireNet/Packages/"
30+
pipeline_id = "retiredotnet_importer_v2"
31+
run_once = True
32+
33+
@classmethod
34+
def steps(cls):
35+
return (
36+
cls.clone,
37+
cls.collect_and_store_advisories,
38+
cls.clean_downloads,
39+
)
40+
41+
def clone(self):
42+
self.log(f"Cloning `{self.repo_url}`")
43+
self.vcs_response = fetch_via_vcs(self.repo_url)
44+
45+
def advisories_count(self):
46+
root = Path(self.vcs_response.dest_dir) / "Content"
47+
return sum(1 for _ in root.rglob("*.json"))
48+
49+
def collect_advisories(self):
50+
base_path = Path(self.vcs_response.dest_dir)
51+
vuln = base_path / "Content"
52+
affected_packages = []
53+
54+
for file in vuln.glob("*.json"):
55+
advisory_id = "retiredotnet-" + file.stem
56+
advisory_url = get_advisory_url(
57+
file=file,
58+
base_path=base_path,
59+
url="https://github.com/RetireNet/Packages/blob/master/",
60+
)
61+
with open(file) as f:
62+
json_doc = json.load(f)
63+
description = json_doc.get("description") or ""
64+
aliases = self.vuln_id_from_desc(description)
65+
66+
# group by package name `id`
67+
# { pkg_id: {'affected_versions': set(), 'fixed': set()} }
68+
grouped_packages = defaultdict(
69+
lambda: {"affected_versions": set(), "fixed_versions": set()}
70+
)
71+
for pkg in json_doc.get("packages") or []:
72+
name = pkg.get("id")
73+
if not name:
74+
continue
75+
76+
affected_version = pkg.get("affected")
77+
if affected_version:
78+
grouped_packages[name]["affected_versions"].add(affected_version)
79+
80+
fixed_version = pkg.get("fix")
81+
if fixed_version:
82+
grouped_packages[name]["fixed_versions"].add(fixed_version)
83+
84+
for pkg in grouped_packages:
85+
affected_version_range = None
86+
affected_versions = grouped_packages[pkg]["affected_versions"]
87+
if affected_versions:
88+
affected_version_range = NugetVersionRange.from_versions(affected_versions)
89+
90+
fixed_version_range = None
91+
fixed_versions = grouped_packages[pkg]["fixed_versions"]
92+
if fixed_versions:
93+
fixed_version_range = NugetVersionRange.from_versions(fixed_versions)
94+
95+
if affected_version_range or fixed_version_range:
96+
affected_packages.append(
97+
AffectedPackageV2(
98+
package=PackageURL(type="nuget", name=pkg),
99+
affected_version_range=affected_version_range,
100+
fixed_version_range=fixed_version_range,
101+
)
102+
)
103+
104+
link = json_doc.get("link")
105+
if link:
106+
vuln_reference = [
107+
ReferenceV2(
108+
url=link,
109+
)
110+
]
111+
112+
yield AdvisoryDataV2(
113+
advisory_id=advisory_id,
114+
aliases=[aliases] if aliases else [],
115+
summary=description,
116+
affected_packages=affected_packages,
117+
references=vuln_reference,
118+
url=advisory_url,
119+
)
120+
121+
@staticmethod
122+
def vuln_id_from_desc(desc):
123+
cve_regex = re.compile(r"CVE-\d+-\d+")
124+
res = cve_regex.search(desc)
125+
if res:
126+
return desc[res.start() : res.end()]
127+
else:
128+
return None
129+
130+
def clean_downloads(self):
131+
"""Cleanup any temporary repository data."""
132+
if self.vcs_response:
133+
self.log(f"Removing cloned repository")
134+
self.vcs_response.delete()
135+
136+
def on_failure(self):
137+
"""Ensure cleanup is always performed on failure."""
138+
self.clean_downloads()
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
from pathlib import Path
11+
from unittest.mock import Mock
12+
from unittest.mock import patch
13+
14+
import pytest
15+
16+
from vulnerabilities.pipelines.v2_importers.retiredotnet_importer import (
17+
RetireDotnetImporterPipeline,
18+
)
19+
from vulnerabilities.tests import util_tests
20+
21+
TEST_DATA = Path(__file__).parent.parent.parent / "test_data" / "retiredotnet_v2"
22+
23+
24+
def test_vuln_id_from_desc():
25+
importer = RetireDotnetImporterPipeline()
26+
gibberish = "xyzabcpqr123" * 50 + "\n" * 100
27+
res = importer.vuln_id_from_desc(gibberish)
28+
assert res is None
29+
30+
desc = "abcdef CVE-2002-1968 pqrstuvwxyz:_|-|"
31+
res = importer.vuln_id_from_desc(desc)
32+
assert res == "CVE-2002-1968"
33+
34+
35+
@pytest.mark.django_db
36+
def test_retiredotnet_advisories_per_file():
37+
pipeline = RetireDotnetImporterPipeline()
38+
test_file = TEST_DATA / "12.json"
39+
expected_file = TEST_DATA / "expected_file.json"
40+
pipeline.vcs_response = Mock(dest_dir=TEST_DATA)
41+
42+
with patch.object(Path, "glob", return_value=[test_file]):
43+
result = [adv.to_dict() for adv in pipeline.collect_advisories()]
44+
45+
util_tests.check_results_against_json(result, expected_file)
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
{
2+
"link": "https://github.com/aspnet/Announcements/issues/334",
3+
"description": "Microsoft Security Advisory CVE-2019-0564: ASP.NET Core Denial of Service Vulnerability",
4+
"packages": [
5+
{
6+
"id": "Microsoft.AspNetCore.WebSockets",
7+
"affected": "2.1.0",
8+
"fix": "2.1.7"
9+
},
10+
{
11+
"id": "Microsoft.AspNetCore.WebSockets",
12+
"affected": "2.1.1",
13+
"fix": "2.1.7"
14+
},
15+
{
16+
"id": "Microsoft.AspNetCore.WebSockets",
17+
"affected": "2.2.0",
18+
"fix": "2.2.1"
19+
},
20+
{
21+
"id": "Microsoft.AspNetCore.Server.Kestrel.Core",
22+
"affected": "2.1.0",
23+
"fix": "2.1.7"
24+
},
25+
{
26+
"id": "Microsoft.AspNetCore.Server.Kestrel.Core",
27+
"affected": "2.1.1",
28+
"fix": "2.1.7"
29+
},
30+
{
31+
"id": "Microsoft.AspNetCore.Server.Kestrel.Core",
32+
"affected": "2.1.2",
33+
"fix": "2.1.7"
34+
},
35+
{
36+
"id": "Microsoft.AspNetCore.Server.Kestrel.Core",
37+
"affected": "2.1.3",
38+
"fix": "2.1.7"
39+
},
40+
{
41+
"id": "System.Net.WebSockets.WebSocketProtocol",
42+
"affected": "4.5.0",
43+
"fix": "4.5.3"
44+
},
45+
{
46+
"id": "System.Net.WebSockets.WebSocketProtocol",
47+
"affected": "4.5.1",
48+
"fix": "4.5.3"
49+
},
50+
{
51+
"id": "System.Net.WebSockets.WebSocketProtocol",
52+
"affected": "4.5.2",
53+
"fix": "4.5.3"
54+
},
55+
{
56+
"id": "Microsoft.NETCore.App",
57+
"affected": "2.1.0",
58+
"fix": "2.1.7"
59+
},
60+
{
61+
"id": "Microsoft.NETCore.App",
62+
"affected": "2.1.1",
63+
"fix": "2.1.7"
64+
},
65+
{
66+
"id": "Microsoft.NETCore.App",
67+
"affected": "2.1.2",
68+
"fix": "2.1.7"
69+
},
70+
{
71+
"id": "Microsoft.NETCore.App",
72+
"affected": "2.1.3",
73+
"fix": "2.1.7"
74+
},
75+
{
76+
"id": "Microsoft.NETCore.App",
77+
"affected": "2.1.4",
78+
"fix": "2.1.7"
79+
},
80+
{
81+
"id": "Microsoft.NETCore.App",
82+
"affected": "2.1.5",
83+
"fix": "2.1.7"
84+
},
85+
{
86+
"id": "Microsoft.NETCore.App",
87+
"affected": "2.1.6",
88+
"fix": "2.1.7"
89+
},
90+
{
91+
"id": "Microsoft.NETCore.App",
92+
"affected": "2.2.0",
93+
"fix": "2.2.1"
94+
},
95+
{
96+
"id": "Microsoft.AspNetCore.App",
97+
"affected": "2.1.0",
98+
"fix": "2.1.7"
99+
},
100+
{
101+
"id": "Microsoft.AspNetCore.App",
102+
"affected": "2.1.1",
103+
"fix": "2.1.7"
104+
},
105+
{
106+
"id": "Microsoft.AspNetCore.App",
107+
"affected": "2.1.2",
108+
"fix": "2.1.7"
109+
},
110+
{
111+
"id": "Microsoft.AspNetCore.App",
112+
"affected": "2.1.3",
113+
"fix": "2.1.7"
114+
},
115+
{
116+
"id": "Microsoft.AspNetCore.App",
117+
"affected": "2.1.4",
118+
"fix": "2.1.7"
119+
},
120+
{
121+
"id": "Microsoft.AspNetCore.App",
122+
"affected": "2.1.5",
123+
"fix": "2.1.7"
124+
},
125+
{
126+
"id": "Microsoft.AspNetCore.App",
127+
"affected": "2.1.6",
128+
"fix": "2.1.7"
129+
},
130+
{
131+
"id": "Microsoft.AspNetCore.App",
132+
"affected": "2.2.0",
133+
"fix": "2.2.1"
134+
},
135+
{
136+
"id": "Microsoft.AspNetCore.All",
137+
"affected": "2.1.0",
138+
"fix": "2.1.7"
139+
},
140+
{
141+
"id": "Microsoft.AspNetCore.All",
142+
"affected": "2.1.1",
143+
"fix": "2.1.7"
144+
},
145+
{
146+
"id": "Microsoft.AspNetCore.All",
147+
"affected": "2.1.2",
148+
"fix": "2.1.7"
149+
},
150+
{
151+
"id": "Microsoft.AspNetCore.All",
152+
"affected": "2.1.3",
153+
"fix": "2.1.7"
154+
},
155+
{
156+
"id": "Microsoft.AspNetCore.All",
157+
"affected": "2.1.4",
158+
"fix": "2.1.7"
159+
},
160+
{
161+
"id": "Microsoft.AspNetCore.All",
162+
"affected": "2.1.5",
163+
"fix": "2.1.7"
164+
},
165+
{
166+
"id": "Microsoft.AspNetCore.All",
167+
"affected": "2.1.6",
168+
"fix": "2.1.7"
169+
},
170+
{
171+
"id": "Microsoft.AspNetCore.All",
172+
"affected": "2.2.0",
173+
"fix": "2.2.1"
174+
}
175+
]
176+
}

0 commit comments

Comments
 (0)