Skip to content

Commit 7e2813c

Browse files
committed
add-cwe-data-in-multiple-importers
Signed-off-by: ambuj <kulshreshthaak.12@gmail.com>
1 parent 433d3a6 commit 7e2813c

5 files changed

Lines changed: 194 additions & 1 deletion

File tree

vulnerabilities/importers/apache_httpd.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
#
99

1010
import logging
11+
import re
1112
import urllib
1213

1314
import requests
1415
from bs4 import BeautifulSoup
16+
from cwe2.database import Database
1517
from packageurl import PackageURL
1618
from univers.version_constraint import VersionConstraint
1719
from univers.version_range import ApacheVersionRange
@@ -23,6 +25,7 @@
2325
from vulnerabilities.importer import Reference
2426
from vulnerabilities.importer import VulnerabilitySeverity
2527
from vulnerabilities.severity_systems import APACHE_HTTPD
28+
from vulnerabilities.utils import get_cwe_id
2629
from vulnerabilities.utils import get_item
2730

2831
logger = logging.getLogger(__name__)
@@ -102,11 +105,14 @@ def to_advisory(self, data):
102105
)
103106
)
104107

108+
weaknesses = get_weaknesses(data)
109+
105110
return AdvisoryData(
106111
aliases=[alias],
107112
summary=description or "",
108113
affected_packages=affected_packages,
109114
references=[reference],
115+
weaknesses=weaknesses,
110116
url=reference.url,
111117
)
112118

@@ -152,3 +158,70 @@ def fetch_links(url):
152158
continue
153159
links.append(urllib.parse.urljoin(url, link))
154160
return links
161+
162+
163+
def get_weaknesses(cve_data):
164+
"""
165+
Extract CWE IDs from CVE data.
166+
167+
Args:
168+
cve_data (dict): The CVE data in a dictionary format.
169+
170+
Returns:
171+
List[int]: A list of unique CWE IDs.
172+
173+
>>> mock_cve_data = {
174+
... "containers": {
175+
... "cna": {
176+
... "providerMetadata": {
177+
... "orgId": "f0158376-9dc2-43b6-827c-5f631a4d8d09"
178+
... },
179+
... "title": "mod_macro buffer over-read",
180+
... "problemTypes": [
181+
... {
182+
... "descriptions": [
183+
... {
184+
... "description": "CWE-125 Out-of-bounds Read",
185+
... "lang": "en",
186+
... "cweId": "CWE-125",
187+
... "type": "CWE"
188+
... }
189+
... ]
190+
... }
191+
... ]
192+
... }
193+
... }
194+
... }
195+
>>> get_weaknesses(mock_cve_data)
196+
[125]
197+
"""
198+
problem_types = cve_data.get("containers", {}).get("cna", {}).get("problemTypes", [])
199+
descriptions = problem_types[0].get("descriptions", []) if len(problem_types) > 0 else []
200+
cwe_string = descriptions[0].get("cweId", "") if len(descriptions) > 0 else ""
201+
cwe_pattern = r"CWE-\d+"
202+
description = descriptions[0].get("description", "") if len(descriptions) > 0 else ""
203+
matches = re.findall(cwe_pattern, description)
204+
db = Database()
205+
weaknesses = []
206+
cwe_string_from_description = ""
207+
if matches:
208+
cwe_string_from_description = matches[0]
209+
if cwe_string or cwe_string_from_description:
210+
if cwe_string:
211+
cwe_id = get_cwe_id(cwe_string)
212+
try:
213+
db.get(cwe_id)
214+
weaknesses.append(cwe_id)
215+
except Exception:
216+
logger.error("Invalid CWE id")
217+
elif cwe_string_from_description:
218+
cwe_id = get_cwe_id(cwe_string_from_description)
219+
try:
220+
db.get(cwe_id)
221+
weaknesses.append(cwe_id)
222+
except Exception:
223+
logger.error("Invalid CWE id")
224+
225+
seen = set()
226+
unique_cwe = [x for x in weaknesses if not (x in seen or seen.add(x))]
227+
return unique_cwe

vulnerabilities/importers/debian.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
#
99

1010
import logging
11+
import re
1112
from typing import Any
1213
from typing import Iterable
1314
from typing import List
1415
from typing import Mapping
1516

1617
import requests
18+
from cwe2.database import Database
1719
from packageurl import PackageURL
1820
from univers.version_range import DebianVersionRange
1921
from univers.versions import DebianVersion
@@ -23,6 +25,7 @@
2325
from vulnerabilities.importer import Importer
2426
from vulnerabilities.importer import Reference
2527
from vulnerabilities.utils import dedupe
28+
from vulnerabilities.utils import get_cwe_id
2629
from vulnerabilities.utils import get_item
2730

