Skip to content

Commit a3ec468

Browse files
author
Phil Roche
committed
feat: Add support for highlighting CVEs referenced in changelog entries
Some changelog entries can be very long and hard to manually parse, especially when looking for references to CVEs in kernel changelogs. This commits adds three new arguments. All three are flags. `--highlight-cves`: Highlight the CVEs referenced in each individual changelog entry `--highlight-cves-only`: Highlight the CVEs referenced in each individual changelog entry but do NOT show the rest of the content of the changelog entry `--highlight-cves-show-cve-description`: When highlighting CVEs, show the CVE description `--highlight-cves` or `--highlight-cve-only` must also be used for this to take affect. Examples added to Readme too.
1 parent 74d6846 commit a3ec468

2 files changed

Lines changed: 210 additions & 13 deletions

File tree

README.rst

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,97 @@ must be given. Eg::
3333
fips-initramfs (0.0.11~rc5) bionic; urgency=medium
3434
[...]
3535

36+
37+
`ubuntu-package-changelog` can be also used to highlight the CVEs referenced in a changelog for
38+
a given Ubuntu source package. Note this also shows the CVE priority assigned by the Ubuntu security team. Eg::
39+
40+
ubuntu-package-changelog --highlight-cves focal Updates linux-azure
41+
linux-azure (5.4.0-1094.100) focal; urgency=medium
42+
43+
CVEs addressed/mitigated in linux-azure version 5.4.0-1094.100:
44+
CVE-2022-2602 (high priority)
45+
CVE-2022-41674 (medium priority)
46+
CVE-2022-42721 (medium priority)
47+
CVE-2022-42720 (medium priority)
48+
49+
[ Ubuntu: 5.4.0-131.147 ]
50+
51+
* CVE-2022-2602
52+
- SAUCE: io_uring/af_unix: defer registered files gc to io_uring release
53+
- SAUCE: io_uring/af_unix: fix memleak during unix GC
54+
* CVE-2022-41674
55+
- SAUCE: wifi: cfg80211: fix u8 overflow in
56+
cfg80211_update_notlisted_nontrans()
57+
- SAUCE: wifi: cfg80211/mac80211: reject bad MBSSID elements
58+
- SAUCE: wifi: cfg80211: ensure length byte is present before access
59+
- SAUCE: wifi: mac80211_hwsim: avoid mac80211 warning on bad rate
60+
- SAUCE: wifi: cfg80211: update hidden BSSes to avoid WARN_ON
61+
* CVE-2022-42721
62+
- SAUCE: wifi: cfg80211: avoid nontransmitted BSS list corruption
63+
* CVE-2022-42720
64+
- SAUCE: wifi: cfg80211: fix BSS refcounting bugs
65+
66+
-- Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Sun, 16 Oct 2022 23:55:23 -0300
67+
68+
If you wish to use `ubuntu-package-changelog` to show CVE descriptions when highlighting the CVEs referenced in a changelog for
69+
a given Ubuntu source package. Eg::
70+
71+
ubuntu-package-changelog --highlight-cves --highlight-cves-show-cve-description focal Updates linux-azure
72+
linux-azure (5.4.0-1094.100) focal; urgency=medium
73+
74+
CVEs addressed/mitigated in linux-azure version 5.4.0-1094.100:
75+
CVE-2022-2602 (high priority): David Bouman and Billy Jheng Bing Jhong discovered that a race condition existed in the io_uring subsystem in the Linux kernel, leading to a use- after-free vulnerability. A local attacker could use this to cause a denial of service (system crash) or possibly execute arbitrary code.
76+
CVE-2022-41674 (medium priority): Sönke Huster discovered that an integer overflow vulnerability existed in the WiFi driver stack in the Linux kernel, leading to a buffer overflow. A physically proximate attacker could use this to cause a denial of service (system crash) or possibly execute arbitrary code.
77+
CVE-2022-42721 (medium priority): Sönke Huster discovered that the WiFi driver stack in the Linux kernel did not properly handle BSSID/SSID lists in some situations. A physically proximate attacker could use this to cause a denial of service (infinite loop).
78+
CVE-2022-42720 (medium priority): Sönke Huster discovered that the WiFi driver stack in the Linux kernel did not properly perform reference counting in some situations, leading to a use-after-free vulnerability. A physically proximate attacker could use this to cause a denial of service (system crash) or possibly execute arbitrary code.
79+
80+
[ Ubuntu: 5.4.0-131.147 ]
81+
82+
* CVE-2022-2602
83+
- SAUCE: io_uring/af_unix: defer registered files gc to io_uring release
84+
- SAUCE: io_uring/af_unix: fix memleak during unix GC
85+
* CVE-2022-41674
86+
- SAUCE: wifi: cfg80211: fix u8 overflow in
87+
cfg80211_update_notlisted_nontrans()
88+
- SAUCE: wifi: cfg80211/mac80211: reject bad MBSSID elements
89+
- SAUCE: wifi: cfg80211: ensure length byte is present before access
90+
- SAUCE: wifi: mac80211_hwsim: avoid mac80211 warning on bad rate
91+
- SAUCE: wifi: cfg80211: update hidden BSSes to avoid WARN_ON
92+
* CVE-2022-42721
93+
- SAUCE: wifi: cfg80211: avoid nontransmitted BSS list corruption
94+
* CVE-2022-42720
95+
- SAUCE: wifi: cfg80211: fix BSS refcounting bugs
96+
97+
-- Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Sun, 16 Oct 2022 23:55:23 -0300
98+
99+
100+
If you wish to use `ubuntu-package-changelog` to only show referenced CVEs in a changelog for
101+
a given Ubuntu source package. Eg::
102+
103+
ubuntu-package-changelog --highlight-cves-only focal Updates linux-azure
104+
linux-azure (5.4.0-1094.100) focal; urgency=medium
105+
106+
CVEs addressed/mitigated in linux-azure version 5.4.0-1094.100:
107+
CVE-2022-2602 (high priority)
108+
CVE-2022-41674 (medium priority)
109+
CVE-2022-42721 (medium priority)
110+
CVE-2022-42720 (medium priority)
111+
112+
-- Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Sun, 16 Oct 2022 23:55:23 -0300
113+
114+
This `--highlight-cves-only` flag can be used in conjunction with `--highlight-cves-show-cve-description` to only show referenced CVEs in a changelog and also include the CVE description. Eg::
115+
116+
ubuntu-package-changelog --highlight-cves-only --highlight-cves-show-cve-description focal Updates linux-azure
117+
linux-azure (5.4.0-1094.100) focal; urgency=medium
118+
119+
CVEs addressed/mitigated in linux-azure version 5.4.0-1094.100:
120+
CVE-2022-2602 (high priority): David Bouman and Billy Jheng Bing Jhong discovered that a race condition existed in the io_uring subsystem in the Linux kernel, leading to a use- after-free vulnerability. A local attacker could use this to cause a denial of service (system crash) or possibly execute arbitrary code.
121+
CVE-2022-41674 (medium priority): Sönke Huster discovered that an integer overflow vulnerability existed in the WiFi driver stack in the Linux kernel, leading to a buffer overflow. A physically proximate attacker could use this to cause a denial of service (system crash) or possibly execute arbitrary code.
122+
CVE-2022-42721 (medium priority): Sönke Huster discovered that the WiFi driver stack in the Linux kernel did not properly handle BSSID/SSID lists in some situations. A physically proximate attacker could use this to cause a denial of service (infinite loop).
123+
CVE-2022-42720 (medium priority): Sönke Huster discovered that the WiFi driver stack in the Linux kernel did not properly perform reference counting in some situations, leading to a use-after-free vulnerability. A physically proximate attacker could use this to cause a denial of service (system crash) or possibly execute arbitrary code.
124+
125+
-- Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Sun, 16 Oct 2022 23:55:23 -0300
126+
36127
Installation
37128
============
38129

