#!/usr/bin/python3
# -*- coding: utf-8 -*-

#	cve-inform : Tool that figures out unpublished vulnerabilities
#	Copyright (C) 2019-2026 Alexey Appolonov
#
#	This program 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 3 of the License, or
#	(at your option) any later version.
#
#	This program 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 program.  If not, see <http://www.gnu.org/licenses/>.

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

import argparse
from collections               import defaultdict
from datetime                  import date
from sys                       import argv
from cve_inform.const          import TARGETS
from cve_inform.cve_manager    import CVEManager
from cve_inform.process_tasks  import DetectFixes
from cve_inform.err            import Error

DESCRIPTION = 'Check fixed vulnerabilities of specified package update'
BRANCHES = sorted({b for branches, _ in TARGETS.values() for b in branches})
UNKNOWN_PRODUCT = 'unknown'

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Parsing the arguments

argparser = argparse.ArgumentParser(description=DESCRIPTION)
argparser.add_argument(
	'-b', '--branch',
	metavar='BRANCH_NAME', type=str, required=True,
	choices=BRANCHES,
	help=f'Name of the branch where the package is from ({", ".join(BRANCHES)})'
	)
argparser.add_argument(
	'-n', '--name',
	metavar='PACKAGE_NAME', type=str, required=True,
	help='Name of the inspected package'
	)
argparser.add_argument(
	'-v', '--versions',
	metavar='PACKAGE_VER-REL', type=str, nargs=2, required=True,
	help='Compared versions of the package'
	)
argparser.add_argument(
	'--host', 
	metavar='SSH_HOSTNAME', type=str, nargs='+', default=[],
	help='Chain of SSH hostnames that leads to a computer running cve-manager'
	)
argparser.add_argument(
	'-a', '--allow_discarded_alt_errata',
	action='store_true',
	help='Include fix vulnerabilities taken from ALT errata even if those '
	'vulnerabilities is not included in the cve-manager basic analysis'
	)
argparser.add_argument(
	'--verbose',
	action='store_true',
	help='Enable verbose mode'
	)
argparser.add_argument(
	'--debug',
	action='store_true',
	help='Run in debug mode'
	)
args = argparser.parse_args()

if len(argv) < 2:
	argparser.print_help()
	exit(1)

cve_manager = CVEManager(args.host, args.debug)
bdu_to_cve_map, err = cve_manager.GetMappedCVEIds()
if err:
	Error(err)

packages = {f'{args.name}-{ver_rel}' for ver_rel in args.versions}
if len(packages) != 2:
	Error('Same package versions')

tasks = {(0, 0): {args.name: args.versions}}
unpub_tasks_with_fixes = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))

issues, err = cve_manager.Check(
	args.branch, packages, args.allow_discarded_alt_errata)
if issues == None:
	Error(err)
if err and args.verbose:
	print(f'[WARNING: {err}]')

# Detect fixes for identified issues
ids_of_processed_tasks, irrelevant_enries, warnings = DetectFixes(
	tasks,  # {(task_id, timestamp): {src_name : (old_ver_rel, new_ver_rel)}
	UNKNOWN_PRODUCT, # product
	args.branch, # branch
	{},     # selection (of packages)
	issues, # issues
	bdu_to_cve_map, # bdu_to_cve_map
	{},     # published_vuls
	False,  # look_behind
	False,  # detect_irrelevant_entries
	unpub_tasks_with_fixes # unpub_tasks_with_fixes
	)
if args.verbose:
	for w in warnings:
		print(f'[WARNING: {w}]')

fixed_vuls_in_branch = unpub_tasks_with_fixes \
	.get(date(1970, 1, 1), {}) \
	.get(UNKNOWN_PRODUCT, {}) \
	.get(args.branch, {})
fixed_vuls_of_packages = {} if not fixed_vuls_in_branch else \
	fixed_vuls_in_branch[0].get('fixes', {})
for fixed_vuls in fixed_vuls_of_packages.values():
	for vul in sorted(fixed_vuls):
		print(vul)

exit(0)
