#!/usr/bin/perl -w
use strict;

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

my $cverepobase=`dirname $dn`;
chomp($cverepobase);

%CanDBReader::advisoryid2date = ();
require CanDBReader;

my $dummy;
my $year;
my $mon;
my $mday;
($dummy,$dummy,$dummy,$mday,$mon,$year,$dummy,$dummy,$dummy) = localtime(time);

$year += 1900;

my $path = 'lists.opensuse.org/archives/list/security-announce\@lists.opensuse.org/';
system("cd $tempdir; wget -I /archives/list/security-announce\@lists.opensuse.org/$year/ -m https://$path >/dev/null 2>&1");
my @files = `find $tempdir/$path -type f`;

$path = 'lists.suse.com/pipermail/sle-security-updates/';
system("cd $tempdir; wget -I /pipermail/sle-security-updates/ -m https://$path >/dev/null 2>&1");
push @files,`find $tempdir/$path -type f`;

$path = 'lists.suse.com/pipermail/suse-liberty-linux-updates/';
system("cd $tempdir; wget -I /pipermail/suse-liberty-linux-updates/ -m https://$path >/dev/null 2>&1");
push @files,`find $tempdir/$path -type f`;

$path = 'lists.suse.com/pipermail/sle-updates/';
system("cd $tempdir; wget -I /pipermail/sle-updates/ -m https://$path >/dev/null 2>&1");
push @files,`find $tempdir/$path -type f`;

$path = 'lists.suse.com/pipermail/neuvector-updates/';
system("cd $tempdir; wget -I /pipermail/neuvector-updates/ -m https://$path >/dev/null 2>&1");
push @files,`find $tempdir/$path -type f`;

# this might DOS the website due to wget being stupid... and it is very slow.
#$path = 'www.suse.com/support/update/announcement/';
#system("cd $tempdir; wget -I /support/update/ -m https://www.suse.com/support/update/ >/dev/null 2>&1");
#push @files,`find $tempdir/$path -type f`;

chomp @files;


my $susesa="unknown";
my $xurl = '';
my $inreferences = 0;
my $can;
my $date;

my $addcans = '';
my $addsa = '';
my $addsumap = '';

