blob: 6c388895570e677a36a3702cae17f1af98788778 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2020 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tool that triages a given CVE."""
import argparse
import logging
import sys
import os
import json
from cvelib import logutils, patchapplier, contextgenerator, clgenerator, webscraper, common
KERNELS = ['v5.4', 'v4.19', 'v4.14', 'v4.4', 'v3.18', 'v3.14', 'v3.10', 'v3.8']
ENV_VARS = ['CHROMIUMOS_KERNEL', 'LINUX', 'STABLE', 'STABLE_RC']
def get_parser():
"""Returns an ArgumentParser instance."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('cve_number', type=str, help='CVE number to be triaged.')
parser.add_argument('--bug', type=str, required=True, help='BUG id to be used.')
parser.add_argument('--debug', help='Display debug messages.', action='store_true')
parser.add_argument('-rc', '--relevant_commit', help='Display commits referring to CVE fix.',
action='store_true')
parser.add_argument('--pull', help='Update kernel repositories.', action='store_true')
parser.add_argument('--json', help='Print results as JSON string.', action='store_true')
return parser
def main(argv):
"""Main."""
logger = logutils.setuplogging(logging.INFO, 'triage')
parser = get_parser()
opts = parser.parse_args(argv)
cve_num = opts.cve_number
for env in ENV_VARS:
if not os.getenv(env):
logger.error(f'${env} not set in virtual environment.')
return 1
patches = {}
loglvl = logging.INFO
if opts.debug:
loglvl = logging.DEBUG
# Change log levels.
modules = [webscraper, patchapplier, clgenerator]
for m in modules:
m.LOGGER.setLevel(loglvl)
# Allows pull command to execute on each branch.
if opts.pull:
common.DO_PULL = True
logger.info('Searching for possible fixes.')
commits = webscraper.find_relevant_commits(cve_num)
if len(commits) == 0:
logger.error('No commits found.')
return 1
for commit in commits:
logger.info(f'Generating context for {commit}')
cg = contextgenerator.ContextGenerator(KERNELS, opts.relevant_commit, loglvl)
cg.generate_context(commit)
patches[commit] = cg.kernels
# Dictionary for JSON string conversion.
commit_status = {
'cve': cve_num,
'status': {}
}
for commit in commits:
logger.info(f'Trying to apply patch: {commit}')
kernels = patchapplier.apply_patch(commit, opts.bug, patches[commit])
patched_kernels = []
# Records patch status for JSON string.
commit_status['status'][commit] = {}
# Displays status of kernels after attempting to patch each one.
for kern in KERNELS:
cros_branch = common.get_cros_branch(kern)
if kern not in patches[commit]:
commit_status['status'][commit][kern] = 'FIXED'
logger.info(f'{cros_branch}: FIXED')
else:
if kernels[kern]:
commit_status['status'][commit][kern] = 'MISSING, applies cleanly'
patched_kernels.append(kern)
logger.info(f'{cros_branch}: MISSING, applies cleanly')
else:
commit_status['status'][commit][kern] = 'MISSING, conflict'
logger.info(f'{cros_branch}: MISSING, conflict')
if len(patched_kernels) != 0:
logger.info(f'Generating CLs for {commit}')
cl_links = clgenerator.create_cls(opts.bug, patched_kernels)
for kernel in cl_links:
logger.info(f'{kernel} CL Link: {cl_links[kernel]}')
if opts.json:
json_out = json.dumps(commit_status, indent=4)
print(json_out)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))