Last active
May 14, 2024 17:13
-
-
Save haginara/89e2c89d3f4f7bdc449516f5915e6106 to your computer and use it in GitHub Desktop.
Getting Microsoft Security Update from api.msrc.microsoft.com via Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
from dataclasses import dataclass, asdict, field | |
from typing import Dict, List, Optional | |
import pprint | |
import json | |
try: | |
import requests | |
except ImportError: | |
raise SystemExit("Please install 'requests' first") | |
@dataclass | |
class Note: | |
Title: str | |
Type: int | |
Ordinal: str | |
Value: str | |
@dataclass | |
class Vulnerability: | |
Title: Dict | |
Notes: List[Note] | |
DiscoveryDateSpecified: bool | |
ReleaseDateSpecified: bool | |
Ordinal: str | |
RevisionHistory: List[Dict] | |
CVE: str | |
ProductStatuses: List[Dict] | |
Threats: List[Dict] | |
CVSSScoreSets: List | |
Remediations: List | |
Acknowledgments: List | |
@dataclass(init=False) | |
class CVRF: | |
DocumentTitle: str | |
DocumentType: str | |
DocumentPublisher : Dict[str, Dict] | |
DocumentTracking: Dict[str, Dict] | |
DocumentNotes: List[Dict] | |
ProductTree: Dict | |
Vulnerabilities: List[Vulnerability] | |
@classmethod | |
def init(cls, raw): | |
obj = cls() | |
obj.DocumentTitle = raw.get("DocumentTitle").get("Value") | |
obj.DocumentType = raw.get("DocumentType").get("Value") | |
obj.DocumentPublisher = raw.get("DcoumentPublisher") | |
obj.DocumentTracking = raw.get("DcoumentTracking") | |
obj.DocumentNotes = raw.get("DcoumentNotes") | |
obj.ProductTree = raw.get("ProductTree") | |
obj.Vulnerabilities = [] | |
for vuln in raw.get("Vulnerability", []): | |
pprint.pprint(vuln) | |
obj.Vulnerabilities.append(Vulnerability(**vuln)) | |
return obj | |
def is_security(self) -> bool: | |
if self.DocuemntType['Value'] == 'Security Update': | |
return True | |
return False | |
def Affected(self) -> List[str]: | |
values = set() | |
for item in self.ProductTree['Branch']['Items']: | |
for product in item['Items']: | |
values.add(product['Value']) | |
return list(values) | |
@dataclass | |
class SecurityUpdate: | |
ID: str | |
Alias: str = field(repr=False) | |
DocumentTitle: str | |
Severity: str | |
InitialReleaseDate: str = field(repr=False) | |
CurrentReleaseDate: str | |
CvrfUrl: str = field(repr=False) | |
Cvrf: CVRF = field(repr=False, default=None) | |
def summary(self): | |
return f"{self.DocumentTitle}({self.ID}): {len(self.Cvrf.Vulnerability)}" | |
def dump(self, filename: str=None): | |
if filename is None: | |
filename = f"{self.ID}_Security_Update.json" | |
data = { | |
'ID': self.ID, | |
'title': self.DocumentTitle, | |
'ReleaseDate': self.CurrentReleaseDate, | |
} | |
if self.Cvrf: | |
data['cvrf'] = asdict(self.Cvrf) | |
with open(filename, 'w') as f: | |
f.write(json.dumps(data, indent=2)) | |
url = 'https://api.msrc.microsoft.com' | |
key = 'MSRC_API_KEY' | |
headers = { | |
'Accept': 'application/json', | |
'api-key': key, | |
} | |
params = {'api-version': 2020} | |
updates_query = f"{url}/updates" | |
r = requests.get(updates_query, headers=headers, params=params) | |
if r.status_code != 200: | |
raise SystemExit("Failed to get updates") | |
updates = [SecurityUpdate(**update) for update in r.json().get('value')] | |
for update in updates: | |
r = requests.get(update.CvrfUrl, headers=headers, params=params) | |
if r.status_code != 200: | |
print(f"Failed to get update: {ID}") | |
continue | |
try: | |
data = r.json() | |
print(f"Init {update.ID}") | |
cvrf = CVRF.init(data) | |
update.Cvrf = cvrf | |
#print(f"{update.summary()}") | |
print(f"{update}") | |
update.dump() | |
except Exception as e: | |
print(f"Failed to get CVRF: {update.ID} Error: {e}") | |
raise(e) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment