From: Daniel Turull Date: Thu, 10 Apr 2025 09:48:35 +0000 (+0200) Subject: linux/generate-cve-exclusions: use data from CVEProject X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ab4d2a79188da5b58bf9a4eacc2460a00a5c1d70;p=thirdparty%2Fopenembedded%2Fopenembedded-core-contrib.git linux/generate-cve-exclusions: use data from CVEProject The old script was relying on linuxkernelcves.com that was archived in May 2024 when kernel.org became a CNA. The new script reads CVE json files from the datadir that can be either from the official kernel.org CNA [1] or CVEProject [2] [1] https://git.kernel.org/pub/scm/linux/security/vulns.git [2] https://github.com/CVEProject/cvelistV5 Signed-off-by: Daniel Turull Signed-off-by: Mathieu Dubois-Briand Signed-off-by: Richard Purdie (cherry picked from commit 12612e8680798bdce39fbb79885e661596dbd53c) Signed-off-by: Steve Sakoman --- diff --git a/meta/recipes-kernel/linux/generate-cve-exclusions.py b/meta/recipes-kernel/linux/generate-cve-exclusions.py index aa9195aab4..82fb4264e3 100755 --- a/meta/recipes-kernel/linux/generate-cve-exclusions.py +++ b/meta/recipes-kernel/linux/generate-cve-exclusions.py @@ -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})")