#!/usr/bin/perl use strict; use File::Temp; use Getopt::Std; #use Smart::Comments; my %opts; getopts('ln:fhi:t:T', \%opts); if ($opts{h}) { print <<"EOF"; downloads allitems.txt from cve.mitre.org and shows full decription for each "TODO: check" item (2003 and newer). Then - tries to guess product name and php filename and does apt-cache and apt-file search - waits for input: * blank line to skip to next issue * .fname to do "apt-file search name" * .cname to do "apt-cache search name" * v or e to launch an editor with the current item * q to save and quit * CTRL-C to quit without saving * everything else is inserted as product name for a NOT-FOR-US Use "svn diff" and "svn revert" as needed ;-) OPTIONS: [ -l [-n ] [-f] ] -l : just list issues -n : show max n lines of each description (default 2) -f : show full CVE/list entry as well -i regexp : use regexp to select issues (default: 'CVE-200[3-9]' ) -t regexp : use regexp to select todos (default: '^\s+TODO: check$' ) -T : same as -t '^\s+TODO: check' (note the missing $) EOF exit(0); } # TODO/BUGS: # - go back to previous issue / undo # - handle entries with several TODO lines # - handle claimed-by # - look for ITPs? my $datafile="./secure-testing/data/CVE/list"; my $allitemsfile="gunzip -c allitems.txt.gz|"; my $allitemsurl="http://cve.mitre.org/cve/downloads/allitems.txt.gz"; my $issue_regexp= $opts{i} || 'CVE-200[3-9]'; my $todo_regexp= $opts{t} || ( $opts{T} ? '^\s+TODO: check' : '^\s+TODO: check$' ); my $editor=$ENV{EDITOR} || $ENV{VISUAL} || "vi"; system "wget -N $allitemsurl"; print "Reading data...\n"; my $entries=read_file($datafile, qr/^CVE/ ); my $CVEs=read_file($allitemsfile, qr/^=+$/ ); my $data; my @todos; my %afcache; foreach my $cve (@{$CVEs}) { $cve =~ /^Name:\s*(CVE\S+)/m or next; $data->{$1}->{CVE}=\$cve; } foreach my $entry (@{$entries}) { my $name; if ( $entry =~ /^(CVE-....-\d\d\d\d)/ ) { $name=$1; } elsif ( $entry =~ /^(CVE-....-XXXX.*)\n/ ){ $name=$1; } else { die "invlid entry:\n$entry"; } $data->{$name}->{entry}=\$entry; if ( $entry =~ /$todo_regexp/m and $name =~ /$issue_regexp/ ) { push @todos, $name; } } print scalar(@{$CVEs}), "/", scalar(@{$entries}), "/", scalar(@todos), "\n"; if ($opts{l}) { #list only foreach my $todo (reverse sort @todos) { my $desc=description($todo); if ($desc) { my $lines=$opts{n} || 2; if ($desc =~ /((?:.*\n){1,$lines})/) { $desc = $1; $desc =~ s/^/ /mg; if ($opts{f}) { print ${$data->{$todo}->{entry}}, $desc; } else { print "$todo:\n$desc"; } } } else { print "${$data->{$todo}->{entry}}"; } } exit 0; } TODO: foreach my $todo (reverse sort @todos) { print ${$data->{$todo}->{CVE}} if $data->{$todo}->{CVE}; print ${$data->{$todo}->{entry}}; auto_search($todo); READ: while (my $r=) { chomp $r; if ($r =~ /^\s*$/) { next TODO; } elsif ($r=~ /^\.c(.*)$/ ) { my $s = $1; $s =~ tr{a-zA-Z0-9_@-}{ }cs; print "=== apt-cache search $s :\n"; system("apt-cache search $s|less -FX"); print "===\n"; next READ; } elsif ($r=~ /^\.f(.*)$/ ) { my $s = $1; $s =~ s/^\s*(.*?)\s*$/$1/; print "=== apt-file search '$s':\n"; system("apt-file search '$s'|less -FX"); print "===\n"; next READ; } elsif ($r=~ /^q$/i ) { last TODO; } elsif ($r=~ /^[ve]$/i ) { my $newentry=edit_entry(${$data->{$todo}->{entry}}); if ( $newentry eq ${$data->{$todo}->{entry}} ) { print "Not changed.\n"; next READ; } else { ${$data->{$todo}->{entry}}=$newentry; print "New entry set to:\n$newentry"; next TODO; } } else { ${$data->{$todo}->{entry}} =~ s/^\s*TODO: check/\tNOT-FOR-US: $r/m ; print "New entry set to:\n${$data->{$todo}->{entry}}"; next TODO; } } } open(my $fh, ">", $datafile); print $fh @{$entries}; close($fh); sub description { my $name=shift; defined $data->{$name}->{CVE} or return ""; ${$data->{$name}->{CVE}} =~ /\n\n(.*)^Current Votes:/ms; my $desc = $1; $desc =~ s/\n\n+/\n/; return $desc; } sub read_file { my $file=shift; my $re=shift; open(my $fh, $file) or die "could not open $file"; my @data; my $cur=""; while (my $line=<$fh>) { if ($line =~ $re and $cur) { push @data, $cur; $cur = ""; } $cur.=$line; } push @data, $cur if $cur; close($fh); return \@data; } sub edit_entry { my $entry=shift; my $tmp=new File::Temp(); my $tmpname=$tmp->filename; print $tmp $entry; close $tmp; system "$editor $tmpname"; local $/; #slurp open($tmp, $tmpname); return <$tmp>; } sub auto_search { my $name=shift; my $desc=description($name); $desc =~ s/[\s\n]+/ /g; my $file; my $prog; if ( $desc =~ / in (\S+\.\S+) in (\S+) / ) { $file = $1; $prog = $2; } elsif ( $desc =~ / in (?:the )?(\S+) / ) { $prog = $1; } if ($prog) { print "doing apt-cache search..."; my $ac=`apt-cache search '$prog' |wc -l`; chomp $ac; print "\r$ac results from apt-cache search $prog\n"; } if ( $file eq 'index.php' ) { return; } if ( $file =~ /(php3?|asp|cgi)$/ ) { if (! exists $afcache{$file}) { print "doing apt-file search..."; $afcache{$file}=`apt-file -i search '$file' |wc -l`; chomp $afcache{$file}; } print "\r$afcache{$file} results from apt-file -i search $file\n"; } }