while (my $fn = pop @files) {
	#if ($fn =~ m{$tempdir/(lists.opensuse.*)}) {
	#	$xurl = "http://$1";
	#}
	if ($fn =~ m{$tempdir/(lists.opensuse.*)}) {
		$xurl = "https://$1";
	}
	if ($fn =~ m{$tempdir/(lists.suse.com.*)}) {
		$xurl = "https://$1";
	}
	if ($fn =~ m{$tempdir/(www.suse.com.*)}) {
		$xurl = "https://$1";
	}
	next if ($fn =~ /rss/);
	next if ($fn =~ /mbox/);
	# skip the full monthly ones http://lists.suse.com/pipermail/sle-security-updates/2017-February.txt
	next if ($fn =~ /[a-z].txt$/);
	die unless $xurl;
	open(CURL,"$fn")||die "$fn:$!";
	$inreferences = 0;
	$date = "";
	my %patches = ();

	my $targeturl = $xurl;
	while (<CURL>) {
		chomp;
		s/&nbsp;/ /g;
		s/<br>/ /g;
		s/\s*$//;

		#                        href="/archives/list/security-announce@lists.opensuse.org/thread/WNHKIYENNBZYQZDAXUMNWHSYC2SN5WOD/">
		if (m{ href="(/archives/list/security-announce\@lists.opensuse.org/thread/[^"]*)"}) {
			$targeturl = "https://lists.opensuse.org$1";
		}

		if (/Announcement.ID:\s*(openSUSE-SU\S*)/i) {
			$susesa = $1;
			print STDERR "susesa is $1, href $targeturl\n" if (-t STDERR);

			if (defined($targeturl) && ($targeturl =~ /lists.opensuse.org/)) {
				last if ($susesa =~ /openSUSE-SU-22022/);
				last if ($susesa =~ /openSUSE-SU-42022-1/);
				# we do not have a date in the archive
				my $xtime = localtime;
				if (	!defined($CanDBReader::advisoryid2date{$susesa}) ||
					!defined($CanDBReader::advisoryid2url{$susesa})
				) {
					$addsa .= "$susesa;$targeturl;$xtime\n";
				}
				undef $date;
			}
			next;
		}
		#   <H1>SUSE-SU-2023:2416-1: important: Security update for the Linux Kernel (Live Patch 28 for SLE 15 SP3)</H1>
		if (/H1>(SUSE-[^- ]*-[^-]*-\S*):/i) {
			$susesa = $1;
			print STDERR "html id: susesa is $1\n" if (-t STDERR);
			next;
		}
		if (/Announcement.ID:\s*(SUSE-[^- ]*-[^-]*-\S*)/i) {
			$susesa = $1;
			print STDERR "announce id 1: susesa is $1\n" if (-t STDERR);
			next;
		}
		if (/Container Advisory ID :\s*(SUSE-C\S*)/i) {
			$susesa = $1;
			print STDERR "susesa is $1\n" if (-t STDERR);
			next;
		}
		if (/Image Advisory ID :\s*(SUSE-I\S*)/i) {
			$susesa = $1;
			print STDERR "susesa is $1\n" if (-t STDERR);
			next;
		}
		if (/Announcement.ID:.*(SUSE-[^- ]*-[^-]*-\S*)/i) {	# in html code
			$susesa = $1;
			print STDERR "announce id2: susesa is $1\n" if (-t STDERR);
			next;
		}
		if (/Announcement.ID:\s*((RHSA|ESSA)-[^:]*:.*)/i) {
			$susesa = $1;
			print STDERR "announce id 1: susesa is $1\n" if (-t STDERR);
			next;
		}
		if (/Cross.References/i) {
			$inreferences = 1;
		}
		if (/References/i) {
			$inreferences = 1;
		}
		if (/problem description/i) {
			$inreferences = 0;
			next;
		}
		if (/Affected/i) {
			$inreferences = 0;
			next;
		}
		if (/Pending/i) {
			$inreferences = 0;
			next;
		}
		if (/Authentication/i) {
			$inreferences = 0;
			next;
		}
		if (/Date:\s*(.*)$/) {
			$date = $1;
			next;
		}
		#     <I>Tue Sep  8 09:09:42 MDT 2015</I>
		if (/<I>([A-Za-z][A-Za-z][A-Za-z] [A-Za-z][A-Za-z][A-Za-z].*20[0-9][0-9])<\/I>/) {
			$date = $1;
			# print "found: $1\n";
			next;
		}
		if (/Date<.span>/) {
			$date = <CURL>;
			chomp $date;
			$date =~ s/<.*$//g;
			$date =~ s/^\s*//;
			next;
		}
		if ($inreferences) {
			next unless (defined($susesa));
			last if ($susesa =~ /openSUSE-SU-22022/);
			last if ($susesa =~ /openSUSE-SU-42022-1/);

			while (/(C[AV][NE]-20[0-9][0-9]-\d\d\d\d*)/) {
				$can = $1;
				$can =~ s/CNE/CVE/;
				$can =~ s/CAN/CVE/;
				if (	!defined($CanDBReader::advisoryids{$can}) ||
					!grep(/$susesa/,$CanDBReader::advisoryids{$can})
				) {

					unless ($can eq "CVE-2016-0332") { # burned
						print STDERR "adding $susesa at $targeturl for $can\n";
						$addcans .= sprintf("$can,%04d%02d%02d,ADVISORY:$targeturl\n",$year,$mon+1,$mday);
						$addcans .= sprintf("$can,%04d%02d%02d,ADVISORYID:$susesa\n",$year,$mon+1,$mday);
					}
					# add them to the DB temporary to avoid duplicate additions
					if (defined($CanDBReader::advisoryids{$can})) {
						$CanDBReader::advisoryids{$can}.= ",$susesa";
					} else {
						$CanDBReader::advisoryids{$can} = $susesa;
					}
				}
				s/(C[AV][NE]-20[0-9][0-9]-\d\d\d\d*)//;
			}
		}

		# zypper in -t patch slestso13-openssl-12999=1</code>
		# livepatches can have multiple patches on one line.
		while (/zypper in -t patch ([^=< ]*)[=<]/) {
			my $patchname = $1;
			$patches{$patchname} = 1;
			# print STDERR "parsing patch line:\n$_\n";
			print STDERR "\t$patchname\n" if (-t STDERR);

			# remove the first, and rerun.
			s/-t patch ([^=< ]*)[=<]\d*\s*/-t patch /;
		}

		# flush the content...
		if (/Package List/) {
			if (defined($susesa) && %patches && ($susesa ne "")) {
				if (!defined($CanDBReader::susenotice2patches{$susesa})) {
					$addsumap .= "$susesa;" . join(",",sort keys %patches) . "\n";
				} else {
					foreach my $xpatch (sort keys %patches) {
						if (!grep (/$xpatch/,keys %{$CanDBReader::susenotice2patches{$susesa}})) {
							# $addsumap .= "$susesa;" . join(",",sort keys %patches) . "\n";
							# print STDERR "found id in existing entry ( $CanDBReader::susenotice2patches{$susesa} vs $xpatch )\n" if -t STDERR;
							last;
						}
					}
				}
				$CanDBReader::susenotice2patches{$susesa} = \%patches;
			}
			if (!defined($susesa)) {
				print STDERR "no susesa, in $xurl\n" if -t STDERR;
			}
			%patches = ();
		}
	}
	close(CURL)||die "curl failed";
	if (defined($susesa)) {
		next if ($susesa =~ /openSUSE-SU-22022/);
		next if ($susesa =~ /openSUSE-SU-42022-1/);
		if (defined($targeturl) && defined($date)) {
			if (	!defined($CanDBReader::advisoryid2date{$susesa}) ||
				!defined($CanDBReader::advisoryid2url{$susesa})
			) {
				$addsa .= "$susesa;$targeturl;$date\n";
			}
		}
	}

	undef $targeturl;
	undef $xurl;
	undef $susesa;
}

