Net-CVE-0.009/0000755000031300001440000000000014607423104012121 5ustar00merijnusersNet-CVE-0.009/t/0000755000031300001440000000000014607423104012364 5ustar00merijnusersNet-CVE-0.009/t/40-lang.t0000644000031300001440000000201614433056620013714 0ustar00merijnusers#!/usr/bin/perl use 5.014002; use warnings; use Test::More; use Test::Warnings; use Net::CVE; my %expect = ( en => { problem => "Probleem 1\nProbleem 2", description => "en-US line 1\nen-US line 2\nen-US line 3", }, es => { problem => "Problema 1\nProblema 2", description => "Hola", }, nl => { problem => "Probleem 1\nProbleem 2", description => "NL regel 1\nNL regel 2", }, xx => { problem => "Probleem 1\nProbleem 2", description => "en-US line 1\nen-US line 2\nen-US line 3", }, ); foreach my $lang (sort keys %expect) { ok (my $cr = Net::CVE->new (lang => $lang), "$lang New reporter"); ok ($cr->get ("Files/CVE-1970-1.json"), "$lang Read stored report"); ok (my $r = $cr->summary, "$lang Generate summary"); is ($r->{problem}, $expect{$lang}{problem}, "$lang Problem"); is ($r->{description}, $expect{$lang}{description}, "$lang Description"); } done_testing; Net-CVE-0.009/t/25-data.t0000644000031300001440000000172714605000336013711 0ustar00merijnusers#!/usr/bin/perl use 5.014002; use warnings; use Test::More; use Test::Warnings; use Net::CVE; my $jf = "Files/CVE-2022-26928.json"; # https://www.cpantesters.org/cpan/report/4ec2920e-7174-11ee-98b0-b3c3213a625c # # Failed test 'Data direct' # # at t/30-get.t line 32. # # Structures begin differing at: # # $got->{containers} = Does not exist # # $expected->{containers} = HASH(0x7f86ff8f1888) # # Looks like you failed 1 test of 16. # t/30-get.t ...... # Dubious, test returned 1 (wstat 256, 0x100) # Failed 1/16 subtests sub chk_data { my ($msg, $r) = @_; ok ($r, "$msg Has value"); is (ref $r, "HASH", "$msg Data type"); is ($r->{dataType}, "CVE_RECORD", "$msg Content"); } # chk_data ok (my $cr = Net::CVE->new, "New reporter"); ok ($cr->get ($jf), "Read stored report"); chk_data ("new/get/data", $cr->data); chk_data ("get/data", Net::CVE->get ($jf)->data); chk_data ("data", Net::CVE->data ($jf)); done_testing; Net-CVE-0.009/t/10-base.t0000644000031300001440000000127614433115062013705 0ustar00merijnusers#!/usr/bin/perl use 5.014002; use warnings; use Test::More; use Test::Warnings; use Net::CVE; my $v = $Net::CVE::VERSION or BAIL_OUT ("Net::CVE does not return a VERSION"); ok ($v, "Net::CVE-$v"); ok (my $cr = Net::CVE->new, "New reporter"); isa_ok ($cr, "Net::CVE", "Of class Net::CVE"); can_ok ($cr, qw( VERSION Version new get data summary status platforms vendor product )); is (Net::CVE->Version, $v, "Version"); is (Net::CVE->VERSION, $v, "VERSION"); ok (Net::CVE->new, "new"); ok (Net::CVE->new (), "new ()"); ok (Net::CVE->new ( lang => "nl" ), "new ( lang => nl )"); ok (Net::CVE->new ({ lang => "nl" }), "new ({ lang => nl })"); done_testing; Net-CVE-0.009/t/30-get.t0000644000031300001440000000201014512556425013551 0ustar00merijnusers#!/usr/bin/perl use 5.014002; use warnings; use Test::More; use Test::Warnings; use Net::CVE; if ($ENV{NO_NETWORK_TESTING}) { print "1..0 # SKIP Live tests disabled due to NO_NETWORK_TESTING\n"; exit 0; } ok (my $c1 = Net::CVE->new, "New reporter"); ok ($c1->get ("CVE-2022-26928"), "Read report with prefix"); ok (my $d1 = $c1->data, "Generate data"); ok (my $s1 = $c1->summary, "Generate summary"); ok (my $c2 = Net::CVE->new, "New reporter"); ok ($c2->get ("2022-26928"), "Read report without prefix"); ok (my $d2 = $c2->data, "Generate data"); ok (my $s2 = $c2->summary, "Generate summary"); isnt ($d1, $d2, "Not the same data"); isnt ($s1, $s2, "Not the same structure"); is_deeply ($d1, $d2, "Same data content"); is_deeply ($s1, $s2, "Same summary content"); is_deeply (Net::CVE->new->data ("CVE-2022-26928"), $d1, "Data direct"); is_deeply (Net::CVE->new->summary ("2022-26928"), $s1, "Summary direct"); is_deeply (Net::CVE->new->get ("")->data, {}, "Empty fetch"); done_testing; Net-CVE-0.009/t/20-summary.t0000644000031300001440000000245714433116353014477 0ustar00merijnusers#!/usr/bin/perl use 5.014002; use warnings; use Test::More; use Test::Warnings; use Net::CVE; my $jf = "Files/CVE-2022-26928.json"; ok (my $cr = Net::CVE->new, "New reporter"); ok ($cr->get ($jf), "Read stored report"); ok (my $r = $cr->summary, "Generate summary"); my $expect = { date => "2022-09-13T18:41:25", description => "Windows Photo Import API Elevation of Privilege Vulnerability", id => "CVE-2022-26928", platforms => [ "32-bit Systems", "ARM64-based Systems", "x64-based Systems" ], problem => "Elevation of Privilege", product => [ "Windows 10 Version 1507", "Windows 10 Version 1607", "Windows 10 Version 1809", "Windows 10 Version 20H2", "Windows 10 Version 21H1", "Windows 10 Version 21H2", "Windows 11 version 21H2", "Windows Server 2016", "Windows Server 2019", "Windows Server 2022" ], score => "7", severity => "high", status => "PUBLISHED", vendor => [ "Microsoft" ], }; is_deeply ($r, $expect, "Report returned a summary"); is_deeply (Net::CVE->get ($jf)->summary, $expect, "->get->summary (file)"); is_deeply (Net::CVE->summary ($jf), $expect, "->summary (file)"); done_testing; Net-CVE-0.009/t/90-diag.t0000644000031300001440000000410114437627744013720 0ustar00merijnusers#!/usr/bin/perl use 5.014002; use warnings; use Test::More; use Test::Warnings; use Net::CVE; my $bad = "XYZ-2-BAZ"; my @w; local $SIG{__WARN__} = sub { push @w => @_ }; my $r = Net::CVE->new->get ($bad); is_deeply ($r->data, {}, "Bad CVE"); is_deeply ($r->diag, { status => -1, reason => "Invalid CVE format: '$bad'", action => "get", source => "tag", usage => 'get ("CVE-2022-26928")', }, "Got diagnostics"); # TODO: autodiag #is (scalar @w, 1, "Got warning"); #is ($w[0], "Invalid CVE format: '$bad' - expected format CVE-2023-12345\n", "Error"); ok ($r->get ($0), "Get non-JSON"); ok (my $d = $r->diag, "Get diagnostics"); is ($d->{status}, -2, "Status"); is ($d->{action}, "decode_json", "Action decode_json"); like ($d->{reason}, qr{malformed JSON}, "Error"); my $tf = "cve-1234-5678.json"; unlink $tf; if (open my $fh, ">", $tf) { say $fh "Invalid"; close $fh; ok ($r->get ($tf), "Get non-JSON 2"); ok ($d = $r->diag, "Get diagnostics"); is ($d->{action}, "decode_json", "Action decode_json"); is ($d->{source}, $tf, "Source"); is ($d->{status}, -2, "Status"); like ($d->{reason}, qr{malformed JSON}, "Error"); if ($> && $^O eq "linux") { # Useless test for root chmod 006, $tf; $r->get ($tf); ok ($r->get ($tf), "Get unreadable"); ok ($d = $r->diag, "Get diagnostics"); is ($d->{action}, "get", "Action get"); is ($d->{source}, $tf, "Source"); is ($d->{status}, 13, "Status"); like ($d->{reason}, qr{denied}, "Error"); } } unlink $tf; # Force a bad URL $r->{url} = "https://foo.bar.cve.google.org/wibletrog/ipa$$/cve"; $r->get ("2021-12232"); # Number doesn't matter ok ($d = $r->diag, "Diag on a bad URL"); is ($d->{action}, "get", "Action get"); is ($d->{source}, "$r->{url}/CVE-2021-12232", "Source"); is ($d->{status}, 599, "Status"); like ($d->{reason}, qr{^(?:Internal \s+ Exception:\s+ Could \s+ not \s+ connect |Internal \s+ Exception:\s+ SSL \s+ connection\s+failed |Bad \s+ Gateway)}ix, "Error"); done_testing; Net-CVE-0.009/Files/0000755000031300001440000000000014607423104013163 5ustar00merijnusersNet-CVE-0.009/Files/CVE-1970-1.json0000644000031300001440000000413414433056465015242 0ustar00merijnusers{ "containers" : { "cna" : { "affected" : [ { "platforms" : [ "x64-based Systems" ], "product" : "Windows 10 Version 1809", "vendor" : "Microsoft", "versions" : [ { "lessThan" : "10.0.17763.3406", "status" : "affected", "version" : "10.0.0", "versionType" : "custom" } ] } ], "datePublic" : "2022-09-13T07:00:00+00:00", "descriptions" : [ { "lang" : "en-US", "value" : "en-US line 1" }, { "lang" : "nl", "value" : "NL regel 1" }, { "lang" : "es", "value" : "Hola" }, { "lang" : "en-US", "value" : "en-US line 2" }, { "lang" : "en-US", "value" : "en-US line 3" }, { "lang" : "nl", "value" : "NL regel 2" } ], "metrics" : [ { "cvssV3_1" : { "baseScore" : 7, "baseSeverity" : "HIGH" }, "format" : "CVSS", "scenarios" : [ { "lang" : "en-US", "value" : "GENERAL" } ] } ], "problemTypes" : [ { "descriptions" : [ { "description" : "Probleem 1", "lang" : "NL", "type" : "Impact" }, { "description" : "Problema 1", "lang" : "es", "type" : "Impact" }, { "description" : "Problema 2", "lang" : "es", "type" : "Impact" }, { "description" : "Probleem 2", "lang" : "NL", "type" : "Impact" } ] } ], "title" : "Windows Photo Import API Elevation of Privilege Vulnerability" } }, "cveMetadata" : { "assignerShortName" : "microsoft", "cveId" : "CVE-2022-26928", "datePublished" : "2022-09-13T18:41:25", "state" : "PUBLISHED" }, "dataType" : "CVE_RECORD", "dataVersion" : "5.0" } Net-CVE-0.009/Files/CVE-2022-26928.json0000644000031300001440000001200614431662021015543 0ustar00merijnusers{"containers":{"cna":{"title":"Windows Photo Import API Elevation of Privilege Vulnerability","datePublic":"2022-09-13T07:00:00+00:00","affected":[{"vendor":"Microsoft","product":"Windows 10 Version 1809","cpes":["cpe:2.3:o:microsoft:windows_10_1809:10.0.17763.3406:*:*:*:*:*:x86:*","cpe:2.3:o:microsoft:windows_10_1809:10.0.17763.3406:*:*:*:*:*:x64:*","cpe:2.3:o:microsoft:windows_10_1809:10.0.17763.3406:*:*:*:*:*:arm64:*"],"platforms":["32-bit Systems","x64-based Systems","ARM64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.17763.3406","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows Server 2019","cpes":["cpe:2.3:o:microsoft:windows_server_2019:10.0.17763.3406:*:*:*:*:*:*:*"],"platforms":["x64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.17763.3406","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows 10 Version 21H1","cpes":["cpe:2.3:o:microsoft:windows_10_21H1:10.0.19043.2006:*:*:*:*:*:x64:*","cpe:2.3:o:microsoft:windows_10_21H1:10.0.19043.2006:*:*:*:*:*:arm64:*","cpe:2.3:o:microsoft:windows_10_21H1:10.0.19043.2006:*:*:*:*:*:x86:*"],"platforms":["x64-based Systems","ARM64-based Systems","32-bit Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.19043.2006","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows Server 2022","cpes":["cpe:2.3:o:microsoft:windows_server_2022:10.0.20348.1006:*:*:*:*:*:*:*","cpe:2.3:o:microsoft:windows_server_2022:10.0.20348.916:*:*:*:*:*:*:*"],"platforms":["x64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.20348.1006","versionType":"custom","status":"affected"},{"version":"10.0.0","lessThan":"10.0.20348.916","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows 10 Version 20H2","cpes":["cpe:2.3:o:microsoft:windows_10_20H2:10.0.19042.2006:*:*:*:*:*:x64:*","cpe:2.3:o:microsoft:windows_10_20H2:10.0.19042.2006:*:*:*:*:*:x86:*","cpe:2.3:o:microsoft:windows_10_20H2:10.0.19042.2006:*:*:*:*:*:arm64:*"],"platforms":["x64-based Systems","32-bit Systems","ARM64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.19042.2006","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows 11 version 21H2","cpes":["cpe:2.3:o:microsoft:windows_11_21H2:10.0.22000.978:*:*:*:*:*:x64:*","cpe:2.3:o:microsoft:windows_11_21H2:10.0.22000.978:*:*:*:*:*:arm64:*"],"platforms":["x64-based Systems","ARM64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.22000.978","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows 10 Version 21H2","cpes":["cpe:2.3:o:microsoft:windows_10_21H2:10.0.19044.2006:*:*:*:*:*:x86:*","cpe:2.3:o:microsoft:windows_10_21H2:10.0.19044.2006:*:*:*:*:*:arm64:*","cpe:2.3:o:microsoft:windows_10_21H2:10.0.19044.2006:*:*:*:*:*:x64:*"],"platforms":["32-bit Systems","ARM64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.19044.2006","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows 10 Version 1507","cpes":["cpe:2.3:o:microsoft:windows_10_1507:10.0.10240.19444:*:*:*:*:*:x86:*","cpe:2.3:o:microsoft:windows_10_1507:10.0.10240.19444:*:*:*:*:*:x64:*"],"platforms":["32-bit Systems","x64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.10240.19444","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows 10 Version 1607","cpes":["cpe:2.3:o:microsoft:windows_10_1607:10.0.14393.5356:*:*:*:*:*:x86:*","cpe:2.3:o:microsoft:windows_10_1607:10.0.14393.5356:*:*:*:*:*:x64:*"],"platforms":["32-bit Systems","x64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.14393.5356","versionType":"custom","status":"affected"}]},{"vendor":"Microsoft","product":"Windows Server 2016","cpes":["cpe:2.3:o:microsoft:windows_server_2016:10.0.14393.5356:*:*:*:*:*:*:*"],"platforms":["x64-based Systems"],"versions":[{"version":"10.0.0","lessThan":"10.0.14393.5356","versionType":"custom","status":"affected"}]}],"descriptions":[{"value":"Windows Photo Import API Elevation of Privilege Vulnerability","lang":"en-US"}],"problemTypes":[{"descriptions":[{"description":"Elevation of Privilege","lang":"en-US","type":"Impact"}]}],"providerMetadata":{"orgId":"f38d906d-7342-40ea-92c1-6c4a2c6478c8","shortName":"microsoft","dateUpdated":"2023-04-11T19:11:48.537Z"},"references":[{"name":"Windows Photo Import API Elevation of Privilege Vulnerability","tags":["vendor-advisory"],"url":"https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-26928"}],"metrics":[{"format":"CVSS","scenarios":[{"lang":"en-US","value":"GENERAL"}],"cvssV3_1":{"version":"3.1","baseSeverity":"HIGH","baseScore":7,"vectorString":"CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H/E:U/RL:O/RC:C"}}]}},"cveMetadata":{"assignerOrgId":"f38d906d-7342-40ea-92c1-6c4a2c6478c8","assignerShortName":"microsoft","cveId":"CVE-2022-26928","datePublished":"2022-09-13T18:41:25","dateReserved":"2022-03-11T00:00:00","dateUpdated":"2023-04-11T19:11:48.537Z","state":"PUBLISHED"},"dataType":"CVE_RECORD","dataVersion":"5.0"}Net-CVE-0.009/lib/0000755000031300001440000000000014607423104012667 5ustar00merijnusersNet-CVE-0.009/lib/Net/0000755000031300001440000000000014607423104013415 5ustar00merijnusersNet-CVE-0.009/lib/Net/CVE.pm0000755000031300001440000003354414607422614014411 0ustar00merijnusers#!/usr/bin/perl package Net::CVE; use 5.014002; use warnings; our $VERSION = "0.009"; use Carp; use HTTP::Tiny; use JSON::MaybeXS; use List::Util qw( first ); # https://cveawg.mitre.org/api/cve/CVE-2022-26928 # But that is likely to change to cve.org sub Version { $VERSION } sub new { my $class = shift; my %r = ( url => "https://cveawg.mitre.org/api/cve", ua => undef, lang => "en", data => {}, diag => undef, ); if (@_) { if (@_ == 1 && ref $_[0] eq "HASH") { $r{$_} = $_[0]{$_} for keys %{$_[0]}; } elsif (@_ == 2) { my %p = @_; $r{$_} = $p{$_} for keys %p; } } bless \%r => $class; } # new sub diag { my $self = shift or return; ref $self or return; my $d = $self->{diag} or return; unless (defined wantarray) { # void context my $act = $d->{action}; warn "$act: ", (join " " => grep { length } $d->{status}, $d->{reason}), "\n"; $act =~ s/./ /g; warn "$act source = $d->{source}\n" if $d->{source}; warn "$act usage: $d->{usage}\n" if $d->{usage}; } return $d; } # diag sub get { my ($self, $cve) = @_; ref $self or $self = __PACKAGE__->new (); $self->{data} = {}; $self->{diag} = undef; $cve or return $self; $cve =~ s/^(?=[0-9])/CVE-/; if ($cve =~ m/^CVE-[0-9]{4}-([0-9]+)$/) { $self->{ua} //= HTTP::Tiny->new (); my $url = join "/" => $self->{url}, $cve; my $r = $self->{ua}->get ($url); unless ($r->{success}) { # if pseudo-HTTP status code 599 and reason "Internal Exception" # the content field will contain the text of the error my $status = $r->{status}; my $reason = join ": " => grep { length } $r->{reason}, $status =~ m/^5[0-9][0-9]$/ ? $r->{content} : ""; $self->{diag} = { status => $status, reason => $reason, action => "get", source => $url, usage => undef, }; return $self; } $self->{data} = decode_json ($r->{content}); } elsif (-s $cve) { my $fh; unless (open $fh, "<:encoding(utf-8)", $cve) { $self->{diag} = { status => 0 + $!, reason => "$!", action => "get", source => $cve, usage => 'get ("cve-2022-26928.json")', }; return $self; } unless (eval { $self->{data} = decode_json (do { local $/; <$fh> }); 1 }) { $self->{diag} = { status => -2, reason => $@ =~ s{\s+at\s+\S+\s+line\s+\d+.*}{}rs, action => "decode_json", source => $cve, usage => undef, }; return $self; } close $fh; } else { #warn "Invalid CVE format: '$cve' - expected format CVE-2023-12345\n"; $self->{diag} = { status => -1, reason => "Invalid CVE format: '$cve'", action => "get", source => "tag", usage => 'get ("CVE-2022-26928")', }; return $self; } $self; } # get sub data { my $self = shift; ref $self or $self = __PACKAGE__->new (); @_ and $self->get (@_); $self->{data}; } # data sub summary { my $self = shift; ref $self or $self = __PACKAGE__->new (); @_ and $self->get (@_); my $j = $self->{data} or croak "summary only available after get"; my $cna = $j->{containers}{cna} or return +{}; #y $weak = ... weaknesses [{ description [{ lang => "en", value => "CWE-123"}] my $severity = join ", " => grep { length } map { $_->{cvssV3_1}{baseSeverity} } @{$cna->{metrics} || []}; my $score = join ", " => grep { length } map { $_->{cvssV3_1}{baseScore} } @{$cna->{metrics} || []}; my %desc; for (@{$cna->{descriptions} || []}) { my $d = $_->{value} or next; push @{$desc{$_->{lang}}} => $d; } my @lang = sort keys %desc; my $lang = first { m/\b $self->{lang} /ix } @lang; $lang //= first { m/\b en /ix } @lang; $lang //= $lang[0]; my $desc = join "\n" => @{$desc{$lang}}; my %problem; for (map { @{$_->{descriptions} || []} } @{$cna->{problemTypes} || []}) { my $d = $_->{description} or next; push @{$problem{$_->{lang}}} => $d; } @lang = sort keys %problem; $lang = first { m/\b $self->{lang} /ix } @lang; $lang //= first { m/\b en /ix } @lang; $lang //= $lang[0]; my $problem = defined $lang ? join "\n" => @{$problem{$lang}} : ""; { id => $j->{cveMetadata}{cveId}, date => $j->{cveMetadata}{datePublished}, description => $desc, severity => lc $severity, score => $score, problem => $problem, product => [ $self->product ], vendor => [ $self->vendor ], platforms => [ $self->platforms ], status => $self->status, }; } # summary sub status { my $self = shift; @_ and $self->get (@_); my $j = $self->{data} or croak "status only available after get"; $j->{cveMetadata}{state}; } # status sub _affected_tag { my ($self, $tag) = @_; my $j = $self->{data} or croak "$tag only available after get"; my $cna = $j->{containers}{cna} or return; my %v = map { $_->{$tag} => 1 } @{$cna->{affected} || []}; my @v = sort keys %v; return wantarray ? @v : join ", " => @v; } # _affected_tag sub _affected_tag_a { my ($self, $tag) = @_; my $j = $self->{data} or croak "$tag only available after get"; my $cna = $j->{containers}{cna} or return; my %v = map { $_ => 1 } map { @{$_ || []} } map { $_->{$tag} } @{$cna->{affected} || []}; my @v = sort keys %v; return wantarray ? @v : join ", " => @v; } # _affected_tag_a sub platforms { my $self = shift; @_ and $self->get (@_); $self->_affected_tag_a ("platforms"); } # platforms sub vendor { my $self = shift; @_ and $self->get (@_); $self->_affected_tag ("vendor"); } # vendor sub product { my $self = shift; @_ and $self->get (@_); $self->_affected_tag ("product"); } # vendor 1; __END__ =head1 NAME Net::CVE - Fetch CVE (Common Vulnerabilities and Exposures) information from cve.org =head1 SYNOPSIS use Net::CVE; my $cr = Net::CVE->new (); $cr->get ("CVE-2022-26928"); my $full_report = $cr->data; my $summary = $cr->summary; $cr->diag; use Data::Peek; DDumper $cr->summary ("CVE-2022-26928"); =head1 DESCRIPTION This module provides a Perl interface to retrieve information from the L provided by L based on a CVE tag. =head1 METHODS =head2 new my $reporter = CVE->new ( url => "https://cveawg.mitre.org/api/cve", ua => undef, lang => "en", ); Instantiates a new object. All attributes are optional. =over 2 =item url Base url for REST API =item ua User agent. Needs to know about C<< ->get >>. Defaults to L. Initialized on first use. my $reporter = CVE->new (ua => HTTP::Tiny->new); Other agents not yet tested, so they might fail. =item lang Set preferred language for L. Defaults to C. If the preferred language is present in descriptions use that. If it is not, use C. If that is also not present, use the first language found. =back =head2 get $reporter->get ("CVE-2022-26928"); $reporter->get ("2022-26928"); $reporter->get ("Files/CVE-2022-26928.json"); Fetches the CVE data for a given tag. On success stores the results internally. Returns the object. The leading C is optional. If the argument is a non-empty file, that is parsed instead of fetching the information from the internet. The decoded information is stored internally and will be re-used for other methods. C returns the object and allows to omit a call to C which will be implicit but does not allow attributes my $reporter = Net::CVE->get ("2022-26928"); is a shortcut to my $reporter = Net::CVE->new->get ("2022-26928"); =head2 data my $info = $reporter->data; Returns the data structure from the last successful fetch, C if none. Giving an argument enables you to skip the L call, which is implicit, so my $info = $reporter->data ("CVE-2022-26928"); is identical to my $info = $reporter->get ("CVE-2022-26928")->data; or $reporter->get ("CVE-2022-26928"); my $info = $reporter->data; or even, without an object my $info = Net::CVE->data ("CVE-2022-26928"); =head2 summary my $info = $reporter->summary; my $info = $reporter->summary ("CVE-2022-26928"); Returns a hashref with basic information from the last successful fetch, C if none. Giving an argument enables you to skip the L call, which is implicit, so my $info = $reporter->summary ("CVE-2022-26928"); is identical to my $info = $reporter->get ("CVE-2022-26928")->summary; or $reporter->get ("CVE-2022-26928"); my $info = $reporter->summary; or even, without an object my $info = Net::CVE->summary ("CVE-2022-26928"); The returned hash looks somewhat like this { date => "2022-09-13T18:41:25", description => "Windows Photo Import API Elevation of Privilege Vulnerability", id => "CVE-2022-26928", problem => "Elevation of Privilege", score => "7", severity => "high", status => "PUBLISHED", vendor => [ "Microsoft" ] platforms => [ "32-bit Systems", "ARM64-based Systems", "x64-based Systems", ], product => [ "Windows 10 Version 1507", "Windows 10 Version 1607", "Windows 10 Version 1809", "Windows 10 Version 20H2", "Windows 10 Version 21H1", "Windows 10 Version 21H2", "Windows 11 version 21H2", "Windows Server 2016", "Windows Server 2019", "Windows Server 2022", ], } As this is work in progress, likely to be changed =head2 status my $status = $reporter->status; Returns the status of the CVE, most likely C. =head2 vendor my @vendor = $reporter->vendor; my $vendors = $reporter->vendor; Returns the list of vendors for the affected parts of the CVE. In scalar context a string where the (sorted) list of unique vendors is joined by C<, > in list context the (sorted) list itself. =head2 product my @product = $reporter->product; my $products = $reporter->product; Returns the list of products for the affected parts of the CVE. In scalar context a string where the (sorted) list of unique products is joined by C<, > in list context the (sorted) list itself. =head2 platforms my @platform = $reporter->platforms; my $platforms = $reporter->platforms; Returns the list of platforms for the affected parts of the CVE. In scalar context a string where the (sorted) list of unique platforms is joined by C<, > in list context the (sorted) list itself. =head2 diag $reporter->diag; my $diag = $reporter->diag; If an error occurred, returns information about the error. In void context prints the diagnostics using C. The diagnostics - if any - will be returned in a hashref with the following fields: =over 2 =item status Status code =item reason Failure reason =item action Tag of where the failure occurred =item source The URL or filename leading to the failure =item usage Help message =back Only the C field is guaranteed to be set, all others are optional. =head1 BUGS None so far =head1 TODO =over 2 =item Better error reporting Obviously =item Tests There are none yet =item Meta-stuff Readme, Changelog, Makefile.PL, ... =item Fallback to Net::NVD Optionally. It does not (yet) provide vendor, product and platforms. It however provides nice search capabilities. =item RHSA support Extend to return results for C type vulnerability tags. https://access.redhat.com/errata/RHSA-2023:1791 https://access.redhat.com/hydra/rest/securitydata/crf/RHSA-2023:1791.json The CRF API provides the list of CVE's related to this tag: my $url = "https://access.redhat.com/hydra/rest/securitydata/crf"; my $crf = decode_json ($ua->get ("$url/RHSA-2023:1791.json")); my @cve = map { $_->{cve} } @{$crf->{cvrfdoc}{vulnerability} || []} Will set C<@cve> to qw( CVE-2023-1945 CVE-2023-1999 CVE-2023-29533 CVE-2023-29535 CVE-2023-29536 CVE-2023-29539 CVE-2023-29541 CVE-2023-29548 CVE-2023-29550 ); See L. =back =head1 SEE ALSO =over 2 =item CVE search L and L =item L Returns OpenSource Vulnerabilities. =item CVE database L =back =head1 AUTHOR H.Merijn Brand =head1 COPYRIGHT AND LICENSE Copyright (C) 2023-2024 H.Merijn Brand This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. This interface uses data from the CVE API but is not endorsed by any of the CVE partners. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Net-CVE-0.009/scripts/0000755000031300001440000000000014607423104013610 5ustar00merijnusersNet-CVE-0.009/scripts/cve.pl0000755000031300001440000001012614605500634014727 0ustar00merijnusers#!/pro/bin/perl use 5.014002; use warnings; our $VERSION = "0.04 - 20240408"; our $CMD = $0 =~ s{.*/}{}r; sub usage { my $err = shift and select STDERR; say "usage: $CMD [-f] [-d] CVE ..."; say " -f --full Return full report (default: summary)"; say " -d --dump Full data dump of JSON structure"; say " Will use Data::Peek if available,"; say " otherwise Data::Dumper"; say " -j --json Dump as json"; say " -J --json-pp Dump as json (formatted)"; say " -Q --json-jq Dump as json (formatted by jq)"; say " -c --csv Dump as CSV (NYI)"; exit $err; } # usage use Net::CVE; use Data::Peek; use Data::Dumper; use JSON::MaybeXS; use List::Util qw( first ); use Getopt::Long qw(:config bundling); GetOptions ( "help|?" => sub { usage (0); }, "V|version" => sub { say "$CMD [$VERSION]"; exit 0; }, "f|full!" => \ my $opt_f, "d|dump!" => \ my $opt_d, "j|json!" => \ my $opt_j, "J|json-pp!" => \ my $opt_J, "Q|json-jq|jq!" => \ my $opt_Q, "v|verbose:1" => \(my $opt_v = 0), ) or usage (1); my $cr = Net::CVE->new; my %cve; first { $_ !~ m/^(?:cve-)?[0-9]{4}-[0-9]+$/i } @ARGV and die "Not all CVE's are of acceptable formate CVE-9999-99999\n"; $cve{$_} = $opt_f ? $cr->data ($_) : $cr->summary ($_) for @ARGV; if ($opt_d) { eval "local \$_; require Data::Peek;"; if ($@) { print STDERR Data::Dumper->Dump (\%cve); } else { print STDERR Data::Peek::DDumper (\%cve); } exit 0; } if ($opt_j || $opt_J || $opt_Q) { $opt_Q and open STDOUT, "|-", "jq"; if ($opt_J) { print JSON::MaybeXS->new (utf8 => 1, pretty => 1)->encode (\%cve); } else { say encode_json (\%cve); } exit 0; } my @cve = sort keys %cve; foreach my $cve (@cve) { my $r = $cve{$cve}; if ($opt_f) { say $cve; say " State : ", $r->{cveMetadata}{state}; say " Published : ", $r->{cveMetadata}{datePublished}; my $cc = $r->{containers}{cna} or next; say " Title : ", $cc->{title} // "-none-"; say " Public : ", $cc->{datePublic} // "-unknown-"; if (my $md = $cc->{providerMetadata}) { printf " Provider : %s:%s\n", $md->{shortName}, $md->{orgId}; } my $lead = " " x 19; foreach my $rd (@{$cc->{descriptions} || []}) { printf "%16s : %s\n", $rd->{lang}, $rd->{value} =~ s/\n\K/$lead/gr; } foreach my $rr (@{$cc->{references} || []}) { my $trim = "References"; for (grep { length } $rr->{name}, (join ", " => @{$rr->{tags} || []}), $rr->{url}) { printf " %-15s : %s\n", $trim, $_; $trim = ""; } } foreach my $pt (@{$cc->{problemTypes} || []}) { foreach my $ptd (@{$pt->{descriptions} || []}) { printf " Problem %7s : %-12s %s\n", $ptd->{lang}, $ptd->{type}, $ptd->{description}; } } foreach my $af (@{$cc->{affected} || []}) { ($af->{vendor} || "n/a") eq "n/a" && ($af->{product} || "n/a") eq "n/a" and next; say " Affected : ", $af->{vendor}, " : ", $af->{product}; if (my $p = $af->{platforms}) { say " Platforms : ", join ", " => @$p; } foreach my $v (@{$af->{versions} || []}) { my ($vs, $vv, $vvt) = delete @{$v}{qw( status version versionType )}; $vv && $vv eq "n/a" and $vvt ||= "n/a"; $vs && $vs eq "affected" && $vvt eq "n/a" and next; printf " Versions : %-12s %s (%s)\n", $vs, $vv, $vvt; foreach my $vc (sort keys %$v) { printf "%16s : %12s %s\n", "", $vc, $v->{$vc}; } } say " CPE : ", $_ for @{$af->{cpes} || []}; } foreach my $m (@{$cc->{metrics} || []}) { say " Metric : ", $m->{format}; if (my $v31 = $m->{cvssV3_1}) { printf " Severity %5s : %3d:%-8s %s\n", $v31->{version}, $v31->{baseScore}, $v31->{baseSeverity}, $v31->{vectorString}; } foreach my $s (@{$m->{scenarios} || []}) { printf " Scenario%7s : %s\n", $s->{lang}, $s->{value}; } } next; } # Print summary printf "%-20s %-9s %4s %-25.25s %s\n", map { $_ // "" } @{$r}{qw( id severity score problem description )}; } Net-CVE-0.009/Makefile.PL0000644000031300001440000000535614607423037014111 0ustar00merijnusersuse strict; require 5.014002; use ExtUtils::MakeMaker; use File::Copy; sub link_or_copy { my ($source, $dest) = @_; link ($source, $dest) or copy ($source, $dest); } # link_or_copy my @exe; unless (exists $ENV{AUTOMATED_TESTING} and $ENV{AUTOMATED_TESTING} == 1) { for ( [ "cve.pl", "CLI to Net::CVE" ], ) { prompt ("Do you want to install $_->[0]\t$_->[1] ?", "y") =~ m/[Yy]/ and push @exe => "scripts/$_->[0]"; } } my %wm = ( NAME => "Net::CVE", DISTNAME => "Net-CVE", ABSTRACT => "Fetch CVE info from cve.org", AUTHOR => "H.Merijn Brand ", VERSION_FROM => "lib/Net/CVE.pm", LICENSE => "perl", EXE_FILES => [ @exe ], PREREQ_FATAL => 0, PREREQ_PM => { # Core modules "Carp" => 0, "HTTP::Tiny" => 0.025, "JSON::MaybeXS" => 1.004005, "List::Util" => 0, # For https "IO::Socket::SSL" => 1.42, # For testing "Test::More" => 0.90, "Test::Warnings" => 0, }, macro => { TARFLAGS => "--format=ustar -c -v -f", }, ); if ($ENV{EXTENDED_TESTING}) { # for CpanCover and masochists # Backend parsers, all optional #$wm{PREREQ_PM}{"Net::OSV"} = "0.0.1"; #$wm{PREREQ_PM}{"Net::NSD"} = "0.0.1"; } my $rv = WriteMakefile (%wm); # perlcriticrc uses Config::Tiny, which does not support nesting if (-f ".perlcriticrc" && -s "$ENV{HOME}/.perlcriticrc") { open my $fh, ">", ".perlcriticrc"; print $fh do { local (@ARGV, $/) = ("$ENV{HOME}/.perlcriticrc"); <> =~ s{^hard_max = \K\d+$}{150}rm }; print $fh join "\n" => "", "[-Community::EmptyReturn]", # "[-Community::Wantarray]", # ""; close $fh; } package MY; sub postamble { my @pc = -f ".perlcriticrc" ? ("\tperlcritic -1 lib/Net/CVE.pm") : (); -d "xt" && ($ENV{AUTOMATED_TESTING} || 0) != 1 and push @pc, '', 'test::', ' -@env TEST_FILES="xt/*.t" make -e test_dynamic'; join "\n" => 'cover test_cover:', ' ccache -C', ' cover -test', '', 'spellcheck:', ' pod-spell-check --aspell --ispell lib', '', 'checkmeta: spellcheck', ' perl sandbox/genMETA.pl -c', '', 'fixmeta: distmeta', ' perl sandbox/genMETA.pl', ' ls -l */META.yml', '', 'tgzdist: checkmeta fixmeta $(DISTVNAME).tar.gz distcheck', ' -@mv -f $(DISTVNAME).tar.gz $(DISTVNAME).tgz', ' -@cpants_lint.pl $(DISTVNAME).tgz', ' -@rm -f Debian_CPANTS.txt', '', 'doc docs: doc/Net-CVE.md doc/Net-CVE.html doc/Net-CVE.man', ' -@rm -f pod2html.tmp', 'doc/Net-CVE.md: lib/Net/CVE.pm', ' pod2markdown < $? > $@', 'doc/Net-CVE.html: lib/Net/CVE.pm', ' pod2html < $? 2>&1 | grep -v "^Cannot find" > $@', 'doc/Net-CVE.3: lib/Net/CVE.pm', ' pod2man < $? > $@', 'doc/Net-CVE.man: doc/Net-CVE.3', ' nroff2man < $? > $@', @pc, ''; } # postamble 1; Net-CVE-0.009/ChangeLog0000644000031300001440000000227114607422530013677 0ustar00merijnusers0.009 - 2024-04-16, H.Merijn Brand * Require HTTP::Tiny 0.025 0.008 - 2024-04-08, H.Merijn Brand * Prevent warning if report is incomplete * It is 2024 * Skip version lines in reports from cve.pl with only n/a * Prefer Data::Peek over Data::Dumper on cve.pl -d (no hard dependency) 0.007 - 2023-10-14, H.Merijn Brand * Transfered authority to CPAN-Security * Update docs for groff-1.24 * Better SKIP in xt (pr#10, Haelwenn) * Skip NETWORK tests when NO_NETWORK_TESTING (pr#10 Haelwenn) * More output formats for cve.pl (and text output for -f) 0.006 - 2023-06-06, H.Merijn Brand * More possible known fails for SSL fails in diag.t 0.005 - 2023-06-05, H.Merijn Brand * Verify certificates when using HTTP::Tiny (stigtsp, issue#9) 0.004 - 2023-05-31, H.Merijn Brand * Skip unreadable test for root * Require Net::Socket::SSL 1.42 for https 0.003 - 2023-05-24, H.Merijn Brand * Shortcuts * Use existing CVE in SYNOPSIS (szabgab, issue#1) * GitHub Actions (szabgab, issue#2) * Add ->diag 0.002 - 2023-05-23, H.Merijn Brand * Tests * Consistent summary for selected lang 0.001 - 2023-05-22, H.Merijn Brand * First release Net-CVE-0.009/README.md0000644000031300001440000000226714446502214013410 0ustar00merijnusers## Net::CVE Fetch CVE (Common Vulnerabilities and Exposures) information from cve.org ```perl use Net::CVE; my $cr = Net::CVE->new (); $cr->get ("CVE-2022-12345"); my $full_report = $cr->data; my $summary = $cr->summary; use Data::Peek; DDumper $cr->summary ("CVE-2022-12345"); ``` ### Prerequisites perl version 5.14.2 and up. Very well possible, you will be able to use this with a previous version, but that is not officially supported. Network access: the default operation mode is to fetch reports directly from [the CVE database](https://cve.org), but using downloaded reports in JSON is undocumented supported. ### Installation ``` $ perl Makefile.PL $ make $ make test $ make install ``` Recent changes can be (re)viewed in the public GIT repository at https://github.com/CPAN-Security/Net-CVE Feel free to clone your own copy: ``` $ git clone https://github.com/CPAN-Security/Net-CVE Net-CVE ``` or get it as a tgz: ``` $ wget --output-document=Net-CVE-git.tgz \ https://github.com/CPAN-Security/Net-CVE/archive/main.tar.gz ``` ### Contibuting New ideas and fixes welcome. Please read [this guide](CONTRIBUTING.md) ### Author H.Merijn Brand Net-CVE-0.009/MANIFEST0000644000031300001440000000056214607423105013256 0ustar00merijnusersChangeLog cpanfile Files/CVE-1970-1.json Files/CVE-2022-26928.json lib/Net/CVE.pm Makefile.PL MANIFEST README.md scripts/cve.pl t/10-base.t t/20-summary.t t/25-data.t t/30-get.t t/40-lang.t t/90-diag.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Net-CVE-0.009/cpanfile0000644000031300001440000000140414607423105013625 0ustar00merijnusersrequires "Carp"; requires "Data::Dumper"; requires "HTTP::Tiny" => "0.025"; requires "IO::Socket::SSL" => "1.42"; requires "JSON::MaybeXS" => "1.004005"; requires "List::Util"; recommends "Data::Dumper" => "2.188"; recommends "Data::Peek" => "0.52"; recommends "HTTP::Tiny" => "0.088"; recommends "IO::Socket::SSL" => "2.085"; on "configure" => sub { requires "ExtUtils::MakeMaker"; recommends "ExtUtils::MakeMaker" => "7.22"; suggests "ExtUtils::MakeMaker" => "7.70"; }; on "test" => sub { requires "Test::More" => "0.90"; requires "Test::Warnings"; recommends "Test::More" => "1.302198"; }; Net-CVE-0.009/META.yml0000644000031300001440000000174214607423105013377 0ustar00merijnusers--- abstract: Fetch CVE information from cve.org author: - H.Merijn Brand build_requires: perl: '5.014002' configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: Author, CPAN::Meta::Converter version 2.150010 license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Net-CVE provides: Net::CVE: file: lib/Net/CVE.pm version: '0.009' recommends: Data::Dumper: '2.188' Data::Peek: '0.52' HTTP::Tiny: '0.088' IO::Socket::SSL: '2.085' Test::More: '1.302198' requires: Carp: 0 Data::Dumper: 0 HTTP::Tiny: '0.025' IO::Socket::SSL: '1.42' JSON::MaybeXS: '1.004005' List::Util: 0 Test::More: '0.90' Test::Warnings: 0 perl: '5.014002' resources: IRC: irc://irc.perl.org/#metacpan bugtracker: https://github.com/CPAN-Security/Net-CVE/issues license: http://dev.perl.org/licenses/ repository: https://github.com/CPAN-Security/Net-CVE version: '0.009' Net-CVE-0.009/META.json0000644000031300001440000000374614607423105013555 0ustar00merijnusers{ "prereqs" : { "build" : { "requires" : { "perl" : "5.014002" } }, "test" : { "requires" : { "Test::Warnings" : "0", "Test::More" : "0.90" }, "recommends" : { "Test::More" : "1.302198" } }, "runtime" : { "requires" : { "HTTP::Tiny" : "0.025", "Carp" : "0", "JSON::MaybeXS" : "1.004005", "IO::Socket::SSL" : "1.42", "Data::Dumper" : "0", "perl" : "5.014002", "List::Util" : "0" }, "recommends" : { "Data::Peek" : "0.52", "HTTP::Tiny" : "0.088", "IO::Socket::SSL" : "2.085", "Data::Dumper" : "2.188" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" }, "recommends" : { "ExtUtils::MakeMaker" : "7.22" }, "suggests" : { "ExtUtils::MakeMaker" : "7.70" } } }, "author" : [ "H.Merijn Brand " ], "dynamic_config" : 1, "version" : "0.009", "provides" : { "Net::CVE" : { "version" : "0.009", "file" : "lib/Net/CVE.pm" } }, "name" : "Net-CVE", "release_status" : "stable", "abstract" : "Fetch CVE information from cve.org", "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "generated_by" : "Author", "license" : [ "perl_5" ], "resources" : { "license" : [ "http://dev.perl.org/licenses/" ], "x_IRC" : "irc://irc.perl.org/#metacpan", "repository" : { "type" : "git", "url" : "https://github.com/CPAN-Security/Net-CVE", "web" : "https://github.com/CPAN-Security/Net-CVE" }, "bugtracker" : { "web" : "https://github.com/CPAN-Security/Net-CVE/issues" } } }