ubuntu_package_changelog/__init__.py

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
#!/usr/bin/python3
22

3+
import re
34
import sys
45
import argparse
56
import urllib.parse
7+
from lazr.restfulclient.errors import NotFound
68
from launchpadlib.launchpad import Launchpad
79

810

9-
def _lp_get_changelog_url(args, lp):
11+
def _lp_get_changelog_url(sources):
12+
return sources.changelogUrl()
13+
14+
15+
def _lp_get_sources(args, lp):
1016
ubuntu = lp.distributions["ubuntu"]
1117
pocket = args.pocket
1218
if args.ppa:
@@ -19,6 +25,7 @@ def _lp_get_changelog_url(args, lp):
1925
archive = ubuntu.main_archive
2026

2127
lp_series = ubuntu.getSeries(name_or_version=args.series)
28+
2229
sources = _get_published_sources(archive,
2330
args.package,
2431
lp_series,
@@ -39,11 +46,7 @@ def _lp_get_changelog_url(args, lp):
3946
source_package_name,
4047
lp_series,
4148
pocket)
42-
43-
if len(sources) == 1:
44-
return sources[0].changelogUrl()
45-
else:
46-
return None
49+
return sources
4750

4851

4952
def _get_binary_packages(archive, binary_package_name, pocket):
@@ -79,11 +82,25 @@ def _parser():
7982
description='Ubuntu package changelog finder')
8083
parser.add_argument('--lp-user', help='Launchpad username', default=None)
8184
parser.add_argument('--ppa', help='Search for a package in the given PPA instead'
82-
'of the Ubuntu archive. Given PPA must have the '
83-
'format "owner/ppa-name". Eg. "toabctl/testing"',
85+
'of the Ubuntu archive. Given PPA must have the '
86+
'format "owner/ppa-name". Eg. "toabctl/testing"',
8487
type=_args_validate_ppa_name)
8588
parser.add_argument('--entries', help='number of changelog entries to get from the '
86-
'changelog. 0 mean all. Default: %(default)s', type=int, default=1)
89+
'changelog. 0 mean all. Default: %(default)s', type=int, default=1)
90+
parser.add_argument('--highlight-cves',
91+
help='Highlight the CVEs referenced in each individual changelog entry'
92+
'. Default: %(default)s',
93+
action='store_true')
94+
parser.add_argument('--highlight-cves-only',
95+
help='Highlight the CVEs referenced in each individual changelog entry '
96+
'but do NOT show the rest of the content of the changelog entry'
97+
'. Default: %(default)s',
98+
action='store_true')
99+
parser.add_argument('--highlight-cves-show-cve-description',
100+
help='When highlighting CVEs, show the CVE description. '
101+
'`--highlight-cves` or `--highlight-cve-only` '
102+
'must also be used for this to take affect.. Default: %(default)s',
103+
action='store_true')
87104
parser.add_argument('series', help='the Ubuntu series eg. "20.04" or "focal"')
88105
parser.add_argument('pocket',
89106
choices=['Release', 'Security', 'Updates', 'Proposed', 'Backports'],
@@ -104,22 +121,111 @@ def main():
104121
'ubuntu-package-changelog',
105122
'production', version='devel')
106123

