@@ -1,7 +1,7 @@
#! /usr/bin/env python3
# Generate granular CVE status metadata for a specific version of the kernel
-# using data from linuxkernelcves.com.
+# using json data from cvelistV5 or vulns repository
#
# SPDX-License-Identifier: GPL-2.0-only
@@ -9,7 +9,8 @@ import argparse
import datetime
import json
import pathlib
-import re
+import os
+import glob
from packaging.version import Version
@@ -25,22 +26,75 @@ def parse_version(s):
return Version(s)
return None
+def get_fixed_versions(cve_info, base_version):
+ '''
+ Get fixed versionss
+ '''
+ first_affected = None
+ fixed = None
+ fixed_backport = None
+ next_version = Version(str(base_version) + ".5000")
+ for affected in cve_info["containers"]["cna"]["affected"]:
+ # In case the CVE info is not complete, it might not have default status and therefore
+ # we don't know the status of this CVE.
+ if not "defaultStatus" in affected:
+ return first_affected, fixed, fixed_backport
+ if affected["defaultStatus"] == "affected":
+ for version in affected["versions"]:
+ v = Version(version["version"])
+ if v == 0:
+ #Skiping non-affected
+ continue
+ if version["status"] == "affected" and not first_affected:
+ first_affected = v
+ elif (version["status"] == "unaffected" and
+ version['versionType'] == "original_commit_for_fix"):
+ fixed = v
+ elif base_version < v and v < next_version:
+ fixed_backport = v
+ elif affected["defaultStatus"] == "unaffected":
+ # Only specific versions are affected. We care only about our base version
+ if "versions" not in affected:
+ continue
+ for version in affected["versions"]:
+ if "versionType" not in version:
+ continue
+ if version["versionType"] == "git":
+ continue
+ v = Version(version["version"])
+ # in case it is not in our base version
+ less_than = Version(version["lessThan"])
+
+ if not first_affected:
+ first_affected = v
+ fixed = less_than
+ if base_version < v and v < next_version:
+ first_affected = v
+ fixed = less_than
+ fixed_backport = less_than
+
+ return first_affected, fixed, fixed_backport
+
+def is_linux_cve(cve_info):
+ '''Return true is the CVE belongs to Linux'''
+ if not "affected" in cve_info["containers"]["cna"]:
+ return False
+ for affected in cve_info["containers"]["cna"]["affected"]:
+ if not "product" in affected:
+ return False
+ if affected["product"] == "Linux" and affected["vendor"] == "Linux":
+ return True
+ return False
def main(argp=None):
parser = argparse.ArgumentParser()
- parser.add_argument("datadir", type=pathlib.Path, help="Path to a clone of https://github.com/nluedtke/linux_kernel_cves")
+ parser.add_argument("datadir", type=pathlib.Path, help="Path to a clone of https://github.com/CVEProject/cvelistV5 or https://git.kernel.org/pub/scm/linux/security/vulns.git")
parser.add_argument("version", type=Version, help="Kernel version number to generate data for, such as 6.1.38")
args = parser.parse_args(argp)
datadir = args.datadir
version = args.version
- base_version = f"{version.major}.{version.minor}"
-
- with open(datadir / "data" / "kernel_cves.json", "r") as f:
- cve_data = json.load(f)
-
- with open(datadir / "data" / "stream_fixes.json", "r") as f:
- stream_data = json.load(f)
+ base_version = Version(f"{version.major}.{version.minor}")
print(f"""
# Auto-generated CVE metadata, DO NOT EDIT BY HAND.
@@ -55,17 +109,23 @@ python check_kernel_cve_status_version() {{
do_cve_check[prefuncs] += "check_kernel_cve_status_version"
""")
- for cve, data in cve_data.items():
- if "affected_versions" not in data:
- print(f"# Skipping {cve}, no affected_versions")
- print()
- continue
+ # Loop though all CVES and check if they are kernel related, newer than 2015
+ pattern = os.path.join(datadir, '**', "CVE-20*.json")
- affected = data["affected_versions"]
- first_affected, fixed = re.search(r"(.+) to (.+)", affected).groups()
- first_affected = parse_version(first_affected)
- fixed = parse_version(fixed)
+ files = glob.glob(pattern, recursive=True)
+ for cve_file in sorted(files):
+ # Get CVE Id
+ cve = cve_file[cve_file.rfind("/")+1:cve_file.rfind(".json")]
+ # We process from 2015 data, old request are not properly formated
+ year = cve.split("-")[1]
+ if int(year) < 2015:
+ continue
+ with open(cve_file, 'r', encoding='utf-8') as json_file:
+ cve_info = json.load(json_file)
+ if not is_linux_cve(cve_info):
+ continue
+ first_affected, fixed, backport_ver = get_fixed_versions(cve_info, base_version)
if not fixed:
print(f"# {cve} has no known resolution")
elif first_affected and version < first_affected:
@@ -75,19 +135,13 @@ do_cve_check[prefuncs] += "check_kernel_cve_status_version"
f'CVE_STATUS[{cve}] = "fixed-version: Fixed from version {fixed}"'
)
else:
- if cve in stream_data:
- backport_data = stream_data[cve]
- if base_version in backport_data:
- backport_ver = Version(backport_data[base_version]["fixed_version"])
- if backport_ver <= version:
- print(
- f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"'
- )
- else:
- # TODO print a note that the kernel needs bumping
- print(f"# {cve} needs backporting (fixed from {backport_ver})")
+ if backport_ver:
+ if backport_ver <= version:
+ print(
+ f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"'
+ )
else:
- print(f"# {cve} needs backporting (fixed from {fixed})")
+ print(f"# {cve} needs backporting (fixed from {backport_ver})")
else:
print(f"# {cve} needs backporting (fixed from {fixed})")