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

use Data::Dumper;

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

require CanDBReader;
require CVEListReader;
require SMASHData;
require UpdateInfoReader;
UpdateInfoReader->import_product_updates();


&read_all_cached_issues();

my $match = "^SUSE.Linux.Enterprise.Server.*12-SP[23]";

my @nolivepatch = ();
my @nocvssv3 = ();
my @nocvssv3other = ();

my $cvsslevel = 4;

my %cve2cvss = ();
my %cve2cvssnvd = ();

my %allcvss7 = ();

my @allcves = ();

foreach my $patchid (keys %{$UpdateInfoReader::patches{"SUSE Linux Enterprise Server 12 SP2 BCL"}}) {
	next if ($UpdateInfoReader::patchissued{$patchid} <= 1618908907);	# the highest 12-sp2-ltss entry

	foreach my $cve (keys %{$UpdateInfoReader::patchreferences{$patchid}}) {

		next unless ($cve =~ /CVE/);
		read_smash_issue($cve);
		if (defined($SMASHData::cvssv3{$cve}))  {
			my %entry = %{$SMASHData::cvssv3{$cve}};
			my %score;
			my %nvdscore;

			my $basenum = -1;
			if (defined($entry{'SUSE'})) {
				%score = %{$entry{'SUSE'}};
				my $basescore = $score{'base_score'};
				my $basevector = $score{'base_vector'};
				if ($basescore  =~ /^(\d+.*)/) {
					$cve2cvss{$cve} = $1;
					if ($cve2cvss{$cve} >= $cvsslevel) {
						push @allcves,$cve;

						foreach my $pkg (keys %{$UpdateInfoReader::patchpackages{$patchid}}) {
							$allcvss7{$cve}->{$pkg} = 1;
						}
					}
				} else {
					$cve2cvss{$cve} = -1;
				}
			}
			if (defined($entry{'National Vulnerability Database'})) {
				%nvdscore = %{$entry{'National Vulnerability Database'}};
				my $nvdbasescore = $nvdscore{'base_score'};
				my $nvdbasevector = $nvdscore{'base_vector'};
				my $nvdbasenum = -1;
				if ($nvdbasescore  =~ /^(\d+.*)/) {
					$cve2cvssnvd{$cve} = $1;
					if (($cve2cvssnvd{$cve} >= $cvsslevel) && ($cve2cvss{$cve} == -1)) {
						print STDERR "SUSE UNRATED $cve , NVD score $nvdbasenum\n";
						push @allcves,$cve;

						foreach my $pkg (keys %{$UpdateInfoReader::patchpackages{$patchid}}) {
							$allcvss7{$cve}->{$pkg} = 1;
						}
					}
				}
			}
		}
	}
}


#open(CVSS,">allreports.txt");
foreach my $cve (sort keys %CanDBReader::bugzillas) {
	read_smash_issue($cve);

	# embargoed flag is not cleared after going public, check if we have an advisory listed or a note.
	if (	($CanDBReader::embargoed{$cve} || $SMASHData::embargoedcves{$cve}) &&
		!defined($CanDBReader::advisoryids{$cve}) &&
		!defined($CanDBReader::note{$cve})
	) {
		next;
	}

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

	my $refetchsmash = 0;

	if (!defined($SMASHData::pkgstate{$cve})) {
		next;
	}
	my %prods = %{$SMASHData::pkgstate{$cve}};

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

	my $iskernel = 0;
	my $isglibc = 0;
	my $isopenssl = 0;

	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'};
		}
	}
	# { PROD -> { PKG -> STATE } }

	my %foundone = ();

	my %fixedinsp2 = ();
	my %sp2notaffected = ();
	foreach my $prod (keys %prods) {
		#print "$cve - $prod\n";
		next unless (	($prod =~ /^SUSE.Linux.Enterprise.Server.*12.SP[23]/i)  ||
				($prod =~ /^SLES-LTSS-SAP/i)  ||
				($prod =~ /^SLES-LTSS-ERICSSON/i));
		print STDERR "$cve - $prod - ok\n" ; #if -t STDERR;
		next if ($prod eq "SLES_TERADATA-10-SP3");

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


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

			# special hack, as GEHC does not have those, are only interested in kernel-source.
			next if ($pkg eq "kernel-source-azure");
			next if ($pkg eq "kernel-source-rt");
			next if ($pkg eq "kernel-source");
			next if ($pkg =~ /kgraft-patch/);

			# as we have some live SP2 products still, skip CVEs declared "not affected" or similar.
			if (	(	($state eq "Not affected") ||
					($state eq "Already fixed") ||
					($state eq "Ignore")
				) && (($prod =~ /SP2 LTSS/) ||  ($prod =~ /SP2$/))) {
				print STDERR "$cve - NOT AFFECTED!\n";
				$sp2notaffected{$pkg} = 1;
				next;
			}
			next if ($state eq "Not affected");
			next if ($state eq "Already fixed");
			next if ($state eq "Unsupported"); # Unsupported means was affected previously, now not affected.
			next if ($state eq "Ignore");

			next if ($state eq "Analysis");	# FIXME

			print STDERR "$cve - $prod - $pkg - $state\n" ; #if -t STDERR;
# also getting these as SP2 ... they should not be ignored.
#CVE-2021-3497 - SUSE Linux Enterprise Server 12 SP2 LTSS ERICSSON - gstreamer-plugins-good - Affected
#CVE-2021-3497 - SUSE Linux Enterprise Server 12 SP2 BCL - gstreamer-plugins-good - Affected
#CVE-2021-3497 - SUSE Linux Enterprise Server 12 SP2 LTSS SAP - gstreamer-plugins-good - Affected
			#if (($state eq "Released") && (($prod =~ /SP2 LTSS$/) ||  ($prod =~ /SP2$/))) {
			# we want all SP2 LTSS fixes here
			if (($state eq "Released") && ($prod =~ /SP2$/)) {
				# we released it for SP2, ignore it...
				print STDERR "$cve in $pkg RELEASED for 12 SP2 before LTSS\n" ; #if -t STDERR;
				$fixedinsp2{$pkg} = 1;
				next;
			}

			print STDERR "\t$pkg -> $pkgstates{$pkg}\n" ; #if -t STDERR;

			$allcvss7{$cve}->{$pkg} = 1;
			$foundone{$pkg} = 1;
		}
	}
	#read_smash_issue($cve,1);

	foreach my $pkg (keys %foundone) {
		print STDERR "$cve - checking $pkg...\n" ; #if -t STDERR;
		if ($fixedinsp2{$pkg} || $sp2notaffected{$pkg}) {
			print STDERR " pruning $pkg ($fixedinsp2{$pkg} / $sp2notaffected{$pkg})\n" ; #if -t STDERR;
			delete $foundone{$pkg};
			delete $allcvss7{$cve}->{$pkg};
		}
	}
	print STDERR Dumper(\%foundone) ; #if -t STDERR;
	next unless (%foundone);

	my $nvdbasenum = -1;
	if ($nvdbasescore  =~ /^(\d+.*)/) {
		$nvdbasenum = $1;
	}
	$cve2cvssnvd{$cve} = $nvdbasenum;

	my $basenum = -1;
	if ($basescore  =~ /^(\d+.*)/) {
		$basenum = $1;
	}
	if (($basenum < $cvsslevel) && ($nvdbasenum > $cvsslevel)) {
		print STDERR "TOO LOW score for $cve (SUSE score $basenum, NVD score $nvdbasenum)\n" if -t STDERR;
		delete $allcvss7{$cve};
		next;
	}
	$cve2cvss{$cve} = $basenum;

	push @allcves,$cve;

	#read_smash_issue($cve,1) if ($refetchsmash);
}

