commit:     df1cdd52ddf34c0c0650169b34ea332b23aae90e
Author:     Matt Jolly <kangie <AT> gentoo <DOT> org>
AuthorDate: Thu Sep 26 02:34:18 2024 +0000
Commit:     Matt Jolly <kangie <AT> gentoo <DOT> org>
CommitDate: Thu Sep 26 02:36:24 2024 +0000
URL:        
https://gitweb.gentoo.org/proj/chromium-tools.git/commit/?id=df1cdd52

get-edge-cves.py: new script

This script currently grabs the current month's CVRF from MS
and filters for Edge (Chromium-based) CVEs, then does some
magick to identify the version that it was fixed in if the
API is for some reason deficient...

Signed-off-by: Matt Jolly <kangie <AT> gentoo.org>

 get-edge-cves.py | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+)

diff --git a/get-edge-cves.py b/get-edge-cves.py
new file mode 100755
index 0000000..0761b4e
--- /dev/null
+++ b/get-edge-cves.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+
+# SPDX-License-Identifier: GPL-2.0-or-later
+# This script extracts the Chromium version mapping for Microsoft Edge based 
on a given CVE ID.
+# It uses the Microsoft Security Response Center (MSRC) API to get the Common 
Vulnerability Reporting Framework (CVRF)
+# for a given month and extracts the Chromium version mapping for Microsoft 
Edge (Chromium-based) from the CVRF.
+
+# API Docs https://api.msrc.microsoft.com/cvrf/v3.0/swagger/v3/swagger.json
+
+# We can use the CVRF API to get the Common Vulnerability Reporting Framework 
(CVRF) for a given month.
+# We can query the API via CVE ID to get the CVRF for a specific CVE, but that 
just leads us back to querying
+# the month. Stretch goal to ingest directly from bgo ticket aliases and 
confirm the month & version?
+# https://api.msrc.microsoft.com/cvrf/v3.0/updates/CVE-2024-7969
+
+# https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/2024-Aug
+# is the URL for the CVRF for August 2024
+
+# The XML looks like this:
+# <cvrfdoc
+#  . . .
+# <vuln:Vulnerability
+#     Ordinal="261">
+#     <vuln:Title>Chromium: CVE-2024-7969 Type Confusion in V8</vuln:Title>
+#     . . .
+#     <vuln:ProductStatuses>
+#       <vuln:Status
+#         Type="Known Affected">
+#         <vuln:ProductID>11655</vuln:ProductID>
+#         . . .
+#     </vuln:ProductStatuses>
+#     . . .
+#     <vuln:CVE>CVE-2024-7969</vuln:CVE>
+#     . . .
+#     <vuln:Remediations>
+#       <vuln:Remediation
+#         Type="Vendor Fix">
+#         <vuln:Description>Release Notes</vuln:Description>
+#         <vuln:URL />
+#         <vuln:ProductID>11655</vuln:ProductID>
+#         <vuln:AffectedFiles />
+#         <vuln:RestartRequired>No</vuln:RestartRequired>
+#         <vuln:SubType>Security Update</vuln:SubType>
+#         <vuln:FixedBuild>128.0.2739.42</vuln:FixedBuild>
+#         . . .
+#     </vuln:Remediations>
+#     . . .
+# </vuln:Vulnerability>
+
+# Process: Pick a month, get the CVRF for that month, then iterate over 
vulnerabilities to find the ones
+# that are for Microsoft Edge (Chromium-based) 
`<vuln:ProductID>11655</vuln:ProductID>`.
+# Extract the <vuln:CVE>CVE-2024-7969</vuln:CVE> to extract a CVE ID and
+# map to Chromium versions using the 
<vuln:FixedBuild>128.0.2739.42</vuln:FixedBuild> tag
+
+# It would be possible to parse the </vuln:Note> tag which contains a table 
with the chromium and edge versions
+# but each row looks like this: &lt;td&gt;128.0.6613.84/.85&lt;/td&gt; which 
is not very useful, and we already
+# know which version of Chromium a CVE got fixed in.
+
+import datetime
+import requests
+from portage import versions as portage_versions
+import sys
+import xml.etree.ElementTree as ET
+from bs4 import BeautifulSoup
+
+
+class EdgeCVE:
+    def __init__(self, cve, title, fixedbuild):
+        self.cve: str = cve
+        self.title: str = title
+        self.fixedbuild: str | None = fixedbuild
+
+    def __str__(self):
+        return f"{self.cve}: {self.title}: Fixed {self.fixedbuild if not None 
else 'unknown'}"
+
+
+def get_edge_cves(year, month):
+    msrcapi = f"https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/{year}-{month}";
+
+    # Get the CVRF for the specified month
+    response = requests.get(msrcapi)
+
+    if response.status_code != 200:
+        print(f"Website returned {response.status_code}")
+        print(f"Failed to get CVRF for {year}-{month}")
+        sys.exit(1)
+
+    # Parse the XML
+    root = ET.fromstring(response.text)
+
+    # Find all the vulnerabilities
+    vulnerabilities = 
root.findall(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}Vulnerability";)
+
+    edge_cves = []  # Store the edge cves here
+    for vulnerability in vulnerabilities:
+        productstatuses = 
vulnerability.findall(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}ProductStatuses";)
+        for productstatus in productstatuses:
+            productid = 
productstatus.find(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}ProductID";)
+            if productid.text == "11655":
+                # This is a Microsoft Edge (Chromium-based) vulnerability
+                cve_id = 
vulnerability.find(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}CVE";).text
+                cve_title = 
vulnerability.find(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}Title";).text
+                remediations = 
vulnerability.findall(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}Remediations";)
+                for remediation in remediations:
+                    fixedbuild = 
remediation.find(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}FixedBuild";)
+                    if fixedbuild is not None:
+                        edge_cves.append(
+                            EdgeCVE(cve_id,
+                                    cve_title,
+                                    fixedbuild.text)
+                        )
+                    else:
+                        # Fall back to parsing that horrible, horrible table 
in the notes
+                        notes = 
vulnerability.find(".//{http://www.icasi.org/CVRF/schema/vuln/1.1}Notes";)
+                        # There appear to be multiple notes, but only one has 
content that we want:
+                        # <vuln:Note Title="FAQ" Type="FAQ" 
Ordinal="10">&lt;p&gt;&lt;strong&gt;What is the version information for this 
release?&lt;/strong&gt;&lt;/p&gt;
+                        found = False
+                        for note in notes:
+                            if note.attrib['Title'] == "FAQ" and 
note.attrib['Type'] == "FAQ":
+
+                                # The note contains a table with the chromium 
and edge versions, written in "HTML"
+                                # &lt;td&gt;8/22/2024&lt;/td&gt;
+                                content = note.text
+
+                                soup = BeautifulSoup(content, 'html.parser')
+
+                                # Now you can work with the parsed HTML
+
+                                # Find all the table rows
+                                rows = soup.find_all('tr')
+                                # We want the second row, second cell
+                                if len(rows) > 1:
+                                    cells = rows[1].find_all('td')
+                                    if len(cells) > 1:
+                                        # We want the second cell (The first 
is the channel, the third the chromium version it's based on)
+                                        edge_version = cells[1].text
+                                        if 
portage_versions.ververify(edge_version):
+                                            found = True
+                                            edge_cves.append(
+                                                EdgeCVE(cve_id,
+                                                        cve_title,
+                                                        edge_version)
+                                            )
+
+                        if not found:
+                            edge_cves.append(
+                                EdgeCVE(cve_id,
+                                        cve_title,
+                                        None)
+                                )
+
+    return edge_cves
+
+
+now = datetime.datetime.now()
+year = now.year
+month = now.strftime("%B")[0:3]
+
+# Call the function with current year and month
+edge_cves = get_edge_cves(year, month)
+for cve in edge_cves:
+    print(cve)

Reply via email to