File Coverage

blib/lib/Rapid7/NeXpose/API.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Rapid7::NeXpose::API;
2              
3 1     1   22408 use warnings;
  1         2  
  1         33  
4 1     1   6 use strict;
  1         2  
  1         38  
5              
6 1     1   1161 use XML::Simple;
  0            
  0            
7             use LWP::UserAgent;
8             use HTTP::Request::Common;
9              
10             =head1 NAME
11              
12             Rapid7::NeXpose::API - Communicate with NeXpose via XML NeXpose API v1.1
13              
14             =head1 VERSION
15              
16             Version 0.03
17              
18             =cut
19              
20             our $VERSION = '0.03';
21              
22              
23             =head1 SYNOPSIS
24              
25             This is Perl interface for communication with NeXpose scanner over API v1.1.
26             You can start, stop, pause and resume scan. Watch progress and status of
27             scan, download report, etc.
28              
29             Currently you can only start scan, list sites and delete site.
30              
31             use Rapid7::NeXpose::API;
32              
33             my $n = Rapid7::NeXpose::API->new(
34             url=>'https://localhost:3780',password=>'test');
35             my $sl = $n->sitelist();
36             print "Starting scan for first site found: ";
37             printf "%s with ID: %s\n", $sl->[0]->{'name'}, $sl->[0]->{'id'};
38             $n->sitescan($sl->[0]->{'id'});
39              
40             =head1 NOTICE
41              
42             This CPAN module uses LWP for communicating with NeXpose over its API via https.
43             Therefore, make sure that you have Net::SSL (provided by Crypt::SSLeay):
44             http://search.cpan.org/perldoc?Crypt::SSLeay
45             or IO::Socket::SSL:
46             http://search.cpan.org/perldoc?IO::Socket::SSL
47              
48             If you think you have login problems, check this first!
49              
50             =head1 METHODS
51              
52             =head2 new ( [key=>value, key2=>value2, ...] )
53              
54             creates new object Rapid7::NeXpose::API
55              
56             my $n = Rapid7::NeXpose::API->new(
57             url=>'https://localhost:3780', debug=>1,
58             user=>'user', password=>'test', nologin=>1
59             );
60              
61             =cut
62             sub new {
63             # Check for common user mistake - taken from LWP
64             Carp::croak("Options to Rapid7::NeXpose::API constructor should be key/value pairs, not hash reference")
65             if ref($_[1]) eq 'HASH';
66              
67             my($class, %cnf) = @_;
68             my $self;
69              
70             $self->{_url} = delete $cnf{url};
71             if (!defined($self->{_url}) or $self->{_url} eq '') {
72             $self->{_url}='https://localhost:3780/';
73             } elsif (substr($self->{_url},-1,1) ne '/') {
74             $self->{_url}= $self->{_url}.'/';
75             }
76              
77             $self->{_urlapi} = delete $cnf{urlapi};
78             if (!defined($self->{_urlapi})) {
79             $self->{_urlapi}=$self->{_url}."api/1.1/xml";
80             }
81              
82             $self->{_user} = delete $cnf{user};
83             $self->{_user} = "nxadmin" unless defined $self->{_user};
84              
85             $self->{_password} = delete $cnf{password};
86              
87             $self->{'_debug'} = 0 unless defined $cnf{'debug'};
88              
89             $self->{_ua} = LWP::UserAgent->new;
90             if ($self->{'_debug'}) {
91             $self->lwpdebug();
92             }
93              
94             bless $self, $class;
95             unless ($cnf{nologin} and !defined($self->{_password})) {
96             $self->login();
97             }
98             return $self;
99             }
100              
101             =head2 url ( [$nexpose_url] )
102              
103             get/set NeXpose base URL
104             =cut
105             sub url {
106             my ( $self, $url ) = @_;
107             $self->{_url} = $url if defined($url);
108             return ( $self->{_url} );
109             }
110              
111             =head2 urlapi ( [$nexpose_url_api] )
112              
113             get/set NeXpose API URL
114             =cut
115             sub urlapi {
116             my ( $self, $urlapi ) = @_;
117             $self->{_urlapi} = $urlapi if defined($urlapi);
118             return ( $self->{_urlapi} );
119             }
120              
121             =head2 user ( [$user] )
122              
123             set NeXpose credentials, returns $user
124             =cut
125             sub user {
126             my ( $self, $user ) = @_;
127             $self->{_user} = $user if defined($user);
128             return ( $self->{_user} );
129             }
130              
131             =head2 password ( [$password])
132              
133             set NeXpose credentials, returns $password
134             =cut
135             sub password {
136             my ( $self, $password ) = @_;
137             $self->{_password} = $password if defined($password);
138             return ( $self->{_password} );
139             }
140              
141             =head2 session ( [$session])
142              
143             set NeXpose session-id, returns $session
144             =cut
145             sub session {
146             my ( $self, $session ) = @_;
147             $self->{_session} = $session if defined($session);
148             return ( $self->{_session} );
149             }
150              
151             =head2 syncid ( [$syncid])
152              
153             set NeXpose sync-id, returns $id
154             =cut
155             sub syncid {
156             my ( $self, $syncid ) = @_;
157             my $sid;
158             if (defined($syncid)) {
159             $sid = $syncid;
160             } else {
161             $sid=int(rand(65535));
162             }
163             return ( $sid );
164             }
165              
166             =head2 lwpdebug
167              
168             get/set LWP debugging
169             =cut
170             sub lwpdebug {
171             my ( $self ) = @_;
172             my $ua = $self->{_ua};
173             $ua->add_handler("request_send", sub { shift->dump; return });
174             $ua->add_handler("response_done", sub { shift->dump; return });
175             }
176              
177             =head2 xml_request ( <$req> )
178              
179             perform XML request to nexpose
180             =cut
181             sub xml_request {
182             my ( $self, $req ) = @_;
183              
184             my $xml = XMLout($req, RootName => '', XMLDecl => '');
185            
186             if ($self->{'_debug'}>2) {
187             print STDERR $xml."\n";
188             }
189             my $cont = $self->http_api ($xml);
190             my $xmls;
191             eval {
192             $xmls=XMLin($cont, KeepRoot => 1, ForceArray => 1, KeyAttr => '', SuppressEmpty => '' );
193             } or return '';
194             return ($xmls);
195             }
196              
197             =head2 http_api <$post_data> )
198              
199             perform api request to nexpose and return content
200             =cut
201             sub http_api {
202             my ( $self, $post_data ) = @_;
203              
204             my $ua = $self->{_ua};
205             my $r = POST $self->urlapi(), 'Content-Type'=>'text/xml', Content=>$post_data;
206             my $result = $ua->request($r);
207             if ($result->is_success) {
208             return $result->content;
209             } else {
210             return '';
211             }
212             }
213              
214             =head2 login ()
215              
216             login to NeXpose
217             =cut
218             sub login {
219             my ( $self ) = @_;
220             my $hashref = { 'LoginRequest' => {
221             'sync-id' => $self->syncid(),
222             'user-id' => $self->user(),
223             'password' => $self->password()
224             } };
225             my $xmlh = $self->xml_request($hashref);
226             if ($xmlh->{'LoginResponse'}->[0]->{'success'}==1) {
227             $self->session($xmlh->{'LoginResponse'}->[0]->{'session-id'});
228             return $xmlh;
229             } else {
230             return ''
231             }
232             }
233              
234             =head2 logout ()
235              
236             sends logout request, returns 1 on success, 0 on failure
237             =cut
238              
239             sub logout {
240             my ( $self ) = @_;
241              
242             my $sid=int(rand(65535));
243             my $hashref = { 'LogoutRequest' => {
244             'sync-id' => $self->syncid(),
245             'session-id' => $self->session()
246             } };
247            
248             my $xmlh = $self->xml_request($hashref);
249             if ($xmlh->{'LogoutResponse'}->[0]->{'success'}==1) {
250             return 1;
251             } else {
252             return 0;
253             }
254             }
255              
256             =head2 sitelist ()
257              
258             list sites, returns list of sites
259             =cut
260             sub sitelist {
261             my ( $self ) = @_;
262             my $hashref = { 'SiteListingRequest' => {
263             'sync-id' => $self->syncid(),
264             'session-id' => $self->session()
265             } };
266             my $xmlh = $self->xml_request($hashref);
267             if ($xmlh->{'SiteListingResponse'}->[0]->{'success'}==1) {
268             return $xmlh->{'SiteListingResponse'}->[0]->{'SiteSummary'};
269             } else {
270             return ''
271             }
272             }
273              
274             =head2 sitescan ( $siteid )
275              
276             scan site specified by ID
277             =cut
278             sub sitescan {
279             my ( $self, $siteid ) = @_;
280             my $hashref = { 'SiteScanRequest' => {
281             'sync-id' => $self->syncid(),
282             'session-id' => $self->session(),
283             'site-id' => $siteid
284             } };
285             my $xmlh = $self->xml_request($hashref);
286             if ($xmlh->{'SiteScanResponse'}->[0]->{'success'}==1) {
287             my $hashref={
288             'scan-id' => $xmlh->{'Scan'}->[0]->{'scan-id'},
289             'engine-id' => $xmlh->{'Scan'}->[0]->{'engine-id'}
290             };
291             return $hashref;
292             } else {
293             return ''
294             }
295             }
296              
297             =head2 sitedelete ( $siteid )
298              
299             delete site specified by ID
300             =cut
301             sub sitedelete {
302             my ( $self, $siteid ) = @_;
303             my $hashref = { 'SiteDeleteRequest' => {
304             'sync-id' => $self->syncid(),
305             'session-id' => $self->session(),
306             'site-id' => $siteid
307             } };
308             my $xmlh = $self->xml_request($hashref);
309             if ($xmlh->{'SiteDeleteResponse'}->[0]->{'success'}==1) {
310             return 1;
311             } else {
312             return 0;
313             }
314             }
315              
316             =head2 DESTROY
317              
318             destructor, calls logout method on destruction
319             =cut
320             sub DESTROY {
321             my ($self) = @_;
322             $self->logout();
323             }
324              
325             =head1 AUTHOR
326              
327             Vlatko Kosturjak, C<< >>
328              
329             =head1 BUGS
330              
331             Please report any bugs or feature requests to C, or through
332             the web interface at L. I will be notified, and then you'll
333             automatically be notified of progress on your bug as I make changes.
334              
335              
336              
337              
338             =head1 SUPPORT
339              
340             You can find documentation for this module with the perldoc command.
341              
342             perldoc Rapid7::NeXpose::API
343              
344              
345             You can also look for information at:
346              
347             =over 4
348              
349             =item * RT: CPAN's request tracker
350              
351             L
352              
353             =item * AnnoCPAN: Annotated CPAN documentation
354              
355             L
356              
357             =item * CPAN Ratings
358              
359             L
360              
361             =item * Search CPAN
362              
363             L
364              
365             =back
366              
367              
368             =head1 REPOSITORY
369              
370             Repository is available on GitHub: https://github.com/kost/rapid7-nexpose-api-perl
371              
372             =head1 ACKNOWLEDGEMENTS
373              
374              
375             =head1 LICENSE AND COPYRIGHT
376              
377             Copyright 2010 Vlatko Kosturjak.
378              
379             This program is free software; you can redistribute it and/or modify it
380             under the terms of either: the GNU General Public License as published
381             by the Free Software Foundation; or the Artistic License.
382              
383             See http://dev.perl.org/licenses/ for more information.
384              
385              
386             =cut
387              
388             1; # End of Rapid7::NeXpose::API