logo

cve-client

CLI-based client / toolbox for CVE.org

cve-client.pl (3204B)


  1. #!/usr/bin/env perl
  2. # CVE-Client: CLI-based client / toolbox for CVE.org
  3. # Copyright © 2021 CVE-Client Authors <https://hacktivis.me/git/cve-client/>
  4. # SPDX-License-Identifier: AGPL-3.0-only
  5. use strict;
  6. use utf8;
  7. no warnings; # Wide Character…
  8. use Getopt::Std;
  9. use LWP::UserAgent;
  10. use JSON::MaybeXS;
  11. my $json = JSON::MaybeXS->new(utf8 => 1);
  12. my %options = ();
  13. sub usage {
  14. print "usage: ap-fetch.pl [-r|-j] <CVE-ID>\n";
  15. print " -r Raw output\n";
  16. print " -j Pipe into jq(1)\n";
  17. print " -g Gemtext format\n";
  18. print "Otherwise it tries to do a plain-text representation.\n";
  19. exit 1;
  20. }
  21. sub print_cve {
  22. my ($object, $cve_id) = @_;
  23. print "CVE ID: ", $cve_id, "\n\n";
  24. if ($object->{'error'} and $object->{'message'}) {
  25. print "Error (", $object->{'error'}, "): ", $object->{'message'}, "\n";
  26. exit 1;
  27. }
  28. if ($object->{'data_version'} != "4.0") {
  29. print "Warning: Got unknown CVE format \"", $object->{'data_version'},
  30. "\"\n";
  31. }
  32. if ($object->{'CVE_data_meta'}->{'ID'} != $cve_id) {
  33. print "Warning: Got ", $object->{'CVE_data_meta'}->{'ID'},
  34. " instead of ", $cve_id, "\n";
  35. }
  36. if ($object->{'description'}->{'description_data'}) {
  37. my $descs = $object->{'description'}->{'description_data'};
  38. foreach (@{$descs}) {
  39. print "Description Language: ", $_->{'lang'}, "\n";
  40. print "Description:\n", $_->{'value'}, "\n\n";
  41. }
  42. } else {
  43. print "Warning: No CVE description could be found!";
  44. }
  45. if ($object->{'references'}->{'reference_data'}) {
  46. my $refs = $object->{'references'}->{'reference_data'};
  47. foreach (@{$refs}) {
  48. if (defined $options{g}) {
  49. print "Reference Source: ", $_->{'refsource'}, "\n";
  50. print "=> ", $_->{'url'} if $_->{'url'};
  51. if ($_->{'name'}) {
  52. print " ", $_->{'name'}, "\n\n";
  53. } else {
  54. print "\n\n";
  55. }
  56. } else {
  57. print "Reference Source: ", $_->{'refsource'}, "\n";
  58. print "- Name: ", $_->{'name'}, "\n" if $_->{'name'};
  59. print "- URL: ", $_->{'url'}, "\n" if $_->{'url'};
  60. print "\n";
  61. }
  62. }
  63. } else {
  64. print "Warning: No CVE references could be found!";
  65. }
  66. }
  67. sub fetch_cve {
  68. my ($cve_id) = @_;
  69. my $url = 'https://www.cve.org/api/?action=getCveById&cveId=' . $cve_id;
  70. #print "Fetching: ", $url, "\n";
  71. my $ua = LWP::UserAgent->new;
  72. $ua->agent("CVE-Client fetch <https://hacktivis.me/git/cve-client/>");
  73. my $req = HTTP::Request->new(GET => $url);
  74. #$req->header('Accept' => 'application/json');
  75. my $res = $ua->request($req);
  76. if ($res->is_success) {
  77. my $content_type = $res->header("Content-Type");
  78. my $content_match = 'application/json';
  79. if ($content_type == $content_match) {
  80. if (defined $options{r}) {
  81. print $res->content;
  82. } elsif (defined $options{j}) {
  83. open(my $pipe_out, '|-', 'jq .')
  84. or die "Couldn't open a pipe into jq: $!";
  85. print $pipe_out $res->content;
  86. } else {
  87. my $object = $json->decode($res->content);
  88. print_cve($object, $cve_id);
  89. }
  90. } else {
  91. print "Got $content_type instead of $content_match";
  92. }
  93. } else {
  94. print "Got ", $res->status_line, " instead of 2xx\n";
  95. }
  96. }
  97. getopts("rjg", \%options);
  98. if ($#ARGV != 0) { usage; }
  99. if ($ARGV[0] =~ /^CVE-\d{4}-\d+$/) {
  100. fetch_cve($ARGV[0]);
  101. } else {
  102. usage();
  103. }