diff options
Diffstat (limited to 'meta/recipes-kernel/linux/generate-cve-exclusions.py')
-rwxr-xr-x | meta/recipes-kernel/linux/generate-cve-exclusions.py | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/meta/recipes-kernel/linux/generate-cve-exclusions.py b/meta/recipes-kernel/linux/generate-cve-exclusions.py new file mode 100755 index 0000000000..12ae3b0b1d --- /dev/null +++ b/meta/recipes-kernel/linux/generate-cve-exclusions.py | |||
@@ -0,0 +1,101 @@ | |||
1 | #! /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 | |||
8 | import argparse | ||
9 | import datetime | ||
10 | import json | ||
11 | import pathlib | ||
12 | import re | ||
13 | |||
14 | from packaging.version import Version | ||
15 | |||
16 | |||
17 | def 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 | |||
29 | def 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(f""" | ||
46 | # Auto-generated CVE metadata, DO NOT EDIT BY HAND. | ||
47 | # Generated at {datetime.datetime.now()} for version {version} | ||
48 | |||
49 | python check_kernel_cve_status_version() {{ | ||
50 | this_version = "{version}" | ||
51 | kernel_version = d.getVar("LINUX_VERSION") | ||
52 | if kernel_version != this_version: | ||
53 | bb.warn("Kernel CVE status needs updating: generated for %s but kernel is %s" % (this_version, kernel_version)) | ||
54 | }} | ||
55 | do_cve_check[prefuncs] += "check_kernel_cve_status_version" | ||
56 | """) | ||
57 | |||
58 | for cve, data in cve_data.items(): | ||
59 | if "affected_versions" not in data: | ||
60 | print(f"# Skipping {cve}, no affected_versions") | ||
61 | print() | ||
62 | continue | ||
63 | |||
64 | affected = data["affected_versions"] | ||
65 | first_affected, last_affected = re.search(r"(.+) to (.+)", affected).groups() | ||
66 | first_affected = parse_version(first_affected) | ||
67 | last_affected = parse_version(last_affected) | ||
68 | |||
69 | handled = False | ||
70 | if not last_affected: | ||
71 | print(f"# {cve} has no known resolution") | ||
72 | elif first_affected and version < first_affected: | ||
73 | print(f"# fixed-version: only affects {first_affected} onwards") | ||
74 | handled = True | ||
75 | elif last_affected < version: | ||
76 | print(f"# fixed-version: Fixed after version {last_affected}") | ||
77 | handled = True | ||
78 | else: | ||
79 | if cve in stream_data: | ||
80 | backport_data = stream_data[cve] | ||
81 | if base_version in backport_data: | ||
82 | backport_ver = Version(backport_data[base_version]["fixed_version"]) | ||
83 | if backport_ver <= version: | ||
84 | print(f"# cpe-stable-backport: Backported in {backport_ver}") | ||
85 | handled = True | ||
86 | else: | ||
87 | # TODO print a note that the kernel needs bumping | ||
88 | print(f"# {cve} needs backporting (fixed from {backport_ver})") | ||
89 | else: | ||
90 | print(f"# {cve} needs backporting (fixed from {last_affected})") | ||
91 | else: | ||
92 | print(f"# {cve} needs backporting (fixed from {last_affected})") | ||
93 | |||
94 | if handled: | ||
95 | print(f'CVE_CHECK_WHITELIST += "{cve}"') | ||
96 | |||
97 | print() | ||
98 | |||
99 | |||
100 | if __name__ == "__main__": | ||
101 | main() | ||