#unify array
my %xxcves = map { $_ => 1} @allcves;
@allcves = sort keys %xxcves;

sub bugzillacmp($$) {
	my ($a,$b) = @_;
	die "no bug for $a" if (!defined($CanDBReader::bugzillas{$a}));
	die "no bug for $b" if (!defined($CanDBReader::bugzillas{$b}));
	my @abugs = sort split(/,/,$CanDBReader::bugzillas{$a});
	my @bbugs = sort split(/,/,$CanDBReader::bugzillas{$b});
	return  $abugs[$[] <=> $bbugs[$[];
}

#my @sortedcves = sort bugzillacmp @allcves;
my @sortedcves = sort {$CanDBReader::firstdate{$a} cmp $CanDBReader::firstdate{$b}}  @allcves;

my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon += 1;

my $fn = sprintf("gehc-cvss7-list-%04d-%02d-%02d.csv",$year,$mon,$mday);
open(ATTACHMENT,">$fn");

# wanted:
# CVE, NVD rating, SUSE rating,packages,12-sp2channels,covery bcl oval,

print ATTACHMENT "# CVE,update,SUSE-CVSS3-base-score,NVD-CVSS3-base-score,sourcepackage,inbcl\n";
for my $cve (@sortedcves) {
	my @bugs = sort split(/,/,$CanDBReader::bugzillas{$cve});
	my $firstbug = $bugs[0];


	# 2018 march was begin of LTSS ... filter before that to cut the noise.
	next if ($CanDBReader::firstdate{$cve} < 20180101);

	my $desc = get_description($cve);
	$desc =~ s/"/\\"/g;


	my $advisories = $CanDBReader::advisoryids{$cve};
	my @allpatches = ();
	my @advisories = ();
	my %advisories = ();
	my $inbcl = 0;
	if (defined($advisories)) {
		print STDERR "$cve -> $advisories\n";
		# map advisories to patches, filter out the 12-sp2 ones
		@advisories = split(/,/,$advisories);
		my %sortedadvisories = map { $_ => 1 } @advisories;
		@advisories = sort keys %sortedadvisories;
		foreach my $advisory (@advisories) {
			my $patches = $CanDBReader::susenotice2patches{$advisory};

			if (defined($patches)) {
				push @allpatches,grep (/12-SP2/,sort keys %{$patches});
				if (grep(/BCL/,keys %{$patches})) {
					$inbcl = 1;
				}
			}
		}
		# map the 12-sp2 patches back to the advisories.

		foreach my $patch (@allpatches) {
			$advisories{$CanDBReader::patch2susenotice{$patch}} = 1;
		}
	}
	my $allnotices = join(";",sort keys %advisories);

	my $pkgs = join(";",sort keys %{$allcvss7{$cve}});
	print ATTACHMENT "$cve,$allnotices,$cve2cvss{$cve},$cve2cvssnvd{$cve},$pkgs,$inbcl\n";
}
close(ATTACHMENT);
#
#open(SENDMAIL,"|/usr/bin/mail -R meissner\@suse.de -s 'weekly GEHC CVSS >= 7.0 report' -a $fn TVance\@suse.com,AnDavis\@suse.com,JMORTENSON\@suse.com,security-reports\@suse.de");
#open(SENDMAIL,"|/usr/bin/mail -R meissner\@suse.de -s 'weekly GEHC CVSS >= 7.0 report' -a $fn meissner\@suse.de");
#print SENDMAIL "This report was generated by $0 from https://gitlab.suse.de/security/cve-database.git at securitybot\@maintenance.suse.de\n";
#print SENDMAIL ".\n";
#close(SENDMAIL);
