blob: 4d96f19fe7a72f2cb6370c5aa6c22dd67854de76 [file] [log] [blame]
Patrick Williams2a254922023-08-11 09:48:11 -05001#! /usr/bin/env python3
2
3# Generate granular CVE status metadata for a specific version of the kernel
4# using data from linuxkernelcves.com.
5#
6# SPDX-License-Identifier: GPL-2.0-only
7
8import argparse
9import datetime
10import json
11import pathlib
12import re
13
14from packaging.version import Version
15
16
17def parse_version(s):
18 """
19 Parse the version string and either return a packaging.version.Version, or
20 None if the string was unset or "unk".
21 """
22 if s and s != "unk":
23 # packaging.version.Version doesn't approve of versions like v5.12-rc1-dontuse
24 s = s.replace("-dontuse", "")
25 return Version(s)
26 return None
27
28
29def main(argp=None):
30 parser = argparse.ArgumentParser()
31 parser.add_argument("datadir", type=pathlib.Path, help="Path to a clone of https://github.com/nluedtke/linux_kernel_cves")
32 parser.add_argument("version", type=Version, help="Kernel version number to generate data for, such as 6.1.38")
33
34 args = parser.parse_args(argp)
35 datadir = args.datadir
36 version = args.version
37 base_version = f"{version.major}.{version.minor}"
38
39 with open(datadir / "data" / "kernel_cves.json", "r") as f:
40 cve_data = json.load(f)
41
42 with open(datadir / "data" / "stream_fixes.json", "r") as f:
43 stream_data = json.load(f)
44
45 print("# Auto-generated CVE metadata, DO NOT EDIT BY HAND.")
46 print(f"# Generated at {datetime.datetime.now()} for version {version}")
47 print()
48
49 for cve, data in cve_data.items():
50 if "affected_versions" not in data:
51 print(f"# Skipping {cve}, no affected_versions")
52 print()
53 continue
54
55 affected = data["affected_versions"]
56 first_affected, last_affected = re.search(r"(.+) to (.+)", affected).groups()
57 first_affected = parse_version(first_affected)
58 last_affected = parse_version(last_affected)
59
60 if not last_affected:
61 print(f"# {cve} has no known resolution")
62 elif first_affected and version < first_affected:
63 print(f'CVE_STATUS[{cve}] = "fixed-version: only affects {first_affected} onwards"')
64 elif last_affected < version:
65 print(
66 f'CVE_STATUS[{cve}] = "fixed-version: Fixed after version {last_affected}"'
67 )
68 else:
69 if cve in stream_data:
70 backport_data = stream_data[cve]
71 if base_version in backport_data:
72 backport_ver = Version(backport_data[base_version]["fixed_version"])
73 if backport_ver < version:
74 print(
75 f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"'
76 )
77 else:
78 # TODO print a note that the kernel needs bumping
79 print(f"# {cve} needs backporting (fixed from {backport_ver})")
80 else:
81 print(f"# {cve} needs backporting (fixed from {last_affected})")
82 else:
83 print(f"# {cve} needs backporting (fixed from {last_affected})")
84
85 print()
86
87
88if __name__ == "__main__":
89 main()