#!/usr/bin/env python3 # Copyright 2015 Raphael Hertzog # # This file is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This file is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this file. If not, see . import setup_paths import sys import argparse import collections import re from tracker_data import TrackerData from unsupported_packages import UnsupportedPackages, LimitedSupportPackages import config RELEASES = { 'lts': config.get_supported_releases()[0], 'next_lts': config.get_supported_releases()[1], } def colored(x, *args, **kwargs): return x colored_on = False try: if sys.stdout.isatty(): from termcolor import colored # noqa colored_on = True except ImportError: print("Note: you can install python3-termcolor for colored output", file=sys.stderr) TRACKER_URL = 'https://security-tracker.debian.org/tracker/' LIST_NAMES = ( ('triage_end_of_life', 'Issues to mark as for {lts}'.format(**RELEASES)), ('triage_limited_support', 'Issues on packages with limited support (review support rules)'), ('triage_already_in_dsa_needed', ('Issues to triage for {lts} that are already in dsa-needed') .format(**RELEASES)), ('triage_likely_nodsa', ('Issues to triage for {lts} that are no-dsa in {next_lts}') .format(**RELEASES)), ('triage_possible_easy_fixes', ('Issues not yet triaged for {lts}, but already fixed in {next_lts}') .format(**RELEASES)), ('triage_possible_missed_fixes', ('Issues postponed for {lts}, but fixed in {next_lts} via DSA or point releases') .format(**RELEASES)), ('triage_other_not_triaged_in_next_lts', ('Other issues to triage for {lts} (not yet triaged for {next_lts})') .format(**RELEASES)), ('triage_other', 'Other issues to triage (no special status)'), ('unexpected_nodsa', ('Issues tagged no-dsa in {lts} that are open in {next_lts}') .format(**RELEASES)), ('possible_easy_fixes', ('Issues from dla-needed.txt that are already fixed in {next_lts}') .format(**RELEASES)), ('undetermined', ('Undetermined issues in {lts}').format(**RELEASES)), ) lists = collections.defaultdict(lambda: collections.defaultdict(lambda: [])) parser = argparse.ArgumentParser( description='Find CVEs to triage') parser.add_argument('--skip-dla-needed', action='store_true', help='Skip packages already in dla-needed.txt') parser.add_argument('--skip-cache-update', action='store_true', help='Skip updating the tracker data cache') parser.add_argument('--filter', nargs='+', choices=[x[0] for x in LIST_NAMES], help='Only report on specified lists') parser.add_argument('--exclude', nargs='+', choices=[x[0] for x in LIST_NAMES], help='Do not report on the specified lists') args = parser.parse_args() tracker = TrackerData(update_cache=not args.skip_cache_update) unsupported = UnsupportedPackages(debian_version=9, update_cache=not args.skip_cache_update) limited = LimitedSupportPackages(update_cache=not args.skip_cache_update) # unsupport/limited package names can be regexps unsupported_re = re.compile('|'.join(unsupported)) limited_re = re.compile('|'.join(limited)) def add_to_list(key, pkg, issue): assert key in [l[0] for l in LIST_NAMES] lists[key][pkg].append(issue) for pkg in tracker.iterate_packages(): if args.skip_dla_needed and pkg in tracker.dla_needed: continue for issue in tracker.iterate_pkg_issues(pkg): status_in_lts = issue.get_status(RELEASES['lts']) status_in_next_lts = issue.get_status(RELEASES['next_lts']) if status_in_lts.status in ('not-affected', 'resolved'): continue elif status_in_lts.status == 'open': if re.fullmatch(unsupported_re, pkg): add_to_list('triage_end_of_life', pkg, issue) continue if pkg not in tracker.dla_needed: # Issues not triaged yet # package issues in LTS that still need being triaged if re.fullmatch(limited_re, pkg): add_to_list('triage_limited_support', pkg, issue) continue if status_in_next_lts.status == 'open': if (pkg in tracker.dsa_needed or pkg+'/stable' in tracker.dsa_needed or pkg+'/oldstable' in tracker.dsa_needed): add_to_list('triage_already_in_dsa_needed', pkg, issue) else: add_to_list('triage_other_not_triaged_in_next_lts', pkg, issue) elif (status_in_next_lts.status == 'ignored' and status_in_next_lts.reason == 'no-dsa'): add_to_list('triage_likely_nodsa', pkg, issue) elif status_in_next_lts.status == 'resolved': add_to_list('triage_possible_easy_fixes', pkg, issue) else: add_to_list('triage_other', pkg, issue) else: # package issues already triaged for LTS... if status_in_next_lts.status == 'resolved': add_to_list('possible_easy_fixes', pkg, issue) # status=='ignored': //// elif status_in_lts.status == 'ignored': if (status_in_lts.reason == 'no-dsa' and status_in_next_lts.status == 'open'): add_to_list('unexpected_nodsa', pkg, issue) elif (status_in_lts.reason == 'no-dsa' and status_in_next_lts.status == 'resolved' and pkg not in tracker.dla_needed): # include fixes from DSA or stable/oldstable point releases # exclude issues explicitly ignored, and old fixes back in unstable nodsa_reason = issue.data['releases'][RELEASES['lts']]['nodsa_reason'] fixed_version = issue.data['releases'][RELEASES['next_lts']]['fixed_version'] if (nodsa_reason != 'ignored' and ('~deb' in fixed_version or '+deb' in fixed_version)): add_to_list('triage_possible_missed_fixes', pkg, issue) elif status_in_lts.reason == 'undetermined': add_to_list('undetermined', pkg, issue) for key, desc in LIST_NAMES: if args.filter is not None and key not in args.filter: continue if args.exclude is not None and key in args.exclude: continue if not len(lists[key]): continue print('\n{}:'.format(colored(desc, attrs=('bold',)))) for pkg in sorted(lists[key].keys()): formatstring = '\n* {:<18s} {}' if colored_on: formatstring = '\n* {:<35s} {}' print(formatstring.format( colored(pkg, 'red', attrs=('bold', 'underline')), colored('{}source-package/{}'.format(TRACKER_URL, pkg), 'blue'), )) for x in sorted(lists[key][pkg], key=lambda x: x.name): url = '{}{}'.format(TRACKER_URL, x.name) print(' - {:<16s} {} {}'.format( x.name, colored(url, 'blue'), (key == 'unexpected_nodsa' and x.data['releases'][RELEASES['lts']]['nodsa_reason'] or '')), ) print('')