# extract TIDs

my @newtids = ();

#my $url="https://www.suse.com/support/kb/?maxIndex=1000&q=CVE&bu_suse=true";
my $url="https://www.suse.com/support/kb/?maxIndex=1000&q=CVE&bu_novell=true&bu_netiq=true&bu_suse=true";
my $startIndex=1;
my $next=0;
do {
	open(KBINDEX,"curl -s \'$url&startIndex=$startIndex\'|")||die ;
	$next = 0;
	while (<KBINDEX>) {

		if (/class=.kbpagination/) {
			#print STDERR $_;
			# prev entry first, then next entry
			while (/startIndex=(\d*)/) {
				print STDERR "pagination $startIndex vs $1\n";
				if ($1 > $startIndex) {
					$startIndex=$1;
					$next = 1;
					last;
				}
				s/startIndex=(\d*)//;
			}
			next;
		}
		#	<a href="/support/kb/doc?id=7021041" target="_blank">CVE-2017-3142 and CVE-2017-3143 
		#<a href="doc.php?id=7015702"
		if (/a href=".*doc.id=(\d*)/) {
			my $susesa = "TID$1";
			my $xurl = "https://www.suse.com/support/kb/doc/?id=$1";

			#print STDERR "tid id $susesa, $xurl\n";
			my @kbnewtids = ();

			open(KBENTRY,"curl -s 'https://www.suse.com/support/kb/doc/?id=$1'|")||die "curl 'https://www.suse.com/support/kb/doc/?id=$1'";
			my $summary = "";
			while (my $kb = <KBENTRY>) {
				if ($kb =~ /<h1>(.*)<\/h1>/) {
					$summary = $1;
					#print STDERR "Summary $summary detected in $susesa\n";
				}
				while ($kb =~ /(C[AV][NE]-20[0-9][0-9]-\d\d\d\d\d*)/) {
					$can = $1;
					$can =~ s/CNE/CVE/;
					$can =~ s/CAN/CVE/;
					# print STDERR "$susesa: cve $can\n";
					if (	!defined($CanDBReader::advisoryids{$can}) ||
						!grep(/$susesa/,$CanDBReader::advisoryids{$can})
					) {
						print STDERR "adding $susesa at $xurl for $can\n";
						$addcans .= sprintf("$can,%04d%02d%02d,ADVISORY:$xurl\n",$year,$mon+1,$mday);
						$addcans .= sprintf("$can,%04d%02d%02d,ADVISORYID:$susesa\n",$year,$mon+1,$mday);
						push @kbnewtids,$can;

						# add them to the DB temporary to avoid duplicate additions
						if (defined($CanDBReader::advisoryids{$can})) {
							$CanDBReader::advisoryids{$can}.= ",$susesa";
						} else {
							$CanDBReader::advisoryids{$can} = $susesa;
						}
					}
					$kb =~ s/(C[AV][NE]-20[0-9][0-9]-\d\d\d\d\d*)//;
				}
			}
			close(KBENTRY);
			foreach my $tid (@kbnewtids) {
				push @newtids, "$tid: $xurl ($summary)";
			}
			if (!defined($CanDBReader::advisoryid2date{$susesa})) {
				$addsa .= "$susesa;$xurl;". `date`;
			}
		}
	}
	close(KBINDEX);
} while ($next);

