mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-19 12:59:02 +02:00

Replace the term 'needs backporting' with 'may need backporting' in generate-cve-exclusions.py when the checked kernel version may or may not be in the vulnerable version range, thus making backporting necessary only in the former case. In tandem we regenerate the content of cve-exclusion_6.12.inc using https://github.com/CVEProject/cvelistV5.git repository main branch at git hash b20d0043711588b6409ae3118bc0510ab888c316 to keep the content in sync with the script. (From OE-Core rev: feb80e6be16f27611a018d0ef7841cbb466c47d1) Signed-off-by: Niko Mauno <niko.mauno@vaisala.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
153 lines
5.7 KiB
Python
Executable File
153 lines
5.7 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
|
|
# Generate granular CVE status metadata for a specific version of the kernel
|
|
# using json data from cvelistV5 or vulns repository
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
import argparse
|
|
import datetime
|
|
import json
|
|
import pathlib
|
|
import os
|
|
import glob
|
|
|
|
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 get_fixed_versions(cve_info, base_version):
|
|
'''
|
|
Get fixed versionss
|
|
'''
|
|
first_affected = None
|
|
fixed = None
|
|
fixed_backport = None
|
|
next_version = Version(str(base_version) + ".5000")
|
|
for affected in cve_info["containers"]["cna"]["affected"]:
|
|
# In case the CVE info is not complete, it might not have default status and therefore
|
|
# we don't know the status of this CVE.
|
|
if not "defaultStatus" in affected:
|
|
return first_affected, fixed, fixed_backport
|
|
if affected["defaultStatus"] == "affected":
|
|
for version in affected["versions"]:
|
|
v = Version(version["version"])
|
|
if v == Version('0'):
|
|
#Skiping non-affected
|
|
continue
|
|
if version["status"] == "unaffected" and first_affected and v < first_affected:
|
|
first_affected = Version(f"{v.major}.{v.minor}")
|
|
if version["status"] == "affected" and not first_affected:
|
|
first_affected = v
|
|
elif (version["status"] == "unaffected" and
|
|
version['versionType'] == "original_commit_for_fix"):
|
|
fixed = v
|
|
elif base_version < v and v < next_version:
|
|
fixed_backport = v
|
|
elif affected["defaultStatus"] == "unaffected":
|
|
# Only specific versions are affected. We care only about our base version
|
|
if "versions" not in affected:
|
|
continue
|
|
for version in affected["versions"]:
|
|
if "versionType" not in version:
|
|
continue
|
|
if version["versionType"] == "git":
|
|
continue
|
|
v = Version(version["version"])
|
|
# in case it is not in our base version
|
|
less_than = Version(version["lessThan"])
|
|
|
|
if not first_affected:
|
|
first_affected = v
|
|
fixed = less_than
|
|
if base_version < v and v < next_version:
|
|
fixed_backport = less_than
|
|
|
|
return first_affected, fixed, fixed_backport
|
|
|
|
def is_linux_cve(cve_info):
|
|
'''Return true is the CVE belongs to Linux'''
|
|
if not "affected" in cve_info["containers"]["cna"]:
|
|
return False
|
|
for affected in cve_info["containers"]["cna"]["affected"]:
|
|
if not "product" in affected:
|
|
return False
|
|
if affected["product"] == "Linux" and affected["vendor"] == "Linux":
|
|
return True
|
|
return False
|
|
|
|
def main(argp=None):
|
|
parser = argparse.ArgumentParser()
|
|
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")
|
|
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 = Version(f"{version.major}.{version.minor}")
|
|
|
|
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"
|
|
""")
|
|
|
|
# Loop though all CVES and check if they are kernel related, newer than 2015
|
|
pattern = os.path.join(datadir, '**', "CVE-20*.json")
|
|
|
|
files = glob.glob(pattern, recursive=True)
|
|
for cve_file in sorted(files):
|
|
# Get CVE Id
|
|
cve = cve_file[cve_file.rfind("/")+1:cve_file.rfind(".json")]
|
|
# We process from 2015 data, old request are not properly formated
|
|
year = cve.split("-")[1]
|
|
if int(year) < 2015:
|
|
continue
|
|
with open(cve_file, 'r', encoding='utf-8') as json_file:
|
|
cve_info = json.load(json_file)
|
|
|
|
if not is_linux_cve(cve_info):
|
|
continue
|
|
first_affected, fixed, backport_ver = get_fixed_versions(cve_info, base_version)
|
|
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 backport_ver:
|
|
if backport_ver <= version:
|
|
print(
|
|
f'CVE_STATUS[{cve}] = "cpe-stable-backport: Backported in {backport_ver}"'
|
|
)
|
|
else:
|
|
print(f"# {cve} may need backporting (fixed from {backport_ver})")
|
|
else:
|
|
print(f"# {cve} needs backporting (fixed from {fixed})")
|
|
|
|
print()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|