summaryrefslogtreecommitdiffstats
path: root/bin/lts-cve-triage.py
blob: 5304af1538622afb6a052699cbd43ffbbe00237f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env python3

# Copyright 2015 Raphael Hertzog <hertzog@debian.org>
#
# 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 <https://www.gnu.org/licenses/>.

import os
import sys
import argparse
import collections

from tracker_data import TrackerData
from unsupported_packages import UnsupportedPackages, LimitedSupportPackages

def setup_path():
    dirname = os.path.dirname
    base = dirname(dirname(os.path.realpath(sys.argv[0])))
    sys.path.insert(0, os.path.join(base, "lib", "python"))

setup_path()
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 <end-of-life> 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_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=8,
                                  update_cache=not args.skip_cache_update)
limited = LimitedSupportPackages(update_cache=not args.skip_cache_update)


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

        if status_in_lts.status == 'open':
            if pkg in unsupported:
                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 pkg in limited:
                    add_to_list('triage_limited_support', pkg, issue)
                    continue

                if status_in_next_lts.status == 'open':
                    if pkg 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)

        if 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 == '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('')

© 2014-2024 Faster IT GmbH | imprint | privacy policy