if (@newtids) {
	open(SENDMAIL,"|/usr/sbin/sendmail -r meissner\@suse.de -oi -t");
	print SENDMAIL "From: meissner\@suse.de\n";
	print SENDMAIL "To: high-impact-vul-info\@suse.de\n";
	print SENDMAIL "Subject: New TID(s) published for high impact vulnerability issues\n";
	print SENDMAIL "\n";
	print SENDMAIL "We detected that the following TIDs were published with new CVE IDs,\n";
	print SENDMAIL "indicating release of a new High Impact Vulnerability:\n";
	print SENDMAIL join("\n",@newtids) . "\n";
	print SENDMAIL "\n";
	print SENDMAIL "Ciao, https://gitlab.suse.de/security/cve-database.git:bin/cron-advisory.pl (operated by securitybot\@maintenance.suse.de)\n";
	close(SENDMAIL);
}


# Extract rancher or other github advisories

my @gitlocations = (
	'https://github.com/rancher/rancher/security/advisories',
	'https://github.com/rancher/norman/security/advisories',
	'https://github.com/rancher/apiserver/security/advisories',
	'https://github.com/rancher/wrangler/security/advisories',
	'https://github.com/rancher/rke2/security/advisories',
	'https://github.com/rancher/rke/security/advisories',
	'https://github.com/k3s-io/k3s/security/advisories',
	'https://github.com/squid-cache/squid/security/advisories/',
	'https://github.com/rancher-sandbox/rancher-desktop/security/advisories/',
	'https://github.com/neuvector/neuvector/security/advisories',
	'https://github.com/kubewarden/helm-charts/security/advisories',

	'https://github.com/longhorn/security/advisories/',
	'https://github.com/longhorn/longhorn/security/advisories/',
	'https://github.com/distribution/distribution/security/advisories/',
);

foreach my $gitloc (@gitlocations) {
	open(ADVISORYINDEX,"curl -s $gitloc|")||die;
	while (my $gitsecindex = <ADVISORYINDEX>) {
		next unless ($gitsecindex =~ m{security/advisories/([^/"]*)});

		my $url = "$gitloc/$1";
		my $susesa = $1;

		print STDERR "github advisory: $url / $susesa\n" if -t STDERR;
		open(ADVISORY,"curl -s $url|")||die;
		while (<ADVISORY>) {
			if (/(CVE-\d*-\d*)/)  {
				my $cve = $1;
				# print STDERR "$susesa: cve $cve\n";

				if (	!defined($CanDBReader::advisoryids{$cve}) ||
					!grep(/$susesa/,$CanDBReader::advisoryids{$cve})
				) {
					print STDERR "adding rancher $susesa at $url for $cve\n";
					$addcans .= sprintf("$cve,%04d%02d%02d,ADVISORY:$url\n",$year,$mon+1,$mday);
					$addcans .= sprintf("$cve,%04d%02d%02d,ADVISORYID:$susesa\n",$year,$mon+1,$mday);

					# add them to the DB temporary to avoid duplicate additions
					if (defined($CanDBReader::advisoryids{$cve})) {
						$CanDBReader::advisoryids{$cve}.= ",$susesa";
					} else {
						$CanDBReader::advisoryids{$cve} = $susesa;
					}
				}
			}
		}
		if (!defined($CanDBReader::advisoryid2date{$susesa})) {
			$addsa .= "$susesa;$url;". `date`;
		}
		close(ADVISORY);
	}
	close(ADVISORYINDEX);
}

if ($addcans ne '') {
	open(CANNUMBERS,">>$cverepobase/data/advisories")||die "open $cverepobase/data/advisories: $!";
	print CANNUMBERS $addcans;
	close(CANNUMBERS);
}
if ($addsa ne '') {
	open(SUSESA,">>$cverepobase/data/suse-sa")||die "open $cverepobase/data/suse-sa: $!";
	print SUSESA $addsa;
	close(SUSESA);
}

if ($addsumap ne '') {
	open(SUSESA,">>$cverepobase/data/su2patch")||die "open $cverepobase/data/su2patch: $!";
	print SUSESA $addsumap;
	close(SUSESA);
}
