12
12
logger = logging .getLogger (__name__ )
13
13
14
14
msrc_url = "https://api.msrc.microsoft.com"
15
+ version = "2.0"
15
16
16
17
17
18
def get_value (data , key , default = None ):
@@ -23,11 +24,11 @@ def get_value(data, key, default=None):
23
24
"""
24
25
try :
25
26
for k in key .split ("." ):
26
- try :
27
+ if k . isdigit () :
27
28
idx = int (k )
28
29
data = data [idx ]
29
- except ValueError :
30
- data = data [ k ]
30
+ else :
31
+ data = data . get ( k , default )
31
32
except Exception as e :
32
33
if default :
33
34
return default
@@ -86,7 +87,12 @@ def __init__(self, data):
86
87
self .url = None
87
88
self .products = []
88
89
else :
89
- self .kb = "KB" + self .get_value ("Description.Value" )
90
+ self .kb = ""
91
+ if (
92
+ self .get_value ("Description.Value" )
93
+ and self .get_value ("Description.Value" , "" ).isdigit ()
94
+ ):
95
+ self .kb = "KB" + self .get_value ("Description.Value" )
90
96
self .url = self .get_value ("URL" )
91
97
self .products = self .get_value ("ProductID" )
92
98
@@ -124,11 +130,11 @@ def __init__(self, data):
124
130
self .updated_date = self .get_value ("DocumentTracking.CurrentReleaseDate" )
125
131
126
132
self .vulnerabilites = [
127
- Vulnerability (vuln ) for vuln in self .get_value ("Vulnerability" )
133
+ Vulnerability (vuln ) for vuln in self .get_value ("Vulnerability" , [] )
128
134
] # Hash is better ?
129
135
self .products = [
130
136
Product (product )
131
- for product in self .get_value ("ProductTree.FullProductName" )
137
+ for product in self .get_value ("ProductTree.FullProductName" , [] )
132
138
]
133
139
134
140
def __str__ (self ):
@@ -152,41 +158,54 @@ def get_cvrf(cvrf: str):
152
158
return
153
159
"""
154
160
155
- url = f"{ msrc_url } /cvrf/v2.0 /cvrf/{ cvrf } "
161
+ url = f"{ msrc_url } /cvrf/v { version } /cvrf/{ cvrf } "
156
162
logger .debug (url )
157
163
response = requests .get (url , headers = {"Accept" : "application/json" })
158
164
if response .status_code != 200 :
159
165
raise Exception ("Failed to get CVRF, %d" % (response .status_code ))
166
+ data = response .json ()
167
+ return CVRF (data )
160
168
161
- return CVRF (response .json ())
162
169
163
-
164
- def get_updates (update_id = None , vul_id = None , year = None ):
170
+ def get_cvrfs (query = None ):
165
171
"""
166
172
Get a list of Microsoft security updates
167
173
"""
168
- query = update_id or vul_id or year or None
169
- if query is None :
170
- url = f"{ msrc_url } /cvrf/v2.0/updates"
171
- else :
172
- url = f"{ msrc_url } /cvrf/v2.0/Updates('{ query } ')"
174
+ url = f"{ msrc_url } /cvrf/v{ version } /"
175
+ url += f"Updates('{ query } ')" if query else "updates"
173
176
logger .debug (url )
174
177
response = requests .get (url , headers = {"Accept" : "application/json" })
175
178
if response .status_code != 200 :
176
179
raise Exception (
177
- "Failed to get a list of MS security udpates, %d" % ( response .status_code )
180
+ "Failed to get a list of MS security udpates, " f" { response .status_code } "
178
181
)
179
- updates = response .json ()["value" ]
182
+ cvrfs = response .json ().get ("value" , [])
183
+ return cvrfs
184
+
185
+
186
+ def get_updates (update_id = None , vul_id = None , year = None ):
187
+ """
188
+ Get a list of Microsoft security updates
189
+ """
190
+ query = update_id or vul_id or year or None
191
+ cvrfs = get_cvrfs (query )
180
192
181
193
if vul_id :
182
- cvrf = get_cvrf (updates [0 ]["ID" ])
194
+ cvrf = get_cvrf (cvrfs [0 ]["ID" ])
183
195
return cvrf .get_cve (vul_id )
196
+ return [get_cvrf (cvrf ["ID" ]) for cvrf in cvrfs ]
184
197
185
198
186
199
def main ():
187
200
parser = argparse .ArgumentParser ()
188
- parser .add_argument ("-v" , "--version" , action = "version" , version = __version__ )
189
- parser .add_argument ("search" , help = "CVE ex) CVE-2017-0144, CVRF ex) 2020-Sep" )
201
+ parser .add_argument (
202
+ "-v" , "--version" ,
203
+ action = "version" , version = __version__
204
+ )
205
+ parser .add_argument (
206
+ "search" ,
207
+ help = "CVE ex) CVE-2017-0144, CVRF ex) 2020-Sep, KB ex) KB5014699"
208
+ )
190
209
options = parser .parse_args ()
191
210
192
211
search = options .search
@@ -196,6 +215,15 @@ def main():
196
215
for remediation in cve .remediations :
197
216
if remediation .type == 5 :
198
217
print (remediation .kb , remediation .url )
218
+ elif search .upper ().startswith ("KB" ):
219
+ cvrfs = get_cvrfs ()
220
+ for cvrf in cvrfs :
221
+ cvrf = get_cvrf (cvrf ["ID" ])
222
+ for vuln in cvrf .vulnerabilites :
223
+ for remediation in vuln .remediations :
224
+ if remediation .kb == search .upper ():
225
+ print (f"{ remediation .kb } : { remediation .url } " )
226
+ return
199
227
elif re .match (r"\d{4}\-\w{3}" , search ):
200
228
cvrf = get_cvrf (search )
201
229
for vuln in cvrf .vulnerabilites :
0 commit comments