#!/usr/bin/perl -w
# dumps all CVSS scores for all released updates into CSV.
use strict;

my $dn = `dirname $0`;chomp($dn);
my $pwd = `pwd`;chomp($pwd);
if ($dn !~ /^\//) { $dn = $pwd . "/" . $dn; }
push @INC,$dn;

use DateTime;

require CanDBReader;
require SMASHData;
require Products;

delete $CanDBReader::bugzillas{"0"};
delete $CanDBReader::firstdate{"0"};
delete $SMASHData::state{"0"};

# CVE -> PKG -> DAYDIFF
my %missing4 = ();
my %progress4 = ();
# CVE -> PKG -> DAYDIFF
my %missing7 = ();
my %progress7 = ();
my %missinganalysis = ();
# CVE -> basescore number
my %scores = ();

foreach my $cve (sort keys %CanDBReader::bugzillas) {
	&SMASHData::read_smash_issue($cve);

	if (!defined($SMASHData::state{$cve})) {
		print STDERR "no smash state for $cve?\n" if -t STDERR;
		next;
	}

	next if (!defined($SMASHData::pkgstate{$cve}));


	my ($year,$month,$day) = ($CanDBReader::firstdate{$cve} =~ /(\d\d\d\d)(\d\d)(\d\d)/);
	# print STDERR "FIRST: $cve: $CanDBReader::firstdate{$cve}, year $year month $month day $day\n" if -t STDERR;

	my $ft = DateTime->new( year => $year, month => $month, day => $day);

	my $diff = int(((time - $ft->epoch)/24/3600));

	my $basescore 		= "unknown";
	my $basevector		= "unknown";
	my $nvdbasescore	= "unknown";
	my $nvdbasevector	= "unknown";

	if (defined($SMASHData::cvssv3{$cve}))  {
		my %entry = %{$SMASHData::cvssv3{$cve}};
		my %score;
		my %nvdscore;

		if (defined($entry{'SUSE'})) {
			%score = %{$entry{'SUSE'}};
			$basescore = $score{'base_score'};
			$basevector = $score{'base_vector'};
		}
		if (defined($entry{'National Vulnerability Database'})) {
			%nvdscore = %{$entry{'National Vulnerability Database'}};
			$nvdbasescore = $nvdscore{'base_score'};
			$nvdbasevector = $nvdscore{'base_vector'};
		}
	}
	my $basenum = -1;
	if ($basescore  =~ /^(\d+.*)/) {
		$basenum = $1;
	}

	# { PROD -> { PKG -> STATE } }

	my %prods = %{$SMASHData::pkgstate{$cve}};
	foreach my $prod (keys %prods) {
		my $major = $prod;
		next if ($prod eq "SLES_TERADATA-10-SP3");

		if (!defined($Products::product2state{$prod})) {
			print STDERR "no state for $prod\n" if -t STDERR;
			next;
		}

		next if ($Products::product2state{$prod} eq "eol");

		# 12 SP5 only currently
		#next unless (($prod =~ /12.SP5/) || ($prod =~ /Module.*12/));
		next unless (($prod =~ /12.SP5/) || ($prod =~ /Module.*12/) || ($prod =~ /15.SP[23456]/) || ($prod =~ /11.*SP4/));

		# next if ($prod =~ /SUSE.*11/);	# also filters TERADATA 11-sp3 currently
		next unless ($prod =~ /^SUSE Linux/);

		if ($prod =~ /(\d+(.SP\d)?)/) {
			$major= $1;
		} else {
			next if ($prod eq "");
			die "|$prod|\n";
		}

		#print STDERR "$prod -> $major\n";

		my %pkgstates = %{$prods{$prod}};
		#print STDERR "$prod\n";

		foreach my $pkg (sort keys %pkgstates) {
			my $state = $pkgstates{$pkg};

			my $xpkg = "$major - $pkg";

			next if ($state eq "Not affected");
			next if ($state eq "Ignore");

			if ($state eq "Affected") {
				if ($basenum >= 4.0) {
					$missing4{$cve}->{$xpkg} = $diff;
				}
				if ($basenum >= 7.0) {
					$missing7{$cve}->{$xpkg} = $diff;
				}
			}
			if ($state eq "In progress") {
				if ($basenum >= 4.0) {
					$progress4{$cve}->{$xpkg} = $diff;
				}
				if ($basenum >= 7.0) {
					$progress7{$cve}->{$xpkg} = $diff;
				}
			}
			if ($state eq "Analysis") {
				$missinganalysis{$cve}->{$xpkg} = $diff;
				if ($basenum >= 7.0) {
					$missing7{$cve}->{$xpkg} = $diff;
				}
			}
		}
	}
}

my %pending7 = ();

open(PENDING,">pending-7.0.csv");
print PENDING "#CVSS 7.0+ days:\n";
print PENDING "#cve,srcpkg,daydiff,state\n";
foreach my $cve (sort keys %missing7) {
	&SMASHData::read_smash_issue($cve,1);
	foreach my $pkg (sort keys %{$missing7{$cve}}) {
		my $missanalysis = $missinganalysis{$cve};

		print PENDING "$cve,$pkg,$missing7{$cve}->{$pkg},pending\n";

		my $sp = $pkg;

		$sp =~ s/ -.*//;

		my $origsp = $sp;
		$origsp =~ s/ /-/;

		my $state = "Waiting Packager";
		if (defined($missanalysis) && defined($missanalysis->{$pkg})) {
			$state = "Missing Analysis";
		}

		if (defined($CanDBReader::kernelcvedates{$origsp}->{$cve})) {
			$state = "In Kernel GIT";
		}

		$pending7{"$cve - $state - $pkg"} = $missing7{$cve}->{$pkg};
	}
	foreach my $pkg (sort keys %{$progress7{$cve}}) {
		print PENDING "$cve,$pkg,$progress7{$cve}->{$pkg},running\n";

		$pending7{"$cve - Staged / in QA   - $pkg"} = $progress7{$cve}->{$pkg};
	}
	foreach my $pkg (sort keys %{$missinganalysis{$cve}}) {
		print PENDING "$cve,$pkg,$missinganalysis{$cve}->{$pkg},analysis\n";

		$pending7{"$cve - Missing Analysis - $pkg"} = $missinganalysis{$cve}->{$pkg};
	}
}
close(PENDING);

open(PENDING,">pending-7.0.txt");
print PENDING "This report displays the CVSS v3.1 score 7.0 issues for SLES 12 SP5 with state 'Pending':\n";
print PENDING "\n";
my $before30 = 1;
foreach my $entry (sort { $pending7{$a} <=> $pending7{$b} }  keys %pending7) {
	if ($before30 && ($pending7{$entry} >= 30)) {
		$before30 = 0;
		print PENDING "-------------- 30 day --------------\n";
	}
	print PENDING "$entry - $pending7{$entry}\n";
}
print PENDING "\n";
print PENDING "Generated by $0.\n";
close(PENDING);

open(PENDING,">pending-kernel-7.0.txt");
print PENDING "This report displays the CVSS v3.1 score 7.0 issues for SLES 12 SP5, SLES 15 SP2, SP3, SP4, SP5, SP6 with state 'Pending':\n";
print PENDING "\n";
$before30 = 1;

sub cmpfunc($$) {
	my ($a,$b) = @_;
	if ($pending7{$a} == $pending7{$b}) {
		return $a cmp $b;
	}
	return $pending7{$a} <=> $pending7{$b};
}

foreach my $entry (sort cmpfunc keys %pending7) {
	my $xentry = $entry;
	next unless ($entry =~ /kernel-source$/);	# filter out livepatches and kernel-default


# need to debug this
	next if ($xentry =~ /In Kernel GIT/); 

	next if ($pending7{$xentry} > 1000);	# ignore old 11-sp4 stuff

	$entry =~ s/Waiting Packager/No submission/;
	if ($before30 && ($pending7{$xentry} >= 30)) {
		$before30 = 0;
		print PENDING "-------------- 30 day --------------\n";
	}
	print PENDING "$entry - $pending7{$xentry}\n";
}
print PENDING "\n";
print PENDING "Generated by $0.\n";
close(PENDING);

my %pending4 = ();

open(PENDING,">pending-4.0.csv");
print PENDING "#CVSS 4.0+ days:\n";
print PENDING "#cve,srcpkg,daydiff\n";
foreach my $cve (sort keys %missing4) {
	foreach my $pkg (sort keys %{$missing4{$cve}}) {
		print PENDING "$cve,$pkg,$missing4{$cve}->{$pkg},pending\n";

		$pending4{"$cve - Waiting Packager - $pkg"} = $missing4{$cve}->{$pkg};
	}
	foreach my $pkg (sort keys %{$progress4{$cve}}) {
		print PENDING "$cve,$pkg,$progress4{$cve}->{$pkg},running\n";

		$pending4{"$cve - Staged / in QA   - $pkg"} = $progress4{$cve}->{$pkg};
	}
}
close(PENDING);

open(PENDING,">pending-kernel-4.0.txt");
print PENDING "This report displays the CVSS v3.1 score 4.0 and higher issues for SLES 12 SP5 with state 'Pending':\n";
print PENDING "\n";
my $before90 = 1;
foreach my $entry (sort { $pending4{$a} <=> $pending4{$b} }  keys %pending4) {
	my $xentry = $entry;
	next unless ($entry =~ /kernel-source$/);	# filter out livepatches

	$entry =~ s/Waiting Packager/No submission/;
	if ($before90 && ($pending4{$xentry} >= 90)) {
		$before90 = 0;
		print PENDING "-------------- 90 day --------------\n";
	}
	print PENDING "$entry - $pending4{$xentry}\n";
}
print PENDING "\n";
print PENDING "Generated by $0.\n";
close(PENDING);

open(PENDING,">pending-analysis.csv");
print PENDING "#Analysis days:\n";
print PENDING "#cve,srcpkg,daydiff\n";
foreach my $cve (sort keys %missinganalysis) {
	foreach my $pkg (sort keys %{$missinganalysis{$cve}}) {
		print PENDING "$cve,$pkg,$missinganalysis{$cve}->{$pkg}\n";
	}
}
close(PENDING);