107-
changelog_url = _lp_get_changelog_url(args, lp)
108-
if not changelog_url:
124+
sources = _lp_get_sources(args, lp)
125+
126+
if len(sources) != 1:
109127
print('no changelog found')
110128
sys.exit(0)
111129

130+
changelog_url = _lp_get_changelog_url(sources[0])
131+
source_package_name = sources[0].source_package_name
132+
112133
url = lp._root_uri.append(urllib.parse.urlparse(changelog_url).path.lstrip('/'))
113134
resp = lp._browser.get(url).decode('utf-8')
114135
entry_count = 0
136+
changelog_entry_lines = []
115137
for line in resp.splitlines():
116138
line = line.rstrip()
139+
140+
# this is the first line of a changelog entry. Find the version and reset lists for
141+
# changelog entry lines and changelog entry cves
142+
if source_package_name in line and line.startswith(source_package_name) and ' (' in line and ')' in line:
143+
individual_changelog_entry_lines = []
144+
individual_changelog_entry_cves = []
145+
individual_changelog_entry_source_package_version = line[line.index('(') + 1: line.index(')')]
146+
147+
if (args.highlight_cves or args.highlight_cves_only) and 'CVE' in line:
148+
cve_pos = [(m.start(), m.end()) for m in re.finditer(r'CVE-\d+-\d+', line)]
149+
for start, end in cve_pos:
150+
cve = line[start:end].strip()
151+
if cve not in [individual_changelog_entry_cve['cve']
152+
for individual_changelog_entry_cve in individual_changelog_entry_cves]:
153+
cve_details = {}
154+
cve_details['cve'] = cve
155+
cve_details_lines = _get_cve_details(cve, lp)
156+
cve_ubuntu_description = ''
157+
cve_description = ''
158+
for cve_details_line in cve_details_lines:
159+
# only get the CVE description if the user has requested it
160+
if args.highlight_cves_show_cve_description and not cve_ubuntu_description and cve_details_line.startswith('Ubuntu-Description:'):
161+
# get the string in the line after the Ubuntu-Description: line
162+
# while the next line is not 'Notes' keep appending to cve_description
163+
while True:
164+
next_line = next(cve_details_lines)
165+
if next_line.startswith('Notes'):
166+
break
167+
cve_ubuntu_description += next_line
168+
if args.highlight_cves_show_cve_description and not cve_description and cve_details_line.startswith('Description:'):
169+
# get the string in the line after the Ubuntu-Description: line
170+
# while the next line is not 'Notes' keep appending to cve_description
171+
while True:
172+
next_line = next(cve_details_lines)
173+
if next_line.startswith('Ubuntu-Description:'):
174+
break
175+
cve_description += next_line
176+
cve_details['cve_description'] = cve_ubuntu_description \
177+
if cve_ubuntu_description else cve_description
178+
if 'Priority:' in cve_details_line:
179+
cve_details['cve_priority'] = cve_details_line.split('Priority:')[1].strip()
180+
individual_changelog_entry_cves.append(cve_details)
181+
182+
individual_changelog_entry_lines.append(line)
183+
184+
# the '--' string is used to separate entries in the changelog as it is the last line of a changelog entry
185+
# example "-- Maintainer Name <maintainer@email.com> Mon, 17 Oct 2022 10:32:58 -0300"
117186
if line.startswith(' -- '):
118187
entry_count += 1
188+
if (args.highlight_cves or args.highlight_cves_only) and individual_changelog_entry_cves:
189+
individual_changelog_entry_lines.insert(1, '\n CVEs addressed/mitigated in {} version {}:'.format(
190+
source_package_name, individual_changelog_entry_source_package_version))
191+
cve_details_insertion_index_start = 2
192+
for individual_changelog_entry_cve in individual_changelog_entry_cves:
193+
individual_changelog_entry_lines.insert(cve_details_insertion_index_start, ' {} ({} priority){}'.format(
194+
individual_changelog_entry_cve['cve'],
195+
individual_changelog_entry_cve['cve_priority'],
196+
': {}'.format(individual_changelog_entry_cve['cve_description'])
197+
if args.highlight_cves_show_cve_description else ''))
198+
cve_details_insertion_index_start += 1
199+
200+
if args.highlight_cves_only:
201+
changelog_entry_lines.extend(individual_changelog_entry_lines[0:cve_details_insertion_index_start])
202+
changelog_entry_lines.extend(individual_changelog_entry_lines[-2:])
203+
changelog_entry_lines.append('\n')
204+
else:
205+
changelog_entry_lines.extend(individual_changelog_entry_lines)
206+
207+
# If we have parsed all the entries we want, then we can stop parsing the changelog
119208
if args.entries > 0 and entry_count >= args.entries:
120-
print(line)
121209
break
122-
print(line)
210+
211+
# print the lines in changlog_entry_lines
212+
print('\n'.join(changelog_entry_lines))
213+
214+
215+
def _get_cve_details(cve, lp):
216+
# download the cve details and parse so we can get the CVE description and the CVE priority
217+
cve_details_lines = []
218+
possible_cve_detail_locations = ['active', 'retired', 'ignored']
219+
for possible_cve_detail_location in possible_cve_detail_locations:
220+
try:
221+
cve_details_url = 'https://git.launchpad.net/ubuntu-cve-tracker/plain/{}/{}'.format(
222+
possible_cve_detail_location, cve)
223+
cve_details_resp = lp._browser.get(cve_details_url).decode('utf-8')
224+
cve_details_lines = iter(cve_details_resp.splitlines())
225+
return cve_details_lines
226+
except NotFound:
227+
pass # Keep trying until we find the cve details
228+
return cve_details_lines
123229

124230

125231
if __name__ == '__main__':

0 commit comments

Comments
 (0)