#!/usr/bin/perl # # This script tries to detect missing CVE names in VuXML. It does # this by comparing Mitre's CVE references with VuXML references. # # http://cve.mitre.org/cve/downloads/allitems.csv.gz # use Data::Dumper; use XML::Simple; my %data; my %refs; my %cves; my @chops = ( 'cvename', 'status', 'description', 'refs', 'phase','votes', 'comments' ); my $CVEURL = "http://cve.mitre.org/cgi-bin/cvename.cgi?name="; my $VUXURL = "http://www.vuxml.org/freebsd/"; my $CVEOUT = "vuxml_missing_cve.csv"; my $LINOUT = "vuxml_missing_links.csv"; if(!defined $ARGV[0]) { die("Usage: ./mcve.pl \n"); } ## ## First read the CVE data into the ## %data hash ## open(CVE, "<$ARGV[0]") || die("Cannot open CVE file\n"); print "X- Loading CVE data\n"; while() { next if($_ =~ /^\"/ || $_ =~ /^,,/); #my @parts = split(',',$_); my @parts = split_string($_); my $idx = 0; foreach (@chops) { $data{$parts[0]}{$chops[$idx]} = $parts[$idx]; $idx++; } foreach my $ref (split(/ \| /,$parts[3])) { $ref =~ s/^\"//g; $ref =~ s/^(URL|MISC|CONFIRM)://g; # Only use FTP and HTTP links for now next if($ref !~ /^(http|ftp)/); # Multiple CVE's possible. For example, one advisory # that contains 2 vulnerabilities push @{$refs{$ref}}, $data{$parts[0]}; push @{$cves{$parts[0]}},$ref; } } close(CVE); print "X- Loading CVE data -> done\n"; print "X- Loading VuXML\n"; ## ## Now read the VuXML document into another hash ## my $ref = XMLin($ARGV[1]) || die("Cannot open VuXML"); my $ref = $ref->{'vuln'}; #print Dumper(keys %{$ref}); print "X- Loading VuXML -> done\n"; print "X- Starting comparison\n"; my %cresult; my %lresult; open(CRES, ">$CVEOUT") || die("Cannot open: $CVEOUT\n"); open(LRES, ">$LINOUT") || die("Cannot open: $LINOUT\n"); foreach my $vuln (@{$ref}) { my %tmp; my $vrefs = $vuln->{'references'}; my $vcves = $vuln->{'references'}{'cvename'}; if(exists $vrefs->{'url'} && ref($vrefs->{'url'}) ne 'ARRAY') { $vrefs->{'url'} = [ $vrefs->{'url'} ]; } ## ## First check: no CVE in VuXML but we do have ## a reference that was also used in the CVE description ## if(!defined $vcves && exists $vrefs->{'url'}) { foreach my $url (@{$vrefs->{'url'}}) { if(exists $refs{$url}) { # Bingo! my $cvematch = ""; foreach my $cvehash (@{$refs{$url}}) { print CRES "$vuln->{'vid'},$cvehash->{'cvename'},$url\n"; } } } } else { ## ## Compare URL's from CVE with VuXML ## and propose to add new ones ## if(exists $vrefs->{'url'}) { foreach my $url (@{$vrefs->{'url'}}) { #print "Adding \"$url\"\n"; $tmp{$url} = 1; } } if(ref($vcves) ne 'ARRAY') { $vcves = [ $vcves ]; } foreach my $cve (@{$vcves}) { foreach my $url (@{$cves{$cve}}) { if(!exists $tmp{$url}) { print LRES "$vuln->{'vid'},$cve,$url\n"; } } } ## ## TODO: Check if the URL's still exist ## } } close(LRES); close(CRES); ## ## From http://www.perlmonks.org/?node_id=552860 ## sub split_string { my $text = shift; my @new = (); push(@new, $+) while $text =~ m{ \s*( # groups the phrase inside double quotes "([^\"\\]*(?:\\.[^\"\\]*)*)"\s*,? # groups the phrase inside single quotes | '([^\'\\]*(?:\\.[^\'\\]*)*)'\s*,? # trims leading/trailing space from phrase | ([^,\s]+(?:\s+[^,\s]+)*)\s*,? # just to grab empty phrases | (), )\s*}gx; push(@new, undef) if $text =~ m/,\s*$/; return @new; } ## ## return array ## sub array { my $r = shift; if(ref($r) eq 'ARRAY') { return $r; } return @$r; }