tenable-ruby 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/tenable-ruby.rb +82 -104
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3ffc535c0804bfd2ec0c8ed49901f5071bd5ecb
|
4
|
+
data.tar.gz: 802afef027172f267dc908d5c203a0846aec68f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1792020ee77d3775f358673764d10594b7ba4662013a102f59cd82c7e2a1d09e19904d7bb7a3bc8c8d242c6bc3791c5247a4e795ae5cfa7afaa2e0885855e99c
|
7
|
+
data.tar.gz: 72753b4e0962e754b837817735d2722d3665e5ffd93ad3df8eb6a55cb1e9bdebed8885ce514a689427e028457e5e3ad0be324770fcae0a71746893419b1fa30a
|
data/lib/tenable-ruby.rb
CHANGED
@@ -1,17 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# coding: utf-8
|
3
|
-
# = tenable-ruby.rb:
|
3
|
+
# = tenable-ruby.rb: Ruby library for communicating with the tenable.io API
|
4
4
|
#
|
5
|
-
# Authors:: Vlatko Kosturjak, Patrick Craston
|
6
5
|
#
|
7
|
-
# (C) Vlatko Kosturjak, Kost. Distributed under MIT license.
|
6
|
+
# (C) Copyright (c) 2010 Vlatko Kosturjak, Kost, 2019 Intruder Systems Ltd. Distributed under MIT license.
|
8
7
|
#
|
9
8
|
# == What is this library?
|
10
9
|
#
|
11
|
-
#
|
10
|
+
# Ruby library for communicating with the tenable.io API.
|
12
11
|
# You can start, stop, pause and resume scans. Get status of scans, download reports, create policies, etc.
|
13
|
-
# Based on the excellent library for interacting with Nessus
|
14
|
-
# https://github.com/kost/nessus_rest-ruby by https://github.com/kost.
|
15
12
|
#
|
16
13
|
# == Requirements
|
17
14
|
#
|
@@ -56,9 +53,6 @@ module TenableRuby
|
|
56
53
|
# or
|
57
54
|
# TenableRuby::Client.new (:credentials => {access_key: 'XXX', secret_key: 'XXX'})
|
58
55
|
#
|
59
|
-
# default url is set to tenable.io, change to Nessus appliance url if required, e.g.
|
60
|
-
# TenableRuby::Client.new (:url => 'https://nessus_url:8834',
|
61
|
-
# :credentials => {access_key: 'XXX', secret_key: 'XXX'})
|
62
56
|
def initialize(params = {})
|
63
57
|
# defaults
|
64
58
|
@tenable_url = params.fetch(:url, 'https://cloud.tenable.com')
|
@@ -95,19 +89,19 @@ module TenableRuby
|
|
95
89
|
:json => 1,
|
96
90
|
:authenticationmethod => true
|
97
91
|
}
|
98
|
-
|
99
|
-
if
|
100
|
-
@token = "token=#{
|
92
|
+
response = http_post(:uri => "/session", :data => payload)
|
93
|
+
if response['token']
|
94
|
+
@token = "token=#{response['token']}"
|
101
95
|
@header = {'X-Cookie' => @token}
|
102
96
|
else
|
103
|
-
|
97
|
+
raise TenableRuby::Error::AuthenticationError, "Authentication failed. Could not authenticate using
|
104
98
|
username/password."
|
105
99
|
end
|
106
100
|
elsif @credentials[:access_key] and @credentials[:secret_key]
|
107
101
|
@header = {'X-ApiKeys' => "accessKey=#{@credentials[:access_key]}; secretKey=#{@credentials[:secret_key]}"}
|
108
102
|
else
|
109
|
-
|
110
|
-
" either a username and password or an API access key and secret key (these can be generated at " \
|
103
|
+
raise TenableRuby::Error::AuthenticationError, "Authentication credentials were not provided. You must " \
|
104
|
+
"provide either a username and password or an API access key and secret key (these can be generated at " \
|
111
105
|
"https://cloud.tenable.com/app.html#/settings/my-account/api-keys."
|
112
106
|
end
|
113
107
|
end
|
@@ -140,8 +134,8 @@ module TenableRuby
|
|
140
134
|
# Reference:
|
141
135
|
# https://cloud.tenable.com/api#/resources/users/delete
|
142
136
|
def user_delete(user_id)
|
143
|
-
|
144
|
-
|
137
|
+
response = http_delete(:uri => "/users/#{user_id}", :fields => header)
|
138
|
+
response.code
|
145
139
|
end
|
146
140
|
|
147
141
|
# Changes the password for the given user
|
@@ -153,8 +147,8 @@ module TenableRuby
|
|
153
147
|
:password => password,
|
154
148
|
:json => 1
|
155
149
|
}
|
156
|
-
|
157
|
-
|
150
|
+
response = http_put(:uri => "/users/#{user_id}/chpasswd", :data => payload, :fields => header)
|
151
|
+
response.code
|
158
152
|
end
|
159
153
|
|
160
154
|
# Logs the current user out and destroys the session
|
@@ -162,8 +156,8 @@ module TenableRuby
|
|
162
156
|
# Reference:
|
163
157
|
# https://cloud.tenable.com/api#/resources/session/destroy
|
164
158
|
def user_logout
|
165
|
-
|
166
|
-
|
159
|
+
response = http_delete(:uri => "/session", :fields => header)
|
160
|
+
response.code
|
167
161
|
end
|
168
162
|
|
169
163
|
# Returns the policy list
|
@@ -334,12 +328,8 @@ module TenableRuby
|
|
334
328
|
# Reference:
|
335
329
|
# https://cloud.tenable.com/api#/resources/scans/delete
|
336
330
|
def scan_delete(scan_id)
|
337
|
-
|
338
|
-
|
339
|
-
true
|
340
|
-
else
|
341
|
-
false
|
342
|
-
end
|
331
|
+
response = http_delete(:uri => "/scans/#{scan_id}", :fields => header)
|
332
|
+
return response.code == 200
|
343
333
|
end
|
344
334
|
|
345
335
|
# Returns details for the given host
|
@@ -428,17 +418,8 @@ module TenableRuby
|
|
428
418
|
# Reference:
|
429
419
|
# https://cloud.tenable.com/api#/resources/policies/delete
|
430
420
|
def policy_delete(policy_id)
|
431
|
-
|
432
|
-
|
433
|
-
end
|
434
|
-
|
435
|
-
# Schedules a software update for all components (only Nessus 6)
|
436
|
-
#
|
437
|
-
def software_update
|
438
|
-
if @tenable_url == 'https://cloud.tenable.com'
|
439
|
-
return "software_update only works on a Nessus 6 appliance"
|
440
|
-
end
|
441
|
-
http_post(:uri => "/settings/software-update", :fields => header)
|
421
|
+
response = http_delete(:uri => "/policies/#{policy_id}", :fields => header)
|
422
|
+
response.code
|
442
423
|
end
|
443
424
|
|
444
425
|
# Performs scan with templatename provided (name, title or uuid of scan).
|
@@ -496,38 +477,38 @@ module TenableRuby
|
|
496
477
|
|
497
478
|
# Returns scan status by performing a 'scan_details' API call
|
498
479
|
def scan_status(scan_id)
|
499
|
-
|
500
|
-
unless
|
480
|
+
details = scan_details(scan_id)
|
481
|
+
unless details['error'].nil?
|
501
482
|
return 'error'
|
502
483
|
end
|
503
|
-
if
|
484
|
+
if details.nil?
|
504
485
|
return 'error'
|
505
486
|
end
|
506
|
-
|
487
|
+
details['info']['status']
|
507
488
|
end
|
508
489
|
|
509
490
|
# Returns the status of the latest history object of a scan by performing a 'scan_details' API call.
|
510
491
|
# Note this is currently updated more frequently than the scan status in the tenable.io API
|
511
492
|
def scan_latest_history_status(scan_id)
|
512
|
-
|
513
|
-
unless
|
493
|
+
details = scan_details(scan_id)
|
494
|
+
unless details['error'].nil?
|
514
495
|
return 'error'
|
515
496
|
end
|
516
|
-
if
|
497
|
+
if details.nil?
|
517
498
|
return 'error'
|
518
499
|
end
|
519
|
-
history =
|
500
|
+
history = details['history']
|
520
501
|
if history.nil? or history.length == 0
|
521
502
|
'error'
|
522
503
|
else
|
523
|
-
|
504
|
+
details['history'].last['status']
|
524
505
|
end
|
525
506
|
end
|
526
507
|
|
527
508
|
# Parse the scan status command to determine if a scan has finished
|
528
509
|
def scan_finished?(scan_id)
|
529
|
-
|
530
|
-
if
|
510
|
+
status = scan_status(scan_id)
|
511
|
+
if status == 'completed' or status == 'canceled' or status == 'imported'
|
531
512
|
true
|
532
513
|
else
|
533
514
|
false
|
@@ -536,24 +517,21 @@ module TenableRuby
|
|
536
517
|
|
537
518
|
# use download scan API call to download a report in raw format
|
538
519
|
def report_download_quick(scan_id, format)
|
539
|
-
|
520
|
+
export_details = scan_export(scan_id, format)
|
540
521
|
# ready, loading
|
541
|
-
while (
|
542
|
-
if
|
543
|
-
return nil
|
544
|
-
end
|
545
|
-
if status == "error"
|
522
|
+
while (export_status = scan_export_status(scan_id, export_details['file'])['status']) != "ready" do
|
523
|
+
if export_status.nil? or export_status == '' or export_status == "error"
|
546
524
|
raise TenableRuby::Error::TenableError, "Tenable.io returned an error while exporting the scan"
|
547
525
|
end
|
548
526
|
sleep @defsleep
|
549
527
|
end
|
550
|
-
report_download(scan_id,
|
528
|
+
report_download(scan_id, export_details['file'])
|
551
529
|
end
|
552
530
|
|
553
531
|
# use download scan API call to save a report as file
|
554
|
-
def report_download_file(scan_id, format,
|
532
|
+
def report_download_file(scan_id, format, output_file_name)
|
555
533
|
report_content = report_download_quick(scan_id, format)
|
556
|
-
File.open(
|
534
|
+
File.open(output_file_name, 'w') do |f|
|
557
535
|
f.write(report_content)
|
558
536
|
end
|
559
537
|
end
|
@@ -565,12 +543,12 @@ module TenableRuby
|
|
565
543
|
#
|
566
544
|
# returns: HTTP result object
|
567
545
|
def http_put(opts = {})
|
568
|
-
|
569
|
-
if
|
546
|
+
response = http_put_low(opts)
|
547
|
+
if response.is_a?(Hash) and response.has_key?('error') and response['error'] == 'Invalid Credentials'
|
570
548
|
authenticate
|
571
549
|
http_put_low(opts)
|
572
550
|
else
|
573
|
-
|
551
|
+
response
|
574
552
|
end
|
575
553
|
end
|
576
554
|
|
@@ -578,27 +556,27 @@ module TenableRuby
|
|
578
556
|
uri = opts[:uri]
|
579
557
|
data = opts[:data]
|
580
558
|
fields = opts[:fields] || {}
|
581
|
-
|
559
|
+
response = nil
|
582
560
|
tries = @httpretry
|
583
561
|
|
584
|
-
|
585
|
-
|
562
|
+
request = Net::HTTP::Put.new(uri)
|
563
|
+
request.set_form_data(data) unless (data.nil? || data.empty?)
|
586
564
|
fields.each_pair do |name, value|
|
587
|
-
|
565
|
+
request.add_field(name, value)
|
588
566
|
end
|
589
567
|
|
590
568
|
begin
|
591
569
|
tries -= 1
|
592
|
-
|
570
|
+
response = @connection.request(request)
|
593
571
|
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
|
594
572
|
if tries > 0
|
595
573
|
sleep @httpsleep
|
596
574
|
retry
|
597
575
|
else
|
598
|
-
return
|
576
|
+
return response
|
599
577
|
end
|
600
578
|
rescue URI::InvalidURIError
|
601
|
-
return
|
579
|
+
return response
|
602
580
|
end
|
603
581
|
end
|
604
582
|
|
@@ -606,40 +584,40 @@ module TenableRuby
|
|
606
584
|
#
|
607
585
|
# returns: HTTP result object
|
608
586
|
def http_delete(opts = {})
|
609
|
-
|
610
|
-
if
|
587
|
+
response = http_delete_low(opts)
|
588
|
+
if response.is_a?(Hash) and response.has_key?('error') and response['error'] == 'Invalid Credentials'
|
611
589
|
authenticate
|
612
590
|
http_delete_low(opts)
|
613
|
-
|
591
|
+
response
|
614
592
|
else
|
615
|
-
|
593
|
+
response
|
616
594
|
end
|
617
595
|
end
|
618
596
|
|
619
597
|
def http_delete_low(opts = {})
|
620
598
|
uri = opts[:uri]
|
621
599
|
fields = opts[:fields] || {}
|
622
|
-
|
600
|
+
response = nil
|
623
601
|
tries = @httpretry
|
624
602
|
|
625
|
-
|
603
|
+
request = Net::HTTP::Delete.new(uri)
|
626
604
|
|
627
605
|
fields.each_pair do |name, value|
|
628
|
-
|
606
|
+
request.add_field(name, value)
|
629
607
|
end
|
630
608
|
|
631
609
|
begin
|
632
610
|
tries -= 1
|
633
|
-
|
611
|
+
response = @connection.request(request)
|
634
612
|
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
|
635
613
|
if tries > 0
|
636
614
|
sleep @httpsleep
|
637
615
|
retry
|
638
616
|
else
|
639
|
-
return
|
617
|
+
return response
|
640
618
|
end
|
641
619
|
rescue URI::InvalidURIError
|
642
|
-
return
|
620
|
+
return response
|
643
621
|
end
|
644
622
|
end
|
645
623
|
|
@@ -648,17 +626,17 @@ module TenableRuby
|
|
648
626
|
# returns: JSON parsed object (if JSON parseable)
|
649
627
|
def http_get(opts = {})
|
650
628
|
raw_content = opts[:raw_content] || false
|
651
|
-
|
629
|
+
response = http_get_low(opts)
|
652
630
|
if !raw_content
|
653
|
-
if
|
631
|
+
if response.is_a?(Hash) and response.has_key?('error') and response['error'] == 'Invalid Credentials'
|
654
632
|
authenticate
|
655
|
-
|
656
|
-
return
|
633
|
+
response = http_get_low(opts)
|
634
|
+
return response
|
657
635
|
else
|
658
|
-
return
|
636
|
+
return response
|
659
637
|
end
|
660
638
|
else
|
661
|
-
|
639
|
+
response
|
662
640
|
end
|
663
641
|
end
|
664
642
|
|
@@ -669,14 +647,14 @@ module TenableRuby
|
|
669
647
|
json = {}
|
670
648
|
tries = @httpretry
|
671
649
|
|
672
|
-
|
650
|
+
request = Net::HTTP::Get.new(uri)
|
673
651
|
fields.each_pair do |name, value|
|
674
|
-
|
652
|
+
request.add_field(name, value)
|
675
653
|
end
|
676
654
|
|
677
655
|
begin
|
678
656
|
tries -= 1
|
679
|
-
|
657
|
+
response = @connection.request(request)
|
680
658
|
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
|
681
659
|
if tries > 0
|
682
660
|
sleep @httpsleep
|
@@ -688,9 +666,9 @@ module TenableRuby
|
|
688
666
|
return json
|
689
667
|
end
|
690
668
|
if !raw_content
|
691
|
-
parse_json(
|
669
|
+
parse_json(response.body)
|
692
670
|
else
|
693
|
-
|
671
|
+
response.body
|
694
672
|
end
|
695
673
|
end
|
696
674
|
|
@@ -703,15 +681,15 @@ module TenableRuby
|
|
703
681
|
authzmethod = opts[:authenticationmethod]
|
704
682
|
opts.delete(:authenticationmethod)
|
705
683
|
end
|
706
|
-
|
707
|
-
if
|
684
|
+
response = http_post_low(opts)
|
685
|
+
if response.is_a?(Hash) and response.has_key?('error') and response['error'] == 'Invalid Credentials'
|
708
686
|
unless authzmethod
|
709
687
|
authenticate
|
710
|
-
|
711
|
-
return
|
688
|
+
response = http_post_low(opts)
|
689
|
+
return response
|
712
690
|
end
|
713
691
|
else
|
714
|
-
|
692
|
+
response
|
715
693
|
end
|
716
694
|
end
|
717
695
|
|
@@ -724,17 +702,17 @@ module TenableRuby
|
|
724
702
|
json = {}
|
725
703
|
tries = @httpretry
|
726
704
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
705
|
+
request = Net::HTTP::Post.new(uri)
|
706
|
+
request.set_form_data(data) unless (data.nil? || data.empty?)
|
707
|
+
request.body = body unless (body.nil? || body.empty?)
|
708
|
+
request['Content-Type'] = ctype unless (ctype.nil? || ctype.empty?)
|
731
709
|
fields.each_pair do |name, value|
|
732
|
-
|
710
|
+
request.add_field(name, value)
|
733
711
|
end
|
734
712
|
|
735
713
|
begin
|
736
714
|
tries -= 1
|
737
|
-
|
715
|
+
response = @connection.request(request)
|
738
716
|
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
|
739
717
|
if tries > 0
|
740
718
|
sleep @httpsleep
|
@@ -746,21 +724,21 @@ module TenableRuby
|
|
746
724
|
return json
|
747
725
|
end
|
748
726
|
|
749
|
-
parse_json(
|
727
|
+
parse_json(response.body)
|
750
728
|
end
|
751
729
|
|
752
730
|
# Perform JSON parsing of body
|
753
731
|
#
|
754
732
|
# returns: JSON parsed object (if JSON parseable)
|
755
733
|
def parse_json(body)
|
756
|
-
|
734
|
+
parsed_json = {}
|
757
735
|
|
758
736
|
begin
|
759
|
-
|
737
|
+
parsed_json = JSON.parse(body)
|
760
738
|
rescue JSON::ParserError
|
761
739
|
end
|
762
740
|
|
763
|
-
|
741
|
+
parsed_json
|
764
742
|
end
|
765
743
|
|
766
744
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tenable-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vlatko Kosturjak
|
@@ -9,12 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description:
|
15
|
-
|
16
|
-
|
17
|
-
for interacting with Nessus https://github.com/kost/nessus_rest-ruby by https://github.com/kost. "
|
14
|
+
description: |-
|
15
|
+
Ruby library for communicating with the tenable.io API.
|
16
|
+
You can start, stop, pause and resume scan. Get status of scans, download reports, create policies, etc.
|
18
17
|
email: patrick.craston@intruder.io
|
19
18
|
executables: []
|
20
19
|
extensions: []
|
@@ -46,5 +45,5 @@ rubyforge_project:
|
|
46
45
|
rubygems_version: 2.5.2.3
|
47
46
|
signing_key:
|
48
47
|
specification_version: 4
|
49
|
-
summary:
|
48
|
+
summary: Ruby library for communicating with the tenable.io API
|
50
49
|
test_files: []
|