Skip to content

Commit

Permalink
show-fixed-kernel-cve: use another source
Browse files Browse the repository at this point in the history
the github repository is archived and not maintained.

Signed-off-by: Mathieu Tortuyaux <[email protected]>
  • Loading branch information
tormath1 committed Aug 8, 2024
1 parent 6495dd7 commit e55e4e7
Showing 1 changed file with 98 additions and 51 deletions.
149 changes: 98 additions & 51 deletions show-fixed-kernel-cves.py
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))})")

0 comments on commit e55e4e7

Please sign in to comment.