Skip to content

Commit 425592d

Browse files
authored
Merge pull request haginara#5 from haginara/search-kb
Supports the searching KB
2 parents fb9ee4a + 0f7c813 commit 425592d

File tree

6 files changed

+69
-32
lines changed

6 files changed

+69
-32
lines changed

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
python-version: [3.6, 3.7, 3.8, 3.9]
14+
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
1515
steps:
1616
- uses: actions/checkout@v2
1717
- name: Set up python ${{ matrix.python-version }}
@@ -31,4 +31,4 @@ jobs:
3131
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
3232
- name: Test
3333
run: |
34-
pytest test.py
34+
pytest

msrc.py

+48-20
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
logger = logging.getLogger(__name__)
1313

1414
msrc_url = "https://api.msrc.microsoft.com"
15+
version = "2.0"
1516

1617

1718
def get_value(data, key, default=None):
@@ -23,11 +24,11 @@ def get_value(data, key, default=None):
2324
"""
2425
try:
2526
for k in key.split("."):
26-
try:
27+
if k.isdigit():
2728
idx = int(k)
2829
data = data[idx]
29-
except ValueError:
30-
data = data[k]
30+
else:
31+
data = data.get(k, default)
3132
except Exception as e:
3233
if default:
3334
return default
@@ -86,7 +87,12 @@ def __init__(self, data):
8687
self.url = None
8788
self.products = []
8889
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")
9096
self.url = self.get_value("URL")
9197
self.products = self.get_value("ProductID")
9298

@@ -124,11 +130,11 @@ def __init__(self, data):
124130
self.updated_date = self.get_value("DocumentTracking.CurrentReleaseDate")
125131

126132
self.vulnerabilites = [
127-
Vulnerability(vuln) for vuln in self.get_value("Vulnerability")
133+
Vulnerability(vuln) for vuln in self.get_value("Vulnerability", [])
128134
] # Hash is better ?
129135
self.products = [
130136
Product(product)
131-
for product in self.get_value("ProductTree.FullProductName")
137+
for product in self.get_value("ProductTree.FullProductName", [])
132138
]
133139

134140
def __str__(self):
@@ -152,41 +158,54 @@ def get_cvrf(cvrf: str):
152158
return
153159
"""
154160

155-
url = f"{msrc_url}/cvrf/v2.0/cvrf/{cvrf}"
161+
url = f"{msrc_url}/cvrf/v{version}/cvrf/{cvrf}"
156162
logger.debug(url)
157163
response = requests.get(url, headers={"Accept": "application/json"})
158164
if response.status_code != 200:
159165
raise Exception("Failed to get CVRF, %d" % (response.status_code))
166+
data = response.json()
167+
return CVRF(data)
160168

161-
return CVRF(response.json())
162169

163-
164-
def get_updates(update_id=None, vul_id=None, year=None):
170+
def get_cvrfs(query=None):
165171
"""
166172
Get a list of Microsoft security updates
167173
"""
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"
173176
logger.debug(url)
174177
response = requests.get(url, headers={"Accept": "application/json"})
175178
if response.status_code != 200:
176179
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}"
178181
)
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)
180192

181193
if vul_id:
182-
cvrf = get_cvrf(updates[0]["ID"])
194+
cvrf = get_cvrf(cvrfs[0]["ID"])
183195
return cvrf.get_cve(vul_id)
196+
return [get_cvrf(cvrf["ID"]) for cvrf in cvrfs]
184197

185198

186199
def main():
187200
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+
)
190209
options = parser.parse_args()
191210

192211
search = options.search
@@ -196,6 +215,15 @@ def main():
196215
for remediation in cve.remediations:
197216
if remediation.type == 5:
198217
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
199227
elif re.match(r"\d{4}\-\w{3}", search):
200228
cvrf = get_cvrf(search)
201229
for vuln in cvrf.vulnerabilites:

test.py

-10
This file was deleted.

tests/__init__.py

Whitespace-only changes.
File renamed without changes.

tests/test_msrc.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# coding: utf-8
2+
import json
3+
import msrc
4+
import os
5+
6+
7+
def test_msrc_cve():
8+
sample_path = os.path.join(
9+
os.path.dirname(__file__),
10+
'sample.json'
11+
)
12+
sample = json.load(open(sample_path))
13+
cvrf = msrc.CVRF(sample)
14+
assert isinstance(cvrf, msrc.CVRF)
15+
16+
17+
def test_msrc_search_cve():
18+
cve = msrc.get_updates(vul_id="CVE-2017-0144")
19+
assert isinstance(cve, msrc.Vulnerability)

0 commit comments

Comments
 (0)