summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xmeta/recipes-kernel/linux/generate-cve-exclusions.py116
1 files changed, 85 insertions, 31 deletions
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 @@
1#! /usr/bin/env python3 1#! /usr/bin/env python3
2 2
3# Generate granular CVE status metadata for a specific version of the kernel 3# Generate granular CVE status metadata for a specific version of the kernel
4# using data from linuxkernelcves.com. 4# using json data from cvelistV5 or vulns repository
5# 5#
6# SPDX-License-Identifier: GPL-2.0-only 6# SPDX-License-Identifier: GPL-2.0-only
7 7
@@ -9,7 +9,8 @@ import argparse
9import datetime 9import datetime
10import json 10import json
11import pathlib 11import pathlib
12import re 12import os
13import glob
13 14
14from packaging.version import Version 15from packaging.version import Version
15 16
@@ -25,22 +26,75 @@ def parse_version(s):
25 return Version(s) 26 return Version(s)
26 return None 27 return None
27 28
29def get_fixed_versions(cve_info, base_version):
30 '''
31 Get fixed versionss
32 '''
33 first_affected = None
34 fixed = None
35 fixed_backport = None
36 next_version = Version(str(base_version) + ".5000")
37 for affected in cve_info["containers"]["cna"]["affected"]:
38 # In case the CVE info is not complete, it might not have default status and therefore
39 # we don't know the status of this CVE.
40 if not "defaultStatus" in affected:
41 return first_affected, fixed, fixed_backport
42 if affected["defaultStatus"] == "affected":
43 for version in affected["versions"]:
44 v = Version(version["version"])
45 if v == 0:
46 #Skiping non-affected
47 continue
48 if version["status"] == "affected" and not first_affected:
49 first_affected = v
50 elif (version["status"] == "unaffected" and
51 version['versionType'] == "original_commit_for_fix"):
52 fixed = v
53 elif base_version < v and v < next_version:
54 fixed_backport = v
55 elif affected["defaultStatus"] == "unaffected":
56 # Only specific versions are affected. We care only about our base version
57 if "versions" not in affected:
58 continue
59 for version in affected["versions"]:
60 if "versionType" not in version:
61 continue
62 if version["versionType"] == "git":
63 continue
64 v = Version(version["version"])
65 # in case it is not in our base version
66 less_than = Version(version["lessThan"])
67
68 if not first_affected:
69 first_affected = v
70 fixed = less_than
71 if base_version < v and v < next_version:
72 first_affected = v
73 fixed = less_than
74 fixed_backport = less_than
75
76 return first_affected, fixed, fixed_backport
77
78def is_linux_cve(cve_info):
79 '''Return true is the CVE belongs to Linux'''
80 if not "affected" in cve_info["containers"]["cna"]:
81 return False
82 for affected in cve_info["containers"]["cna"]["affected"]:
83 if not "product" in affected:
84 return False
85 if affected["product"] == "Linux" and affected["vendor"] == "Linux":
86 return True
87 return False
28 88
29def main(argp=None): 89def main(argp=None):
30 parser = argparse.ArgumentParser() 90 parser = argparse.ArgumentParser()
31 parser.add_argument("datadir", type=pathlib.Path, help="Path to a clone of https://github.com/nluedtke/linux_kernel_cves") 91 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")
32 parser.add_argument("version", type=Version, help="Kernel version number to generate data for, such as 6.1.38") 92 parser.add_argument("version", type=Version, help="Kernel version number to generate data for, such as 6.1.38")
33 93
34 args = parser.parse_args(argp) 94 args = parser.parse_args(argp)
35 datadir = args.datadir 95 datadir = args.datadir
36 version = args.version 96 version = args.version
37 base_version = f"{version.major}.{version.minor}" 97 base_version = 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 98
45 print(f""" 99 print(f"""
46# Auto-generated CVE metadata, DO NOT EDIT BY HAND. 100# Auto-generated CVE metadata, DO NOT EDIT BY HAND.
@@ -55,17 +109,23 @@ python check_kernel_cve_status_version() {{
55do_cve_check[prefuncs] += "check_kernel_cve_status_version" 109do_cve_check[prefuncs] += "check_kernel_cve_status_version"
56""") 110""")
57 111
58 for cve, data in cve_data.items(): 112 # Loop though all CVES and check if they are kernel related, newer than 2015
59 if "affected_versions" not in data: 113 pattern = os.path.join(datadir, '**', "CVE-20*.json")
60 print(f"# Skipping {cve}, no affected_versions")
61 print()
62 continue
63 114
64 affected = data["affected_versions"] 115 files = glob.glob(pattern, recursive=True)
65 first_affected, fixed = re.search(r"(.+) to (.+)", affected).groups() 116 for cve_file in sorted(files):
66 first_affected = parse_version(first_affected) 117 # Get CVE Id
67 fixed = parse_version(fixed) 118 cve = cve_file[cve_file.rfind("/")+1:cve_file.rfind(".json")]
119 # We process from 2015 data, old request are not properly formated
120 year = cve.split("-")[1]
121 if int(year) < 2015:
122 continue
123 with open(cve_file, 'r', encoding='utf-8') as json_file:
124 cve_info = json.load(json_file)
68 125
126 if not is_linux_cve(cve_info):
127 continue
128 first_affected, fixed, backport_ver = get_fixed_versions(cve_info, base_version)
69 if not fixed: 129 if not fixed:
70 print(f"# {cve} has no known resolution") 130 print(f"# {cve} has no known resolution")
71 elif first_affected and version < first_affected: 131 elif first_affected and version < first_affected:
@@ -75,19 +135,13 @@ do_cve_check[prefuncs] += "check_kernel_cve_status_version"
75 f'CVE_STATUS[{cve}] = "fixed-version: Fixed from version {fixed}"' 135 f'CVE_STATUS[{cve}] = "fixed-version: Fixed from version {fixed}"'
76 ) 136 )
77 else: 137 else:
78 if cve in stream_data: 138 if backport_ver:
79 backport_data = stream_data[cve] 139 if backport_ver <= version:
80 if base_version in backport_data: 140 print(
81 backport_ver = Version(backport_data[base_version]["fixed_version"]) 141 f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"'
82 if backport_ver <= version: 142 )
83 print(
84 f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"'
85 )
86 else:
87 # TODO print a note that the kernel needs bumping
88 print(f"# {cve} needs backporting (fixed from {backport_ver})")
89 else: 143 else:
90 print(f"# {cve} needs backporting (fixed from {fixed})") 144 print(f"# {cve} needs backporting (fixed from {backport_ver})")
91 else: 145 else:
92 print(f"# {cve} needs backporting (fixed from {fixed})") 146 print(f"# {cve} needs backporting (fixed from {fixed})")
93 147