tenable-ruby 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|