summaryrefslogtreecommitdiffstats
path: root/meta/recipes-kernel/linux/generate-cve-exclusions.py
blob: aa9195aab4f31436423b5889da2fac9b11371f87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#! /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()