2831
logger = logging.getLogger(__name__)
@@ -93,6 +96,7 @@ def advisory_data(self) -> Iterable[AdvisoryData]:
9396
yield from self.parse(pkg_name, records)
9497

9598
def parse(self, pkg_name: str, records: Mapping[str, Any]) -> Iterable[AdvisoryData]:
99+
96100
for cve_id, record in records.items():
97101
affected_versions = []
98102
fixed_versions = []
@@ -150,10 +154,38 @@ def parse(self, pkg_name: str, records: Mapping[str, Any]) -> Iterable[AdvisoryD
150154
fixed_version=DebianVersion(fixed_version),
151155
)
152156
)
157+
weaknesses = get_cwe_from_debian_advisory(record)
158+
153159
yield AdvisoryData(
154160
aliases=[cve_id],
155161
summary=record.get("description", ""),
156162
affected_packages=affected_packages,
157163
references=references,
164+
weaknesses=weaknesses,
158165
url=self.api_url,
159166
)
167+
168+
169+
def get_cwe_from_debian_advisory(record):
170+
"""
171+
Extracts CWE ID strings from the given raw_data and returns a list of CWE IDs.
172+
173+
>>> get_cwe_from_debian_advisory({"description":"PEAR HTML_QuickForm version 3.2.14 contains an eval injection (CWE-95) vulnerability in HTML_QuickForm's getSubmitValue method, HTML_QuickForm's validate method, HTML_QuickForm_hierselect's _setOptions method, HTML_QuickForm_element's _findValue method, HTML_QuickForm_element's _prepareValue method. that can result in Possible information disclosure, possible impact on data integrity and execution of arbitrary code. This attack appear to be exploitable via A specially crafted query string could be utilised, e.g. http://www.example.com/admin/add_practice_type_id[1]=fubar%27])%20OR%20die(%27OOK!%27);%20//&mode=live. This vulnerability appears to have been fixed in 3.2.15."})
174+
[95]
175+
>>> get_cwe_from_debian_advisory({"description":"There is no WEAKNESS DATA"})
176+
[]
177+
"""
178+
description = record.get("description") or ""
179+
pattern = r"CWE-\d+"
180+
cwe_strings = re.findall(pattern, description)
181+
weaknesses = []
182+
db = Database()
183+
for cwe_string in cwe_strings:
184+
if cwe_string:
185+
cwe_id = get_cwe_id(cwe_string)
186+
try:
187+
db.get(cwe_id)
188+
weaknesses.append(cwe_id)
189+
except Exception:
190+
logger.error("Invalid CWE id")
191+
return weaknesses

vulnerabilities/importers/fireeye.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
from typing import Iterable
1313
from typing import List
1414

15+
from cwe2.database import Database
16+
1517
from vulnerabilities.importer import AdvisoryData
1618
from vulnerabilities.importer import Importer
1719
from vulnerabilities.importer import Reference
1820
from vulnerabilities.utils import build_description
1921
from vulnerabilities.utils import dedupe
22+
from vulnerabilities.utils import get_cwe_id
2023

2124
logger = logging.getLogger(__name__)
2225

@@ -77,10 +80,13 @@ def parse_advisory_data(raw_data, file, base_path) -> AdvisoryData:
7780
disc_credits = md_dict.get("## Discovery Credits") # not used
7881
disc_timeline = md_dict.get("## Disclosure Timeline") # not used
7982
references = md_dict.get("## References") or []
83+
cwe_data = md_dict.get("## Common Weakness Enumeration") or []
84+
8085
return AdvisoryData(
8186
aliases=get_aliases(database_id, cve_ref),
8287
summary=build_description(" ".join(summary), " ".join(description)),
8388
references=get_references(references),
89+
weaknesses=get_weaknesses(cwe_data),
8490
url=advisory_url,
8591
)
8692

