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