| #! /usr/bin/env python3 |
| |
| # Generate granular CVE status metadata for a specific version of the kernel |
| # using data from linuxkernelcves.com. |
| # |
| # SPDX-License-Identifier: GPL-2.0-only |
| |
| import argparse |
| import datetime |
| import json |
| import pathlib |
| import re |
| |
| from packaging.version import Version |
| |
| |
| def parse_version(s): |
| """ |
| Parse the version string and either return a packaging.version.Version, or |
| None if the string was unset or "unk". |
| """ |
| if s and s != "unk": |
| # packaging.version.Version doesn't approve of versions like v5.12-rc1-dontuse |
| s = s.replace("-dontuse", "") |
| return Version(s) |
| return None |
| |
| |
| 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("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) |
| |
| print(f""" |
| # Auto-generated CVE metadata, DO NOT EDIT BY HAND. |
| # Generated at {datetime.datetime.now(datetime.timezone.utc)} for version {version} |
| |
| python check_kernel_cve_status_version() {{ |
| this_version = "{version}" |
| kernel_version = d.getVar("LINUX_VERSION") |
| if kernel_version != this_version: |
| bb.warn("Kernel CVE status needs updating: generated for %s but kernel is %s" % (this_version, kernel_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 |
| |
| affected = data["affected_versions"] |
| first_affected, fixed = re.search(r"(.+) to (.+)", affected).groups() |
| first_affected = parse_version(first_affected) |
| fixed = parse_version(fixed) |
| |
| if not fixed: |
| print(f"# {cve} has no known resolution") |
| elif first_affected and version < first_affected: |
| print(f'CVE_STATUS[{cve}] = "fixed-version: only affects {first_affected} onwards"') |
| elif fixed <= version: |
| print( |
| 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})") |
| else: |
| print(f"# {cve} needs backporting (fixed from {fixed})") |
| else: |
| print(f"# {cve} needs backporting (fixed from {fixed})") |
| |
| print() |
| |
| |
| if __name__ == "__main__": |
| main() |