mirror of
https://git.yoctoproject.org/poky
synced 2026-02-15 05:03:03 +01:00
Add another comment to state what the data source for the CVE data was, specifically the basename of the repository and the "git describe" output of HEAD. (From OE-Core rev: a06234b35977b9a2af2679d6c601a79acbde5f7c) Signed-off-by: Ross Burton <ross.burton@arm.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> (cherry picked from commit 5e66e2b79faec2285d249b16457ecc63c4042444) Signed-off-by: Steve Sakoman <steve@sakoman.com>
157 lines
5.9 KiB
Python
Executable File
157 lines
5.9 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
|
|
import subprocess
|
|
|
|
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.resolve()
|
|
version = args.version
|
|
base_version = Version(f"{version.major}.{version.minor}")
|
|
|
|
data_version = subprocess.check_output(("git", "describe", "--tags", "HEAD"), cwd=datadir, text=True)
|
|
|
|
print(f"""
|
|
# Auto-generated CVE metadata, DO NOT EDIT BY HAND.
|
|
# Generated at {datetime.datetime.now(datetime.timezone.utc)} for kernel version {version}
|
|
# From {datadir.name} {data_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()
|