Net-Nessus-REST-v0.7.0/0000755000175000017500000000000012670354105015220 5ustar guillaumeguillaumeNet-Nessus-REST-v0.7.0/Makefile.PL0000644000175000017500000000152012670070173017170 0ustar guillaumeguillaumeuse ExtUtils::MakeMaker; WriteMakefile( NAME => 'Net::Nessus::REST', VERSION_FROM => 'lib/Net/Nessus/REST.pm', ABSTRACT_FROM => 'lib/Net/Nessus/REST.pm', LICENSE => 'GPL_3', PREREQ_PM => { 'JSON' => 0, 'LWP::UserAgent' => 6.04, }, CONFIGURE_REQUIRES => { 'version' => 0, }, TEST_REQUIRES => { 'IO::Socket::SSL' => 0, 'List::MoreUtils' => 0, 'Test::Exception' => 0, 'Test::More' => 0, }, META_MERGE => { 'meta-spec' => { version => 2 }, 'resources' => { repository => { type => 'git', url => 'git://github.com/guillomovitch/Net-Nessus-REST', web => 'https://github.com/guillomovitch/Net-Nessus-REST' }, } } ); Net-Nessus-REST-v0.7.0/lib/0000755000175000017500000000000012670354105015766 5ustar guillaumeguillaumeNet-Nessus-REST-v0.7.0/lib/Net/0000755000175000017500000000000012670354105016514 5ustar guillaumeguillaumeNet-Nessus-REST-v0.7.0/lib/Net/Nessus/0000755000175000017500000000000012670354105017774 5ustar guillaumeguillaumeNet-Nessus-REST-v0.7.0/lib/Net/Nessus/REST.pm0000644000175000017500000005445712670353712021131 0ustar guillaumeguillaumepackage Net::Nessus::REST; use warnings; use strict; use Carp; use LWP::UserAgent; use JSON; use List::Util qw(first); use version; our $VERSION = version->declare('v0.7.0'); sub new { my ($class, %params) = @_; my $url = $params{url} || 'https://localhost:8834/'; my $agent = LWP::UserAgent->new(); $agent->timeout($params{timeout}) if $params{timeout}; $agent->ssl_opts(%{$params{ssl_opts}}) if $params{ssl_opts} && ref $params{ssl_opts} eq 'HASH'; my $self = { url => $url, agent => $agent }; bless $self, $class; return $self; } sub create_session { my ($self, %params) = @_; my $result = $self->_post("/session", %params); $self->{agent}->default_header('X-Cookie' => "token=$result->{token}"); } sub destroy_session { my ($self, %params) = @_; $self->{agent}->delete($self->{url} . '/session'); } sub list_policies { my ($self) = @_; my $result = $self->_get('/policies'); return $result->{policies} ? @{$result->{policies}} : (); } sub get_policy_id { my ($self, %params) = @_; croak "missing name parameter" unless $params{name}; my $policy = first { $_->{name} eq $params{name} } $self->list_policies(); return unless $policy; return $policy->{id}; } sub import_policy { my ($self, %params) = @_; croak "missing file name parameter" unless $params{file}; my $result = $self->_post("/policies/import", %params); return $result; } sub get_policy_details { my ($self, %params) = @_; croak "missing id parameter" unless $params{id}; my $policy_id = delete $params{id}; my $result = $self->_get("/policies/$policy_id"); return $result; } sub delete_policy { my ($self, %params) = @_; croak "missing Policy id parameter" unless $params{id}; my $policy_id = delete $params{id}; my $result = $self->_delete("/policies/$policy_id"); return $result; } # Experimental sub configure_policy { my ($self, %params) = @_; croak "missing id parameter" unless $params{id}; croak "missing uuid parameter" unless $params{uuid}; croak "missing settings parameter" unless $params{settings}; my $policy_id = delete $params{id}; my $result = $self->_put("/policies/$policy_id", %params); return $result; } # Experimental sub create_policy { my ($self, %params) = @_; croak "missing uuid parameter" unless $params{uuid}; croak "missing settings parameter" unless $params{settings}; my $uuid = delete $params{uuid}; my $result = $self->_post("/policies", %params); return $result; } sub create_scan { my ($self, %params) = @_; croak "missing uuid parameter" unless $params{uuid}; croak "missing settings parameter" unless $params{settings}; my $result = $self->_post("/scans", %params); return $result->{scan}; } sub configure_scan { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing uuid parameter" unless $params{uuid}; croak "missing settings parameter" unless $params{settings}; my $scan_id = delete $params{scan_id}; my $result = $self->_put("/scans/$scan_id", %params); return $result; } sub delete_scan { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; my $scan_id = delete $params{scan_id}; my $result = $self->_delete("/scans/$scan_id"); return 1; } sub delete_scan_history { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing history_id parameter" unless $params{history_id}; my $scan_id = delete $params{scan_id}; my $history_id = delete $params{history_id}; my $result = $self->_delete("/scans/$scan_id/history/$history_id"); return 1; } sub list_scans { my ($self, %params) = @_; my $result = $self->_get('/scans', %params); return $result->{scans} ? @{$result->{scans}} : (); } sub launch_scan { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; my $scan_id = delete $params{scan_id}; my $result = $self->_post("/scans/$scan_id/launch", %params); return $result->{scan_uuid}; } sub get_scan_details { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; my $scan_id = delete $params{scan_id}; my $result = $self->_get("/scans/$scan_id", %params); return $result; } sub stop_scan { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; my $scan_id = delete $params{scan_id}; my $result = $self->_post("/scans/$scan_id/stop", %params); return $result; } sub set_scan_read_status { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing status parameter" unless $params{status}; croak "invalid status parameter" unless $params{status} eq 'read' or $params{status} eq 'unread'; my $scan_id = delete $params{scan_id}; my $result = $self->_put("/scans/$scan_id/status", %params); return 1; } sub export_scan { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing format parameter" unless $params{format}; croak "invalid format parameter" unless $params{format} eq 'nessus' or $params{format} eq 'html' or $params{format} eq 'pdf' or $params{format} eq 'csv' or $params{format} eq 'db'; my $scan_id = delete $params{scan_id}; my $history_id = delete $params{history_id}; my $history_param = defined $history_id ? "?history_id=$history_id" : ""; my $result = $self->_post("/scans/$scan_id/export$history_param", %params); return $result->{file}; } sub get_scan_export_status { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing file_id parameter" unless $params{file_id}; my $scan_id = delete $params{scan_id}; my $file_id = delete $params{file_id}; my $result = $self->_get("/scans/$scan_id/export/$file_id/status"); return $result->{status}; } sub download_scan { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing file_id parameter" unless $params{file_id}; my $scan_id = delete $params{scan_id}; my $file_id = delete $params{file_id}; my $response = $self->{agent}->get( $self->{url} . "/scans/$scan_id/export/$file_id/download", $params{filename} ? (':content_file' => $params{filename}) : () ); if ($response->is_success()) { return $params{filename} ? 1 : $response->content(); } else { croak "communication error: " . $response->message() } } sub get_scan_host_details { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing host_id parameter" unless $params{host_id}; my $scan_id = delete $params{scan_id}; my $host_id = delete $params{host_id}; my $result = $self->_get("/scans/$scan_id/hosts/$host_id", %params); return $result; } sub get_scan_plugin_output { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing host_id parameter" unless $params{host_id}; croak "missing plugin_id parameter" unless $params{plugin_id}; my $scan_id = delete $params{scan_id}; my $host_id = delete $params{host_id}; my $plugin_id = delete $params{plugin_id}; my $result = $self->_get("/scans/$scan_id/hosts/$host_id/plugins/$plugin_id", %params); return $result; } sub list_templates { my ($self, %params) = @_; croak "missing type parameter" unless $params{type}; croak "invalid type parameter" unless $params{type} eq 'scan' or $params{type} eq 'policy'; my $type = delete $params{type}; my $result = $self->_get("/editor/$type/templates"); return $result->{templates} ? @{$result->{templates}} : (); } sub get_template_id { my ($self, %params) = @_; croak "missing name parameter" unless $params{name}; my $template = first { $_->{name} eq $params{name} } $self->list_templates(type => $params{type}); return unless $template; return $template->{uuid}; } sub get_scan_id { my ($self, %params) = @_; croak "missing name parameter" unless $params{name}; my $scan = first { $_->{name} eq $params{name} } $self->list_scans(); return unless $scan; return $scan->{id}; } sub get_scan_status { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; my $details = $self->get_scan_details(scan_id => $params{scan_id}); return $details->{info}->{status}; } sub get_scan_history_id { my ($self, %params) = @_; croak "missing scan_id parameter" unless $params{scan_id}; croak "missing scan_uuid parameter" unless $params{scan_uuid}; my $details = $self->get_scan_details(scan_id => $params{scan_id}); my $history = first { $_->{uuid} eq $params{scan_uuid} } @{$details->{history}}; return $history->{history_id}; } sub list_scanners { my ($self) = @_; my $result = $self->_get("/scanners"); return $result->{scanners} ? @{$result->{scanners}} : (); } sub list_folders { my ($self) = @_; my $result = $self->_get("/folders"); return $result->{folders} ? @{$result->{folders}} : (); } sub get_folder_id { my ($self, %params) = @_; croak "missing name parameter" unless $params{name}; my $folder = first { $_->{name} eq $params{name} } $self->list_folders(); return unless $folder; return $folder->{id}; } sub list_plugin_families { my ($self) = @_; my $result = $self->_get("/plugins/families"); return $result->{families} ? @{$result->{families}} : (); } sub get_plugin_family_details { my ($self, %params) = @_; croak "missing id parameter" unless $params{id}; my $family_id = delete $params{id}; my $result = $self->_get("/plugins/families/$family_id", %params); return $result; } sub get_plugin_details { my ($self, %params) = @_; croak "missing id parameter" unless $params{id}; my $plugin_id = delete $params{id}; my $result = $self->_get("/plugins/plugin/$plugin_id", %params); return $result; } sub get_scanner_id { my ($self, %params) = @_; croak "missing name parameter" unless $params{name}; my $scanner = first { $_->{name} eq $params{name}} $self->list_scanners(); return unless $scanner; return $scanner->{id}; } sub file_upload { my ($self, %params) = @_; croak "missing file name" unless $params{file}; my $file = delete $params{file}; my $result = $self->_post_file('/file/upload', $file); return $result; } sub _get { my ($self, $path, %params) = @_; my $url = URI->new($self->{url} . $path); $url->query_form(%params); my $response = $self->{agent}->get($url); my $result = eval { from_json($response->content()) }; if ($response->is_success()) { return $result; } else { if ($result) { croak "server error: " . $result->{error}; } else { croak "communication error: " . $response->message() } } } sub _delete { my ($self, $path) = @_; my $response = $self->{agent}->delete($self->{url} . $path); my $result = eval { from_json($response->content()) }; if ($response->is_success()) { return $result; } else { if ($result) { croak "server error: " . $result->{error}; } else { croak "communication error: " . $response->message() } } } sub _post { my ($self, $path, %params) = @_; my $content = to_json(\%params); my $response = $self->{agent}->post( $self->{url} . $path, 'Content-Type' => 'application/json', 'Content' => $content ); my $result = eval { from_json($response->content()) }; if ($response->is_success()) { return $result; } else { if ($result) { croak "server error: " . $result->{error}; } else { croak "communication error: " . $response->message() } } } sub _post_file { my ($self, $path, $file) = @_; my $response = $self->{agent}->post( $self->{url} . $path, 'Content-Type' => 'multipart/form-data', 'Content' => [ Filedata => [$file] ] ); my $result = eval { from_json($response->content()) }; if ($response->is_success()) { return $result; } else { if ($result) { croak "server error: " . $result->{error}; } else { croak "communication error: " . $response->message() } } } sub _put { my ($self, $path, %params) = @_; my $content = to_json(\%params); my $response = $self->{agent}->put( $self->{url} . $path, 'Content-Type' => 'application/json', 'Content' => $content ); my $result = eval { from_json($response->content()) }; if ($response->is_success()) { return $result; } else { if ($result) { croak "server error: " . $result->{error}; } else { croak "communication error: " . $response->message() } } } sub DESTROY { my ($self) = @_; $self->destroy_session() if $self->{agent}->default_header('X-Cookie'); } 1; __END__ =head1 NAME Net::Nessus::REST - REST interface for Nessus 6.0 =head1 DESCRIPTION This module provides a Perl interface for communication with Nessus scanner using REST interface. =head1 SYNOPSIS use Net::Nessus::REST; my $nessus = Net::Nessus::REST->new( url => 'https://my.nessus:8834' ): $nessus->create_session( username => 'user', password => 's3cr3t', ); my $policy_template_id = $nessus->get_template_id( name => 'basic', type => 'policy' ); my $scan = $nessus->create_scan( uuid => $policy_template_id, settings => { text_targets => '127.0.0.1', name => 'localhost scan' } ); $nessus->launch_scan(scan_id => $scan->{id}); while ($nessus->get_scan_status(scan_id => $scan->{id}) ne 'completed') { sleep 10; } my $file_id = $nessus->export_scan( scan_id => $scan->{id}, format => 'pdf' ); while ($nessus->get_scan_export_status( scan_id => $scan->{id}, file_id => $file_id ) ne 'ready') { sleep 1; } $nessus->download_scan( scan_id => $scan->{id}, file_id => $file_id, filename => 'localhost.pdf' ); =head1 CLASS METHODS =head2 Net::Nessus::REST->new(url => $url, [ssl_opts => $opts, timeout => $timeout]) Creates a new L instance. =head1 INSTANCE METHODS =head2 $nessus->create_session(username => $username, password => $password) Creates a new session token for the given user. See L for details. =head2 $nessus->destroy_session() Logs the current user out and destroys the session. See L for details. =head2 $nessus->list_policies() Returns the policy list. See L for details. =head2 $nessus->get_policy_id(name => $name) Returns the identifier for the policy with given name. =head2 $nessus->get_policy_details(id => $policy_id) Returns a reference to a hash with all settings and parameters for a given scan policy. See L for details. =head2 $nessus->import_policy(file => $file_id) Returns reference to hash with name and identifier of the policy imported. NB $file_id must be a valid identifier to a file uploaded to the Nessus server, e.g. with method file_upload() Example: $result = $nessus->import_policy(file => $fileuploaded); print "Policy imported: " . $result->{'name'} . "\n"; See L for details. =head2 $nessus->delete_policy(id => $policy_id) Deletes a given scan policy off the Nessus server See L for details. =head2 $nessus->configure_policy(id => $policy_id, uuid => $uuid, settings => $settings) See L for details. =head2 $nessus->create_policy(uuid => $uuid, settings => $settings) See L for details. =head2 $nessus->list_scanners() Returns the scanner list. See L for details. =head2 $nessus->list_folders() Returns the current user's scan folders. See L for details. =head2 $nessus->get_folder_id(name => $name) Returns the identifier for the folder with given name. =head2 $nessus->create_scan(uuid => $uuid, settings => $settings) Creates a scan See L for details. =head2 $nessus->configure_scan(scan_id => $scan_id, uuid => $uuid, settings => $settings) Changes the schedule or policy parameters of a scan. See L for details. =head2 $nessus->delete_scan(scan_id => $scan_id) Deletes a scan. See L for details. =head2 $nessus->delete_scan_history(scan_id => $scan_id, history_id => $history_id) Deletes historical results from a scan. See L for details. =head2 $nessus->download_scan(scan_id => $scan_id, file_id => $file_id, filename => $filename) Download an exported scan. Without filename parameter it will return the content of the file. See L for details. =head2 $nessus->export_scan(scan_id => $scan_id, format => $format) Export the given scan. See L for details. =head2 $nessus->launch_scan(scan_id => $scan_id) Launches a scan. See L for details. =head2 $nessus->list_scans([folder_id => $id, last_modification_date => $date]) Returns the scan list. See L for details. =head2 $nessus->set_scan_read_status(scan_id => $scan_id, status => $status) Changes the status of a scan. See L for details. =head2 $nessus->get_scan_details(scan_id => $scan_id, [history_id => $history_id]) Returns details for the given scan. See L for details. =head2 $nessus->get_scan_host_details(scan_id => $scan_id, host_id => $host_id, [history_id => $history_id]) Returns details for the given host. See L for details. =head2 $nessus->get_scan_export_status(scan_id => $scan_id, file_id => $file_id) Check the file status of an exported scan. See L for details. =head2 $nessus->get_scan_plugin_output(scan_id => $scan_id, host_id => $host_id, plugin_id => $plugin_id, [history_id => $history_id]) Returns the output for a given plugin. See L for details. =head2 $nessus->get_scan_id(name => $name) Returns the identifier for the scan with given name. =head2 $nessus->get_scan_status(scan_id => $scan_id) Returns the status for given scan. =head2 $nessus->get_scan_history_id(scan_id => $scan_id, scan_uuid => $scan_uuid) Returns the identifier for the historical results for given scan and run. =head2 $nessus->list_templates(type => $type) Returns the template list. See L for details. =head2 $nessus->get_template_id(type => $type, name => $name) Returns the identifier for template with given name. =head2 $nessus->get_plugin_details( id => $plugin_id ) returns the details of a plugin See L for details. =head2 $nessus->list_plugin_families( ) returns a list of plugin families See L for details. =head2 $nessus->get_plugin_family_details( id => $family_id ) returns the details about a plugin family See L for details. =head2 $nessus->get_scanner_id( name => $name ) returns the identifier for the scanner with given name. =head2 $nessus->file_upload(file => $file) Uploads a file to the Nessus server. Returns a reference to hash with identifier to the uploaded file. Example: my $result = $nessus->file_upload(file => $file); my $fileuploaded = $result->{'fileuploaded'}; See L for details. =head2 $nessus->stop_scan(scan_id => $scan_id ) Returns details for the given scan. See L for details. =head1 LICENSE This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Net-Nessus-REST-v0.7.0/MANIFEST0000644000175000017500000000046112670354105016352 0ustar guillaumeguillaumeChanges lib/Net/Nessus/REST.pm Makefile.PL MANIFEST This list of files README t/critic.t t/kwalite.t t/pod-coverage.t t/pod.t t/test.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Net-Nessus-REST-v0.7.0/META.yml0000664000175000017500000000120212670354105016466 0ustar guillaumeguillaume--- abstract: 'REST interface for Nessus 6.0' author: - unknown build_requires: ExtUtils::MakeMaker: '0' IO::Socket::SSL: '0' List::MoreUtils: '0' Test::Exception: '0' Test::More: '0' configure_requires: version: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.0401, CPAN::Meta::Converter version 2.150001' license: gpl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Net-Nessus-REST no_index: directory: - t - inc requires: JSON: '0' LWP::UserAgent: '6.04' resources: repository: git://github.com/guillomovitch/Net-Nessus-REST version: v0.7.0 Net-Nessus-REST-v0.7.0/README0000644000175000017500000000062012616730465016105 0ustar guillaumeguillaumeNet::Nessus::REST ================= DESCRIPTION ----------- This module provides a Perl object-oriented interface over Nessus REST API, available since version 6. NSTALLATION ------------ To install this module type the following: tar xvzf Net-Nessus-REST-X.Y.tar.gz cd Net-Nessus-REST-X.Y perl Makefile.PL make make test make install AUTHORS ------- Guillaume Rousse Net-Nessus-REST-v0.7.0/t/0000755000175000017500000000000012670354105015463 5ustar guillaumeguillaumeNet-Nessus-REST-v0.7.0/t/kwalite.t0000755000175000017500000000050612616730465017323 0ustar guillaumeguillaume#!/usr/bin/perl use strict; use warnings; use Test::More; use English qw(-no_match_vars); plan(skip_all => 'Author test, set $ENV{AUTHOR_TESTING} to a true value to run') if !$ENV{AUTHOR_TESTING}; eval { require Test::Kwalitee; }; plan(skip_all => 'Test::Kwalitee required') if $EVAL_ERROR; Test::Kwalitee->import(); Net-Nessus-REST-v0.7.0/t/critic.t0000755000175000017500000000054312616730465017141 0ustar guillaumeguillaume#!/usr/bin/perl use strict; use warnings; use Test::More; use English qw(-no_match_vars); plan(skip_all => 'Author test, set $ENV{AUTHOR_TESTING} to a true value to run') if !$ENV{AUTHOR_TESTING}; eval { require Test::Perl::Critic; }; plan(skip_all => 'Test::Perl::Critic required') if $EVAL_ERROR; Test::Perl::Critic->import(); all_critic_ok(); Net-Nessus-REST-v0.7.0/t/pod.t0000755000175000017500000000051312655340517016441 0ustar guillaumeguillaume#!/usr/bin/perl use strict; use warnings; use Test::More; use English qw(-no_match_vars); plan(skip_all => 'Author test, set $ENV{AUTHOR_TESTING} to a true value to run') if !$ENV{AUTHOR_TESTING}; eval { require Test::Pod; }; plan(skip_all => 'Test::Pod required') if $EVAL_ERROR; Test::Pod->import(); all_pod_files_ok(); Net-Nessus-REST-v0.7.0/t/test.t0000755000175000017500000002174012667632006016643 0ustar guillaumeguillaume#!/usr/bin/perl use strict; use warnings; use Digest::file qw(digest_file_hex); use English qw(-no_match_vars); use File::Temp qw(tempdir); use List::MoreUtils qw(any none); use Net::Nessus::REST; use IO::Socket::SSL; use Test::More; use Test::Exception; plan(skip_all => 'live test, set $ENV{NESSUS_TEST_URL} to a true value to run') if !$ENV{NESSUS_TEST_URL}; plan(skip_all => 'live test, set $ENV{NESSUS_TEST_USERNAME} to a true value to run') if !$ENV{NESSUS_TEST_USERNAME}; plan(skip_all => 'live test, set $ENV{NESSUS_TEST_PASSWORD} to a true value to run') if !$ENV{NESSUS_TEST_PASSWORD}; plan tests => 59; my $nessus; lives_ok { $nessus = Net::Nessus::REST->new( url => $ENV{NESSUS_TEST_URL}, ssl_opts => { verify_hostname => 0, SSL_verify_mode => SSL_VERIFY_NONE } ); } 'connection succeeds'; isa_ok($nessus, 'Net::Nessus::REST'); lives_ok { $nessus->create_session( username => $ENV{NESSUS_TEST_USERNAME}, password => $ENV{NESSUS_TEST_PASSWORD}, ); } 'authentication succeeds'; BAIL_OUT('unable to connect, skipping remaining tests') if $EVAL_ERROR; like( $nessus->{agent}->default_header('X-Cookie'), qr/^token=\S+/, 'nessus handle has authentication token' ); my @scanners; lives_ok { @scanners = $nessus->list_scanners(); } 'scanners list retrieval succeeds'; ok(@scanners, 'scanners list is not empty'); my @policies; lives_ok { @policies = $nessus->list_policies(); } 'policies list retrieval succeeds'; my @templates; throws_ok { @templates = $nessus->list_templates(); } qr/^missing type parameter/, 'templates list retrieval without type argument fails'; lives_ok { @templates = $nessus->list_templates(type => 'policy'); } 'templates list retrieval succeeds'; ok(@templates, 'templates list is not empty'); my $policy_template_id; lives_ok { $policy_template_id = $nessus->get_template_id( name => 'discovery', type => 'policy' ); } 'policy ID retrieval succeeds'; ok(defined $policy_template_id, "policy ID is defined"); my @scans; lives_ok { @scans = $nessus->list_scans(); } 'initial scan list retrieval succeeds'; my $initial_scan_count = scalar @scans; # use a random scan name to ensure empty history my @chars = ("A".."Z", "a".."z"); my $scan_name = 'test scan ' . join('' , map { $chars[rand @chars] } 1 .. 8); my $scan; lives_ok { $scan = $nessus->create_scan( uuid => $policy_template_id, settings => { text_targets => '127.0.0.1', name => $scan_name } ); } 'scan creation succeeds'; ok(ref $scan eq 'HASH', "scan handle is an hashref"); # first run lives_ok { $nessus->launch_scan(scan_id => $scan->{id}); } 'scan first run launch succeeds'; while ($nessus->get_scan_status(scan_id => $scan->{id}) ne 'completed') { sleep 3; } lives_ok { @scans = $nessus->list_scans(); } 'new scan list retrieval succeeds'; ok( any(sub { $_->{name} eq $scan_name }, @scans), 'the scan lists contains the new scan' ); cmp_ok( scalar @scans, '==', $initial_scan_count + 1, 'the scan list has one more element' ); my $details; throws_ok { $details = $nessus->get_scan_details(); } qr/^missing scan_id parameter/, 'scan details retrieval without scan_id argument fails'; lives_ok { $details = $nessus->get_scan_details(scan_id => $scan->{id}); } 'scan details retrieval succeeds'; is( $details->{info}->{name}, $scan_name, 'scan details has expected scan name' ); is( $details->{info}->{targets}, '127.0.0.1', 'scan details has expected scan target' ); cmp_ok( scalar @{$details->{history}}, '==', 1, 'scan history has one element' ); lives_ok { $details = $nessus->get_scan_details( scan_id => $scan->{id}, history_id => $details->{history}->[0]->{history_id}, ); } 'scan details retrieval for an exiting run succeeds'; throws_ok { $details = $nessus->get_scan_details( scan_id => $scan->{id}, history_id => $details->{history}->[0]->{history_id} + 1, ); } qr/^server error: The requested file was not found/, 'scan details retrieval for a non-existing run fails'; # second run lives_ok { $nessus->launch_scan(scan_id => $scan->{id}); } 'scan second run launch succeeds'; while ($nessus->get_scan_status(scan_id => $scan->{id}) ne 'completed') { sleep 3; } lives_ok { $details = $nessus->get_scan_details(scan_id => $scan->{id}); } 'scan details retrieval succeeds'; cmp_ok( scalar @{$details->{history}}, '==', 2, 'scan history has two elements' ); # third run lives_ok { $nessus->launch_scan(scan_id => $scan->{id}); } 'scan third run launch succeeds'; is($nessus->get_scan_status(scan_id => $scan->{id}), 'running', 'scan is running'); lives_ok { $nessus->stop_scan(scan_id => $scan->{id}); } 'scan stop succeeds'; is($nessus->get_scan_status(scan_id => $scan->{id}), 'stopping', 'scan is not running anymore'); while ($nessus->get_scan_status(scan_id => $scan->{id}) ne 'canceled') { sleep 3; } lives_ok { $details = $nessus->get_scan_details(scan_id => $scan->{id}); } 'scan details retrieval succeeds'; cmp_ok( scalar @{$details->{history}}, '==', 3, 'scan history has three elements' ); # history deletion lives_ok { $nessus->delete_scan_history( scan_id => $scan->{id}, history_id => $details->{history}->[2]->{history_id}, ); } 'last run deletion succeeds'; lives_ok { $details = $nessus->get_scan_details(scan_id => $scan->{id}); } 'scan details retrieval succeeds'; cmp_ok( scalar @{$details->{history}}, '==', 2, 'scan history has two elements' ); # first report: last run in history my $dir = tempdir(CLEANUP => $ENV{TEST_DEBUG} ? 0 : 1); my $file_id; lives_ok { $file_id = $nessus->export_scan( scan_id => $scan->{id}, format => 'nessus', chapters => 'vuln_hosts_summary' ); } 'report ID retrieval succeeds'; ok(defined $file_id, "first report ID is defined"); while ($nessus->get_scan_export_status( scan_id => $scan->{id}, file_id => $file_id ) ne 'ready') { sleep 1; } my $report1 = "$dir/localhost1.nessus"; lives_ok { $nessus->download_scan( scan_id => $scan->{id}, file_id => $file_id, filename => $report1, ); } 'first report download succeeds'; ok(-f $report1, 'first report file exists'); # second report: first run in history lives_ok { $file_id = $nessus->export_scan( scan_id => $scan->{id}, history_id => $details->{history}->[0]->{history_id}, format => 'nessus', chapters => 'vuln_hosts_summary' ); } 'second report ID retrieval succeeds'; ok(defined $file_id, "second report ID is defined"); while ($nessus->get_scan_export_status( scan_id => $scan->{id}, file_id => $file_id ) ne 'ready') { sleep 1; } my $report2 = "$dir/localhost2.nessus"; lives_ok { $nessus->download_scan( scan_id => $scan->{id}, file_id => $file_id, filename => $report2, ); } 'second report download succeeds'; ok(-f $report2, 'second report file exists'); isnt( digest_file_hex($report1, "MD5"), digest_file_hex($report2, "MD5"), "report for the same run are identical" ); # third report: second run in history lives_ok { $file_id = $nessus->export_scan( scan_id => $scan->{id}, history_id => $details->{history}->[1]->{history_id}, format => 'nessus', chapters => 'vuln_hosts_summary' ); } 'third report ID retrieval succeeds'; ok(defined $file_id, "third report ID is defined"); while ($nessus->get_scan_export_status( scan_id => $scan->{id}, file_id => $file_id ) ne 'ready') { sleep 1; } my $report3 = "$dir/localhost3.nessus"; lives_ok { $nessus->download_scan( scan_id => $scan->{id}, file_id => $file_id, filename => $report3, ); } 'second report download succeeds'; ok(-f $report3, 'third report file exists'); is( digest_file_hex($report1, "MD5"), digest_file_hex($report3, "MD5"), "report for different runs are different" ); lives_ok { $file_id = $nessus->delete_scan( scan_id => $scan->{id}, ); } 'scan deletion succeeds'; throws_ok { $details = $nessus->get_scan_details(scan_id => $scan->{id}); } qr/^server error: The requested file was not found/, 'scan details retrieval fails'; lives_ok { @scans = $nessus->list_scans(); } 'new scan list retrieval succeeds'; ok( none(sub { $_->{name} eq $scan_name }, @scans), 'the scan lists does not contain the new scan' ); cmp_ok( scalar @scans, '==', $initial_scan_count, 'the scan list has initial scan count' ); # deconnection lives_ok { $nessus->destroy_session() } 'deconnection succeeds'; throws_ok { @policies = $nessus->list_policies(); } qr/^server error: Invalid Credentials/, 'policies list retrieval fails'; diag("report files directory: $dir") if $ENV{TEST_DEBUG}; Net-Nessus-REST-v0.7.0/t/pod-coverage.t0000755000175000017500000000055412655340517020237 0ustar guillaumeguillaume#!/usr/bin/perl use strict; use warnings; use Test::More; use English qw(-no_match_vars); plan(skip_all => 'Author test, set $ENV{AUTHOR_TESTING} to a true value to run') if !$ENV{AUTHOR_TESTING}; eval { require Test::Pod::Coverage; }; plan(skip_all => 'Test::Pod::Coverage required') if $EVAL_ERROR; Test::Pod::Coverage->import(); all_pod_coverage_ok(); Net-Nessus-REST-v0.7.0/META.json0000664000175000017500000000242212670354105016643 0ustar guillaumeguillaume{ "abstract" : "REST interface for Nessus 6.0", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.0401, CPAN::Meta::Converter version 2.150001", "license" : [ "gpl_3" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Net-Nessus-REST", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "version" : "0" } }, "runtime" : { "requires" : { "JSON" : "0", "LWP::UserAgent" : "6.04" } }, "test" : { "requires" : { "IO::Socket::SSL" : "0", "List::MoreUtils" : "0", "Test::Exception" : "0", "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "git://github.com/guillomovitch/Net-Nessus-REST", "web" : "https://github.com/guillomovitch/Net-Nessus-REST" } }, "version" : "v0.7.0" } Net-Nessus-REST-v0.7.0/Changes0000644000175000017500000000251612670354055016523 0ustar guillaumeguillaumeRevision history for Net::Nessus::REST 0.7.0 Thu, 10 Mar 2016 - yet another attempt to fix version number 0.6.1 Wed, 09 Mar 2016 - fix test dependencies - fix usage of dotted-decimal version scheme 0.6 Tue, 08 Mar 2016 - first actual tests against a live Nessus server - fix get_scan_details() method (Denis Joiret) - correctly fix list_scanners() method (Todd Bruner) - add stop_scan() method (Todd Bruner) 0.5.2 Sat, 26 Feb 2016 - fix retrieval of scan history (Denis Joiret) 0.5.1 Sat, 06 Feb 2016 - fix import_policy() method (Asgeir Aegisson) - keep pod and kwalitee tests for author only (fix #111699) 0.5 Sun, 31 Jan 2016 * add multiple policy management methods (Asgeir Aegisson) * change list_plugin_families return value, for consistency (Asgeir Aegisson) * additional documentation fixes (Asgeir Aegisson) * use versioned dependency on LWP::UserAgent for delete() method usage 0.4 Sun, 12 Jul 2015 * fix breakage in download_scan() method, introduced by my fault in previous release * fix a few documentation issues in synopsis 0.3 Mon, 06 Jul 2015 * added scanner ID query functionality (Fabrice Durand) * allow to return report content as a string instead of a file (Fabrice Durand) * fix scanners list method (Todd Bruner) 0.2 Sat, 14 Feb 2015 * added plugin query functionality (Todd Bruner) 0.1 Mon, 22 Dec 2014 * initial release