# $Id: CVE.pm,v 1.3 2008/01/14 16:33:12 lkundrak Exp $ # Get CVE information from NVD and maintain NVD XML file cache # Lubomir Kundrak package Libexig::CVE; #use warnings; #use strict; use Exporter 'import'; use XML::Parser; use LWP::Simple; @EXPORT = qw/cve/; my $sourcebase = 'http://nvd.nist.gov/download/'; my $cachebase = $ENV{'HOME'}.'/.nvdcache/'; my $parser = new XML::Parser ( 'Style' => 'Tree', ); sub get_element { my $tree = shift; my $tag = shift @{$tree}; my $content = shift @{$tree}; my $arguments = shift @{$content}; if ($tag and $content and $arguments) { return [$tag, $content, $arguments]; } else { return undef; } } # Gets element and returns description from 'cve' source sub get_desc { my $e = shift; while (my $e = get_element ($e->[1])) { # $e->[2]->{'source'} eq 'cve' or next; return $e->[1]->[1]; } } # Gets element and returns array of all url=s of s sub get_refs { my $e = shift; my @refs; while (my $e = get_element ($e->[1])) { # push @refs, $e->[2]->{'url'}; } return @refs; } # Get and return its description and references sub do_entry { my $e = shift; my $desc; my @refs; $e->[2]->{'type'} eq 'CVE' or die 'Non-CVE entry'; while (my $e = get_element ($e->[1])) { $desc = get_desc ($e) if $e->[0] eq 'desc'; @refs = get_refs ($e) if $e->[0] eq 'refs'; $desc and @refs and return ($desc, [@refs]); } } # Update file in cache if older than age and return its path sub nvdcache { my ($file, $age) = @_; mkdir $cachebase; system ("mkdir -p '$cachebase'"); mirror ($sourcebase.$file, $cachebase.$file) or die ('Failed to update cache'); return $cachebase.$file; } # lala sub cve { my $cve = shift; $cve =~ /^CVE-(\d+)-\d+$/ or die "'$cve' does not look like a CVE id"; my $year = ($1 > 2002 ? $1 : 2002); foreach ( # File name => cache update threshold (minutes, XXX: not implemented) # order is important [ 'nvdcve-modified.xml' => 0 ], [ 'nvdcve-recent.xml' => 0 ], [ 'nvdcve-'.$year.'.xml' => 1440 ], ) { my $file = nvdcache (@{$_}); my $tree = $parser->parsefile ($file); my $e = get_element ($tree); while (my $e = get_element ($e->[1])) { # matching if ($e->[0] eq 'entry' and $e->[2]->{'name'} eq $cve) { return do_entry ($e); } } } return undef; } 1;