From fe5a16bdf9934ebc0f3282903d367bfa7104881f Mon Sep 17 00:00:00 2001 From: Giuseppe Di Terlizzi Date: Tue, 11 Feb 2020 16:25:20 +0100 Subject: [PATCH 1/6] Added Status API and refactoring Net::SecurityCenter module and utils Other notable changes: - Deprecated "Net::SecurityCenter::API::System->get_status" method - Updated .travis.yml - Push coverage results to coveralls.io - Improved sc-api command - Added new tests --- .travis.yml | 36 ++- INSTALL.md | 2 +- LICENSE | 4 +- README.md | 28 +- bin/sc-api | 148 ++++----- lib/Net/SecurityCenter.pm | 184 ++--------- lib/Net/SecurityCenter/API.pm | 4 +- lib/Net/SecurityCenter/API/Analysis.pm | 4 +- lib/Net/SecurityCenter/API/Credential.pm | 4 +- lib/Net/SecurityCenter/API/Feed.pm | 4 +- lib/Net/SecurityCenter/API/File.pm | 4 +- lib/Net/SecurityCenter/API/Plugin.pm | 36 +-- lib/Net/SecurityCenter/API/PluginFamily.pm | 4 +- lib/Net/SecurityCenter/API/Policy.pm | 4 +- lib/Net/SecurityCenter/API/Report.pm | 4 +- lib/Net/SecurityCenter/API/Repository.pm | 4 +- lib/Net/SecurityCenter/API/Scan.pm | 4 +- lib/Net/SecurityCenter/API/ScanResult.pm | 12 +- lib/Net/SecurityCenter/API/Scanner.pm | 4 +- lib/Net/SecurityCenter/API/Status.pm | 126 ++++++++ lib/Net/SecurityCenter/API/System.pm | 25 +- lib/Net/SecurityCenter/API/User.pm | 4 +- lib/Net/SecurityCenter/API/Zone.pm | 4 +- lib/Net/SecurityCenter/Error.pm | 4 +- lib/Net/SecurityCenter/REST.pm | 45 +-- lib/Net/SecurityCenter/Utils.pm | 43 ++- t/00-load.t | 1 + t/99-local.t | 63 +++- t/mock/rest-plugin-0-get.json | 123 +++++++ t/mock/rest-plugin-get.json | 19 ++ t/mock/rest-pluginFamily-1000030-get.json | 14 + t/mock/rest-pluginFamily-get.json | 357 +++++++++++++++++++++ t/mock/rest-scanResult-11-get.json | 131 ++++++++ t/mock/rest-scanResult-86-pause-post.json | 67 ++++ t/mock/rest-scanResult-86-resume-post.json | 67 ++++ t/mock/rest-scanResult-86-stop-post.json | 67 ++++ t/mock/rest-scanResult-get.json | 25 ++ t/mock/rest-status-get.json | 37 +++ 38 files changed, 1343 insertions(+), 373 deletions(-) create mode 100644 lib/Net/SecurityCenter/API/Status.pm create mode 100644 t/mock/rest-plugin-0-get.json create mode 100644 t/mock/rest-plugin-get.json create mode 100644 t/mock/rest-pluginFamily-1000030-get.json create mode 100644 t/mock/rest-pluginFamily-get.json create mode 100644 t/mock/rest-scanResult-11-get.json create mode 100644 t/mock/rest-scanResult-86-pause-post.json create mode 100644 t/mock/rest-scanResult-86-resume-post.json create mode 100644 t/mock/rest-scanResult-86-stop-post.json create mode 100644 t/mock/rest-scanResult-get.json create mode 100644 t/mock/rest-status-get.json diff --git a/.travis.yml b/.travis.yml index e0e82a6..e80bbd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,35 @@ language: perl perl: - - "5.22" - - "5.24" - - "5.26" - - "5.28" - "5.30" + - "5.28" + - "5.26" + - "5.24" + - "5.22" + - "5.20" + - "5.18" + - "5.16" + - "5.14" + - "5.12" + - "5.10" + - "5.8" +matrix: + include: + - perl: 5.30 + env: COVERAGE=1 +install: + - cpan-install --deps + - cpan-install --coverage +before_script: + - coverage-setup +after_success: + - coverage-report +cache: + directories: + - $HOME/perl5 +before_install: + - git clone git://github.com/travis-perl/helpers ~/travis-perl-helpers + - source ~/travis-perl-helpers/init --always-upgrade-modules + - build-perl + - perl -V + - build-dist + - cd $BUILD_DIR diff --git a/INSTALL.md b/INSTALL.md index 890822d..63ff068 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -47,7 +47,7 @@ You can also look for information at: ## LICENSE AND COPYRIGHT -Copyright (C) 2018-2019 Giuseppe Di Terlizzi +Copyright (C) 2018-2020 Giuseppe Di Terlizzi This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a diff --git a/LICENSE b/LICENSE index f808b62..e1ca43b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. @@ -12,7 +12,7 @@ b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- -This software is Copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is Copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software, licensed under: diff --git a/README.md b/README.md index 845f6d7..7ed11f3 100644 --- a/README.md +++ b/README.md @@ -5,25 +5,25 @@ ## Synopsis ```.pl - use Net::SecurityCenter; - my $sc = Net::SecurityCenter('sc.example.org') or die "Error: $@"; +use Net::SecurityCenter; +my $sc = Net::SecurityCenter('sc.example.org') or die "Error: $@"; - $sc->login('secman', 'password'); +$sc->login('secman', 'password'); - if ($sc->error) { - print "Failed login: " . $sc->error; - exit 0; - } +if ($sc->error) { + print "Failed login: " . $sc->error; + exit 0; +} - my $running_scans = $sc->scan_result->list_running; +my $running_scans = $sc->scan_result->list_running; - if ($sc->scan_result->status( id => 1337 ) eq 'completed') { - $sc->scan_result->download( id => 1337, - filename => '/tmp/1337.nessus' ); +if ($sc->scan_result->status( id => 1337 ) eq 'completed') { + $sc->scan_result->download( id => 1337, + filename => '/tmp/1337.nessus' ); - } +} - $sc->logout(); +$sc->logout(); ``` ## Install @@ -37,5 +37,5 @@ To install `Net::SecurityCenter` distribution, run the following commands: ## Copyright - - Copyright 2018-2019 © Giuseppe Di Terlizzi + - Copyright 2018-2020 © Giuseppe Di Terlizzi - Nessus®, Tenable.sc® and SecurityCenter® is a Registered Trademark of Tenable®, Inc. diff --git a/bin/sc-api b/bin/sc-api index c7500ff..8ec398a 100644 --- a/bin/sc-api +++ b/bin/sc-api @@ -2,8 +2,10 @@ use strict; use warnings; +use utf8; use Net::SecurityCenter; +use Net::SecurityCenter::Utils qw(trim dumper); use Getopt::Long qw( :config gnu_compat ); use Pod::Usage; @@ -13,9 +15,7 @@ use JSON; use Carp; use English qw( -no_match_vars ); -use Data::Dumper; - -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; sub slurp { @@ -42,7 +42,7 @@ sub config_parse_line { return 0 if ( $value =~ /^(no|false)$/s ); if ( $value =~ /\,/ ) { - return map { trim($_) } split( /,/, $value ); + return map { trim($_) } split /,/, $value; } return $value; @@ -56,7 +56,7 @@ sub config_parser { my $section = '_'; # Root section my $config_data = {}; - foreach my $line ( split( /\n/, $config_string ) ) { + foreach my $line ( split /\n/, $config_string ) { chomp($line); @@ -91,16 +91,6 @@ sub config_parser { } -sub trim { - - my $string = shift; - return $string unless ($string); - - $string =~ s/^\s+|\s+$//g; - return $string; - -} - sub table { my (%args) = @_; @@ -168,13 +158,7 @@ sub table { for my $row ( @{$rows} ) { if ( $output_format eq 'table' ) { - $table .= sprintf( - $format, - map { - if ($_) {$_} - else {''} - } @{$row} - ); + $table .= sprintf( $format, map { $_ || '' } @{$row} ); } else { $table .= sprintf( $format, map { trim($_) } @{$row} ); } @@ -196,7 +180,7 @@ my @options = ( 'help|h', 'man', - 'version', + 'version|v', 'verbose', 'hostname=s', @@ -210,9 +194,10 @@ my @options = ( 'csv', 'tsv', 'yaml', + 'dumper' ); -my @output_formats = qw/json table csv tsv yaml/; +my @output_formats = qw/json table csv tsv yaml dumper/; my $options = {}; my $params = {}; @@ -228,6 +213,27 @@ $method =~ s/-/_/g if ($method); pod2usage(1) if ( $options->{'help'} ); +if ( $options->{'version'} ) { + + require IO::Socket::SSL; + my $io_socket_ssl = ($IO::Socket::SSL::VERSION) ? $IO::Socket::SSL::VERSION : 'n/a'; + + require LWP::UserAgent; + my $lwp_useragent = ($LWP::UserAgent::VERSION) ? $LWP::UserAgent::VERSION : 'n/a'; + + print <<'EOF'; +sc-api v$VERSION + +CORE + Perl ($PERL_VERSION, $OSNAME) + Net::SecurityCenter ($Net::SecurityCenter::VERSION) + LWP::UserAgent ($lwp_useragent) + IO::Socket::SSL ($io_socket_ssl) + +EOF + exit(0); +} + if ( $options->{'config'} ) { my $config = config_parser( slurp( $options->{'config'} ) ); @@ -263,11 +269,12 @@ if ( $options->{'format'} ) { $options->{'format'} ||= 'json'; -$options->{'format'} = 'yaml' if ( $options->{'yaml'} ); -$options->{'format'} = 'table' if ( $options->{'table'} ); -$options->{'format'} = 'json' if ( $options->{'json'} ); -$options->{'format'} = 'csv' if ( $options->{'csv'} ); -$options->{'format'} = 'tsv' if ( $options->{'tsv'} ); +$options->{'format'} = 'yaml' if ( $options->{'yaml'} ); +$options->{'format'} = 'table' if ( $options->{'table'} ); +$options->{'format'} = 'json' if ( $options->{'json'} ); +$options->{'format'} = 'csv' if ( $options->{'csv'} ); +$options->{'format'} = 'tsv' if ( $options->{'tsv'} ); +$options->{'format'} = 'dumper' if ( $options->{'dumper'} ); pod2usage( -verbose => 0 ) if ( !$api || !$method ); @@ -302,13 +309,8 @@ if ( $options->{'verbose'} ) { $sc_options->{'logger'} = Net::SecurityCenter::LoggerSimple->new; - { - local $Data::Dumper::Indent = 0; - local $Data::Dumper::Terse = 1; - - $sc_options->{'logger'}->debug("INPUT - Call $api -> $method"); - $sc_options->{'logger'}->debug( "INPUT - Params: " . Dumper($params) ); - } + $sc_options->{'logger'}->debug("INPUT - Call $api -> $method"); + $sc_options->{'logger'}->debug( "INPUT - Params: " . dumper($params) ); } @@ -345,7 +347,7 @@ if ( ref $results eq 'ARRAY' || ref $results eq 'HASH' ) { } if ( $options->{'format'} eq 'dumper' ) { - print Dumper($results); + print dumper($results); exit(0); } @@ -415,35 +417,31 @@ exit(0); package Net::SecurityCenter::LoggerSimple; -use Test::More; - sub new { my $class = shift; return bless {}, $class; } -sub info { - my ( $self, $message ) = @_; - print STDERR "INFO - $message\n"; +sub _log { + my ( $self, $level, $message ) = @_; + print STDERR "[$$] $level - $message\n"; return; } +sub info { + return shift->_log( 'INFO', shift ); +} + sub debug { - my ( $self, $message ) = @_; - print STDERR "DEBUG - $message\n"; - return; + return shift->_log( 'DEBUG', shift ); } sub warning { - my ( $self, $message ) = @_; - print STDERR "WARNING - $message\n"; - return; + return shift->_log( 'WARNING', shift ); } sub error { - my ( $self, $message ) = @_; - print STDERR "ERROR - $message\n"; - return; + return shift->_log( 'ERROR', shift ); } __END__ @@ -471,6 +469,7 @@ sc-api - Tenable.sc (SecurityCenter) API command line interface scan scan-result scanner + status system user zone @@ -511,8 +510,6 @@ sc-api - Tenable.sc (SecurityCenter) API command line interface sc-api policy get id=1 - - =head1 DESCRIPTION C Tenable.sc (SecurityCenter) API command line interface. @@ -521,72 +518,67 @@ C Tenable.sc (SecurityCenter) API command line interface. =head2 analysis -See L class. +See L class. =head2 credential -See L class. +See L class. =head2 feed -See L class. +See L class. =head2 file -See L class. +See L class. =head2 plugin -See L class. +See L class. =head2 plugin-family -See L class. +See L class. =head2 policy -See L class. +See L class. =head2 report -See L class. +See L class. =head2 repository -See L class. +See L class. =head2 scan -See L class. +See L class. =head2 scan-result -See L class. +See L class. =head2 scanner -See L class. +See L class. + +=head2 status + +See L class. =head2 system -See L class. +See L class. =head2 user -See L class. +See L class. =head2 zone -See L class. - - -=head1 OPTIONS - -=head2 --help - -=head2 --man - -=head2 --version +See L class. =head1 OUTPUT FORMATS @@ -607,7 +599,7 @@ L =head1 COPYRIGHT AND LICENSE -Copyright © 2018-2019 L +Copyright © 2018-2020 L You may use and distribute this module according to the same terms that Perl is distributed under. diff --git a/lib/Net/SecurityCenter.pm b/lib/Net/SecurityCenter.pm index 72f0839..3f83e74 100644 --- a/lib/Net/SecurityCenter.pm +++ b/lib/Net/SecurityCenter.pm @@ -3,26 +3,15 @@ package Net::SecurityCenter; use warnings; use strict; +use Net::SecurityCenter::Utils qw(decamelize); + use Net::SecurityCenter::REST; use Net::SecurityCenter::Error; -require Net::SecurityCenter::API::Analysis; -require Net::SecurityCenter::API::Credential; -require Net::SecurityCenter::API::Feed; -require Net::SecurityCenter::API::File; -require Net::SecurityCenter::API::Plugin; -require Net::SecurityCenter::API::PluginFamily; -require Net::SecurityCenter::API::Policy; -require Net::SecurityCenter::API::Report; -require Net::SecurityCenter::API::Repository; -require Net::SecurityCenter::API::Scan; -require Net::SecurityCenter::API::ScanResult; -require Net::SecurityCenter::API::Scanner; -require Net::SecurityCenter::API::System; -require Net::SecurityCenter::API::User; -require Net::SecurityCenter::API::Zone; - -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; + +my @apis = qw/Analysis Credential Feed File Plugin PluginFamily Policy Report + Repository Scan ScanResult Scanner Status System User Zone/; #------------------------------------------------------------------------------- # CONSTRUCTOR @@ -42,21 +31,11 @@ sub new { bless $self, $class; - $self->{'analysis'} = Net::SecurityCenter::API::Analysis->new($client); - $self->{'credential'} = Net::SecurityCenter::API::Credential->new($client); - $self->{'feed'} = Net::SecurityCenter::API::Feed->new($client); - $self->{'file'} = Net::SecurityCenter::API::File->new($client); - $self->{'plugin'} = Net::SecurityCenter::API::Plugin->new($client); - $self->{'plugin_family'} = Net::SecurityCenter::API::PluginFamily->new($client); - $self->{'policy'} = Net::SecurityCenter::API::Policy->new($client); - $self->{'report'} = Net::SecurityCenter::API::Report->new($client); - $self->{'repository'} = Net::SecurityCenter::API::Repository->new($client); - $self->{'scan'} = Net::SecurityCenter::API::Scan->new($client); - $self->{'scan_result'} = Net::SecurityCenter::API::ScanResult->new($client); - $self->{'scanner'} = Net::SecurityCenter::API::Scanner->new($client); - $self->{'system'} = Net::SecurityCenter::API::System->new($client); - $self->{'user'} = Net::SecurityCenter::API::User->new($client); - $self->{'zone'} = Net::SecurityCenter::API::Zone->new($client); + foreach (@apis) { + my $api_module = "Net::SecurityCenter::API::$_"; + eval "require $api_module"; ## no critic + $self->{ decamelize $_ } = $api_module->new($client); + } return $self; @@ -115,136 +94,17 @@ sub logout { # HELPER METHODS #------------------------------------------------------------------------------- -sub analysis { - - my ($self) = @_; - return $self->{'analysis'}; - -} - -#------------------------------------------------------------------------------- - -sub credential { - - my ($self) = @_; - return $self->{'credential'}; - -} - -#------------------------------------------------------------------------------- - -sub feed { - - my ($self) = @_; - return $self->{'feed'}; - -} - -#------------------------------------------------------------------------------- - -sub file { - - my ($self) = @_; - return $self->{'file'}; - -} - -#------------------------------------------------------------------------------- - -sub plugin { - - my ($self) = @_; - return $self->{'plugin'}; - -} - -#------------------------------------------------------------------------------- - -sub plugin_family { - - my ($self) = @_; - return $self->{'plugin_family'}; - -} - -#------------------------------------------------------------------------------- - -sub policy { +foreach (@apis) { - my ($self) = @_; - return $self->{'policy'}; + my $helper = decamelize $_; -} - -#------------------------------------------------------------------------------- - -sub report { - - my ($self) = @_; - return $self->{'report'}; - -} - -#------------------------------------------------------------------------------- - -sub repository { - - my ($self) = @_; - return $self->{'repository'}; - -} - -#------------------------------------------------------------------------------- - -sub scan { - - my ($self) = @_; - return $self->{'scan'}; - -} - -#------------------------------------------------------------------------------- - -sub scan_result { - - my ($self) = @_; - return $self->{'scan_result'}; - -} - -#------------------------------------------------------------------------------- - -sub scanner { - - my ($self) = @_; - return $self->{'scanner'}; - -} - -#------------------------------------------------------------------------------- - -sub system { - - my ($self) = @_; - return $self->{'system'}; - -} - -#------------------------------------------------------------------------------- - -sub user { - - my ($self) = @_; - return $self->{'user'}; - -} - -#------------------------------------------------------------------------------- - -sub zone { + my $sub = sub { + my ($self) = @_; + return $self->{$helper}; + }; - my ($self) = @_; - return $self->{'zone'}; + no strict 'refs'; ## no critic + *{$helper} = $sub; } @@ -373,6 +233,10 @@ Return L instance. Return L instance. +=head2 status + +Return L instance. + =head2 system Return L instance. @@ -415,7 +279,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API.pm b/lib/Net/SecurityCenter/API.pm index 515bc49..85bcd0e 100644 --- a/lib/Net/SecurityCenter/API.pm +++ b/lib/Net/SecurityCenter/API.pm @@ -5,7 +5,7 @@ use strict; use Net::SecurityCenter::Error; -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; #------------------------------------------------------------------------------- # CONSTRUCTOR @@ -116,7 +116,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Analysis.pm b/lib/Net/SecurityCenter/API/Analysis.pm index 5447ff7..3ae6cfe 100644 --- a/lib/Net/SecurityCenter/API/Analysis.pm +++ b/lib/Net/SecurityCenter/API/Analysis.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { tool => { @@ -893,7 +893,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Credential.pm b/lib/Net/SecurityCenter/API/Credential.pm index f0a4ed8..8ab9398 100644 --- a/lib/Net/SecurityCenter/API/Credential.pm +++ b/lib/Net/SecurityCenter/API/Credential.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -181,7 +181,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Feed.pm b/lib/Net/SecurityCenter/API/Feed.pm index a52e0d5..2a4ae1b 100644 --- a/lib/Net/SecurityCenter/API/Feed.pm +++ b/lib/Net/SecurityCenter/API/Feed.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; #------------------------------------------------------------------------------- # METHODS @@ -250,7 +250,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/File.pm b/lib/Net/SecurityCenter/API/File.pm index 9f9f0a1..5dd6f7b 100644 --- a/lib/Net/SecurityCenter/API/File.pm +++ b/lib/Net/SecurityCenter/API/File.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; #------------------------------------------------------------------------------- # METHODS @@ -133,7 +133,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Plugin.pm b/lib/Net/SecurityCenter/API/Plugin.pm index f2014b0..d075a42 100644 --- a/lib/Net/SecurityCenter/API/Plugin.pm +++ b/lib/Net/SecurityCenter/API/Plugin.pm @@ -12,7 +12,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -71,22 +71,22 @@ sub list { }, filter_field => { allow => [ - 'copyright', 'description', 'exploitAvailable', 'family', - 'id', 'name', 'patchPubDate', 'patchModDate', - 'pluginPubDate', 'pluginModDate', 'sourceFile', 'type', - 'version', 'vulnPubDate', 'xrefs', 'xrefs:', - 'xrefs:ALAS', 'xrefs:APPLE-SA', 'xrefs:AUSCERT', 'xrefs:BID', - 'xrefs:CERT', 'xrefs:CERT-CC', 'xrefs:CERT-FI', 'xrefs:CERTA', - 'xrefs:CISCO-BUG-ID', 'xrefs:CISCO-SA', 'xrefs:CISCO-SR', 'xrefs:CLSA', - 'xrefs:CONECTIVA', 'xrefs:CVE', 'xrefs:CWE', 'xrefs:DSA', - 'xrefs:EDB-ID', 'xrefs:FEDORA', 'xrefs:FLSA', 'xrefs:FreeBSD', - 'xrefs:GLSA', 'xrefs:HP', 'xrefs:HPSB', 'xrefs:IAVA', - 'xrefs:IAVB', 'xrefs:IAVT', 'xrefs:ICS-ALERT', 'xrefs:ICSA', - 'xrefs:MDKSA', 'xrefs:MDVSA', 'xrefs:MGASA', 'xrefs:MSFT', - 'xrefs:MSVR', 'xrefs:NSFOCUS', 'xrefs:NessusID', 'xrefs:OSVDB', - 'xrefs:OWASP', 'xrefs:OpenPKG-SA', 'xrefs:RHSA', 'xrefs:SSA', - 'xrefs:Secunia', 'xrefs:SuSE', 'xrefs:TLSA', 'xrefs:TSLSA', - 'xrefs:USN', 'xrefs:VMSA', 'xrefs:zone-h' + 'copyright', 'description', 'exploitAvailable', 'family', + 'id', 'name', 'patchPubDate', 'patchModDate', + 'pluginPubDate', 'pluginModDate', 'sourceFile', 'type', + 'version', 'vulnPubDate', 'xrefs', 'xrefs:ALAS', + 'xrefs:APPLE-SA', 'xrefs:AUSCERT', 'xrefs:BID', 'xrefs:CERT', + 'xrefs:CERT-CC', 'xrefs:CERT-FI', 'xrefs:CERTA', 'xrefs:CISCO-BUG-ID', + 'xrefs:CISCO-SA', 'xrefs:CISCO-SR', 'xrefs:CLSA', 'xrefs:CONECTIVA', + 'xrefs:CVE', 'xrefs:CWE', 'xrefs:DSA', 'xrefs:EDB-ID', + 'xrefs:FEDORA', 'xrefs:FLSA', 'xrefs:FreeBSD', 'xrefs:GLSA', + 'xrefs:HP', 'xrefs:HPSB', 'xrefs:IAVA', 'xrefs:IAVB', + 'xrefs:IAVT', 'xrefs:ICS-ALERT', 'xrefs:ICSA', 'xrefs:MDKSA', + 'xrefs:MDVSA', 'xrefs:MGASA', 'xrefs:MSFT', 'xrefs:MSVR', + 'xrefs:NSFOCUS', 'xrefs:NessusID', 'xrefs:OSVDB', 'xrefs:OWASP', + 'xrefs:OpenPKG-SA', 'xrefs:RHSA', 'xrefs:SSA', 'xrefs:Secunia', + 'xrefs:SuSE', 'xrefs:TLSA', 'xrefs:TSLSA', 'xrefs:USN', + 'xrefs:VMSA', 'xrefs:zone-h' ], remap => 'filterField', }, @@ -304,7 +304,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/PluginFamily.pm b/lib/Net/SecurityCenter/API/PluginFamily.pm index fac6264..913ccc3 100644 --- a/lib/Net/SecurityCenter/API/PluginFamily.pm +++ b/lib/Net/SecurityCenter/API/PluginFamily.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -250,7 +250,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Policy.pm b/lib/Net/SecurityCenter/API/Policy.pm index 1e0d4b0..8c41f76 100644 --- a/lib/Net/SecurityCenter/API/Policy.pm +++ b/lib/Net/SecurityCenter/API/Policy.pm @@ -10,7 +10,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -239,7 +239,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Report.pm b/lib/Net/SecurityCenter/API/Report.pm index 22856f7..4456b75 100644 --- a/lib/Net/SecurityCenter/API/Report.pm +++ b/lib/Net/SecurityCenter/API/Report.pm @@ -10,7 +10,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -206,7 +206,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Repository.pm b/lib/Net/SecurityCenter/API/Repository.pm index e37b861..1dd7626 100644 --- a/lib/Net/SecurityCenter/API/Repository.pm +++ b/lib/Net/SecurityCenter/API/Repository.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -220,7 +220,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Scan.pm b/lib/Net/SecurityCenter/API/Scan.pm index 23ef5ab..57b72e5 100644 --- a/lib/Net/SecurityCenter/API/Scan.pm +++ b/lib/Net/SecurityCenter/API/Scan.pm @@ -10,7 +10,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -514,7 +514,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/ScanResult.pm b/lib/Net/SecurityCenter/API/ScanResult.pm index a59d0a5..37113eb 100644 --- a/lib/Net/SecurityCenter/API/ScanResult.pm +++ b/lib/Net/SecurityCenter/API/ScanResult.pm @@ -11,7 +11,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -275,15 +275,15 @@ sub import { }; my $tmpl = { - filename => {}, + filename => {}, dhcp_tracking => { remap => 'dhcpTracking', filter => \&sc_filter_int_to_bool, allow => qr/\d/, }, classify_mitigated_age => { remap => 'classifyMitigatedAge' }, - scan_vhost => { - remap => 'scanningVirtualHosts', + scan_vhost => { + remap => 'scanningVirtualHosts', filter => \&sc_filter_int_to_bool, allow => qr/\d/, }, @@ -300,7 +300,7 @@ sub import { $params->{'filename'} = $sc_filename; - $self->client->post("/scanResult/import", $params); + $self->client->post( "/scanResult/import", $params ); return 1; } @@ -635,7 +635,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Scanner.pm b/lib/Net/SecurityCenter/API/Scanner.pm index 6588b7d..f1845a2 100644 --- a/lib/Net/SecurityCenter/API/Scanner.pm +++ b/lib/Net/SecurityCenter/API/Scanner.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -237,7 +237,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Status.pm b/lib/Net/SecurityCenter/API/Status.pm new file mode 100644 index 0000000..bf17ef6 --- /dev/null +++ b/lib/Net/SecurityCenter/API/Status.pm @@ -0,0 +1,126 @@ +package Net::SecurityCenter::API::Status; + +use warnings; +use strict; + +use parent 'Net::SecurityCenter::API'; + +use Net::SecurityCenter::Utils qw(:all); + +our $VERSION = '0.205_01'; + +#------------------------------------------------------------------------------- +# METHODS +#------------------------------------------------------------------------------- + +sub status { + + my ( $self, %args ) = @_; + + my $tmpl = { + fields => { + filter => \&sc_filter_array_to_string + }, + raw => {}, + }; + + my $params = sc_check_params( $tmpl, \%args ); + my $raw = delete( $params->{'raw'} ); + my $status = $self->client->get( '/status', $params ); + + if ($raw) { + return $status; + } + + return sc_normalize_hash($status); + +} + +#------------------------------------------------------------------------------- + +1; + +__END__ +=pod + +=encoding UTF-8 + + +=head1 NAME + +Net::SecurityCenter::API::System - Perl interface to Tenable.sc (SecurityCenter) System REST API + + +=head1 SYNOPSIS + + use Net::SecurityCenter::REST; + use Net::SecurityCenter::API::System; + + my $sc = Net::SecurityCenter::REST->new('sc.example.org'); + + $sc->login('secman', 'password'); + + my $api = Net::SecurityCenter::API::Status->new($sc); + + $sc->logout(); + + +=head1 DESCRIPTION + +This module provides Perl scripts easy way to interface the System REST API of Tenable.sc +(SecurityCenter). + +For more information about the Tenable.sc (SecurityCenter) REST API follow the online documentation: + +L + + +=head1 CONSTRUCTOR + +=head2 Net::SecurityCenter::API::Status->new ( $client ) + +Create a new instance of B using L class. + + +=head1 METHODS + +=head2 status + +Gets a collection of status information, including license. + + +=head1 SUPPORT + +=head2 Bugs / Feature Requests + +Please report any bugs or feature requests through the issue tracker +at L. +You will be notified automatically of any progress on your issue. + +=head2 Source Code + +This is open source software. The code repository is available for +public review and contribution under the terms of the license. + +L + + git clone https://github.com/giterlizzi/perl-Net-SecurityCenter.git + + +=head1 AUTHOR + +=over 4 + +=item * Giuseppe Di Terlizzi + +=back + + +=head1 LICENSE AND COPYRIGHT + +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +=cut diff --git a/lib/Net/SecurityCenter/API/System.pm b/lib/Net/SecurityCenter/API/System.pm index 67005e1..de5033a 100644 --- a/lib/Net/SecurityCenter/API/System.pm +++ b/lib/Net/SecurityCenter/API/System.pm @@ -7,7 +7,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; #------------------------------------------------------------------------------- # METHODS @@ -17,22 +17,11 @@ sub get_status { my ( $self, %args ) = @_; - my $tmpl = { - fields => { - filter => \&sc_filter_array_to_string - }, - raw => {}, - }; - - my $params = sc_check_params( $tmpl, \%args ); - my $raw = delete( $params->{'raw'} ); - my $status = $self->client->get( '/status', $params ); - - if ($raw) { - return $status; - } + warnings::warnif( 'deprecated', + 'method "Net::SecurityCenter::API::System->get_status" is deprecated use "Net::SecurityCenter::API::Status->status"' + ); - return sc_normalize_hash($status); + return; } @@ -178,7 +167,7 @@ Create a new instance of B using L->status method. =head2 get_info @@ -230,7 +219,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/User.pm b/lib/Net/SecurityCenter/API/User.pm index 9bf1061..e1a3f99 100644 --- a/lib/Net/SecurityCenter/API/User.pm +++ b/lib/Net/SecurityCenter/API/User.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -169,7 +169,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/API/Zone.pm b/lib/Net/SecurityCenter/API/Zone.pm index 5889945..c076003 100644 --- a/lib/Net/SecurityCenter/API/Zone.pm +++ b/lib/Net/SecurityCenter/API/Zone.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; my $common_template = { @@ -161,7 +161,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/Error.pm b/lib/Net/SecurityCenter/Error.pm index 56d8e18..37b0644 100644 --- a/lib/Net/SecurityCenter/Error.pm +++ b/lib/Net/SecurityCenter/Error.pm @@ -5,7 +5,7 @@ use strict; use overload q|""| => 'message', fallback => 1; -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; #------------------------------------------------------------------------------- # CONSTRUCTOR @@ -99,7 +99,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/REST.pm b/lib/Net/SecurityCenter/REST.pm index 0ea24eb..623d426 100644 --- a/lib/Net/SecurityCenter/REST.pm +++ b/lib/Net/SecurityCenter/REST.pm @@ -4,16 +4,15 @@ use warnings; use strict; use Carp; -use Data::Dumper; use English qw( -no_match_vars ); use HTTP::Cookies; use JSON; use LWP::UserAgent; use Net::SecurityCenter::Error; -use Net::SecurityCenter::Utils qw(:all); +use Net::SecurityCenter::Utils qw(trim dumper); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; #------------------------------------------------------------------------------- # CONSTRUCTOR @@ -84,32 +83,6 @@ sub _agent { #------------------------------------------------------------------------------- -sub _dumper { - - my (@data) = @_; - - local $Data::Dumper::Terse = 1; - local $Data::Dumper::Indent = 0; - - return Dumper(@data); - -} - -#------------------------------------------------------------------------------- - -sub _trim { - - my ($string) = @_; - - return if ( !$string ); - - $string =~ s/^\s+|\s+$//g; - return $string; - -} - -#------------------------------------------------------------------------------- - sub check { my ($self) = @_; @@ -182,7 +155,7 @@ sub request { $method = uc($method); $path =~ s{^/}{}; - if ( $method !~ m/(GET|POST|PUT|DELETE|PATCH)/ ) { + if ( $method !~ m/(?:GET|POST|PUT|DELETE|PATCH)/ ) { carp( $method, ' is an unsupported request method' ); croak( 'Usage: ' . __PACKAGE__ . '->request(GET|POST|PUT|DELETE|PATCH, $PATH, [\%PARAMS])' ); } @@ -200,7 +173,7 @@ sub request { # Don't log credential if ( $path !~ /token/ ) { - $self->logger( 'info', "Params: " . _dumper($params) ); + $self->logger( 'info', "Params: " . dumper($params) ); } } @@ -273,7 +246,7 @@ sub request { } elsif ( $result->{'error_msg'} ) { - my $error_msg = _trim( $result->{'error_msg'} ); + my $error_msg = trim( $result->{'error_msg'} ); if ( $self->{'logger'} ) { $self->logger( 'error', $error_msg ); @@ -291,7 +264,7 @@ sub request { if ( $is_json && exists( $result->{'error_msg'} ) ) { - my $error_msg = _trim( $result->{'error_msg'} ); + my $error_msg = trim( $result->{'error_msg'} ); if ( $self->{'logger'} ) { $self->logger( 'error', $error_msg ); @@ -320,7 +293,7 @@ sub upload { my ( $self, $file ) = @_; ( @_ == 2 ) - or croak( 'Usage: ' . __PACKAGE__ . 'upload( $FILE )' ); + or croak( 'Usage: ' . __PACKAGE__ . '->upload( $FILE )' ); return $self->request( 'POST', '/file/upload', { 'file' => $file } ); @@ -407,6 +380,8 @@ sub DESTROY { $self->logout(); } + return; + } #------------------------------------------------------------------------------- @@ -526,7 +501,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/lib/Net/SecurityCenter/Utils.pm b/lib/Net/SecurityCenter/Utils.pm index 6d9593b..2b45ebb 100644 --- a/lib/Net/SecurityCenter/Utils.pm +++ b/lib/Net/SecurityCenter/Utils.pm @@ -6,9 +6,10 @@ use strict; use Carp; use Params::Check qw(allow); use Time::Piece; +use Data::Dumper (); use Exporter qw(import); -our $VERSION = '0.205'; +our $VERSION = '0.205_01'; our @EXPORT_OK = qw( sc_check_params @@ -20,6 +21,10 @@ our @EXPORT_OK = qw( sc_normalize_array sc_method_usage sc_schedule + + decamelize + dumper + trim ); our %EXPORT_TAGS = ( all => \@EXPORT_OK ); @@ -50,14 +55,40 @@ our $NESSUS_SCANNER_STATUS = { }; #------------------------------------------------------------------------------- -# FUNCTIONS +# COMMON UTILS +#------------------------------------------------------------------------------- + +sub decamelize { + return join( '_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/, shift ); +} + +#------------------------------------------------------------------------------- + +sub dumper { + return Data::Dumper->new( [@_] )->Indent(1)->Sortkeys(1)->Terse(1)->Useqq(1)->Dump; +} + +#------------------------------------------------------------------------------- + +sub trim { + + my ($string) = @_; + + return if ( !$string ); + + $string =~ s/^\s+|\s+$//g; + return $string; + +} + +#------------------------------------------------------------------------------- +# COMMON CLASS UTILS #------------------------------------------------------------------------------- sub sc_schedule { my (%args) = @_; - use Data::Dumper; - print Dumper( \%args ); + my $tmpl = { type => { allow => [ 'dependent', 'ical', 'never', 'rollover', 'template', 'now' ], @@ -95,6 +126,8 @@ sub sc_schedule { } +#------------------------------------------------------------------------------- + sub sc_method_usage { my ($template) = @_; @@ -437,7 +470,7 @@ L =head1 LICENSE AND COPYRIGHT -This software is copyright (c) 2018-2019 by Giuseppe Di Terlizzi. +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. diff --git a/t/00-load.t b/t/00-load.t index 7946957..ad2ca11 100644 --- a/t/00-load.t +++ b/t/00-load.t @@ -20,6 +20,7 @@ require_ok('Net::SecurityCenter::API::Repository'); require_ok('Net::SecurityCenter::API::Scan'); require_ok('Net::SecurityCenter::API::ScanResult'); require_ok('Net::SecurityCenter::API::Scanner'); +require_ok('Net::SecurityCenter::API::Status'); require_ok('Net::SecurityCenter::API::System'); require_ok('Net::SecurityCenter::API::User'); require_ok('Net::SecurityCenter::API::Zone'); diff --git a/t/99-local.t b/t/99-local.t index 8b6e29a..3b24655 100644 --- a/t/99-local.t +++ b/t/99-local.t @@ -117,6 +117,16 @@ sub _test { ok( $sc->login( 'secman', 'password' ), 'Login into SecurityCenter' ); + subtest( + 'Status API' => sub { + + my $system_info = $sc->status->status; + + ok( $system_info->{'licenseStatus'}, 'SecurityCenter license' ); + + } + ); + subtest( 'System API' => sub { @@ -144,6 +154,51 @@ sub _test { } ); + subtest( + 'Scan Result API' => sub { + + ok( $sc->scan_result->list, 'Scan Result API: Get the list of scans' ); + + ok( $sc->scan_result->get( id => 11 ), 'Scan Result API: Get Scan Result' ); + + cmp_ok( $sc->scan_result->status( id => 11 ), 'eq', 'completed', 'Get Scan Result status' ); + cmp_ok( $sc->scan_result->progress( id => 11 ), '==', 100, 'Get Scan Result progress' ); + + ok( $sc->scan_result->pause( id => 86 ), 'Scan Result API: Pause scan' ); + ok( $sc->scan_result->resume( id => 86 ), 'Scan Result API: Resume scan' ); + ok( $sc->scan_result->stop( id => 86 ), 'Scan Result API: Stop scan' ); + } + ); + + subtest( + 'Plugin API' => sub { + + my $plugin = $sc->plugin->get( id => 0 ); + + ok( $plugin, 'Plugin API: Get Plugin' ); + cmp_ok( $plugin->{'id'}, '==', 0, 'Get Plugin ID' ); + cmp_ok( $plugin->{'name'}, 'eq', 'Open Port', 'Get Plugin Name' ); + + ok( $sc->plugin->list, 'Plugin API: Get Plugin List' ); + + } + ); + + subtest( + 'Plugin Famiy API' => sub { + + my $plugin_family = $sc->plugin_family->get( id => 1000030 ); + + ok( $plugin_family, 'Plugin Family API: Get Plugin Family' ); + cmp_ok( $plugin_family->{'id'}, '==', 1000030, 'Get Plugin Family ID' ); + cmp_ok( $plugin_family->{'name'}, 'eq', 'Malware', 'Get Plugin Family Name' ); + cmp_ok( $plugin_family->{'type'}, 'eq', 'passive', 'Get Plugin Family Type' ); + + ok( $sc->plugin_family->list, 'Plugin Family API: Get Plugin List' ); + + } + ); + ok( $sc->logout, 'Logout from SecurityCenter' ); done_testing(); @@ -163,22 +218,22 @@ sub new { sub info { my $self = shift; - Test::More::note(@_); + Test::More::note( '[info] ', @_ ); } sub debug { my $self = shift; - Test::More::note(@_); + Test::More::note( '[debug] ', @_ ); } sub warning { my $self = shift; - Test::More::note(@_); + Test::More::note( '[warning] ', @_ ); } sub error { my $self = shift; - Test::More::note(@_); + Test::More::note( '[error] ', @_ ); } 1; diff --git a/t/mock/rest-plugin-0-get.json b/t/mock/rest-plugin-0-get.json new file mode 100644 index 0000000..a38720b --- /dev/null +++ b/t/mock/rest-plugin-0-get.json @@ -0,0 +1,123 @@ +{ + "type": "regular", + "response": { + "id": "0", + "name": "Open Port", + "description": "", + "type": "active", + "copyright": "", + "version": "", + "sourceFile": "", + "dependencies": "", + "requiredPorts": "", + "requiredUDPPorts": "", + "cpe": "", + "srcPort": null, + "dstPort": null, + "protocol": "", + "riskFactor": "", + "solution": "", + "seeAlso": "", + "synopsis": "", + "checkType": "", + "exploitEase": "", + "exploitAvailable": "", + "exploitFrameworks": "", + "cvssVector": "", + "cvssVectorBF": "0", + "baseScore": null, + "temporalScore": null, + "cvssV3Vector": "", + "cvssV3VectorBF": "0", + "cvssV3BaseScore": null, + "cvssV3TemporalScore": null, + "vprScore": "6.1", + "vprContext": [ + { + "id": "age_of_vuln", + "name": "Vulnerability Age", + "value": "730 days +", + "type": "string" + }, + { + "id": "cvssV3_impactScore", + "name": "CvssV3 Impact Score", + "value": 5.5, + "type": "number" + }, + { + "id": "exploit_code_maturity", + "name": "Exploit Code Maturity", + "value": "PoC", + "type": "string" + }, + { + "id": "generated_at", + "name": "Generated At", + "value": 1551695021993, + "type": "number" + }, + { + "id": "predicted_impactScore", + "name": "Predicted Impact Score", + "value": true, + "type": "boolean" + }, + { + "id": "product_coverage", + "name": "Product Coverage", + "value": "Low", + "type": "string" + }, + { + "id": "threat_model_type", + "name": "Threat Model Type", + "value": "non_early_life", + "type": "string" + }, + { + "id": "threat_model_version", + "name": "Threat Model Version", + "value": "v0", + "type": "string" + }, + { + "id": "threat_intensity_last_28", + "name": "Threat Intensity Last 28", + "value": "Very Low", + "type": "string" + }, + { + "id": "threat_recency", + "name": "Threat Recency", + "value": "No recorded events", + "type": "string" + }, + { + "id": "threat_sources_last_28", + "name": "Threat Sources Last 28", + "value": "No recorded events", + "type": "string" + } + ], + "stigSeverity": null, + "pluginPubDate": "-1", + "pluginModDate": "-1", + "patchPubDate": "-1", + "patchModDate": "-1", + "vulnPubDate": "-1", + "modifiedTime": "1400516102", + "md5": "", + "xrefs": "", + "source": "", + "family": { + "id": "42", + "name": "Port scanners", + "type": "active" + } + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1408727888 +} \ No newline at end of file diff --git a/t/mock/rest-plugin-get.json b/t/mock/rest-plugin-get.json new file mode 100644 index 0000000..f29ed25 --- /dev/null +++ b/t/mock/rest-plugin-get.json @@ -0,0 +1,19 @@ +{ + "type": "regular", + "response": [ + { + "id": "15000", + "name": "Debian DSA-163-1 : mhonarc - XSS", + "description": "Jason Molenda and Hiromitsu Takagi foundways to exploit cross site\nscripting bugs in mhonarc, a mail to HTML converter. When processing\nmaliciously crafted mails of type text\/html mhonarc does not\ndeactivate all scripting parts properly. This is fixed in upstream\nversion 2.5.3.\n\nIf you are worried about security, it is recommended that you disable\nsupport of text\/html messages in your mail archives. There is no\nguarantee that the mhtxthtml.pl library is robust enough to eliminate\nall possible exploits that can occur with HTML data.\n\nTo exclude HTML data, you can use the MIMEEXCS resource. For example :\n\n text\/html text\/x-html <\/MIMEExcs>\n\nThe type 'text\/x-html' is probably not used any more, but is good to\ninclude it, just-in-case.\n\nIf you are concerned that this could block out the entire contents of\nsome messages, then you could do the following instead :\n\n text\/html; m2h_text_plain::filter; mhtxtplain.pl\n text\/x-html; m2h_text_plain::filter; mhtxtplain.pl <\/MIMEFilters>\n\nThis treats the HTML as text\/plain.\n\nThe above problems have been fixed in version 2.5.2-1.1 for the\ncurrent stable distribution (woody), in version 2.4.4-1.1 for the old\nstable distribution (potato) and in version 2.5.11-1 for the unstable\ndistribution (sid)." + }, + { + "id": "15004", + "name": "Debian DSA-167-1 : kdelibs - XSS", + "description": "A cross site scripting problem has been discovered in Konqueror, a\nfamous browser for KDE and other programs using KHTML. The KDE team\nreportsthat Konqueror's cross site scripting protection fails to\ninitialize the domains on sub-(i)frames correctly. As a result,\nJavaScript is able to access any foreign subframe which is defined in\nthe HTML source. Users of Konqueror and other KDE software that uses\nthe KHTML rendering engine may become victim of a cookie stealing and\nother cross site scripting attacks." + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1411668488 +} \ No newline at end of file diff --git a/t/mock/rest-pluginFamily-1000030-get.json b/t/mock/rest-pluginFamily-1000030-get.json new file mode 100644 index 0000000..fab3864 --- /dev/null +++ b/t/mock/rest-pluginFamily-1000030-get.json @@ -0,0 +1,14 @@ +{ + "type": "regular", + "response": { + "id": "1000030", + "name": "Malware", + "type": "passive", + "plugins": [], + "count": 0 + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1408728549 +} \ No newline at end of file diff --git a/t/mock/rest-pluginFamily-get.json b/t/mock/rest-pluginFamily-get.json new file mode 100644 index 0000000..5ae59e8 --- /dev/null +++ b/t/mock/rest-pluginFamily-get.json @@ -0,0 +1,357 @@ +{ + "type": "regular", + "response": [ + { + "id": "9", + "name": "AIX Local Security Checks" + }, + { + "id": "1000022", + "name": "Abuse" + }, + { + "id": "54", + "name": "Amazon Linux Local Security Checks" + }, + { + "id": "35", + "name": "Backdoors" + }, + { + "id": "1000001", + "name": "Backdoors" + }, + { + "id": "49", + "name": "Brute force attacks" + }, + { + "id": "1000002", + "name": "CGI" + }, + { + "id": "6", + "name": "CGI abuses" + }, + { + "id": "26", + "name": "CGI abuses : XSS" + }, + { + "id": "33", + "name": "CISCO" + }, + { + "id": "18", + "name": "CentOS Local Security Checks" + }, + { + "id": "1000031", + "name": "Cloud Services" + }, + { + "id": "37", + "name": "DNS" + }, + { + "id": "1000004", + "name": "DNS Servers" + }, + { + "id": "1000024", + "name": "Data Leakage" + }, + { + "id": "1000003", + "name": "Database" + }, + { + "id": "31", + "name": "Databases" + }, + { + "id": "3", + "name": "Debian Local Security Checks" + }, + { + "id": "25", + "name": "Default Unix Accounts" + }, + { + "id": "22", + "name": "Denial of Service" + }, + { + "id": "19", + "name": "FTP" + }, + { + "id": "1000027", + "name": "FTP Clients" + }, + { + "id": "1000006", + "name": "FTP Servers" + }, + { + "id": "5", + "name": "Fedora Local Security Checks" + }, + { + "id": "1000005", + "name": "Finger" + }, + { + "id": "44", + "name": "Finger abuses" + }, + { + "id": "34", + "name": "Firewalls" + }, + { + "id": "13", + "name": "FreeBSD Local Security Checks" + }, + { + "id": "40", + "name": "Gain a shell remotely" + }, + { + "id": "27", + "name": "Gain root remotely" + }, + { + "id": "30", + "name": "General" + }, + { + "id": "1000007", + "name": "Generic" + }, + { + "id": "7", + "name": "Gentoo Local Security Checks" + }, + { + "id": "2", + "name": "HP-UX Local Security Checks" + }, + { + "id": "1000008", + "name": "IMAP Servers" + }, + { + "id": "1000010", + "name": "IRC Clients" + }, + { + "id": "1000026", + "name": "IRC Servers" + }, + { + "id": "1000009", + "name": "Internet Messengers" + }, + { + "id": "1000028", + "name": "Internet Services" + }, + { + "id": "50", + "name": "Junos Local Security Checks" + }, + { + "id": "21", + "name": "MacOS X Local Security Checks" + }, + { + "id": "1000030", + "name": "Malware" + }, + { + "id": "16", + "name": "Mandrake Local Security Checks" + }, + { + "id": "47", + "name": "Mandriva Local Security Checks" + }, + { + "id": "23", + "name": "Misc." + }, + { + "id": "52", + "name": "Mobile Devices" + }, + { + "id": "1000029", + "name": "Mobile Devices" + }, + { + "id": "0", + "name": "N\/A" + }, + { + "id": "46", + "name": "NIS" + }, + { + "id": "43", + "name": "Netware" + }, + { + "id": "1000011", + "name": "Operating System Detection" + }, + { + "id": "53", + "name": "Oracle Linux Local Security Checks" + }, + { + "id": "1000013", + "name": "POP Server" + }, + { + "id": "55", + "name": "Palo Alto Local Security Checks" + }, + { + "id": "32", + "name": "Peer-To-Peer File Sharing" + }, + { + "id": "1000012", + "name": "Peer-To-Peer File Sharing" + }, + { + "id": "1000023", + "name": "Policy" + }, + { + "id": "39", + "name": "Policy Compliance" + }, + { + "id": "42", + "name": "Port scanners" + }, + { + "id": "28", + "name": "RPC" + }, + { + "id": "1000014", + "name": "RPC" + }, + { + "id": "1", + "name": "Red Hat Local Security Checks" + }, + { + "id": "17", + "name": "Remote file access" + }, + { + "id": "36", + "name": "SCADA" + }, + { + "id": "1000025", + "name": "SCADA" + }, + { + "id": "1000016", + "name": "SMTP Clients" + }, + { + "id": "1000017", + "name": "SMTP Servers" + }, + { + "id": "12", + "name": "SMTP problems" + }, + { + "id": "45", + "name": "SNMP" + }, + { + "id": "1000018", + "name": "SNMP Traps" + }, + { + "id": "1000019", + "name": "SSH" + }, + { + "id": "1000015", + "name": "Samba" + }, + { + "id": "51", + "name": "Scientific Linux Local Security Checks" + }, + { + "id": "24", + "name": "Service detection" + }, + { + "id": "41", + "name": "Settings" + }, + { + "id": "15", + "name": "Slackware Local Security Checks" + }, + { + "id": "4", + "name": "Solaris Local Security Checks" + }, + { + "id": "8", + "name": "SuSE Local Security Checks" + }, + { + "id": "14", + "name": "Ubuntu Local Security Checks" + }, + { + "id": "38", + "name": "Useless services" + }, + { + "id": "48", + "name": "VMware ESX Local Security Checks" + }, + { + "id": "1000020", + "name": "Web Clients" + }, + { + "id": "11", + "name": "Web Servers" + }, + { + "id": "1000021", + "name": "Web Servers" + }, + { + "id": "20", + "name": "Windows" + }, + { + "id": "10", + "name": "Windows : Microsoft Bulletins" + }, + { + "id": "29", + "name": "Windows : User management" + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1408728112 +} \ No newline at end of file diff --git a/t/mock/rest-scanResult-11-get.json b/t/mock/rest-scanResult-11-get.json new file mode 100644 index 0000000..e7c9bfd --- /dev/null +++ b/t/mock/rest-scanResult-11-get.json @@ -0,0 +1,131 @@ +{ + "type" : "regular", + "response" : { + "id" : "11", + "name" : "Weekly Scan", + "description" : "", + "details" : "Policy 'Basic 1'", + "status" : "Completed", + "importStatus" : "Error", + "importStart" : "1424815361", + "importFinish" : "-1", + "importDuration" : -1, + "downloadAvailable" : "false", + "downloadFormat" : "v2", + "dataFormat" : "IPv4", + "resultType" : "active", + "resultSource" : "internal", + "running" : "false", + "errorDetails" : "", + "importErrorDetails" : "Scan import error (code #139).", + "totalIPs" : "200", + "scannedIPs" : "200", + "startTime" : "1424815208", + "finishTime" : "1424815360", + "scanDuration" : 152, + "completedIPs" : "200", + "completedChecks" : "10600", + "totalChecks" : "10600", + "progress" : { + "completedIPs" : "200", + "completedChecks" : "10600", + "totalChecks" : "10600", + "checksPerHost" : "53", + "totalIPs" : "200", + "runState" : "Stopped", + "scanningIPs" : "", + "scanningSize" : 0, + "scannedIPs" : "192.168.0.0", + "scannedSize" : 29, + "awaitingDownloadIPs" : "", + "awaitingDownloadSize" : 0, + "distributedSize" : 200, + "status" : "Completed", + "deadHostSize" : 171, + "deadHostIPs" : "192.168.0.0", + "scanners" : [ + { + "id" : "12", + "loadAvg" : "0.0", + "chunkCompleted" : "120", + "completedChecks" : "6360", + "chunks" : [ + { + "id" : "1", + "name" : "fc136656-2ddd-58ab-bccc-78ad4c125567-130064/Chunk 1.", + "ips" : "192.168.0.0", + "size" : "1", + "completedHosts" : "0", + "completedChecks" : "1", + "scanningIPs" : "192.168.0.0", + "canningSize" : "1", + "scannedIPs" : "", + "scannedSize" : "0", + "status" : "Running", + "startTime" : "1455735612", + "endTime" : "1455736826", + "deadHostIPs" : "", + "deadHostSize" : 0 + } + ], + "scannedSize" : 11, + "scannedIPs" : "192.168.0.0", + "scanningSize" : 0, + "scanningIPs" : "", + "awaitingDownloadSize" : 0, + "awaitingDownloadIPs" : "", + "deadHostSize" : 109, + "deadHostIPs" : "192.168.0.0", + "distributedSize" : 120 + }, + { + "id" : "15", + "loadAvg" : "0.0", + "chunkCompleted" : "80", + "completedChecks" : "4240", + "chunks" : [], + "scannedSize" : 18, + "scannedIPs" : "192.168.0.0", + "scanningSize" : 0, + "scanningIPs" : "", + "awaitingDownloadSize" : 0, + "awaitingDownloadIPs" : "", + "deadHostSize" : 62, + "deadHostIPs" : "192.168.0.0", + "distributedSize" : 80 + } + ] + }, + "initiator" : { + "id" : "1", + "username" : "head", + "firstname" : "Security Manager", + "lastname" : "" + }, + "owner" : { + "id" : "1", + "username" : "head", + "firstname" : "Security Manager", + "lastname" : "" + }, + "ownerGroup" : { + "id" : "0", + "name" : "Full Access", + "description" : "Full Access group" + }, + "scan" : { + "id" : -1, + "name" : "", + "description" : "" + }, + "repository" : { + "id" : "38", + "name" : "jm ipv4", + "description" : "copied from 97" + } + }, + "error_code" : 0, + "error_msg" : "", + "warnings" : [], + "timestamp" : 1426878968 +} \ No newline at end of file diff --git a/t/mock/rest-scanResult-86-pause-post.json b/t/mock/rest-scanResult-86-pause-post.json new file mode 100644 index 0000000..44b6eec --- /dev/null +++ b/t/mock/rest-scanResult-86-pause-post.json @@ -0,0 +1,67 @@ +{ + "type": "regular", + "response": { + "id": "86", + "resultsSyncID": "-1", + "jobID": "180641", + "name": "test scan", + "description": "", + "details": "BNS", + "status": "Running", + "importStatus": "No Results", + "importStart": "-1", + "importFinish": "-1", + "diagnosticAvailable": "false", + "downloadAvailable": "false", + "downloadFormat": "v2", + "dataFormat": "IPv4", + "resultType": "active", + "resultSource": "internal", + "running": "true", + "errorDetails": "", + "importErrorDetails": "", + "totalIPs": "256", + "scannedIPs": "0", + "startTime": "1554407208", + "finishTime": "-1", + "createdTime": "1554407202", + "scanDuration": "794", + "importDuration": "-1", + "completedIPs": "0", + "completedChecks": "0", + "totalChecks": "25600", + "canUse": "true", + "canManage": "true", + "initiator": { + "id": "1", + "username": "test user", + "firstname": "", + "lastname": "" + }, + "owner": { + "id": "1", + "username": "test user", + "firstname": "", + "lastname": "" + }, + "scan": { + "id": "18", + "name": "test scan", + "description": "" + }, + "repository": { + "id": "516", + "name": "repo1", + "description": "" + }, + "ownerGroup": { + "id": "0", + "name": "Full Access", + "description": "Full Access group" + } + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1406917515 +} \ No newline at end of file diff --git a/t/mock/rest-scanResult-86-resume-post.json b/t/mock/rest-scanResult-86-resume-post.json new file mode 100644 index 0000000..bd638d3 --- /dev/null +++ b/t/mock/rest-scanResult-86-resume-post.json @@ -0,0 +1,67 @@ +{ + "type": "regular", + "response": { + "id": "86", + "resultsSyncID": "-1", + "jobID": "180641", + "name": "test scan", + "description": "", + "details": "BNS", + "status": "Paused", + "importStatus": "No Results", + "importStart": "-1", + "importFinish": "-1", + "diagnosticAvailable": "false", + "downloadAvailable": "false", + "downloadFormat": "v2", + "dataFormat": "IPv4", + "resultType": "active", + "resultSource": "internal", + "running": "true", + "errorDetails": "", + "importErrorDetails": "", + "totalIPs": "256", + "scannedIPs": "0", + "startTime": "1554407208", + "finishTime": "-1", + "createdTime": "1554407202", + "scanDuration": "794", + "importDuration": "-1", + "completedIPs": "0", + "completedChecks": "0", + "totalChecks": "25600", + "canUse": "true", + "canManage": "true", + "initiator": { + "id": "1", + "username": "test user", + "firstname": "", + "lastname": "" + }, + "owner": { + "id": "1", + "username": "test user", + "firstname": "", + "lastname": "" + }, + "scan": { + "id": "18", + "name": "test scan", + "description": "" + }, + "repository": { + "id": "516", + "name": "repo1", + "description": "" + }, + "ownerGroup": { + "id": "0", + "name": "Full Access", + "description": "Full Access group" + } + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1406917515 +} \ No newline at end of file diff --git a/t/mock/rest-scanResult-86-stop-post.json b/t/mock/rest-scanResult-86-stop-post.json new file mode 100644 index 0000000..1e071ec --- /dev/null +++ b/t/mock/rest-scanResult-86-stop-post.json @@ -0,0 +1,67 @@ +{ + "type": "regular", + "response": { + "id": "86", + "resultsSyncID": "-1", + "jobID": "180641", + "name": "test scan", + "description": "", + "details": "BNS", + "status": "Running", + "importStatus": "No Results", + "importStart": "-1", + "importFinish": "-1", + "diagnosticAvailable": "false", + "downloadAvailable": "false", + "downloadFormat": "v2", + "dataFormat": "IPv4", + "resultType": "active", + "resultSource": "internal", + "running": "true", + "errorDetails": "", + "importErrorDetails": "", + "totalIPs": "256", + "scannedIPs": "0", + "startTime": "1554407208", + "finishTime": "-1", + "createdTime": "1554407202", + "scanDuration": "1008", + "importDuration": "-1", + "completedIPs": "20", + "completedChecks": "2008", + "totalChecks": "25600", + "canUse": "true", + "canManage": "true", + "initiator": { + "id": "1", + "username": "test user", + "firstname": "", + "lastname": "" + }, + "owner": { + "id": "1", + "username": "test user", + "firstname": "", + "lastname": "" + }, + "scan": { + "id": "18", + "name": "test scan", + "description": "" + }, + "repository": { + "id": "516", + "name": "repo1", + "description": "" + }, + "ownerGroup": { + "id": "0", + "name": "Full Access", + "description": "Full Access group" + } + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1406918252 +} \ No newline at end of file diff --git a/t/mock/rest-scanResult-get.json b/t/mock/rest-scanResult-get.json new file mode 100644 index 0000000..a9d83f5 --- /dev/null +++ b/t/mock/rest-scanResult-get.json @@ -0,0 +1,25 @@ +{ + "type": "regular", + "response": { + "usable": [ + { + "id": "1", + "name": "Test Scan", + "description": "", + "status": "Completed" + } + ], + "manageable": [ + { + "id": "1", + "name": "Test Scan", + "description": "", + "status": "Completed" + } + ] + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1407249641 +} \ No newline at end of file diff --git a/t/mock/rest-status-get.json b/t/mock/rest-status-get.json new file mode 100644 index 0000000..b1d1eca --- /dev/null +++ b/t/mock/rest-status-get.json @@ -0,0 +1,37 @@ +{ + "error_code" : 0, + "error_msg" : "", + "response" : { + "LCEPluginSubscriptionStatus" : "Unconfigured", + "activeIPs" : "0", + "feedUpdates" : { + "stale" : "true", + "updateTime" : "1400514960" + }, + "jobd" : "Running", + "licenseStatus" : "Valid", + "licensedIPs" : "1000", + "noLCEs" : "true", + "noReps" : "true", + "passivePluginSubscriptionStatus" : null, + "pluginSubscriptionStatus" : "Unconfigured", + "pluginUpdates" : { + "active" : { + "pluginCurrentSet" : "201307080915", + "stale" : "true", + "updateTime" : "1373297148" + }, + "lce" : { + "stale" : "true", + "updateTime" : "0" + }, + "passive" : { + "stale" : "true", + "updateTime" : "1373297113" + } + } + }, + "timestamp" : 1405023348, + "type" : "regular", + "warnings" : [] +} From 9de94abc903e6e8794f6e2a6ce7f6db253dd4561 Mon Sep 17 00:00:00 2001 From: Giuseppe Di Terlizzi Date: Tue, 11 Feb 2020 17:46:56 +0100 Subject: [PATCH 2/6] Improved API test --- t/99-local.t | 61 ++++++++++++---- t/mock/rest-pluginFamily-2-plugins-get.json | 24 +++++++ t/mock/rest-scan-get.json | 80 ++++++++++----------- t/mock/rest-scanner-5-get.json | 54 ++++++++++++++ t/mock/rest-scanner-5-health-get.json | 57 +++++++++++++++ t/mock/rest-scanner-get.json | 39 ++++++++++ t/mock/rest-zone-5-get.json | 32 +++++++++ t/mock/rest-zone-get.json | 46 ++++++++++++ 8 files changed, 340 insertions(+), 53 deletions(-) create mode 100644 t/mock/rest-pluginFamily-2-plugins-get.json create mode 100644 t/mock/rest-scanner-5-get.json create mode 100644 t/mock/rest-scanner-5-health-get.json create mode 100644 t/mock/rest-scanner-get.json create mode 100644 t/mock/rest-zone-5-get.json create mode 100644 t/mock/rest-zone-get.json diff --git a/t/99-local.t b/t/99-local.t index 3b24655..2cd4d8a 100644 --- a/t/99-local.t +++ b/t/99-local.t @@ -117,6 +117,8 @@ sub _test { ok( $sc->login( 'secman', 'password' ), 'Login into SecurityCenter' ); + is( $sc->error, undef, 'Check errors' ); + subtest( 'Status API' => sub { @@ -139,14 +141,14 @@ sub _test { } ); - ok( $sc->scan->list, 'Scan API: Get list of Active Scan' ); - subtest( 'Scan API' => sub { + ok( $sc->scan->list, 'Get list of Active Scan' ); + my $scan = $sc->scan->get( id => 4 ); - ok( $scan, 'Scan API: Get Active Scan' ); + ok( $scan, 'Get Active Scan' ); cmp_ok( $scan->{'id'}, '==', 4, 'Get Scan ID' ); cmp_ok( $scan->{'policy'}->{'id'}, '==', 1000002, 'Get Scan Policy ID' ); cmp_ok( $sc->scan->launch( id => 2 ), '==', 3, 'Launch Scan ID' ); @@ -157,16 +159,16 @@ sub _test { subtest( 'Scan Result API' => sub { - ok( $sc->scan_result->list, 'Scan Result API: Get the list of scans' ); + ok( $sc->scan_result->list, 'Get the list of scans' ); - ok( $sc->scan_result->get( id => 11 ), 'Scan Result API: Get Scan Result' ); + ok( $sc->scan_result->get( id => 11 ), 'Get Scan Result' ); cmp_ok( $sc->scan_result->status( id => 11 ), 'eq', 'completed', 'Get Scan Result status' ); cmp_ok( $sc->scan_result->progress( id => 11 ), '==', 100, 'Get Scan Result progress' ); - ok( $sc->scan_result->pause( id => 86 ), 'Scan Result API: Pause scan' ); - ok( $sc->scan_result->resume( id => 86 ), 'Scan Result API: Resume scan' ); - ok( $sc->scan_result->stop( id => 86 ), 'Scan Result API: Stop scan' ); + ok( $sc->scan_result->pause( id => 86 ), 'Pause scan' ); + ok( $sc->scan_result->resume( id => 86 ), 'Resume scan' ); + ok( $sc->scan_result->stop( id => 86 ), 'Stop scan' ); } ); @@ -175,26 +177,59 @@ sub _test { my $plugin = $sc->plugin->get( id => 0 ); - ok( $plugin, 'Plugin API: Get Plugin' ); + ok( $plugin, 'Get Plugin' ); cmp_ok( $plugin->{'id'}, '==', 0, 'Get Plugin ID' ); cmp_ok( $plugin->{'name'}, 'eq', 'Open Port', 'Get Plugin Name' ); - ok( $sc->plugin->list, 'Plugin API: Get Plugin List' ); + ok( $sc->plugin->list, 'Get Plugin List' ); } ); subtest( - 'Plugin Famiy API' => sub { + 'Plugin Family API' => sub { my $plugin_family = $sc->plugin_family->get( id => 1000030 ); - ok( $plugin_family, 'Plugin Family API: Get Plugin Family' ); + ok( $plugin_family, 'Get Plugin Family' ); cmp_ok( $plugin_family->{'id'}, '==', 1000030, 'Get Plugin Family ID' ); cmp_ok( $plugin_family->{'name'}, 'eq', 'Malware', 'Get Plugin Family Name' ); cmp_ok( $plugin_family->{'type'}, 'eq', 'passive', 'Get Plugin Family Type' ); - ok( $sc->plugin_family->list, 'Plugin Family API: Get Plugin List' ); + ok( $sc->plugin_family->list, 'Get List' ); + ok( $sc->plugin_family->list_plugins( id => 2 ), 'Get Plugins List' ); + + } + ); + + subtest( + 'Scanner API' => sub { + + ok( $sc->scanner->list, 'Get List' ); + ok( $sc->scanner->get( id => 5 ), 'Get Scanner' ); + ok( $sc->scanner->health( id => 5 ), 'Get Scanner Health' ); + cmp_ok( $sc->scanner->status( id => 5 ), 'eq', 'Updating Status', 'Get scanner status' ); + } + ); + + subtest( + 'Zone API' => sub { + + ok( $sc->zone->list, 'Get list of Scan Zone' ); + ok( $sc->zone->get( id => 5 ), 'Get Scan Zone detail' ); + + } + ); + + subtest( + 'Utils' => sub { + + use Net::SecurityCenter::Utils; + + foreach my $id ( sort { $a <=> $b } keys %{$Net::SecurityCenter::Utils::NESSUS_SCANNER_STATUS} ) { + my $name = $Net::SecurityCenter::Utils::NESSUS_SCANNER_STATUS->{$id}; + cmp_ok( Net::SecurityCenter::Utils::sc_decode_scanner_status($id), 'eq', $name, "$id - $name" ); + } } ); diff --git a/t/mock/rest-pluginFamily-2-plugins-get.json b/t/mock/rest-pluginFamily-2-plugins-get.json new file mode 100644 index 0000000..d09342d --- /dev/null +++ b/t/mock/rest-pluginFamily-2-plugins-get.json @@ -0,0 +1,24 @@ +{ + "type": "regular", + "response": [ + { + "id": "27063", + "name": "HP-UX PHSS_36869 : HP System Management Homepage (SMH) for HP-UX, XSS (HPSBMA02274 SSRT071445 rev.3)", + "description": "s700_800 11.11 HP System Management Homepage A.2.2.6.2 : \n\nPotential security vulnerabilities have been identified with HP System\nManagement Homepage (SMH) for HP-UX. These vulnerabilities could by\nexploited remotely to allow cross site scripting (XSS)." + }, + { + "id": "27064", + "name": "HP-UX PHSS_36870 : HP System Management Homepage (SMH) for HP-UX, XSS (HPSBMA02274 SSRT071445 rev.3)", + "description": "s700_800 11.23 HP System Management Homepage A.2.2.6.2 : \n\nPotential security vulnerabilities have been identified with HP System\nManagement Homepage (SMH) for HP-UX. These vulnerabilities could by\nexploited remotely to allow cross site scripting (XSS)." + }, + { + "id": "27065", + "name": "HP-UX PHSS_36871 : HP System Management Homepage (SMH) for HP-UX, XSS (HPSBMA02274 SSRT071445 rev.3)", + "description": "s700_800 11.31 HP System Management Homepage A.2.2.6.2 : \n\nPotential security vulnerabilities have been identified with HP System\nManagement Homepage (SMH) for HP-UX. These vulnerabilities could by\nexploited remotely to allow cross site scripting (XSS)." + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1411997624 +} \ No newline at end of file diff --git a/t/mock/rest-scan-get.json b/t/mock/rest-scan-get.json index c10b7b6..84403ff 100644 --- a/t/mock/rest-scan-get.json +++ b/t/mock/rest-scan-get.json @@ -1,43 +1,43 @@ { - "type" : "regular", - "response" : { - "usable" : [ - { - "id" : "2", - "name" : "test", - "description" : null - }, - { - "id" : "3", - "name" : "test2", - "description" : null - }, - { - "id" : "4", - "name" : "POSTtest", - "description" : "This is a test for POST" - } - ], - "manageable" : [ - { - "id" : "2", - "name" : "test", - "description" : null - }, - { - "id" : "3", - "name" : "test2", - "description" : null + "type": "regular", + "response": { + "usable": [ + { + "id": "2", + "name": "test", + "description": null }, - { - "id" : "4", - "name" : "POSTtest", - "description" : "This is a test for POST" - } - ] - }, - "error_code" : 0, - "error_msg" : "", - "warnings" : [], - "timestamp" : 1406828340 + { + "id": "3", + "name": "test2", + "description": null + }, + { + "id": "4", + "name": "POSTtest", + "description": "This is a test for POST" + } + ], + "manageable": [ + { + "id": "2", + "name": "test", + "description": null + }, + { + "id": "3", + "name": "test2", + "description": null + }, + { + "id": "4", + "name": "POSTtest", + "description": "This is a test for POST" + } + ] + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1406828340 } \ No newline at end of file diff --git a/t/mock/rest-scanner-5-get.json b/t/mock/rest-scanner-5-get.json new file mode 100644 index 0000000..bef2dde --- /dev/null +++ b/t/mock/rest-scanner-5-get.json @@ -0,0 +1,54 @@ +{ + "type": "regular", + "response": { + "id": "5", + "name": "My Active Scanner", + "description": "", + "ip": "192.168.1.1", + "port": "443", + "useProxy": "false", + "enabled": "true", + "verifyHost": "true", + "managePlugins": "false", + "authType": "password", + "cert": null, + "username": "nonadmin", + "password": "SET", + "version": null, + "webVersion": null, + "admin": "false", + "msp": "false", + "numScans": "0", + "numHosts": "0", + "numSessions": "0", + "numTCPSessions": "0", + "loadAvg": "0.0", + "uptime": -1, + "status": "8192", + "pluginSet": null, + "loadedPluginSet": null, + "serverUUID": null, + "createdTime": "1402435586", + "modifiedTime": "1402435586", + "accessKey": "", + "secretKey": "", + "zones": [ + { + "id": "1", + "name": "Big Zone", + "description": "" + } + ], + "nessusManagerOrgs": [ + { + "id": "1", + "name": "Big Org", + "description": "" + } + ] + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1402435871 +} \ No newline at end of file diff --git a/t/mock/rest-scanner-5-health-get.json b/t/mock/rest-scanner-5-health-get.json new file mode 100644 index 0000000..676bc1d --- /dev/null +++ b/t/mock/rest-scanner-5-health-get.json @@ -0,0 +1,57 @@ +{ + "type": "regular", + "response": { + "perf_stats_history": { + "kbytes_received": 3, + "kbytes_sent": 12, + "avg_dns_lookup_time": 0, + "num_dns_lookups": 0, + "avg_rdns_lookup_time": 0, + "num_rdns_lookups": 0, + "cpu_load_avg": 2, + "nessus_log_disk_free": 42681, + "nessus_log_disk_total": 51175, + "nessus_data_disk_free": 42681, + "nessus_data_disk_total": 51175, + "temp_disk_free": 42681, + "temp_disk_total": 51175, + "num_tcp_sessions": 0, + "nessus_vmem": 1290, + "nessus_mem": 243, + "sys_ram_used": null, + "sys_ram": 7727, + "sys_cores": 2, + "num_hosts": 0, + "num_scans": 0, + "timestamp": 1567631211 + }, + "perf_stats_current": { + "kbytes_received": 0, + "kbytes_sent": 0, + "avg_dns_lookup_time": 0, + "num_dns_lookups": 0, + "avg_rdns_lookup_time": 0, + "num_rdns_lookups": 0, + "cpu_load_avg": 0, + "nessus_log_disk_free": 42672, + "nessus_log_disk_total": 51175, + "nessus_data_disk_free": 42672, + "nessus_data_disk_total": 51175, + "temp_disk_free": 42672, + "temp_disk_total": 51175, + "num_tcp_sessions": 0, + "nessus_vmem": 1290, + "nessus_mem": 195, + "sys_ram_used": null, + "sys_ram": 7727, + "sys_cores": 2, + "num_hosts": 0, + "num_scans": 0, + "timestamp": 1567631211 + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1402435958 + } +} \ No newline at end of file diff --git a/t/mock/rest-scanner-get.json b/t/mock/rest-scanner-get.json new file mode 100644 index 0000000..9bd480b --- /dev/null +++ b/t/mock/rest-scanner-get.json @@ -0,0 +1,39 @@ +{ + "type": "regular", + "response": [ + { + "id": "12", + "name": "Scanner 1", + "description": "Copied from QA", + "status": "1" + }, + { + "id": "14", + "name": "Scanner using Safe Scan Range", + "description": "", + "status": "16" + }, + { + "id": "15", + "name": "mp zone 1 scanner", + "description": "Copied from QA", + "status": "1" + }, + { + "id": "16", + "name": "NessusTest", + "description": "Copied From QA", + "status": "32" + }, + { + "id": "17", + "name": "sc", + "description": "", + "status": "2" + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1426878501 +} \ No newline at end of file diff --git a/t/mock/rest-zone-5-get.json b/t/mock/rest-zone-5-get.json new file mode 100644 index 0000000..d5c73c4 --- /dev/null +++ b/t/mock/rest-zone-5-get.json @@ -0,0 +1,32 @@ +{ + "type": "regular", + "response": { + "id": "5", + "name": "Test Zone #1", + "description": "", + "ipList": "192.168.0.0\/24", + "createdTime": "1426871916", + "modifiedTime": "1426881426", + "scanners": [ + { + "id": "2", + "name": "Nessus 172.168.0.1:8834", + "description": "", + "status": "4" + } + ], + "organizations": [ + { + "id": "38", + "name": "Test Org", + "description": "" + } + ], + "activeScanners": 0, + "totalScanners": 1 + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1426882592 +} \ No newline at end of file diff --git a/t/mock/rest-zone-get.json b/t/mock/rest-zone-get.json new file mode 100644 index 0000000..325657c --- /dev/null +++ b/t/mock/rest-zone-get.json @@ -0,0 +1,46 @@ +{ + "type": "regular", + "response": [ + { + "id": "5", + "name": "Test Zone #1", + "description": "", + "ipList": "192.168.0.0\/24", + "createdTime": "1426871916", + "modifiedTime": "1426881426", + "scanners": [ + { + "id": "2", + "name": "Nessus 192.168.1.1:1234", + "description": "", + "status": "4" + } + ], + "organizations": [ + { + "id": "38", + "name": "Test Org", + "description": "" + } + ], + "activeScanners": 0, + "totalScanners": 1 + }, + { + "id": "6", + "name": "Test Zone #2", + "description": "", + "ipList": "192.168.0.0\/24", + "createdTime": "1426871931", + "modifiedTime": "1426871931", + "scanners": [], + "organizations": [], + "activeScanners": 0, + "totalScanners": 0 + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1426882493 +} \ No newline at end of file From ac30ec2f7624acb43a2a851f1f0c51d3c8041326 Mon Sep 17 00:00:00 2001 From: Giuseppe Di Terlizzi Date: Wed, 12 Feb 2020 10:23:27 +0100 Subject: [PATCH 3/6] Updated MANIFEST and tests --- MANIFEST | 24 ++++++++++++++++++++++++ t/20-utils.t | 18 ++++++++++++++++++ t/99-local.t | 19 +++++++++++++------ 3 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 t/20-utils.t diff --git a/MANIFEST b/MANIFEST index caef3a1..8a60fc3 100644 --- a/MANIFEST +++ b/MANIFEST @@ -15,6 +15,7 @@ lib/Net/SecurityCenter/API/Repository.pm lib/Net/SecurityCenter/API/Scan.pm lib/Net/SecurityCenter/API/Scanner.pm lib/Net/SecurityCenter/API/ScanResult.pm +lib/Net/SecurityCenter/API/Status.pm lib/Net/SecurityCenter/API/System.pm lib/Net/SecurityCenter/API/User.pm lib/Net/SecurityCenter/API/Zone.pm @@ -27,13 +28,36 @@ MANIFEST This list of files README.md t/00-load.t t/10-rest.t +t/20-utils.t t/99-local.t t/manifest.t +t/mock/rest-plugin-0-get.json +t/mock/rest-plugin-get.json +t/mock/rest-pluginFamily-1000030-get.json +t/mock/rest-pluginFamily-2-plugins-get.json +t/mock/rest-pluginFamily-get.json +t/mock/rest-report-1-get.json +t/mock/rest-report-get.json +t/mock/rest-repository-37-get.json +t/mock/rest-repository-get.json t/mock/rest-scan-2-launch-post.json t/mock/rest-scan-4-get.json t/mock/rest-scan-get.json +t/mock/rest-scanner-5-get.json +t/mock/rest-scanner-5-health-get.json +t/mock/rest-scanner-get.json +t/mock/rest-scanResult-11-get.json +t/mock/rest-scanResult-86-pause-post.json +t/mock/rest-scanResult-86-resume-post.json +t/mock/rest-scanResult-86-stop-post.json +t/mock/rest-scanResult-get.json +t/mock/rest-status-get.json +t/mock/rest-system-debug-get.json +t/mock/rest-system-diagnostics-get.json t/mock/rest-system-get.json t/mock/rest-token-delete.json t/mock/rest-token-post.json +t/mock/rest-zone-5-get.json +t/mock/rest-zone-get.json t/pod-coverage.t t/pod.t diff --git a/t/20-utils.t b/t/20-utils.t new file mode 100644 index 0000000..7188b00 --- /dev/null +++ b/t/20-utils.t @@ -0,0 +1,18 @@ +#!perl -T + +use strict; +use warnings; +use Test::More; + +require_ok('Net::SecurityCenter::Utils'); + +foreach my $id ( sort { $a <=> $b } keys %{$Net::SecurityCenter::Utils::NESSUS_SCANNER_STATUS} ) { + my $name = $Net::SecurityCenter::Utils::NESSUS_SCANNER_STATUS->{$id}; + cmp_ok( Net::SecurityCenter::Utils::sc_decode_scanner_status($id), 'eq', $name, "$id - $name" ); +} + +cmp_ok( Net::SecurityCenter::Utils::trim(' trimmed '), 'eq', 'trimmed', 'Trimmed text' ); + +cmp_ok( Net::SecurityCenter::Utils::decamelize('SecurityCenter'), 'eq', 'security_center', 'Decamelize text' ); + +done_testing(); diff --git a/t/99-local.t b/t/99-local.t index 2cd4d8a..c4e839d 100644 --- a/t/99-local.t +++ b/t/99-local.t @@ -138,6 +138,8 @@ sub _test { ok( $system_info->{'buildID'}, 'SecurityCenter build' ); ok( $system_info->{'licenseStatus'}, 'SecurityCenter license' ); + ok( $sc->system->get_diagnostics_info, 'Get diagnostics info' ); + } ); @@ -222,14 +224,19 @@ sub _test { ); subtest( - 'Utils' => sub { + 'Repository API' => sub { + + ok( $sc->repository->list, 'Get list of Repository' ); + ok( $sc->repository->get( id => 37 ), 'Get Repository detail' ); + + } + ); - use Net::SecurityCenter::Utils; + subtest( + 'Report API' => sub { - foreach my $id ( sort { $a <=> $b } keys %{$Net::SecurityCenter::Utils::NESSUS_SCANNER_STATUS} ) { - my $name = $Net::SecurityCenter::Utils::NESSUS_SCANNER_STATUS->{$id}; - cmp_ok( Net::SecurityCenter::Utils::sc_decode_scanner_status($id), 'eq', $name, "$id - $name" ); - } + ok( $sc->report->list, 'Get list of Report' ); + ok( $sc->report->get( id => 1 ), 'Get Report detail' ); } ); From 9391b336aef19d4f14976d8c4fd8d06bd381e7c0 Mon Sep 17 00:00:00 2001 From: Giuseppe Di Terlizzi Date: Wed, 12 Feb 2020 11:15:36 +0100 Subject: [PATCH 4/6] Added new tests mock --- t/99-local.t | 4 +- t/mock/rest-report-1-get.json | 49 +++ t/mock/rest-report-get.json | 105 +++++ t/mock/rest-repository-37-get.json | 57 +++ t/mock/rest-repository-get.json | 34 ++ t/mock/rest-system-debug-get.json | 513 ++++++++++++++++++++++++ t/mock/rest-system-diagnostics-get.json | 18 + 7 files changed, 779 insertions(+), 1 deletion(-) create mode 100644 t/mock/rest-report-1-get.json create mode 100644 t/mock/rest-report-get.json create mode 100644 t/mock/rest-repository-37-get.json create mode 100644 t/mock/rest-repository-get.json create mode 100644 t/mock/rest-system-debug-get.json create mode 100644 t/mock/rest-system-diagnostics-get.json diff --git a/t/99-local.t b/t/99-local.t index c4e839d..bad6118 100644 --- a/t/99-local.t +++ b/t/99-local.t @@ -5,6 +5,7 @@ use warnings; use Test::More; use HTTP::Daemon; + use Net::SecurityCenter; $| = 1; # autoflush @@ -32,7 +33,8 @@ sub dispatch { $c->send_file("t/mock/$mock.json"); } else { - $c->send_404(); + $c->send_basic_header(404); + $c->print("Content-Type: application/json"); } } diff --git a/t/mock/rest-report-1-get.json b/t/mock/rest-report-1-get.json new file mode 100644 index 0000000..4ce4cf8 --- /dev/null +++ b/t/mock/rest-report-1-get.json @@ -0,0 +1,49 @@ +{ + "type": "regular", + "response": { + "id": "1", + "creatorID": "1", + "ownerID": "1", + "reportDefinitionID": "-1", + "jobID": "1614", + "name": "Test Scan", + "description": "This report identifies installed software across a series of hosts, utilizing Nessus plugin 22869, Software Enumeration (SSH). This plugin lists the software installed on the remote host by calling the appropriate command (rpm -qa on RPM-based Linux distributions, qpkg, dpkg, etc.).\nThis report is comprised of a Table of Contents for each identified host. The Identified Hosts Table lists the hosts by IP Address, NetBIOS Name, and DNS Name, and is followed by a detailed look at each host individually.\n\nThis area provides some host details in a header with IP Address, DNS Name, NetBIOS Name, and Last Scan Date, and is followed by the host Operating System and a list of installed software, and version (if available).", + "type": "pdf", + "status": "Completed", + "running": "false", + "errorDetails": "Error removing components.\nError removing component #6.\nError deleting Data Source #10.\nError retrieving owner of Query.\nUser #1 not found.\n", + "totalSteps": "8", + "completedSteps": "4", + "startTime": "1403298387", + "finishTime": "1403299387", + "ownerGID": "0", + "pubSites": [ + { + "id": "2", + "name": "test", + "description": "desc" + } + ], + "creator": { + "id": "1", + "username": "head", + "firstname": "test", + "lastname": "User" + }, + "owner": { + "id": "1", + "username": "head", + "firstname": "test", + "lastname": "User" + }, + "ownerGroup": { + "id": "0", + "name": "Full Access", + "description": "Full Access group" + } + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1404920431 +} \ No newline at end of file diff --git a/t/mock/rest-report-get.json b/t/mock/rest-report-get.json new file mode 100644 index 0000000..2fd52b3 --- /dev/null +++ b/t/mock/rest-report-get.json @@ -0,0 +1,105 @@ +{ + "type": "regular", + "response": { + "usable": [ + { + "id": "1", + "creatorID": "1", + "ownerID": "1", + "reportDefinitionID": "-1", + "jobID": "1614", + "name": "Test Scan", + "description": "This report identifies installed software across a series of hosts, utilizing Nessus plugin 22869, Software Enumeration (SSH). This plugin lists the software installed on the remote host by calling the appropriate command (rpm -qa on RPM-based Linux distributions, qpkg, dpkg, etc.).\nThis report is comprised of a Table of Contents for each identified host. The Identified Hosts Table lists the hosts by IP Address, NetBIOS Name, and DNS Name, and is followed by a detailed look at each host individually.\n\nThis area provides some host details in a header with IP Address, DNS Name, NetBIOS Name, and Last Scan Date, and is followed by the host Operating System and a list of installed software, and version (if available).", + "type": "pdf", + "status": "Completed", + "running": "false", + "errorDetails": "Error removing components.\nError removing component #6.\nError deleting Data Source #10.\nError retrieving owner of Query.\nUser #1 not found.\n", + "totalSteps": "8", + "completedSteps": "4", + "startTime": "1403298387", + "finishTime": "1403299387", + "ownerGID": "0", + "displayType": "pdf", + "pubSites": [ + { + "id": "2", + "name": "test", + "description": "desc" + } + ], + "txLogs": [], + "canUse": "true", + "canManage": "true", + "creator": { + "id": "1", + "username": "head", + "firstname": "test", + "lastname": "User" + }, + "owner": { + "id": "1", + "username": "head", + "firstname": "test", + "lastname": "User" + }, + "ownerGroup": { + "id": "0", + "name": "Full Access", + "description": "Full Access group" + } + } + ], + "manageable": [ + { + "id": "1", + "creatorID": "1", + "ownerID": "1", + "reportDefinitionID": "-1", + "jobID": "1614", + "name": "Test Scan", + "description": "This report identifies installed software across a series of hosts, utilizing Nessus plugin 22869, Software Enumeration (SSH). This plugin lists the software installed on the remote host by calling the appropriate command (rpm -qa on RPM-based Linux distributions, qpkg, dpkg, etc.).\nThis report is comprised of a Table of Contents for each identified host. The Identified Hosts Table lists the hosts by IP Address, NetBIOS Name, and DNS Name, and is followed by a detailed look at each host individually.\n\nThis area provides some host details in a header with IP Address, DNS Name, NetBIOS Name, and Last Scan Date, and is followed by the host Operating System and a list of installed software, and version (if available).", + "type": "pdf", + "status": "Completed", + "running": "false", + "errorDetails": "Error removing components.\nError removing component #6.\nError deleting Data Source #10.\nError retrieving owner of Query.\nUser #1 not found.\n", + "totalSteps": "8", + "completedSteps": "4", + "startTime": "1403298387", + "finishTime": "1403299387", + "ownerGID": "0", + "displayType": "pdf", + "pubSites": [ + { + "id": "2", + "name": "test", + "description": "desc" + } + ], + "txLogs": [], + "canUse": "true", + "canManage": "true", + "creator": { + "id": "1", + "username": "head", + "firstname": "test", + "lastname": "User" + }, + "owner": { + "id": "1", + "username": "head", + "firstname": "test", + "lastname": "User" + }, + "ownerGroup": { + "id": "0", + "name": "Full Access", + "description": "Full Access group" + } + } + ] + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1404919744 +} \ No newline at end of file diff --git a/t/mock/rest-repository-37-get.json b/t/mock/rest-repository-37-get.json new file mode 100644 index 0000000..91a41f2 --- /dev/null +++ b/t/mock/rest-repository-37-get.json @@ -0,0 +1,57 @@ +{ + "type": "regular", + "response": { + "id": "37", + "name": "ag repo1", + "description": "Copied", + "type": "Local", + "dataFormat": "IPv4", + "remoteID": null, + "remoteIP": null, + "running": "false", + "downloadFormat": "v2", + "lastSyncTime": "0", + "createdTime": "1422396357", + "modifiedTime": "1422396357", + "organizations": [ + { + "id": "8", + "groupAssign": "fullAccess", + "name": "Org", + "description": "Testing for Policies with New Schema" + } + ], + "typeFields": { + "lastVulnUpdate": 1423718403, + "vulnCount": 0, + "nessusSchedule": { + "type": "never", + "start": "", + "repeatRule": "" + }, + "correlation": [], + "ipRange": "192.168.0.0\/24", + "ipCount": "0", + "runningNessus": "false", + "lastGenerateNessusTime": "0", + "trendingDays": "0", + "trendWithRaw": "true" + }, + "luminFields": { + "firstSyncTime": "1573594357", + "lastSyncSuccess": "1573594357", + "lastSyncFailure": "-1", + "details": "details for LuminFields", + "enabled": "true", + "repository": { + "id": "37", + "name": "ag repo1", + "description": "Copied" + } + } + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1423767366 +} \ No newline at end of file diff --git a/t/mock/rest-repository-get.json b/t/mock/rest-repository-get.json new file mode 100644 index 0000000..efc4681 --- /dev/null +++ b/t/mock/rest-repository-get.json @@ -0,0 +1,34 @@ +{ + "type": "regular", + "response": [ + { + "id": "37", + "name": "ag repo1", + "description": "Copied from QA" + }, + { + "id": "38", + "name": "jm ipv4", + "description": "copied from QA" + }, + { + "id": "39", + "name": "ipv6 rep", + "description": "Copied from QA (name changed)" + }, + { + "id": "43", + "name": "Test Local mobile Repository", + "description": "DevForm test of mobile repository post" + }, + { + "id": "44", + "name": "Test w\/pluginPrefs", + "description": "" + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1423767348 +} \ No newline at end of file diff --git a/t/mock/rest-system-debug-get.json b/t/mock/rest-system-debug-get.json new file mode 100644 index 0000000..4d8c60b --- /dev/null +++ b/t/mock/rest-system-debug-get.json @@ -0,0 +1,513 @@ +{ + "type": "regular", + "response": [ + { + "id": "0", + "name": "Accept Risk", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "1", + "name": "Activity Log", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "true" + }, + { + "id": "2", + "name": "Apply All Risk", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "3", + "name": "Convert Compliance", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "4", + "name": "Convert Name DB", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "5", + "name": "Convert Repositories", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "6", + "name": "Custom Plugin Upload", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "7", + "name": "DB Locks", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "8", + "name": "Dev Migration", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "9", + "name": "Evaluate ARC", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "10", + "name": "Evaluate ARC Policy Statement Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "11", + "name": "Evaluate AR Sync Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "12", + "name": "Evaluate Blackout Window Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "13", + "name": "Evaluate Dashboard Component Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "14", + "name": "Evaluate Dashboard Element", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "15", + "name": "Evaluate Matrix Cluster", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "16", + "name": "Evaluate Query Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "17", + "name": "Evaluate Report Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "18", + "name": "Evaluate Scan Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "19", + "name": "Expire Accepted Risk", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "20", + "name": "Export", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "21", + "name": "Feed Update", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "22", + "name": "Flush Vulns", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "23", + "name": "IDS Correlations", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "24", + "name": "IDS Signatures", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "25", + "name": "Import", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "26", + "name": "Import ARC", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "27", + "name": "Import Dashboard", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "28", + "name": "Import Mobile", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "29", + "name": "Import Report", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "30", + "name": "Industrial Plugin Update", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "31", + "name": "Industrial Plugin Update Testing", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "32", + "name": "Industrial Results", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "33", + "name": "Job Activity", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "34", + "name": "Job Daemon", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "35", + "name": "LCE Plugin Update", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "36", + "name": "LCE Status", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "37", + "name": "Licensing", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "38", + "name": "Mark Assets Prepared", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "39", + "name": "Migrate User Files", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "40", + "name": "Migration", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "41", + "name": "Mobile Scan", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "42", + "name": "Nightly Cleanup", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "43", + "name": "NNM Results", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "44", + "name": "Parse Nessus", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "45", + "name": "Passive Plugin Update", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "46", + "name": "Passive Plugin Update Testing", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "47", + "name": "Password Convert", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "48", + "name": "Patch", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "49", + "name": "Performance Log", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "50", + "name": "Plugin Update", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "51", + "name": "Plugin Update Testing", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "52", + "name": "Prepare Assets", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "53", + "name": "Publishing", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "54", + "name": "Recast Risk", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "55", + "name": "Remote Client Tools", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "56", + "name": "Reporting", + "category": "common", + "sizeWarning": "true", + "enabled": "false" + }, + { + "id": "57", + "name": "Resolve Host Names", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "58", + "name": "Run Alert", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "59", + "name": "Save Policy", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "60", + "name": "Scan", + "category": "common", + "sizeWarning": "true", + "enabled": "false" + }, + { + "id": "61", + "name": "Scanner Status", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "62", + "name": "Session", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "63", + "name": "Simple Activity", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "64", + "name": "Synchronize Agent Results", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "65", + "name": "Synchronize Repository", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "66", + "name": "Take Repository Snapshots", + "category": "uncommon", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "67", + "name": "Update DNS Assets", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "68", + "name": "Update LCE Silos", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "69", + "name": "Update LCE Types", + "category": "rare", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "70", + "name": "Update LDAP Assets", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + }, + { + "id": "71", + "name": "Disk I/O Errors", + "category": "common", + "sizeWarning": "false", + "enabled": "false" + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1551190238 +} \ No newline at end of file diff --git a/t/mock/rest-system-diagnostics-get.json b/t/mock/rest-system-diagnostics-get.json new file mode 100644 index 0000000..0225e5d --- /dev/null +++ b/t/mock/rest-system-diagnostics-get.json @@ -0,0 +1,18 @@ +{ + "type": "regular", + "response": { + "statusJava": "true", + "statusRPM": "true", + "statusDisk": "true", + "statusThresholdDisk": "5%", + "statusLastChecked": "1425532201", + "diagnosticsGenerated": 1425563166, + "diagnosticsGenerateState": "-1", + "touchDebuggingEnabled": false, + "migrationFailure": false + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1425571142 +} \ No newline at end of file From 0f39d796ff5cec20b6fabd277b980923dffd111a Mon Sep 17 00:00:00 2001 From: Giuseppe Di Terlizzi Date: Wed, 12 Feb 2020 18:20:39 +0100 Subject: [PATCH 5/6] Added Net::SecurityCenter::API::System->debug method Other notable changes: - Deprecated Net::SecurityCenter::API::System->get_info (use "info") - Improved t/99.local.t test --- README.md | 2 +- lib/Net/SecurityCenter/API/System.pm | 93 +++++++++++++++++++++++++++- t/99-local.t | 6 +- 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7ed11f3..1120b31 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Release](https://img.shields.io/github/release/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter/releases) [![Build Status](https://travis-ci.org/giterlizzi/perl-Net-SecurityCenter.svg)](https://travis-ci.org/giterlizzi/perl-Net-SecurityCenter) [![License](https://img.shields.io/github/license/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter) [![Starts](https://img.shields.io/github/stars/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter) [![Forks](https://img.shields.io/github/forks/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter) [![Issues](https://img.shields.io/github/issues/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter/issues) +[![Release](https://img.shields.io/github/release/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter/releases) [![Build Status](https://travis-ci.org/giterlizzi/perl-Net-SecurityCenter.svg)](https://travis-ci.org/giterlizzi/perl-Net-SecurityCenter) [![License](https://img.shields.io/github/license/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter) [![Starts](https://img.shields.io/github/stars/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter) [![Forks](https://img.shields.io/github/forks/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter) [![Issues](https://img.shields.io/github/issues/giterlizzi/perl-Net-SecurityCenter.svg)](https://github.com/giterlizzi/perl-Net-SecurityCenter/issues) [![Coverage Status](https://coveralls.io/repos/github/giterlizzi/perl-Net-SecurityCenter/badge.svg)](https://coveralls.io/github/giterlizzi/perl-Net-SecurityCenter) # Net::SecurityCenter - Perl interface to Tenable.sc (SecurityCenter) REST API diff --git a/lib/Net/SecurityCenter/API/System.pm b/lib/Net/SecurityCenter/API/System.pm index de5033a..ee2857f 100644 --- a/lib/Net/SecurityCenter/API/System.pm +++ b/lib/Net/SecurityCenter/API/System.pm @@ -21,13 +21,23 @@ sub get_status { 'method "Net::SecurityCenter::API::System->get_status" is deprecated use "Net::SecurityCenter::API::Status->status"' ); - return; +} + +sub get_info { + + my ( $self, %args ) = @_; + + warnings::warnif( 'deprecated', + 'method "Net::SecurityCenter::API::Status->get_info" is deprecated use "Net::SecurityCenter::API::Status->info"' + ); + + return $self->info( \%args ); } #------------------------------------------------------------------------------- -sub get_info { +sub info { my ( $self, %args ) = @_; @@ -51,6 +61,57 @@ sub get_info { #------------------------------------------------------------------------------- +sub debug { + + my ( $self, %args ) = @_; + + my $tmpl = { name => {}, id => {}, category => {} }; + + my $params = sc_check_params( $tmpl, \%args ); + my $id = delete( $params->{'id'} ); + my $name = delete( $params->{'name'} ); + my $category = delete( $params->{'category'} ); + my $debug = $self->client->get('/system/debug'); + + if ( !$debug ) { + return; + } + + my $id_results = {}; + my $name_results = {}; + my $category_results = {}; + + foreach my $item ( @{$debug} ) { + + $id_results->{ $item->{id} } = $item; + $name_results->{ $item->{name} } = $item; + + if ( !defined( $category_results->{ $item->{category} } ) ) { + $category_results->{ $item->{category} } = (); + } + + push @{ $category_results->{ $item->{category} } }, $item; + + } + + if ($name) { + return $name_results->{$name}; + } + + if ($id) { + return $id_results->{$id}; + } + + if ($category) { + return $category_results->{$category}; + } + + return $debug; + +} + +#------------------------------------------------------------------------------- + sub get_diagnostics_info { my ( $self, %args ) = @_; @@ -169,10 +230,36 @@ Create a new instance of B using L->status method. -=head2 get_info +=head2 info +=head2 get_info (DEPRECATED) Gets the system initialization information. +=head2 debug + + # Get all Tenble.sc debug informations + my @debug = $sc->debug; + + # Check scan debug flag + if ($sc->debug( id => 60 )->{enabled} eq 'true' ) { + say "Scan Debug enabled!"; + } + + # Get all "common" debug category + my @common = $sc->debug( category => 'common' ); + +Params: + +=over 4 + +=item * C : ID of debug item + +=item * C : Name of category + +=item * C : Debug category + +=back + =head2 get_diagnostics_info Gets the system diagnostics information. diff --git a/t/99-local.t b/t/99-local.t index bad6118..b3a403c 100644 --- a/t/99-local.t +++ b/t/99-local.t @@ -134,7 +134,7 @@ sub _test { subtest( 'System API' => sub { - my $system_info = $sc->system->get_info; + my $system_info = $sc->system->info; ok( $system_info->{'version'}, 'SecurityCenter version' ); ok( $system_info->{'buildID'}, 'SecurityCenter build' ); @@ -142,6 +142,10 @@ sub _test { ok( $sc->system->get_diagnostics_info, 'Get diagnostics info' ); + ok( $sc->system->debug, 'Get debug info' ); + ok( $sc->system->debug( id => 60 ), 'Get debug info (id=60)' ); + ok( $sc->system->debug( category => 'common' ), 'Get debug info (category=common)' ); + } ); From a97345a9bba7c3d2406325c3f74a747d3b70a10e Mon Sep 17 00:00:00 2001 From: Giuseppe Di Terlizzi Date: Wed, 22 Jul 2020 19:36:13 +0200 Subject: [PATCH 6/6] Improvments, fixes and deprecations - Added "Net::SecurityCenter::API::DeviceInfo" class (added in Tenable.sc 5.12) - Deprecated "Net::SecurityCenter::API::System->get_status" method - Deprecated "Net::SecurityCenter::API::System->get_info" method - Improved "Net::SecurityCenter::API::ScanResult->list" method (added "start_time" and "end_time" params) - Fixed unhexpected behavior in "Net::SecurityCenter::API::ScanResult->list" method for "start_date" and "end_date" params - Improved sc-api command - Improved documentations - Added more tests --- Changes | 19 +- MANIFEST | 3 + bin/sc-api | 47 ++-- lib/Net/SecurityCenter.pm | 4 +- lib/Net/SecurityCenter/API.pm | 2 +- lib/Net/SecurityCenter/API/Analysis.pm | 2 +- lib/Net/SecurityCenter/API/Credential.pm | 4 +- lib/Net/SecurityCenter/API/DeviceInfo.pm | 215 +++++++++++++++ lib/Net/SecurityCenter/API/Feed.pm | 4 +- lib/Net/SecurityCenter/API/File.pm | 2 +- lib/Net/SecurityCenter/API/Plugin.pm | 6 +- lib/Net/SecurityCenter/API/PluginFamily.pm | 4 +- lib/Net/SecurityCenter/API/Policy.pm | 7 +- lib/Net/SecurityCenter/API/Report.pm | 47 +++- lib/Net/SecurityCenter/API/Repository.pm | 127 ++++++++- lib/Net/SecurityCenter/API/Scan.pm | 6 +- lib/Net/SecurityCenter/API/ScanResult.pm | 118 ++++++++- lib/Net/SecurityCenter/API/Scanner.pm | 4 +- lib/Net/SecurityCenter/API/Status.pm | 4 +- lib/Net/SecurityCenter/API/System.pm | 14 +- lib/Net/SecurityCenter/API/User.pm | 2 +- lib/Net/SecurityCenter/API/Zone.pm | 4 +- lib/Net/SecurityCenter/Error.pm | 2 +- lib/Net/SecurityCenter/REST.pm | 2 +- lib/Net/SecurityCenter/Utils.pm | 76 +++++- t/20-utils.t | 11 + t/99-local.t | 18 ++ t/mock/rest-policy-1-get.json | 287 +++++++++++++++++++++ t/mock/rest-policy-get.json | 157 +++++++++++ 29 files changed, 1114 insertions(+), 84 deletions(-) create mode 100644 lib/Net/SecurityCenter/API/DeviceInfo.pm create mode 100644 t/mock/rest-policy-1-get.json create mode 100644 t/mock/rest-policy-get.json diff --git a/Changes b/Changes index 32644c3..e90ef4b 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,18 @@ Revision history for Net::SecurityCenter +0.206 2020/07/22 + - Refactoring "Net::SecurityCenter" class + - Added "Net::SecurityCenter::API::Status" class + - Added "Net::SecurityCenter::API::DeviceInfo" class (added in Tenable.sc 5.12) + - Added "Net::SecurityCenter::API::System->debug" method (added in Tenable.sc 5.10) + - Deprecated "Net::SecurityCenter::API::System->get_status" method + - Deprecated "Net::SecurityCenter::API::System->get_info" method + - Improved "Net::SecurityCenter::API::ScanResult->list" method (added "start_time" and "end_time" params) + - Fixed unhexpected behavior in "Net::SecurityCenter::API::ScanResult->list" method for "start_date" and "end_date" params + - Improved sc-api command + - Improved documentations + - Added more tests + 0.205 2020/01/31 - Added new Net::SecurityCenter::API::ScanResult methods - import @@ -13,9 +26,9 @@ Revision history for Net::SecurityCenter 0.203 2019/10/29 - Added initial support for Tenable.sc 5.12 - Added "Net::SecurityCenter::API::Scanner::health" method - - Renamed "Net::SecurityCenter::API::Scanner::get_status" method to "Net::SecurityCenter::API::Scanner::status" - - Renamed "Net::SecurityCenter::API::ScanResult::get_status" method to "Net::SecurityCenter::API::ScanResult::status" - - Renamed "Net::SecurityCenter::API::ScanResult::get_progress" method to "Net::SecurityCenter::API::ScanResult::progress" + - Renamed "Net::SecurityCenter::API::Scanner->get_status" method to "Net::SecurityCenter::API::Scanner->status" + - Renamed "Net::SecurityCenter::API::ScanResult->get_status" method to "Net::SecurityCenter::API::ScanResult->status" + - Renamed "Net::SecurityCenter::API::ScanResult->get_progress" method to "Net::SecurityCenter::API::ScanResult->progress" 0.202 2019/09/10 - Added "download" method for Analysis API diff --git a/MANIFEST b/MANIFEST index 8a60fc3..38a4218 100644 --- a/MANIFEST +++ b/MANIFEST @@ -5,6 +5,7 @@ lib/Net/SecurityCenter.pm lib/Net/SecurityCenter/API.pm lib/Net/SecurityCenter/API/Analysis.pm lib/Net/SecurityCenter/API/Credential.pm +lib/Net/SecurityCenter/API/DeviceInfo.pm lib/Net/SecurityCenter/API/Feed.pm lib/Net/SecurityCenter/API/File.pm lib/Net/SecurityCenter/API/Plugin.pm @@ -36,6 +37,8 @@ t/mock/rest-plugin-get.json t/mock/rest-pluginFamily-1000030-get.json t/mock/rest-pluginFamily-2-plugins-get.json t/mock/rest-pluginFamily-get.json +t/mock/rest-policy-1-get.json +t/mock/rest-policy-get.json t/mock/rest-report-1-get.json t/mock/rest-report-get.json t/mock/rest-repository-37-get.json diff --git a/bin/sc-api b/bin/sc-api index 8ec398a..33516bb 100644 --- a/bin/sc-api +++ b/bin/sc-api @@ -15,7 +15,7 @@ use JSON; use Carp; use English qw( -no_match_vars ); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; sub slurp { @@ -38,8 +38,8 @@ sub config_parse_line { my ($value) = @_; - return 1 if ( $value =~ /^(yes|true)$/s ); - return 0 if ( $value =~ /^(no|false)$/s ); + return 1 if ( $value =~ m/^(yes|true)$/s ); + return 0 if ( $value =~ m/^(no|false)$/s ); if ( $value =~ /\,/ ) { return map { trim($_) } split /,/, $value; @@ -61,15 +61,15 @@ sub config_parser { chomp($line); # skip comments and empty lines - next if ( $line =~ /^\s*(#|;)/ ); - next if ( $line =~ /^\s*$/ ); + next if ( $line =~ m/^\s*([#;])/ ); + next if ( $line =~ m/^\s*$/ ); - if ( $line =~ /^\[(.*)\]\s*$/ ) { + if ( $line =~ m/^\[(.*)\]\s*$/ ) { $section = trim($1); next; } - if ( $line =~ /^([^=]+?)\s*=\s*(.*?)\s*$/ ) { + if ( $line =~ m/^([^=]+?)\s*=\s*(.*?)\s*$/ ) { my ( $field, $raw_value ) = ( $1, $2 ); my $parsed_value = [ config_parse_line($raw_value) ]; @@ -195,6 +195,7 @@ my @options = ( 'tsv', 'yaml', 'dumper' + ); my @output_formats = qw/json table csv tsv yaml dumper/; @@ -205,12 +206,6 @@ my $results = {}; GetOptions( $options, @options ) or pod2usage( -verbose => 0 ); -my $api = $ARGV[0] || undef; -my $method = $ARGV[1] || undef; - -$api =~ s/-/_/g if ($api); -$method =~ s/-/_/g if ($method); - pod2usage(1) if ( $options->{'help'} ); if ( $options->{'version'} ) { @@ -221,7 +216,7 @@ if ( $options->{'version'} ) { require LWP::UserAgent; my $lwp_useragent = ($LWP::UserAgent::VERSION) ? $LWP::UserAgent::VERSION : 'n/a'; - print <<'EOF'; + print " sc-api v$VERSION CORE @@ -230,8 +225,10 @@ CORE LWP::UserAgent ($lwp_useragent) IO::Socket::SSL ($io_socket_ssl) -EOF +"; + exit(0); + } if ( $options->{'config'} ) { @@ -269,12 +266,15 @@ if ( $options->{'format'} ) { $options->{'format'} ||= 'json'; -$options->{'format'} = 'yaml' if ( $options->{'yaml'} ); -$options->{'format'} = 'table' if ( $options->{'table'} ); -$options->{'format'} = 'json' if ( $options->{'json'} ); -$options->{'format'} = 'csv' if ( $options->{'csv'} ); -$options->{'format'} = 'tsv' if ( $options->{'tsv'} ); -$options->{'format'} = 'dumper' if ( $options->{'dumper'} ); +foreach (@output_formats) { + $options->{'format'} = $_ if ( $options->{$_} ); +} + +my $api = $ARGV[0] || undef; +my $method = $ARGV[1] || undef; + +$api =~ s/-/_/g if ($api); +$method =~ s/-/_/g if ($method); pod2usage( -verbose => 0 ) if ( !$api || !$method ); @@ -459,6 +459,7 @@ sc-api - Tenable.sc (SecurityCenter) API command line interface analysis credential + device_info feed file plugin @@ -524,6 +525,10 @@ See L class. See L class. +=head2 device_info + +See L class. + =head2 feed See L class. diff --git a/lib/Net/SecurityCenter.pm b/lib/Net/SecurityCenter.pm index 3f83e74..fa9eb0e 100644 --- a/lib/Net/SecurityCenter.pm +++ b/lib/Net/SecurityCenter.pm @@ -8,9 +8,9 @@ use Net::SecurityCenter::Utils qw(decamelize); use Net::SecurityCenter::REST; use Net::SecurityCenter::Error; -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; -my @apis = qw/Analysis Credential Feed File Plugin PluginFamily Policy Report +my @apis = qw/Analysis Credential DeviceInfo Feed File Plugin PluginFamily Policy Report Repository Scan ScanResult Scanner Status System User Zone/; #------------------------------------------------------------------------------- diff --git a/lib/Net/SecurityCenter/API.pm b/lib/Net/SecurityCenter/API.pm index 85bcd0e..141f19d 100644 --- a/lib/Net/SecurityCenter/API.pm +++ b/lib/Net/SecurityCenter/API.pm @@ -5,7 +5,7 @@ use strict; use Net::SecurityCenter::Error; -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; #------------------------------------------------------------------------------- # CONSTRUCTOR diff --git a/lib/Net/SecurityCenter/API/Analysis.pm b/lib/Net/SecurityCenter/API/Analysis.pm index 3ae6cfe..49e8784 100644 --- a/lib/Net/SecurityCenter/API/Analysis.pm +++ b/lib/Net/SecurityCenter/API/Analysis.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { tool => { diff --git a/lib/Net/SecurityCenter/API/Credential.pm b/lib/Net/SecurityCenter/API/Credential.pm index 8ab9398..7cdc16b 100644 --- a/lib/Net/SecurityCenter/API/Credential.pm +++ b/lib/Net/SecurityCenter/API/Credential.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -50,7 +50,7 @@ sub list { my $credentials = $self->client->get( '/credential', $params ); my $raw = delete( $params->{'raw'} ); - return if ( !$credentials ); + return if ( !$credentials ); return $credentials if ($raw); return sc_merge($credentials); diff --git a/lib/Net/SecurityCenter/API/DeviceInfo.pm b/lib/Net/SecurityCenter/API/DeviceInfo.pm new file mode 100644 index 0000000..1c5fb51 --- /dev/null +++ b/lib/Net/SecurityCenter/API/DeviceInfo.pm @@ -0,0 +1,215 @@ +package Net::SecurityCenter::API::DeviceInfo; + +use warnings; +use strict; + +use parent 'Net::SecurityCenter::API'; + +use Net::SecurityCenter::Utils qw(:all); + +our $VERSION = '0.206'; + +#------------------------------------------------------------------------------- +# METHODS +#------------------------------------------------------------------------------- + +sub get_info { + + my ( $self, %args ) = @_; + + my $tmpl = { + fields => {}, + ip => {}, + uuid => {}, + dns_name => { + remap => 'dnsName', + } + }; + + my $params = sc_check_params( $tmpl, \%args ); + my $device_info = $self->client->get( "/deviceInfo", $params ); + + return if ( !$device_info ); + return sc_normalize_hash $device_info; + +} + +#------------------------------------------------------------------------------- + +1; + +__END__ +=pod + +=encoding UTF-8 + + +=head1 NAME + +Net::SecurityCenter::API::DeviceInfo - Perl interface to Tenable.sc (SecurityCenter) Device Information REST API + + +=head1 SYNOPSIS + + use Net::SecurityCenter::REST; + use Net::SecurityCenter::API::DeviceInfo; + + my $sc = Net::SecurityCenter::REST->new('sc.example.org'); + + $sc->login('secman', 'password'); + + my $api = Net::SecurityCenter::API::DeviceInfo->new($sc); + + $sc->logout(); + + +=head1 DESCRIPTION + +This module provides Perl scripts easy way to interface the Device Information REST API of Tenable.sc +(SecurityCenter). + +For more information about the Tenable.sc (SecurityCenter) REST API follow the online documentation: + +L + + +=head1 CONSTRUCTOR + +=head2 Net::SecurityCenter::API::DeviceInfo->new ( $client ) + +Create a new instance of B using L class. + + +=head1 METHODS + +=head2 get_info + +Gets the device information from the current user. + +B: This will return device information for the first repository. To specify a particular repository, see +L::get_device_info. + + my $scans = $sc->get_info( + ip => '192.168.8.2', + fields => 'os,dnsName,severityCritical,severityHigh' + ); + + +Params: + +=over 4 + +=item * C : Device UUID + +=item * C : IP Address + +=item * C : DNS Name + +=item * C : List of fields + +=back + +Allowed Fields: + +=over 4 + +=item * C * + +=item * C * + +=item * C * + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=back + +(*) always comes back + + + + +=head1 SUPPORT + +=head2 Bugs / Feature Requests + +Please report any bugs or feature requests through the issue tracker +at L. +You will be notified automatically of any progress on your issue. + +=head2 Source Code + +This is open source software. The code repository is available for +public review and contribution under the terms of the license. + +L + + git clone https://github.com/giterlizzi/perl-Net-SecurityCenter.git + + +=head1 AUTHOR + +=over 4 + +=item * Giuseppe Di Terlizzi + +=back + + +=head1 LICENSE AND COPYRIGHT + +This software is copyright (c) 2018-2020 by Giuseppe Di Terlizzi. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +=cut diff --git a/lib/Net/SecurityCenter/API/Feed.pm b/lib/Net/SecurityCenter/API/Feed.pm index 2a4ae1b..baa32de 100644 --- a/lib/Net/SecurityCenter/API/Feed.pm +++ b/lib/Net/SecurityCenter/API/Feed.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; #------------------------------------------------------------------------------- # METHODS @@ -39,7 +39,7 @@ sub status { my $feed = $self->client->get($feed_path); - return if ( !$feed ); + return if ( !$feed ); return $feed if ($raw); return sc_normalize_hash($feed); diff --git a/lib/Net/SecurityCenter/API/File.pm b/lib/Net/SecurityCenter/API/File.pm index 5dd6f7b..4c1f9f3 100644 --- a/lib/Net/SecurityCenter/API/File.pm +++ b/lib/Net/SecurityCenter/API/File.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; #------------------------------------------------------------------------------- # METHODS diff --git a/lib/Net/SecurityCenter/API/Plugin.pm b/lib/Net/SecurityCenter/API/Plugin.pm index d075a42..77775ba 100644 --- a/lib/Net/SecurityCenter/API/Plugin.pm +++ b/lib/Net/SecurityCenter/API/Plugin.pm @@ -12,7 +12,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -118,7 +118,7 @@ sub list { my $raw = delete( $params->{'raw'} ); my $plugins = $self->client->get( '/plugin', $params ); - return if ( !$plugins ); + return if ( !$plugins ); return $plugins if ($raw); return sc_normalize_array($plugins); @@ -142,7 +142,7 @@ sub get { my $plugin_id = delete( $params->{'id'} ); my $plugin = $self->client->get( "/plugin/$plugin_id", $params ); - return if ( !$plugin ); + return if ( !$plugin ); return $plugin if ($raw); return sc_normalize_hash($plugin); diff --git a/lib/Net/SecurityCenter/API/PluginFamily.pm b/lib/Net/SecurityCenter/API/PluginFamily.pm index 913ccc3..71b16e7 100644 --- a/lib/Net/SecurityCenter/API/PluginFamily.pm +++ b/lib/Net/SecurityCenter/API/PluginFamily.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -133,7 +133,7 @@ sub list_plugins { my $plugin_family_id = delete( $params->{'id'} ); my $plugins = $self->client->get( "/pluginFamily/$plugin_family_id/plugins", $params ); - return if ( !$plugins ); + return if ( !$plugins ); return $plugins if ($raw); return sc_normalize_array($plugins); diff --git a/lib/Net/SecurityCenter/API/Policy.pm b/lib/Net/SecurityCenter/API/Policy.pm index 8c41f76..f27ef46 100644 --- a/lib/Net/SecurityCenter/API/Policy.pm +++ b/lib/Net/SecurityCenter/API/Policy.pm @@ -10,7 +10,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -51,7 +51,7 @@ sub list { my $raw = delete( $params->{'raw'} ); my $policies = $self->client->get( '/policy', $params ); - return if ( !$policies ); + return if ( !$policies ); return $policies if ($raw); return sc_merge($policies); @@ -66,6 +66,7 @@ sub get { my $tmpl = { fields => $common_template->{'fields'}, id => $common_template->{'id'}, + raw => {}, }; my $params = sc_check_params( $tmpl, \%args ); @@ -73,7 +74,7 @@ sub get { my $raw = delete( $params->{'raw'} ); my $policy = $self->client->get( "/policy/$policy_id", $params ); - return if ( !$policy ); + return if ( !$policy ); return $policy if ($raw); return sc_normalize_hash($policy); diff --git a/lib/Net/SecurityCenter/API/Report.pm b/lib/Net/SecurityCenter/API/Report.pm index 4456b75..5b4dc5f 100644 --- a/lib/Net/SecurityCenter/API/Report.pm +++ b/lib/Net/SecurityCenter/API/Report.pm @@ -10,7 +10,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -51,7 +51,7 @@ sub list { my $raw = delete( $params->{'raw'} ); my $reports = $self->client->get( '/report', $params ); - return if ( !$reports ); + return if ( !$reports ); return $reports if ($raw); return sc_merge($reports); @@ -76,7 +76,7 @@ sub get { my $report = $self->client->get( "/report/$report_id", $params ); - return if ( !$report ); + return if ( !$report ); return $report if ($raw); return sc_normalize_hash($report); @@ -162,20 +162,49 @@ Create a new instance of B using L. +=over 4 + +=item * C : Report fields + +=item * C : Filter + +=item * C : Return RAW result + +=back + +=head2 get + +Gets the report associated with C. + +Params: + +=over 4 + +=item * C : Report ID + +=back + +=head2 download -=head2 download ( $report_id [, $filename ] ) +Download the report associated with C. -Download the report associated with C. + $report->download( id => 1337, filename => '/tmp/report.pdf'); - $report->download(1337, '/tmp/report.pdf'); +Params: +=over 4 + +=item * C : Report ID + +=item * C : Name of file + +=back =head1 SUPPORT diff --git a/lib/Net/SecurityCenter/API/Repository.pm b/lib/Net/SecurityCenter/API/Repository.pm index 1dd7626..cf8078f 100644 --- a/lib/Net/SecurityCenter/API/Repository.pm +++ b/lib/Net/SecurityCenter/API/Repository.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -51,7 +51,7 @@ sub list { my $raw = delete( $params->{'raw'} ); my $repositories = $self->client->get( '/repository', $params ); - return if ( !$repositories ); + return if ( !$repositories ); return $repositories if ($raw); return sc_normalize_array($repositories); @@ -74,7 +74,7 @@ sub get { my $repository_id = delete( $params->{'id'} ); my $repository = $self->client->get( "/repository/$repository_id", $params ); - return if ( !$repository ); + return if ( !$repository ); return $repository if ($raw); return sc_normalize_hash($repository); @@ -176,19 +176,128 @@ Create a new instance of B using L. +=over 4 + +=item * C : Repository type ('all', 'local', 'remote', 'offline') + +=item * C : List of fields + +=back + + +=head2 get + +Get the repository associated with C. + +Params: + +=over 4 + +=item * C : Repository ID + +=item * C : List of fields + +=back + + +=head2 get_device_info + +Gets the device information for the Repository associated with C + +Params: + +=over 4 + +=item * C : Repository ID + +=item * C : Device UUID + +=item * C : IP Address + +=item * C : DNS Name + +=item * C : List of fields + +=back + + +Allowed Fields: + +=over 4 + +=item * C * + +=item * C * + +=item * C * + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=back + +(*) always comes back -=head2 get_device_info ( $repository_id, $ip_address [, $params ] ) -=head2 get_ip_info ( $ip_address [, $params ]) +=head2 get_ip_info -B: This method has been DEPRECATED as of SecurityCenter 5.7.0. +B: This method has been DEPRECATED as of SecurityCenter 5.7.0 +(see C method or L). =head1 SUPPORT diff --git a/lib/Net/SecurityCenter/API/Scan.pm b/lib/Net/SecurityCenter/API/Scan.pm index 57b72e5..8594270 100644 --- a/lib/Net/SecurityCenter/API/Scan.pm +++ b/lib/Net/SecurityCenter/API/Scan.pm @@ -10,7 +10,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -234,7 +234,7 @@ sub list { my $raw = delete( $params->{'raw'} ); my $scans = $self->client->get( '/scan', $params ); - return if ( !$scans ); + return if ( !$scans ); return $scans if ($raw); return sc_merge($scans); } @@ -255,7 +255,7 @@ sub get { my $raw = delete( $params->{'raw'} ); my $scan = $self->client->get( "/scan/$scan_id", $params ); - return if ( !$scan ); + return if ( !$scan ); return $scan if ($scan); return sc_normalize_hash($scan); diff --git a/lib/Net/SecurityCenter/API/ScanResult.pm b/lib/Net/SecurityCenter/API/ScanResult.pm index 37113eb..646dd58 100644 --- a/lib/Net/SecurityCenter/API/ScanResult.pm +++ b/lib/Net/SecurityCenter/API/ScanResult.pm @@ -11,7 +11,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -84,12 +84,20 @@ sub list { filter => $common_template->{'filter'}, raw => {}, start_date => { - allow => qr/^\d+$/, - remap => 'startDate' + filter => \&sc_filter_datetime_to_epoch, + remap => 'startTime', }, end_date => { + filter => \&sc_filter_datetime_to_epoch, + remap => 'endTime', + }, + start_time => { + allow => qr/^\d+$/, + remap => 'startTime' + }, + end_time => { allow => qr/^\d+$/, - remap => 'endDate' + remap => 'endTime' } }; @@ -414,16 +422,116 @@ Params: Get list of scans results (completed, running, etc.). + + my $scans = $sc->list( + start_date => '2020-01-01', + end_date => '2020-02-01', + fields => 'id,name,description,startTime,finishTime', + ); + + + # Using Time::Piece + + use Time::Piece; + use Time::Seconds; + + my $t = Time::Piece->new; + $t -= ONE_DAY; # Yesterday + + my $scans = $sc->list( + start_date => $t, + ); + + Params: =over 4 =item * C : List of fields +=item * C : Start date of scan in ISO 8601 format (YYYY-MM-DD, YYYY-MM-DD HH:MM:SS or YYYY-MM-DDTHH:MM:SS) or L object + +=item * C : End date of scan (see C) + +=item * C : Start date in epoch + +=item * C : End date in epoch + =item * C : Filter (C, C, C or C) =back +Allowed Fields: + +=over 4 + +=item * C * + +=item * C ** + +=item * C ** + +=item * C ** + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C
+ +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=item * C + +=back + +(*) always comes back +(**) comes back if fields list not specified + =head2 list_running @@ -461,7 +569,7 @@ Params: =item * C : Scan result ID -=item * C : Fields +=item * C : Fields (see C) =back diff --git a/lib/Net/SecurityCenter/API/Scanner.pm b/lib/Net/SecurityCenter/API/Scanner.pm index f1845a2..db18ba3 100644 --- a/lib/Net/SecurityCenter/API/Scanner.pm +++ b/lib/Net/SecurityCenter/API/Scanner.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -74,7 +74,7 @@ sub get { my $raw = delete( $params->{'raw'} ); my $scanner = $self->client->get( "/scanner/$scanner_id", $params ); - return if ( !$scanner ); + return if ( !$scanner ); return $scanner if ($raw); return sc_normalize_hash($scanner); diff --git a/lib/Net/SecurityCenter/API/Status.pm b/lib/Net/SecurityCenter/API/Status.pm index bf17ef6..be6fe1a 100644 --- a/lib/Net/SecurityCenter/API/Status.pm +++ b/lib/Net/SecurityCenter/API/Status.pm @@ -7,7 +7,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; #------------------------------------------------------------------------------- # METHODS @@ -48,7 +48,7 @@ __END__ =head1 NAME -Net::SecurityCenter::API::System - Perl interface to Tenable.sc (SecurityCenter) System REST API +Net::SecurityCenter::API::System - Perl interface to Tenable.sc (SecurityCenter) Status REST API =head1 SYNOPSIS diff --git a/lib/Net/SecurityCenter/API/System.pm b/lib/Net/SecurityCenter/API/System.pm index ee2857f..bf868bd 100644 --- a/lib/Net/SecurityCenter/API/System.pm +++ b/lib/Net/SecurityCenter/API/System.pm @@ -7,7 +7,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; #------------------------------------------------------------------------------- # METHODS @@ -17,19 +17,19 @@ sub get_status { my ( $self, %args ) = @_; - warnings::warnif( 'deprecated', - 'method "Net::SecurityCenter::API::System->get_status" is deprecated use "Net::SecurityCenter::API::Status->status"' - ); + deprecated + '"Net::SecurityCenter::API::System->get_status" is DEPRECATED use "Net::SecurityCenter::API::Status->status" instead'; } +#------------------------------------------------------------------------------- + sub get_info { my ( $self, %args ) = @_; - warnings::warnif( 'deprecated', - 'method "Net::SecurityCenter::API::Status->get_info" is deprecated use "Net::SecurityCenter::API::Status->info"' - ); + deprecated + '"Net::SecurityCenter::API::Status->get_info" is DEPRECATED use "Net::SecurityCenter::API::Status->info"'; return $self->info( \%args ); diff --git a/lib/Net/SecurityCenter/API/User.pm b/lib/Net/SecurityCenter/API/User.pm index e1a3f99..70fcee5 100644 --- a/lib/Net/SecurityCenter/API/User.pm +++ b/lib/Net/SecurityCenter/API/User.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { diff --git a/lib/Net/SecurityCenter/API/Zone.pm b/lib/Net/SecurityCenter/API/Zone.pm index c076003..08f1d56 100644 --- a/lib/Net/SecurityCenter/API/Zone.pm +++ b/lib/Net/SecurityCenter/API/Zone.pm @@ -9,7 +9,7 @@ use parent 'Net::SecurityCenter::API'; use Net::SecurityCenter::Utils qw(:all); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; my $common_template = { @@ -71,7 +71,7 @@ sub get { my $raw = delete( $params->{'raw'} ); my $zone = $self->client->get( "/zone/$zone_id", $params ); - return if ( !$zone ); + return if ( !$zone ); return $zone if ($raw); return sc_normalize_hash($zone); diff --git a/lib/Net/SecurityCenter/Error.pm b/lib/Net/SecurityCenter/Error.pm index 37b0644..3236917 100644 --- a/lib/Net/SecurityCenter/Error.pm +++ b/lib/Net/SecurityCenter/Error.pm @@ -5,7 +5,7 @@ use strict; use overload q|""| => 'message', fallback => 1; -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; #------------------------------------------------------------------------------- # CONSTRUCTOR diff --git a/lib/Net/SecurityCenter/REST.pm b/lib/Net/SecurityCenter/REST.pm index 623d426..4e8c77e 100644 --- a/lib/Net/SecurityCenter/REST.pm +++ b/lib/Net/SecurityCenter/REST.pm @@ -12,7 +12,7 @@ use LWP::UserAgent; use Net::SecurityCenter::Error; use Net::SecurityCenter::Utils qw(trim dumper); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; #------------------------------------------------------------------------------- # CONSTRUCTOR diff --git a/lib/Net/SecurityCenter/Utils.pm b/lib/Net/SecurityCenter/Utils.pm index 2b45ebb..07162be 100644 --- a/lib/Net/SecurityCenter/Utils.pm +++ b/lib/Net/SecurityCenter/Utils.pm @@ -9,13 +9,14 @@ use Time::Piece; use Data::Dumper (); use Exporter qw(import); -our $VERSION = '0.205_01'; +our $VERSION = '0.206'; our @EXPORT_OK = qw( sc_check_params sc_decode_scanner_status sc_filter_array_to_string sc_filter_int_to_bool + sc_filter_datetime_to_epoch sc_merge sc_normalize_hash sc_normalize_array @@ -25,6 +26,8 @@ our @EXPORT_OK = qw( decamelize dumper trim + deprecated + cpe_decode ); our %EXPORT_TAGS = ( all => \@EXPORT_OK ); @@ -81,6 +84,46 @@ sub trim { } +#------------------------------------------------------------------------------- + +sub deprecated { + local $Carp::CarpLevel = 1; + carp @_; +} + +#------------------------------------------------------------------------------- + +sub cpe_decode { + + my ($cpe) = @_; + + $cpe =~ s/cpe:\///; + + my ( + $part, $vendor, $product, $version, $update, $edition, + $language, $sw_edition, $target_sw, $target_hw, $other + ); + + ( $part, $vendor, $product, $version, $update, $edition, $language ) = split( /:/, $cpe ); + + ( $sw_edition, $target_sw, $target_hw, $other ) = split( /~/, $language ) if ($language); + + return { + 'part' => $part, + 'vendor' => $vendor, + 'product' => $product, + 'version' => $version, + 'update' => $update, + 'edition' => $edition, + 'language' => $language, + 'sw_edition' => $sw_edition, + 'target_sw' => $target_sw, + 'target_hw' => $target_hw, + 'other' => $other + }; + +} + #------------------------------------------------------------------------------- # COMMON CLASS UTILS #------------------------------------------------------------------------------- @@ -303,6 +346,7 @@ sub sc_normalize_hash { createdTime finishTime importFinish + importStart lastSyncTime lastTrendUpdate lastVulnUpdate @@ -311,6 +355,9 @@ sub sc_normalize_hash { updateTime diagnosticsGenerated statusLastChecked + lastScan + lastUnauthRun + lastAuthRun ); my @seconds_fields = qw( @@ -408,6 +455,33 @@ sub sc_filter_int_to_bool { return ( $_[0] == 1 ) ? \1 : \0; } +sub sc_filter_datetime_to_epoch { + + my ($date) = @_; + + if ( ref $date eq 'Time::Piece' ) { + return $date->epoch; + } + + if ( $date =~ /^\d{4}-\d{2}-\d{2}$/ ) { + my $t = Time::Piece->strptime( $date, '%Y-%m-%d' ); + return $t->epoch; + } + + if ( $date =~ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/ ) { + my $t = Time::Piece->strptime( $date, '%Y-%m-%d %H:%M:%S' ); + return $t->epoch; + } + + if ( $date =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/ ) { + my $t = Time::Piece->strptime( $date, '%Y-%m-%dT%H:%M:%S' ); + return $t->epoch; + } + + return $date; + +} + #------------------------------------------------------------------------------- 1; diff --git a/t/20-utils.t b/t/20-utils.t index 7188b00..1b7476d 100644 --- a/t/20-utils.t +++ b/t/20-utils.t @@ -15,4 +15,15 @@ cmp_ok( Net::SecurityCenter::Utils::trim(' trimmed '), 'eq', 'trimmed', 'Trimmed cmp_ok( Net::SecurityCenter::Utils::decamelize('SecurityCenter'), 'eq', 'security_center', 'Decamelize text' ); +subtest( + 'SC Schedule' => sub { + + my $sc_schedule_now = Net::SecurityCenter::Utils::sc_schedule( type => 'now' ); + cmp_ok( $sc_schedule_now->{'repeatRule'}, 'eq', 'FREQ=NOW;INTERVAL=1', 'Schedule now' ); + + my $sc_schedule_ical = Net::SecurityCenter::Utils::sc_schedule( type => 'ical', start => '19700101T000000Z' ); + cmp_ok( $sc_schedule_ical->{'start'}, 'eq', '19700101T000000Z', 'Start datetime' ); + + } +); done_testing(); diff --git a/t/99-local.t b/t/99-local.t index b3a403c..86d8268 100644 --- a/t/99-local.t +++ b/t/99-local.t @@ -121,6 +121,14 @@ sub _test { is( $sc->error, undef, 'Check errors' ); + subtest( + 'REST' => sub { + my $client = $sc->client; + ok( $client->request( 'get', '/system' ), 'Request GET' ); + ok( $client->get('/system'), 'Request GET (helper)' ); + } + ); + subtest( 'Status API' => sub { @@ -210,6 +218,16 @@ sub _test { } ); + subtest( + 'Policy API' => sub { + + ok( $sc->policy->list, 'Get List' ); + + ok( $sc->policy->get( id => 1, raw => 1 ), 'Get Policy ID=1' ); #TODO REMOVE RAW + + } + ); + subtest( 'Scanner API' => sub { diff --git a/t/mock/rest-policy-1-get.json b/t/mock/rest-policy-1-get.json new file mode 100644 index 0000000..af21e3e --- /dev/null +++ b/t/mock/rest-policy-1-get.json @@ -0,0 +1,287 @@ +{ + "type": "regular", + "response": [ + { + "id": "1", + "generateXCCDFResults": "false", + "context": "", + "policyTemplateID": "1", + "policyProfileName": "", + "name": "test", + "description": "desc", + "tags": "", + "createdTime": "1406148027", + "modifiedTime": "1406148027", + "status": "0", + "auditFiles": [], + "preferences": { + "preference1": "value1", + "preference2": "value2" + }, + "families": [ + { + "id": "9", + "name": "AIX Local Security Checks", + "count": "11164" + }, + { + "id": "54", + "name": "Amazon Linux Local Security Checks", + "count": "502" + }, + { + "id": "35", + "name": "Backdoors", + "count": "102" + }, + { + "id": "18", + "name": "CentOS Local Security Checks", + "count": "1890" + }, + { + "id": "6", + "name": "CGI abuses", + "count": "3235" + }, + { + "id": "26", + "name": "CGI abuses : XSS", + "count": "600" + }, + { + "id": "33", + "name": "CISCO", + "count": "576" + }, + { + "id": "31", + "name": "Databases", + "count": "372" + }, + { + "id": "3", + "name": "Debian Local Security Checks", + "count": "3179" + }, + { + "id": "25", + "name": "Default Unix Accounts", + "count": "101" + }, + { + "id": "22", + "name": "Denial of Service", + "count": "107" + }, + { + "id": "37", + "name": "DNS", + "count": "110" + }, + { + "id": "57", + "name": "F5 Networks Local Security Checks", + "count": "154" + }, + { + "id": "5", + "name": "Fedora Local Security Checks", + "count": "8067" + }, + { + "id": "34", + "name": "Firewalls", + "count": "139" + }, + { + "id": "13", + "name": "FreeBSD Local Security Checks", + "count": "2616" + }, + { + "id": "19", + "name": "FTP", + "count": "244" + }, + { + "id": "40", + "name": "Gain a shell remotely", + "count": "274" + }, + { + "id": "30", + "name": "General", + "count": "198" + }, + { + "id": "7", + "name": "Gentoo Local Security Checks", + "count": "2071" + }, + { + "id": "2", + "name": "HP-UX Local Security Checks", + "count": "1974" + }, + { + "id": "56", + "name": "Huawei Local Security Checks", + "count": "14" + }, + { + "id": "50", + "name": "Junos Local Security Checks", + "count": "107" + }, + { + "id": "21", + "name": "MacOS X Local Security Checks", + "count": "717" + }, + { + "id": "47", + "name": "Mandriva Local Security Checks", + "count": "2970" + }, + { + "id": "23", + "name": "Misc.", + "count": "972" + }, + { + "id": "52", + "name": "Mobile Devices", + "count": "43" + }, + { + "id": "43", + "name": "Netware", + "count": "14" + }, + { + "id": "53", + "name": "Oracle Linux Local Security Checks", + "count": "1912" + }, + { + "id": "55", + "name": "Palo Alto Local Security Checks", + "count": "20" + }, + { + "id": "32", + "name": "Peer-To-Peer File Sharing", + "count": "72" + }, + { + "id": "39", + "name": "Policy Compliance", + "count": "38" + }, + { + "id": "42", + "name": "Port scanners", + "count": "8" + }, + { + "id": "1", + "name": "Red Hat Local Security Checks", + "count": "3424" + }, + { + "id": "28", + "name": "RPC", + "count": "36" + }, + { + "id": "36", + "name": "SCADA", + "count": "198" + }, + { + "id": "51", + "name": "Scientific Linux Local Security Checks", + "count": "1760" + }, + { + "id": "24", + "name": "Service detection", + "count": "408" + }, + { + "id": "41", + "name": "Settings", + "count": "66" + }, + { + "id": "15", + "name": "Slackware Local Security Checks", + "count": "757" + }, + { + "id": "12", + "name": "SMTP problems", + "count": "135" + }, + { + "id": "45", + "name": "SNMP", + "count": "33" + }, + { + "id": "4", + "name": "Solaris Local Security Checks", + "count": "3798" + }, + { + "id": "8", + "name": "SuSE Local Security Checks", + "count": "7355" + }, + { + "id": "14", + "name": "Ubuntu Local Security Checks", + "count": "2767" + }, + { + "id": "48", + "name": "VMware ESX Local Security Checks", + "count": "94" + }, + { + "id": "11", + "name": "Web Servers", + "count": "876" + }, + { + "id": "20", + "name": "Windows", + "count": "3113" + }, + { + "id": "10", + "name": "Windows : Microsoft Bulletins", + "count": "986" + }, + { + "id": "29", + "name": "Windows : User management", + "count": "28" + } + ], + "creator": { + "id": "1", + "username": "head", + "firstname": "test", + "lastname": "User" + }, + "canUse": "true", + "canManage": "false" + } + ], + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1406223313 +} \ No newline at end of file diff --git a/t/mock/rest-policy-get.json b/t/mock/rest-policy-get.json new file mode 100644 index 0000000..a310282 --- /dev/null +++ b/t/mock/rest-policy-get.json @@ -0,0 +1,157 @@ +{ + "type": "regular", + "response": { + "usable": [ + { + "id": "1", + "name": "test", + "description": "desc", + "status": "0" + }, + { + "id": "2", + "name": "test2", + "description": "desc", + "status": "0" + }, + { + "id": "3", + "name": "test3", + "description": "desc", + "status": "0" + }, + { + "id": "4", + "name": "test4", + "description": "test desc", + "status": "0" + }, + { + "id": "1000001", + "name": "nesus upload - ibm credentials", + "description": "", + "status": "0" + }, + { + "id": "1000002", + "name": "nesus upload - ibm credentials - uploaded one", + "description": "Nessus Policy exported from Tenable.sc", + "status": "0" + }, + { + "id": "1000003", + "name": "IBM iSeries Credentials Name", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000004", + "name": "Nessus Upload 2", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000005", + "name": "Nessus Upload 3", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000016", + "name": "Tom Test", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "100001", + "name": "Nessus Upload 4", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000018", + "name": "Tom Test 2", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000019", + "name": "DOCtest", + "description": "desc", + "status": "0" + }, + { + "id": "1000020", + "name": "test5", + "description": "test desc", + "status": "0" + } + ], + "manageable": [ + { + "id": "1000001", + "name": "nesus upload - ibm credentials", + "description": "", + "status": "0" + }, + { + "id": "1000002", + "name": "nesus upload - ibm credentials - uploaded one", + "description": "Nessus Policy exported from Tenable.sc", + "status": "0" + }, + { + "id": "1000003", + "name": "IBM iSeries Credentials Name", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000004", + "name": "Nessus Upload 2", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000005", + "name": "Nessus Upload 3", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000016", + "name": "Tom Test", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "100001", + "name": "Nessus Upload 4", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000018", + "name": "Tom Test 2", + "description": "Imported Nessus Policy", + "status": "0" + }, + { + "id": "1000019", + "name": "DOCtest", + "description": "desc", + "status": "0" + }, + { + "id": "1000020", + "name": "test5", + "description": "test desc", + "status": "0" + } + ] + }, + "error_code": 0, + "error_msg": "", + "warnings": [], + "timestamp": 1406233675 +}