spstansbury has asked for the wisdom of the Perl Monks concerning the following question:

This should be simple, but being a newbie the syntax escapes me...

I have two files, the first, "vuln_cvd_map" is just a csv file that contains an integer id number, then a CVE id number.
The second is just another two column csv file that contains a CVE Id number and a summary description of the vulnerability.

The input is the integer id number which I find in the first file, return the CVE id, and look that up in the second file, finally return the summary description.

As always, thanks for your patience... Scott
#!/usr/bin/perl -w use Getopt::Std; open(MAP, "./vuln_cve_map.csv") or die("cant open mapping file</body>< +/html>"); open(SUMMARY, "./CVE_summary.csv") or die("cant open CVE Summary file< +/body></html>"); if ($#ARGV == 0) { my @desc = (); my $desc = []; my $summ = []; while (<MAP>) { /^(\d+),(.*)/; if ($1 == $ARGV[0]) { foreach (<SUMMARY>){ if ($_=~ m/"$2"/) { my @vuln= split(/,/, $desc); print $vuln[1]; } } }; }; } close (SUMMARY); close (MAP);

Replies are listed 'Best First'.
Re: Real basic...
by ikegami (Patriarch) on Feb 04, 2009 at 14:13 UTC

    I don't have time to address this in depth, but I can at least explain the problem.

    In the first pass of the outer loop, the inner loop reads SUMMARY to the end. Thus, in subsequent passes of the outer loop, the inner doesn't get executed since the file pointer is already at the end of the file.

    You'll need to seek to the start of the file before the inner loop (poor choice), or load the summary file into memory before entering the outer loop (better choice).

    By the way, there's never any reason to do foreach (<$fh>). Use while (<$fh>) instead.

    Update: Assuming each line has a unique id,

    #!/usr/bin/perl -w use strict; use Text::CSV_XS qw( ); @ARGV == 1 or die("usage: $0 id\n"); my ($query_id) = @ARGV; my $csv = Text::CSV_XS->new(); my $query_cve_id; { my $map_qfn = 'vuln_cve_map.csv'; open(my $map_fh, '<', $map_qfn) or die("Can't open mapping file \"$map_qfn\": $!\n"); while (my $row = $csv->getline($map_fh)) { my ($id, $cve_id) = @$row; if ($id == $query) { $query_cve_id = $cve_id}; last; } } } { my $summary_qfn = 'CVE_summary.csv'; open(my $summary_fh, '<', $sumamry_qfn) or die("Can't open CVE Summary file \"$summary_qfn\": $!\n"); while (my $row = $csv->getline($summary_fh)) { my ($cve_id, $summary) = @$row; if ($cve_id == $query_cve_id) { print("[$cve_id] $summary\n"); last; } } }

      Thanks.

      The identifiers are indeed unique, so this should work perfectly. The first section of code works fine, returning the cve_id.

      In the second section the array isn't getting populated

      Any ideas?

        What array? The output is being printed, as it was in your code. If you wish to place the output in an array instead of printing it, place the output in an array instead of printing it.

Re: Real basic...
by moritz (Cardinal) on Feb 04, 2009 at 14:19 UTC
    Your current approach doesn't work, because you iterate over all lines of CVE_summary.csv in the inner loop, and when you enter that loop again, the file handle is already exhausted.

    So the solution is to read one file, and store its content into a data structure (an array or a hash), and then open the second file, and for each line of the second file you look into the previously created data structure for the information you want.

    If you want a more concrete example, give us some example data (a few lines from each file would be enough).

      That makes sense, thanks. The first file contains just a number and a corresponding NVD/CVE identifier.
      The code is part of a cgi script that get the id number (the 4,9, 10) as it's input.

      I want to find the corresponding CVE identifier and use it to search the next file.

      scott$ head vuln_cve_map.csv
      4,CVE-2005-4727
      5,CVE-2005-4727
      9,CVE-2005-4727
      10,CVE-2005-4727
      18,CVE-2006-5917
      19,CVE-2006-5917
      23,CVE-2006-5917
      24,CVE-2006-5917
      32,CVE-2006-1913
      33,CVE-2006-1913

      The second file looks like this:

      Ping:~/RiskView scott$ head CVE_summary.csv
      "CVE-1999-0095","The debug command in Sendmail is enabled
      "CVE-1999-0082","CWD ~root command in ftpd allows root access."
      "CVE-1999-1471","Buffer overflow in passwd in BSD based operating systems 4.3 and earlier allows local users to gain root privileges by specifying a long shell or GECOS field."
      "CVE-1999-1122","Vulnerability in restore in SunOS 4.0.3 and earlier allows local users to gain privileges."
      "CVE-1999-1467","Vulnerability in rcp on SunOS 4.0.x allows remote attackers from trusted hosts to execute arbitrary commands as root
      "CVE-1999-1506","Vulnerability in SMI Sendmail 4.0 and earlier
      "CVE-1999-0084","Certain NFS servers allow users to use mknod to gain privileges by creating a writable kmem device and setting the UID to 0."
      "CVE-2000-0388","Buffer overflow in FreeBSD libmytinfo library allows local users to execute commands via a long TERMCAP environmental variable."
      "CVE-1999-0209","The SunView (SunTools) selection_svc facility allows remote users to read files."
      "CVE-1999-1198","BuildDisk program on NeXT systems before 2.0 does not prompt users for the root password

      So that's the goal, match the input to the first field in the first file, and output the summary field in the second.

      Thanks, guys...

        The following should get you started. Could be the regexes need a bit of tweaking. Note the use of subs that nicely separate the task into distinct problems and allow easy code reuse.

        Your post is nearly unreadable. Dont use </br>. Use <br> or <br/>! I wonder where that meme comes from.
        my $desc = get_bug_description(4, 'first.csv', 'second.csv'); sub get_bug_description { my ($id, $key_file, $desc_file) = @_; my $cve = get_bug_cve($id, $key_file); open my $file, '<', $desc_file; while (<$file>) { return $1 if /"$cve","([^"]+)/; } close $file; return; } sub get_bug_cve { my ($id, $key_file) = @_; open my $file, '<', $key_file; while (<$file>) { return $1 if /$id,(.+)/; } close $file; return; }


        holli

        When you're up to your ass in alligators, it's difficult to remember that your original purpose was to drain the swamp.\