@@ -140,3 +146,31 @@ def md_list_to_dict(md_list):
140146
else:
141147
md_dict[md_key].append(md_line)
142148
return md_dict
149+
150+
151+
def get_weaknesses(cwe_data):
152+
"""
153+
Return the list of CWE IDs as integers from a list of weakness summaries, e.g., [379].
154+
Extract the CWE strings from a list of weakness descriptions,
155+
e.g., ["CWE-379: Creation of Temporary File in Directory with Insecure Permissions"], to obtain CWE IDs like CWE-379.
156+
Remove the "CWE-" prefix from each CWE string and convert it to an integer (e.g., 379).
157+
Then, check if the CWE ID exists in the CWE database.
158+
"""
159+
cwe_list = []
160+
for line in cwe_data:
161+
cwe_ids = re.findall(r"CWE-\d+", line)
162+
cwe_list.extend(cwe_ids)
163+
164+
weaknesses = []
165+
db = Database()
166+
167+
for cwe_string in cwe_list:
168+
169+
if cwe_string:
170+
cwe_id = get_cwe_id(cwe_string)
171+
try:
172+
db.get(cwe_id)
173+
weaknesses.append(cwe_id)
174+
except Exception:
175+
logger.error("Invalid CWE id")
176+
return weaknesses

vulnerabilities/tests/test_debian.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
# See https://github.com/nexB/vulnerablecode for support or download.
77
# See https://aboutcode.org for more information about nexB OSS projects.
88
#
9-
109
import json
1110
import os
11+
import re
1212
from unittest.mock import patch
1313

1414
from vulnerabilities.importer import AdvisoryData
1515
from vulnerabilities.importers.debian import DebianImporter
16+
from vulnerabilities.importers.debian import get_cwe_from_debian_advisory
1617
from vulnerabilities.improvers.default import DefaultImprover
1718
from vulnerabilities.improvers.valid_versions import DebianBasicImprover
1819
from vulnerabilities.tests import util_tests
@@ -55,3 +56,38 @@ def test_debian_improver(mock_response):
5556
result.extend(inference)
5657
expected_file = os.path.join(TEST_DATA, f"debian-improver-expected.json")
5758
util_tests.check_results_against_json(result, expected_file)
59+
60+
61+
def test_get_cwe_from_debian_advisories():
62+
record = {
63+
"description": "Legion of the Bouncy Castle Legion of the Bouncy Castle Java Cryptography APIs 1.58 up to but not including 1.60 contains a CWE-580: Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection') vulnerability in XMSS/XMSS^MT private key deserialization that can result in Deserializing an XMSS/XMSS^MT private key can result in the execution of unexpected code. This attack appear to be exploitable via A handcrafted private key can include references to unexpected classes which will be picked up from the class path for the executing application. This vulnerability appears to have been fixed in 1.60 and later.",
64+
"scope": "local",
65+
"releases": {
66+
"bookworm": {
67+
"status": "resolved",
68+
"repositories": {"bookworm": "1.72-2"},
69+
"fixed_version": "1.60-1",
70+
"urgency": "low",
71+
},
72+
"bullseye": {
73+
"status": "resolved",
74+
"repositories": {"bullseye": "1.68-2"},
75+
"fixed_version": "1.60-1",
76+
"urgency": "low",
77+
},
78+
"sid": {
79+
"status": "resolved",
80+
"repositories": {"sid": "1.77-1"},
81+
"fixed_version": "1.60-1",
82+
"urgency": "low",
83+
},
84+
"trixie": {
85+
"status": "resolved",
86+
"repositories": {"trixie": "1.77-1"},
87+
"fixed_version": "1.60-1",
88+
"urgency": "low",
89+
},
90+
},
91+
}
92+
result = get_cwe_from_debian_advisory(record)
93+
assert result == [580]

vulnerabilities/tests/test_fireeye.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from vulnerabilities.importer import Reference
1414
from vulnerabilities.importers.fireeye import get_aliases
1515
from vulnerabilities.importers.fireeye import get_references
16+
from vulnerabilities.importers.fireeye import get_weaknesses
1617
from vulnerabilities.importers.fireeye import md_list_to_dict
1718
from vulnerabilities.importers.fireeye import parse_advisory_data
1819
from vulnerabilities.tests import util_tests
@@ -217,3 +218,20 @@ def test_md_list_to_dict_2(self):
217218
md_list = f.readlines()
218219
md_dict = md_list_to_dict(md_list)
219220
assert md_dict == expected_output
221+
222+
def test_get_weaknesses(self):
223+
assert get_weaknesses(
224+
[
225+
"CWE-379: Creation of Temporary File in Directory with Insecure Permissions",
226+
"CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')",
227+
]
228+
) == [379, 362]
229+
230+
assert (
231+
get_weaknesses(
232+
[
233+
"CWE-2345: This cwe id does not exist so it should generate Invalid CWE id error and return empty list."
234+
]
235+
)
236+
== []
237+
)

0 commit comments

Comments
 (0)