Skip to content

Commit b1a80a8

Browse files
committed
add huawei advisories, initial commit
Signed-off-by: NucleonGodX <[email protected]>
1 parent 56eb442 commit b1a80a8

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

vulnerabilities/importers/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from vulnerabilities.importers import mozilla
2424
from vulnerabilities.importers import openssl
2525
from vulnerabilities.importers import oss_fuzz
26+
from vulnerabilities.importers import huawei
2627
from vulnerabilities.importers import postgresql
2728
from vulnerabilities.importers import project_kb_msr2019
2829
from vulnerabilities.importers import redhat
@@ -65,6 +66,7 @@
6566
fireeye.FireyeImporter,
6667
apache_kafka.ApacheKafkaImporter,
6768
oss_fuzz.OSSFuzzImporter,
69+
huawei.HuaweiImporter,
6870
ruby.RubyImporter,
6971
github_osv.GithubOSVImporter,
7072
curl.CurlImporter,

vulnerabilities/importers/huawei.py

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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 re
11+
import logging
12+
import requests
13+
from bs4 import BeautifulSoup
14+
from packageurl import PackageURL
15+
from univers.version_range import GenericVersionRange
16+
from univers.versions import GenericVersion
17+
from vulnerabilities.importer import AdvisoryData, AffectedPackage, Importer
18+
19+
logging.basicConfig(level=logging.INFO)
20+
logger = logging.getLogger(__name__)
21+
22+
class HuaweiImporter(Importer):
23+
root_url = "https://consumer.huawei.com/en/support/bulletin/"
24+
spdx_license_expression = "NOASSERTION"
25+
importer_name = "Huawei Security Bulletin Importer"
26+
27+
def advisory_data(self):
28+
years_months = [
29+
('2024', range(7, 13)), # July 2024 to December 2024
30+
('2025', range(1, 2)) # January 2025
31+
]
32+
for year, months in years_months:
33+
for month in months:
34+
url = f"{self.root_url}{year}/{month}/"
35+
try:
36+
response = requests.get(url)
37+
response.raise_for_status()
38+
yield from self.to_advisories(response.content, url)
39+
except requests.RequestException as e:
40+
logger.error(f"Failed to fetch URL {url}: {e}")
41+
continue
42+
43+
def parse_version(self, version_str):
44+
"""Parse version string and separate OS type and version number."""
45+
version_str = version_str.strip()
46+
47+
harmony_match = re.match(r"HarmonyOS\s*(\d+\.\d+\.\d+)", version_str)
48+
if harmony_match:
49+
return "harmony", harmony_match.group(1)
50+
51+
emui_match = re.match(r"EMUI\s*(\d+\.\d+\.\d+)", version_str)
52+
if emui_match:
53+
return "emui", emui_match.group(1)
54+
55+
return None, None
56+
57+
def group_versions_by_os(self, versions):
58+
"""Group versions by OS type."""
59+
grouped = {
60+
"harmony": [],
61+
"emui": []
62+
}
63+
64+
for version in versions:
65+
os_type, version_num = self.parse_version(version)
66+
if os_type and version_num:
67+
grouped[os_type].append(version_num)
68+
else:
69+
logger.warning(f"Skipping unparseable version: {version}")
70+
71+
return grouped
72+
73+
def create_affected_packages(self, os_type, versions, fixed=False):
74+
"""Create AffectedPackage objects for a given OS type and versions."""
75+
if not versions:
76+
return []
77+
78+
package = PackageURL(
79+
name=os_type,
80+
type="generic",
81+
)
82+
83+
if fixed:
84+
return [
85+
AffectedPackage(
86+
package=package,
87+
fixed_version=GenericVersion(version)
88+
)
89+
for version in versions
90+
]
91+
else:
92+
return [
93+
AffectedPackage(
94+
package=package,
95+
affected_version_range=GenericVersionRange.from_versions(versions)
96+
)
97+
]
98+
99+
def to_advisories(self, content, url):
100+
soup = BeautifulSoup(content, features="lxml")
101+
tables = soup.find_all('table')
102+
if len(tables) < 2:
103+
logger.warning(f"Expected at least 2 tables, found {len(tables)} at {url}")
104+
return
105+
106+
affected_table = tables[0]
107+
fixed_table = tables[1]
108+
cve_data = {}
109+
110+
for row in affected_table.find_all('tr'):
111+
cols = row.find_all('td')
112+
if len(cols) >= 5:
113+
cve_id = cols[0].text.strip()
114+
versions = [v.strip() for v in cols[4].text.strip().split(',') if v.strip()]
115+
grouped_versions = self.group_versions_by_os(versions)
116+
117+
if cve_id not in cve_data:
118+
cve_data[cve_id] = {
119+
'affected_versions': grouped_versions,
120+
'fixed_versions': {'harmony': [], 'emui': []}
121+
}
122+
else:
123+
for os_type in grouped_versions:
124+
cve_data[cve_id]['affected_versions'][os_type].extend(grouped_versions[os_type])
125+
126+
for row in fixed_table.find_all('tr'):
127+
cols = row.find_all('td')
128+
if len(cols) >= 3:
129+
cve_id = cols[0].text.strip()
130+
versions = [v.strip() for v in cols[2].text.strip().split(',') if v.strip()]
131+
grouped_versions = self.group_versions_by_os(versions)
132+
133+
if cve_id not in cve_data:
134+
cve_data[cve_id] = {
135+
'affected_versions': {'harmony': [], 'emui': []},
136+
'fixed_versions': grouped_versions
137+
}
138+
else:
139+
for os_type in grouped_versions:
140+
cve_data[cve_id]['fixed_versions'][os_type].extend(grouped_versions[os_type])
141+
142+
for cve_id, data in cve_data.items():
143+
affected_packages = []
144+
145+
affected_packages.extend(
146+
self.create_affected_packages('harmony', data['affected_versions']['harmony'])
147+
)
148+
affected_packages.extend(
149+
self.create_affected_packages('harmony', data['fixed_versions']['harmony'], fixed=True)
150+
)
151+
152+
affected_packages.extend(
153+
self.create_affected_packages('emui', data['affected_versions']['emui'])
154+
)
155+
affected_packages.extend(
156+
self.create_affected_packages('emui', data['fixed_versions']['emui'], fixed=True)
157+
)
158+
159+
if affected_packages:
160+
yield AdvisoryData(
161+
aliases=[cve_id],
162+
summary="",
163+
references=[],
164+
affected_packages=affected_packages,
165+
url=url
166+
)

0 commit comments

Comments
 (0)