logo

cve-client

CLI-based client / toolbox for CVE.org
commit: a9b91748db240b82d86b69ed8076bc352510633c
parent 5bf05d8e8e0f9701102a428a0049062d2d498412
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Mon, 27 Mar 2023 20:20:32 +0200

lib/App/CveClient: Move-in subs of cve-client

Diffstat:

MMANIFEST1+
Mcve-client178++++---------------------------------------------------------------------------
Alib/App/CveClient.pm175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 183 insertions(+), 171 deletions(-)

diff --git a/MANIFEST b/MANIFEST @@ -1,5 +1,6 @@ .perltidyrc cve-client +lib/App/CveClient.pm Makefile.PL MANIFEST This list of files README.md diff --git a/cve-client b/cve-client @@ -1,17 +1,18 @@ #!/usr/bin/env perl # CVE-Client: CLI-based client / toolbox for CVE.org -# Copyright © 2021 CVE-Client Authors <https://hacktivis.me/git/cve-client/> +# Copyright © 2021-2023 CVE-Client Authors <https://hacktivis.me/git/cve-client/> # SPDX-License-Identifier: AGPL-3.0-only +our $VERSION = '1.0.3'; use strict; use utf8; no warnings; # Wide Character… +use App::CveClient qw(print_cve); + use Getopt::Std; use LWP::UserAgent; use JSON::MaybeXS; -our $VERSION = "1.0.3"; - $Getopt::Std::STANDARD_HELP_VERSION = 1; my $json = JSON::MaybeXS->new(utf8 => 1); @@ -27,169 +28,6 @@ sub HELP_MESSAGE { exit 1; } -sub print_cve { - my ($object, $cve_id) = @_; - - print "CVE ID: ", $cve_id, "\n"; - - if ($object->{'error'} and $object->{'message'}) { - print "Error (", $object->{'error'}, "): ", $object->{'message'}, "\n"; - exit 1; - } - - if ($object->{'dataVersion'} == "5.0") { - print_cve50($object, $cve_id); - } elsif ($object->{'data_version'} == "4.0") { - print_cve40($object, $cve_id); - } else { - print "Error: unknown CVE format:\n"; - print "- data_version: ", $object->{'data_version'}, "\n" - if $object->{'data_version'}; - print "- dataVersion: ", $object->{'dataVersion'}, "\n" - if $object->{'dataVersion'}; - } -} - -# https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/ -sub print_cve50 { - my ($object, $cve_id) = @_; - - if ($object->{'cveMetadata'}->{'cveId'} != $cve_id) { - print "Warning: Got ", $object->{'cveMetadata'}->{'cveId'}, - " instead of ", $cve_id, "\n"; - } - - my $affected = $object->{'containers'}->{'cna'}->{'affected'}; - if ($affected) { - foreach (@{$affected}) { - print "Vendor Name: ", $_->{'vendor'}, "\n"; # vendor required - print "Product Name: ", $_->{'product'}, "\n"; # product required - - foreach (@{$_->{'versions'}}) { - print "- ", $_->{'status'}, ": ", $_->{'version'}, "\n"; - } - } - } else { - print -"Warning: No CVE affected versions could be found! (as required by the spec)\n"; - } - - print "\n"; - - my $metrics = $object->{'containers'}->{'cna'}->{'metrics'}; - if ($metrics) { - foreach (@{$metrics}) { - if ($_->{'cvssV3_1'}) { - my $metric = $_->{'cvssV3_1'}; - print "- Score: ", $metric->{'baseScore'}, " ", - $metric->{'baseSeverity'}, "\n"; - } else { - print "Notice: unhandled metrics (CVSS) data\n"; - } - } - } else { - print -"Warning: No CVE metrics (CVSS) could be found! (as required by the spec)\n"; - } - - print "\n"; - - my $desc = $object->{'containers'}->{'cna'}->{'descriptions'}; - if ($desc) { - foreach (@{$desc}) { - print "Description Language: ", $_->{'lang'}, "\n"; - print "Description:\n", $_->{'value'}, "\n\n"; - } - } else { - print -"Warning: No CVE description could be found! (as required by the spec)\n"; - } - - print "\n"; - - my $refs = $object->{'containers'}->{'cna'}->{'references'}; - if ($refs) { - print "References: \n"; - - foreach (@{$refs}) { - print "=> ", $_->{'url'}, "\n"; - } - } else { - print -"Warning: No CVE references could be found! (as required by the spec)\n"; - } -} - -# https://github.com/CVEProject/cve-schema/blob/master/schema/v4.0/ -sub print_cve40 { - my ($object, $cve_id) = @_; - - if ($object->{'CVE_data_meta'}->{'ID'} != $cve_id) { - print "Warning: Got ", $object->{'CVE_data_meta'}->{'ID'}, - " instead of ", $cve_id, "\n"; - } - - print "TITLE: ", $object->{'CVE_data_meta'}->{'TITLE'}, "\n" - if $object->{'CVE_data_meta'}->{'TITLE'}; - - print "\n"; - - if ($object->{'affects'}->{'vendor'}) { - foreach (@{$object->{'affects'}->{'vendor'}->{'vendor_data'}}) { - print "Vendor Name: ", $_->{'vendor_name'}, "\n" - if $_->{'vendor_name'}; - - foreach (@{$_->{'product'}->{'product_data'}}) { - print "Product name: ", $_->{'product_name'}, "\n"; - print "Product versions: "; - - foreach (@{$_->{'version'}->{'version_data'}}) { - print $_->{'version_value'}, "; "; - } - - print "\n"; - } - } - } - - print "\n"; - - if ($object->{'description'}->{'description_data'}) { - my $descs = $object->{'description'}->{'description_data'}; - - foreach (@{$descs}) { - print "Description Language: ", $_->{'lang'}, "\n"; - print "Description:\n", $_->{'value'}, "\n\n"; - } - } else { - print "Warning: No CVE description could be found!\n"; - } - - if ($object->{'references'}->{'reference_data'}) { - my $refs = $object->{'references'}->{'reference_data'}; - - foreach (@{$refs}) { - if (defined $options{g}) { - print "Reference Source: ", $_->{'refsource'}, "\n"; - - print "=> ", $_->{'url'} if $_->{'url'}; - if ($_->{'name'}) { - print " ", $_->{'name'}, "\n\n"; - } else { - print "\n\n"; - } - } else { - print "Reference Source: ", $_->{'refsource'}, "\n"; - print "- Name: ", $_->{'name'}, "\n" if $_->{'name'}; - print "- URL: ", $_->{'url'}, "\n" if $_->{'url'}; - print "\n"; - } - } - } else { - print "Warning: No CVE references could be found!\n"; - } -} - sub fetch_cve { my ($cve_id) = @_; my $url = 'https://www.cve.org/api/?action=getCveById&cveId=' . $cve_id; @@ -203,6 +41,8 @@ sub fetch_cve { my $res = $ua->request($req); + my $format = $options{g} ? 'gemini' : 'plain'; + if ($res->is_success) { my $content_type = $res->header("Content-Type"); my $content_match = 'application/json'; @@ -216,7 +56,7 @@ sub fetch_cve { print $pipe_out $res->content; } else { my $object = $json->decode($res->content); - print_cve($object, $cve_id); + print_cve($object, $cve_id, $format); } } else { print "Got $content_type instead of $content_match"; @@ -236,7 +76,3 @@ if ($#ARGV < 0) { for (@ARGV) { fetch_cve($_) if $_ =~ /^CVE-[0-9]{4}-[0-9]{4,19}$/; } -=head1 CVE-Client -App::CVE-Client CLI-based client / toolbox for CVE.org - -Because why would you ever rely on someone else's clobbered together JavaScript code to get important security information. diff --git a/lib/App/CveClient.pm b/lib/App/CveClient.pm @@ -0,0 +1,175 @@ +# CVE-Client: CLI-based client / toolbox for CVE.org +# Copyright © 2021-2023 CVE-Client Authors <https://hacktivis.me/git/cve-client/> +# SPDX-License-Identifier: AGPL-3.0-only +package App::CveClient; + +use warnings; +use strict; + +use Exporter qw(import); + +our @EXPORT_OK = qw(print_cve print_cve50 print_cve40); + +sub print_cve { + my ($object, $cve_id, $format) = @_; + + print "CVE ID: ", $cve_id, "\n"; + + if ($object->{'error'} and $object->{'message'}) { + die "Error ($object->{'error'}): $object->{'message'}\n"; + } + + if ($object->{'dataVersion'} == "5.0") { + print_cve50($object, $cve_id, $format); + } elsif ($object->{'data_version'} == "4.0") { + print_cve40($object, $cve_id, $format); + } else { + print STDERR "Error: unknown CVE format:\n"; + print STDERR "- data_version: ", $object->{'data_version'}, "\n" + if $object->{'data_version'}; + print STDERR "- dataVersion: ", $object->{'dataVersion'}, "\n" + if $object->{'dataVersion'}; + } +} + +# https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/ +sub print_cve50 { + my ($object, $cve_id, $format) = @_; + + if ($object->{'cveMetadata'}->{'cveId'} ne $cve_id) { + print STDERR "Warning: Got <", $object->{'cveMetadata'}->{'cveId'}, + "> instead of <", $cve_id, ">\n"; + } + + my $affected = $object->{'containers'}->{'cna'}->{'affected'}; + if ($affected) { + foreach (@{$affected}) { + print "Vendor Name: ", $_->{'vendor'}, "\n"; # vendor required + print "Product Name: ", $_->{'product'}, "\n"; # product required + + foreach (@{$_->{'versions'}}) { + print "- ", $_->{'status'}, ": ", $_->{'version'}, "\n"; + } + } + } else { + print STDERR +"Warning: No CVE affected versions could be found! (as required by the spec)\n"; + } + + print "\n"; + + my $metrics = $object->{'containers'}->{'cna'}->{'metrics'}; + if ($metrics) { + foreach (@{$metrics}) { + if ($_->{'cvssV3_1'}) { + my $metric = $_->{'cvssV3_1'}; + print "- Score: ", $metric->{'baseScore'}, " ", + $metric->{'baseSeverity'}, "\n"; + } else { + print "Notice: unhandled metrics (CVSS) data\n"; + } + } + } else { + print STDERR +"Warning: No CVE metrics (CVSS) could be found! (as required by the spec)\n"; + } + + print "\n"; + + my $desc = $object->{'containers'}->{'cna'}->{'descriptions'}; + if ($desc) { + foreach (@{$desc}) { + print "Description Language: ", $_->{'lang'}, "\n"; + print "Description:\n", $_->{'value'}, "\n\n"; + } + } else { + print STDERR +"Warning: No CVE description could be found! (as required by the spec)\n"; + } + + print "\n"; + + my $refs = $object->{'containers'}->{'cna'}->{'references'}; + if ($refs) { + print "References: \n"; + + foreach (@{$refs}) { + print "=> ", $_->{'url'}, "\n"; + } + } else { + print STDERR +"Warning: No CVE references could be found! (as required by the spec)\n"; + } +} + +# https://github.com/CVEProject/cve-schema/blob/master/schema/v4.0/ +sub print_cve40 { + my ($object, $cve_id, $format) = @_; + + if ($object->{'CVE_data_meta'}->{'ID'} ne $cve_id) { + print STDERR "Warning: Got ", $object->{'CVE_data_meta'}->{'ID'}, + " instead of ", $cve_id, "\n"; + } + + print "TITLE: ", $object->{'CVE_data_meta'}->{'TITLE'}, "\n" + if $object->{'CVE_data_meta'}->{'TITLE'}; + + print "\n"; + + if ($object->{'affects'}->{'vendor'}) { + foreach (@{$object->{'affects'}->{'vendor'}->{'vendor_data'}}) { + print "Vendor Name: ", $_->{'vendor_name'}, "\n" + if $_->{'vendor_name'}; + + foreach (@{$_->{'product'}->{'product_data'}}) { + print "Product name: ", $_->{'product_name'}, "\n"; + print "Product versions: "; + + foreach (@{$_->{'version'}->{'version_data'}}) { + print $_->{'version_value'}, "; "; + } + + print "\n"; + } + } + } + + print "\n"; + + if ($object->{'description'}->{'description_data'}) { + my $descs = $object->{'description'}->{'description_data'}; + + foreach (@{$descs}) { + print "Description Language: ", $_->{'lang'}, "\n"; + print "Description:\n", $_->{'value'}, "\n\n"; + } + } else { + print STDERR "Warning: No CVE description could be found!\n"; + } + + if ($object->{'references'}->{'reference_data'}) { + my $refs = $object->{'references'}->{'reference_data'}; + + foreach (@{$refs}) { + if ($format == 'gemini') { + print "Reference Source: ", $_->{'refsource'}, "\n"; + + print "=> ", $_->{'url'} if $_->{'url'}; + if ($_->{'name'}) { + print " ", $_->{'name'}, "\n\n"; + } else { + print "\n\n"; + } + } else { + print "Reference Source: ", $_->{'refsource'}, "\n"; + print "- Name: ", $_->{'name'}, "\n" if $_->{'name'}; + print "- URL: ", $_->{'url'}, "\n" if $_->{'url'}; + print "\n"; + } + } + } else { + print STDERR "Warning: No CVE references could be found!\n"; + } +} + +1;