-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
show-fixed-kernel-cve: use another source
the github repository is archived and not maintained. Signed-off-by: Mathieu Tortuyaux <[email protected]>
- Loading branch information
Showing
1 changed file
with
98 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,105 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# Helper to show which Linux kernel CVEs got fixed in the update from | ||
# FROM_VERSION to TO_VERSION. | ||
# Usage: ./show-fixed-kernel-cves.py -f 5.15.37 -t 5.15.43 | ||
|
||
# Unfortunately, the data in https://github.com/CVEProject/cvelist is almost | ||
# useless because the version information often doesn't tell if the version | ||
# fixed a CVE or if the version is affected by a CVE and which other versions | ||
# are affected or not. | ||
# Luckily in https://github.com/nluedtke/linux_kernel_cves there are 3 JSON | ||
# databases which get maintained to track what CVEs got fixed where. | ||
# 1) kernel_cves.json is a format that doesn't cover backports: | ||
# "affected_versions": "v3.6-rc1 to v5.17-rc2" | ||
# "last_affected_version": "5.16.4" | ||
# 2) stream_fixes.json is a format that covers backports: | ||
# a list of CVEs having entries like "5.15" with "fixed_version": "5.15.19" | ||
# (for each CVE and each fixed_version of the CVE, include | ||
# the CVE if FROM_VERSION < fixed_version >= TO_VERSION) | ||
# 3) stream_data.json is a format that also covers backports: | ||
# for each stream there is a list of releases and which CVEs they fixed | ||
# (for each release, include the list of fixed CVEs if | ||
# FROM_VERSION < release <= TO_VERSION) | ||
|
||
# Using the stream_data.json format seems to be best for our purpose of and is | ||
# also what can be found under https://www.linuxkernelcves.com/streams/5.15 | ||
|
||
import json | ||
from packaging import version | ||
""" | ||
Helper to show which Linux kernel CVEs got fixed in a given version | ||
``` | ||
virtualenv venv | ||
source venv/bin/activate | ||
pip install feedparser | ||
python show-fixed-kernel-cves.py --from_version 6.6.32 --to_version 6.6.44 | ||
``` | ||
""" | ||
from optparse import OptionParser | ||
import urllib.request | ||
|
||
def print_fixed_linux_cves(from_version_str, to_version_str): | ||
stream_data_url = "https://raw.githubusercontent.com/nluedtke/linux_kernel_cves/master/data/stream_data.json" | ||
payload = urllib.request.urlopen(stream_data_url).read() | ||
streams = json.loads(payload) | ||
from_version=version.Version(from_version_str) | ||
to_version=version.Version(to_version_str) | ||
cvelist = [] | ||
links = [] | ||
for stream, releases in streams.items(): | ||
for release, cves in releases.items(): | ||
if release != "outstanding" and from_version < version.Version(release) <= to_version: | ||
cvelist += cves.keys() | ||
for cve in sorted(cvelist): | ||
links += [f"[{cve}](https://nvd.nist.gov/vuln/detail/{cve})"] | ||
print(", ".join(links)) | ||
import subprocess | ||
|
||
import feedparser | ||
|
||
class Version: | ||
""" | ||
Version implement a simple semver version object. | ||
""" | ||
def __init__(self, v): | ||
# Clean the 'v' from the version (e.g 'v6.6.44') | ||
if v.startswith("v"): | ||
v = v[1:] | ||
|
||
s = v.split(".") | ||
if len(s) != 3: | ||
self.major = "-1" | ||
self.minor = "-1" | ||
self.patch = "-1" | ||
else: | ||
self.major = s[0] | ||
self.minor = s[1] | ||
self.patch = s[2] | ||
|
||
def __str__(self): | ||
return f"{self.major}.{self.minor}.{self.patch}" | ||
|
||
def __eq__(self, v): | ||
if isinstance(v, Version): | ||
return self.major == v.major \ | ||
and self.minor == v.minor \ | ||
and self.patch == v.patch | ||
|
||
return False | ||
|
||
def __lt__(self, v): | ||
return (self.major, self.minor, self.patch) < (v.major, v.minor, v.patch) | ||
|
||
def __ge__(self, v): | ||
return not (self < v) | ||
|
||
def list_all_tags_for_remote_git_repo(url: str, f: Version, t: Version): | ||
""" | ||
Given a repository URL, list all tags for that repository | ||
without cloning it then return the tags between f (from) and t (to) version. | ||
This function use "git ls-remote", so the | ||
"git" command line program must be available. | ||
""" | ||
# Run the 'git' command to fetch and list remote tags | ||
result = subprocess.run([ | ||
"git", "ls-remote", "--tags", url | ||
], stdout=subprocess.PIPE, text=True, check=True) | ||
|
||
# Process the output to extract tag names | ||
output_lines = result.stdout.splitlines() | ||
tags = [ | ||
Version(line.split("refs/tags/")[-1]) for line in output_lines | ||
if "refs/tags/" in line and "^{}" not in line | ||
] | ||
|
||
# Assert that the starting and the ending version are | ||
# in the list of existing tags. | ||
if f not in tags or t not in tags: | ||
return [] | ||
|
||
return list(filter(lambda v: f < v <= t, tags)) | ||
|
||
def fixed_linux_cves(f, t): | ||
""" | ||
fixed_linux_cves return a list of Kernel CVEs | ||
(in Flatcar changelog format) between two versions. | ||
""" | ||
tags = list_all_tags_for_remote_git_repo("https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git", f, t) | ||
links = [] | ||
for tag in tags: | ||
stream_data_url = f'https://lore.kernel.org/linux-cve-announce/?q=%22fixed+in+{tag}%22&x=A' | ||
feed = feedparser.parse(stream_data_url) | ||
for item in feed.entries: | ||
cve = item.title.split(":")[0] | ||
if not cve.startswith("CVE"): | ||
continue | ||
links += [f"[{cve}](https://nvd.nist.gov/vuln/detail/{cve})"] | ||
return ", ".join(links) | ||
|
||
parser = OptionParser() | ||
parser.add_option("-f", "--from_version", dest="from_version", default="") | ||
parser.add_option("-t", "--to_version", dest="to_version", default="") | ||
parser.add_option("-f", "--from_version", dest="from_version") | ||
parser.add_option("-t", "--to_version", dest="to_version") | ||
(options, args) = parser.parse_args() | ||
if not options.from_version: | ||
parser.error("from_version not given") | ||
parser.error("from_version not given") | ||
if not options.to_version: | ||
parser.error("to_version not given") | ||
parser.error("to_version not given") | ||
|
||
print_fixed_linux_cves(options.from_version, options.to_version) | ||
print(f"- Linux ({fixed_linux_cves(Version(options.from_version), Version(options.to_version))})") |